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 includes definitions for heap. 32 * 33 */ 34 35 #ifndef OT_HEAP_HPP_ 36 #define OT_HEAP_HPP_ 37 38 #include "openthread-core-config.h" 39 40 #if !OPENTHREAD_CONFIG_HEAP_EXTERNAL_ENABLE 41 42 #include <stddef.h> 43 #include <stdint.h> 44 45 #include "common/non_copyable.hpp" 46 47 namespace ot { 48 namespace Utils { 49 50 /** 51 * This class represents a memory block. 52 * 53 * A block is of the structure as below. 54 * 55 * +------------------------------+ 56 * | mSize | mMemory | mNext | 57 * |---------|----------| --------| 58 * | 2 bytes | n bytes | 2 bytes | 59 * +------------------------------+ 60 * 61 * Since block metadata is of 4-byte size, mSize and mNext are separated at the beginning 62 * and end of the block to make sure the mMemory is aligned with long. 63 * 64 */ 65 class Block 66 { 67 friend class Heap; 68 69 public: 70 /** 71 * This method returns the size of this block. 72 * 73 * @returns Size of this block. 74 * 75 */ GetSize(void) const76 uint16_t GetSize(void) const { return mSize; } 77 78 /** 79 * This method updates the size of this block. 80 * 81 * @param[in] aSize Size of this block in bytes. 82 * 83 */ SetSize(uint16_t aSize)84 void SetSize(uint16_t aSize) { mSize = aSize; } 85 86 /** 87 * This method returns the offset of the free block after this block. 88 * 89 * @note This offset is relative to the start of the heap. 90 * 91 * @returns Offset of the next free block in bytes. 92 * 93 * @retval 0 This block is not free. 94 * 95 */ GetNext(void) const96 uint16_t GetNext(void) const 97 { 98 return *reinterpret_cast<const uint16_t *>( 99 reinterpret_cast<const void *>(reinterpret_cast<const uint8_t *>(this) + sizeof(mSize) + mSize)); 100 } 101 102 /** 103 * This method updates the offset of the free block after this block. 104 * 105 * @note This offset @p aNext must be relative to the start of the heap. 106 * 107 * @param[in] aNext Offset of the next free block in bytes. 108 * 109 */ SetNext(uint16_t aNext)110 void SetNext(uint16_t aNext) 111 { 112 *reinterpret_cast<uint16_t *>( 113 reinterpret_cast<void *>(reinterpret_cast<uint8_t *>(this) + sizeof(mSize) + mSize)) = aNext; 114 } 115 116 /** 117 * This method returns the pointer to the start of the memory for user. 118 * 119 * @retval Pointer to the user memory. The pointer address is aligned to sizeof(long). 120 * 121 */ GetPointer(void)122 void *GetPointer(void) { return &mMemory; } 123 124 /** 125 * This method returns the offset of the free block after the left neighbor block. 126 * 127 * @returns Offset in bytes. 128 * 129 */ GetLeftNext(void) const130 uint16_t GetLeftNext(void) const { return *(&mSize - 1); } 131 132 /** 133 * This method returns whether the left neighbor block is a free block. 134 * 135 * @retval true The left neighbor block is free. 136 * @retval false The left neighbor block is not free. 137 * 138 */ IsLeftFree(void) const139 bool IsLeftFree(void) const { return GetLeftNext() != 0; } 140 141 /** 142 * This method returns whether the current block is a free block. 143 * 144 * @retval true The block is free. 145 * @retval false The block is not free. 146 * 147 */ IsFree(void) const148 bool IsFree(void) const { return mSize != kGuardBlockSize && GetNext() != 0; } 149 150 private: 151 static constexpr uint16_t kGuardBlockSize = 0xffff; // Size value of the guard block. 152 153 uint16_t mSize; // Number of bytes in mMemory. 154 155 // Memory for user, with size of `mNext` to ensure size of this 156 // structure is equal to size of block metadata, i.e., 157 // sizeof(mSize) + sizeof(mNext). 158 uint8_t mMemory[sizeof(uint16_t)]; 159 }; 160 161 /** 162 * This class defines functionality to manipulate heap. 163 * 164 * This implementation is currently for mbedTLS. 165 * 166 * The memory is divided into blocks. The whole picture is as follows: 167 * 168 * +--------------------------------------------------------------------------+ 169 * | unused | super | block 1 | block 2 | ... | block n | guard | 170 * +----------------+------------+---------+---------+-----+---------+--------+ 171 * | kAlignSize - 2 | kAlignSize | 4 + s1 | 4 + s2 | ... | 4 + s4 | 2 | 172 * +--------------------------------------------------------------------------+ 173 * 174 */ 175 class Heap : private NonCopyable 176 { 177 public: 178 /** 179 * This constructor initializes a memory heap. 180 * 181 */ 182 Heap(void); 183 184 /** 185 * This method allocates at least @p aCount * @aSize bytes memory and initialize to zero. 186 * 187 * @param[in] aCount Number of allocate units. 188 * @param[in] aSize Unit size in bytes. 189 * 190 * @returns A pointer to the allocated memory. 191 * 192 * @retval nullptr Indicates not enough memory. 193 * 194 */ 195 void *CAlloc(size_t aCount, size_t aSize); 196 197 /** 198 * This method free memory pointed by @p aPointer. 199 * 200 * @param[in] aPointer A pointer to the memory to free. 201 * 202 */ 203 void Free(void *aPointer); 204 205 /** 206 * This method returns whether the heap is clean. 207 * 208 */ IsClean(void) const209 bool IsClean(void) const 210 { 211 Heap & self = *const_cast<Heap *>(this); 212 const Block &super = self.BlockSuper(); 213 const Block &first = self.BlockRight(super); 214 return super.GetNext() == self.BlockOffset(first) && first.GetSize() == kFirstBlockSize; 215 } 216 217 /** 218 * This method returns the capacity of this heap. 219 * 220 */ GetCapacity(void) const221 size_t GetCapacity(void) const { return kFirstBlockSize; } 222 223 /** 224 * This method returns free space of this heap. 225 */ GetFreeSize(void) const226 size_t GetFreeSize(void) const { return mMemory.mFreeSize; } 227 228 private: 229 #if OPENTHREAD_CONFIG_DTLS_ENABLE 230 static constexpr uint16_t kMemorySize = OPENTHREAD_CONFIG_HEAP_INTERNAL_SIZE; 231 #else 232 static constexpr uint16_t kMemorySize = OPENTHREAD_CONFIG_HEAP_INTERNAL_SIZE_NO_DTLS; 233 #endif 234 static constexpr uint16_t kAlignSize = sizeof(void *); 235 static constexpr uint16_t kBlockRemainderSize = kAlignSize - sizeof(uint16_t) * 2; 236 static constexpr uint16_t kSuperBlockSize = kAlignSize - sizeof(Block); 237 static constexpr uint16_t kFirstBlockSize = kMemorySize - kAlignSize * 3 + kBlockRemainderSize; 238 static constexpr uint16_t kSuperBlockOffset = kAlignSize - sizeof(uint16_t); 239 static constexpr uint16_t kFirstBlockOffset = kAlignSize * 2 - sizeof(uint16_t); 240 static constexpr uint16_t kGuardBlockOffset = kMemorySize - sizeof(uint16_t); 241 242 static_assert(kMemorySize % kAlignSize == 0, "The heap memory size is not aligned to kAlignSize!"); 243 244 /** 245 * This method returns the block at offset @p aOffset. 246 * 247 * @param[in] aOffset Offset in bytes. 248 * 249 * @returns A reference to the block. 250 * 251 */ BlockAt(uint16_t aOffset)252 Block &BlockAt(uint16_t aOffset) { return *reinterpret_cast<Block *>(&mMemory.m16[aOffset / 2]); } 253 254 /** 255 * This method returns the block of @p aPointer. 256 * 257 * @param[in] aPointer The pointer returned by CAlloc(). 258 * 259 * @returns A reference to the block. 260 * 261 */ BlockOf(void * aPointer)262 Block &BlockOf(void *aPointer) 263 { 264 uint16_t offset = static_cast<uint16_t>(reinterpret_cast<uint8_t *>(aPointer) - mMemory.m8); 265 offset -= sizeof(uint16_t); 266 return BlockAt(offset); 267 } 268 269 /** 270 * This method returns the super block. 271 * 272 * @returns Reference to the super block. 273 * 274 */ BlockSuper(void)275 Block &BlockSuper(void) { return BlockAt(kSuperBlockOffset); } 276 277 /** 278 * This method returns the free block after @p aBlock in the free block list. 279 * 280 * @param[in] aBlock A reference to the block. 281 * 282 * @returns Reference to the free block after this block. 283 * 284 */ BlockNext(const Block & aBlock)285 Block &BlockNext(const Block &aBlock) { return BlockAt(aBlock.GetNext()); } 286 287 /** 288 * This method returns the block on the right side of @p aBlock. 289 * 290 * @param[in] aBlock A reference to the block. 291 * 292 * @returns Reference to the block on the right side. 293 * 294 */ BlockRight(const Block & aBlock)295 Block &BlockRight(const Block &aBlock) { return BlockAt(BlockOffset(aBlock) + sizeof(Block) + aBlock.GetSize()); } 296 297 /** 298 * This method returns the free block before @p aBlock in the free block list. 299 * 300 * @returns Reference to the free block before this block. 301 * 302 */ 303 Block &BlockPrev(const Block &aBlock); 304 305 /** 306 * This method returns whether the block on the left side of @p aBlock is free. 307 * 308 * @param[in] aBlock A reference to the block. 309 * 310 */ IsLeftFree(const Block & aBlock)311 bool IsLeftFree(const Block &aBlock) { return (BlockOffset(aBlock) != kFirstBlockOffset && aBlock.IsLeftFree()); } 312 313 /** 314 * This method returns the offset of @p aBlock. 315 * 316 * @param[in] aBlock A reference to the block. 317 * 318 * @returns Offset in bytes of @p aBlock. 319 * 320 */ BlockOffset(const Block & aBlock)321 uint16_t BlockOffset(const Block &aBlock) 322 { 323 return static_cast<uint16_t>(reinterpret_cast<const uint8_t *>(&aBlock) - mMemory.m8); 324 } 325 326 /** 327 * This method inserts @p aBlock into the free block list. 328 * 329 * The free block list is single linked and is sorted by size from minimal to maximum. 330 * 331 * @param[in] aPrev A reference to the block after which to place @p aBlock. 332 * @param[in] aBlock A reference to the block. 333 * 334 */ 335 void BlockInsert(Block &aPrev, Block &aBlock); 336 337 union 338 { 339 uint16_t mFreeSize; 340 // Make sure memory is long aligned. 341 long mLong[kMemorySize / sizeof(long)]; 342 uint8_t m8[kMemorySize]; 343 uint16_t m16[kMemorySize / sizeof(uint16_t)]; 344 } mMemory; 345 }; 346 347 } // namespace Utils 348 } // namespace ot 349 350 #endif // !OPENTHREAD_CONFIG_HEAP_EXTERNAL_ENABLE 351 352 #endif // OT_HEAP_HPP_ 353