1 /*
2  *  Copyright (c) 2017, The OpenThread Authors.
3  *  All rights reserved.
4  *
5  *  Redistribution and use in source and binary forms, with or without
6  *  modification, are permitted provided that the following conditions are met:
7  *  1. Redistributions of source code must retain the above copyright
8  *     notice, this list of conditions and the following disclaimer.
9  *  2. Redistributions in binary form must reproduce the above copyright
10  *     notice, this list of conditions and the following disclaimer in the
11  *     documentation and/or other materials provided with the distribution.
12  *  3. Neither the name of the copyright holder nor the
13  *     names of its contributors may be used to endorse or promote products
14  *     derived from this software without specific prior written permission.
15  *
16  *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17  *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
20  *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21  *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22  *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23  *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24  *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25  *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26  *  POSSIBILITY OF SUCH DAMAGE.
27  */
28 
29 /**
30  * @file
31  *   This file implements heap.
32  *
33  */
34 
35 #include "heap.hpp"
36 
37 #if !OPENTHREAD_CONFIG_HEAP_EXTERNAL_ENABLE
38 
39 #include <string.h>
40 
41 #include "common/code_utils.hpp"
42 #include "common/debug.hpp"
43 
44 namespace ot {
45 namespace Utils {
46 
Heap(void)47 Heap::Heap(void)
48 {
49     Block &super = BlockAt(kSuperBlockOffset);
50     super.SetSize(kSuperBlockSize);
51 
52     Block &first = BlockRight(super);
53     first.SetSize(kFirstBlockSize);
54 
55     Block &guard = BlockRight(first);
56     guard.SetSize(Block::kGuardBlockSize);
57 
58     super.SetNext(BlockOffset(first));
59     first.SetNext(BlockOffset(guard));
60 
61     mMemory.mFreeSize = kFirstBlockSize;
62 }
63 
CAlloc(size_t aCount,size_t aSize)64 void *Heap::CAlloc(size_t aCount, size_t aSize)
65 {
66     void    *ret  = nullptr;
67     Block   *prev = nullptr;
68     Block   *curr = nullptr;
69     uint16_t size = static_cast<uint16_t>(aCount * aSize);
70 
71     VerifyOrExit(size);
72 
73     size += kAlignSize - 1 - kBlockRemainderSize;
74     size &= ~(kAlignSize - 1);
75     size += kBlockRemainderSize;
76 
77     prev = &BlockSuper();
78     curr = &BlockNext(*prev);
79 
80     while (curr->GetSize() < size)
81     {
82         prev = curr;
83         curr = &BlockNext(*curr);
84     }
85 
86     VerifyOrExit(curr->IsFree());
87 
88     prev->SetNext(curr->GetNext());
89 
90     if (curr->GetSize() > size + sizeof(Block))
91     {
92         const uint16_t newBlockSize = curr->GetSize() - size - sizeof(Block);
93         curr->SetSize(size);
94 
95         Block &newBlock = BlockRight(*curr);
96         newBlock.SetSize(newBlockSize);
97         newBlock.SetNext(0);
98 
99         if (prev->GetSize() < newBlockSize)
100         {
101             BlockInsert(*prev, newBlock);
102         }
103         else
104         {
105             BlockInsert(BlockSuper(), newBlock);
106         }
107 
108         mMemory.mFreeSize -= sizeof(Block);
109     }
110 
111     mMemory.mFreeSize -= curr->GetSize();
112 
113     curr->SetNext(0);
114 
115     memset(curr->GetPointer(), 0, size);
116     ret = curr->GetPointer();
117 
118 exit:
119     return ret;
120 }
121 
BlockInsert(Block & aPrev,Block & aBlock)122 void Heap::BlockInsert(Block &aPrev, Block &aBlock)
123 {
124     Block *prev = &aPrev;
125 
126     for (Block *block = &BlockNext(*prev); block->GetSize() < aBlock.GetSize(); block = &BlockNext(*block))
127     {
128         prev = block;
129     }
130 
131     aBlock.SetNext(prev->GetNext());
132     prev->SetNext(BlockOffset(aBlock));
133 }
134 
BlockPrev(const Block & aBlock)135 Block &Heap::BlockPrev(const Block &aBlock)
136 {
137     Block *prev = &BlockSuper();
138 
139     while (prev->GetNext() != BlockOffset(aBlock))
140     {
141         prev = &BlockNext(*prev);
142     }
143 
144     return *prev;
145 }
146 
Free(void * aPointer)147 void Heap::Free(void *aPointer)
148 {
149     if (aPointer == nullptr)
150     {
151         return;
152     }
153 
154     Block &block = BlockOf(aPointer);
155     Block &right = BlockRight(block);
156 
157     mMemory.mFreeSize += block.GetSize();
158 
159     if (IsLeftFree(block))
160     {
161         Block *prev = &BlockSuper();
162         Block *left = &BlockNext(*prev);
163 
164         mMemory.mFreeSize += sizeof(Block);
165 
166         for (const uint16_t offset = block.GetLeftNext(); left->GetNext() != offset; left = &BlockNext(*left))
167         {
168             prev = left;
169         }
170 
171         // Remove left from free list.
172         prev->SetNext(left->GetNext());
173         left->SetNext(0);
174 
175         if (right.IsFree())
176         {
177             mMemory.mFreeSize += sizeof(Block);
178 
179             if (right.GetSize() > left->GetSize())
180             {
181                 for (const uint16_t offset = BlockOffset(right); prev->GetNext() != offset; prev = &BlockNext(*prev))
182                     ;
183             }
184             else
185             {
186                 prev = &BlockPrev(right);
187             }
188 
189             // Remove right from free list.
190             prev->SetNext(right.GetNext());
191             right.SetNext(0);
192 
193             // Add size of right.
194             left->SetSize(left->GetSize() + right.GetSize() + sizeof(Block));
195         }
196 
197         // Add size of current block.
198         left->SetSize(left->GetSize() + block.GetSize() + sizeof(Block));
199 
200         BlockInsert(*prev, *left);
201     }
202     else
203     {
204         if (right.IsFree())
205         {
206             Block &prev = BlockPrev(right);
207             prev.SetNext(right.GetNext());
208             block.SetSize(block.GetSize() + right.GetSize() + sizeof(Block));
209             BlockInsert(prev, block);
210 
211             mMemory.mFreeSize += sizeof(Block);
212         }
213         else
214         {
215             BlockInsert(BlockSuper(), block);
216         }
217     }
218 }
219 
220 } // namespace Utils
221 } // namespace ot
222 
223 #endif // !OPENTHREAD_CONFIG_HEAP_EXTERNAL_ENABLE
224