1 //===- Allocators.h ---------------------------------------------------------===// 2 // 3 // The MCLinker Project 4 // 5 // This file is distributed under the University of Illinois Open Source 6 // License. See LICENSE.TXT for details. 7 // 8 //===----------------------------------------------------------------------===// 9 #ifndef MCLD_SUPPORT_ALLOCATORS_H 10 #define MCLD_SUPPORT_ALLOCATORS_H 11 #ifdef ENABLE_UNITTEST 12 #include <gtest.h> 13 #endif 14 #include <mcld/ADT/Uncopyable.h> 15 #include <mcld/ADT/TypeTraits.h> 16 17 #include <cstddef> 18 #include <cstdlib> 19 20 namespace mcld { 21 22 /** \class Chunk 23 * \brief Chunk is the basic unit of the storage of the LinearAllocator 24 * 25 * @see LinearAllocator 26 */ 27 template<typename DataType, size_t ChunkSize> 28 class Chunk 29 { 30 public: 31 typedef DataType value_type; 32 public: 33 Chunk() 34 : next(0), bound(0) 35 { } 36 37 static size_t size() { return ChunkSize; } 38 39 static void construct(value_type* pPtr) 40 { new (pPtr) value_type(); } 41 42 static void construct(value_type* pPtr, const value_type& pValue) 43 { new (pPtr) value_type(pValue); } 44 45 static void destroy(value_type* pPtr) 46 { } 47 48 public: 49 Chunk* next; 50 size_t bound; 51 DataType data[ChunkSize]; 52 }; 53 54 template<typename DataType> 55 class Chunk<DataType, 0> 56 { 57 public: 58 typedef DataType value_type; 59 60 public: 61 Chunk() 62 : next(0), bound(0) { 63 if (0 != m_Size) 64 data = (DataType*)malloc(sizeof(DataType)*m_Size); 65 else 66 data = 0; 67 } 68 69 ~Chunk() { 70 if (data) 71 free(data); 72 } 73 74 static size_t size() { return m_Size; } 75 76 static void setSize(size_t pSize) { m_Size = pSize; } 77 78 static void construct(value_type* pPtr) 79 { new (pPtr) value_type(); } 80 81 static void construct(value_type* pPtr, const value_type& pValue) 82 { new (pPtr) value_type(pValue); } 83 84 static void destroy(value_type* pPtr) 85 { pPtr->~value_type(); } 86 87 public: 88 Chunk* next; 89 size_t bound; 90 DataType *data; 91 static size_t m_Size; 92 }; 93 94 template<typename DataType> 95 size_t Chunk<DataType, 0>::m_Size = 0; 96 97 template<typename ChunkType> 98 class LinearAllocatorBase : private Uncopyable 99 { 100 public: 101 typedef ChunkType chunk_type; 102 typedef typename ChunkType::value_type value_type; 103 typedef typename ChunkType::value_type* pointer; 104 typedef typename ChunkType::value_type& reference; 105 typedef const typename ChunkType::value_type* const_pointer; 106 typedef const typename ChunkType::value_type& const_reference; 107 typedef size_t size_type; 108 typedef ptrdiff_t difference_type; 109 typedef unsigned char byte_type; 110 111 protected: 112 LinearAllocatorBase() 113 : m_pRoot(0), 114 m_pCurrent(0), 115 m_AllocatedNum(0) { 116 } 117 118 // LinearAllocatorBase does NOT mean to destroy the allocated memory. 119 // If you want a memory allocator to release memory at destruction, please 120 // use GCFactory series. 121 virtual ~LinearAllocatorBase() 122 { } 123 124 public: 125 pointer address(reference X) const 126 { return &X; } 127 128 const_pointer address(const_reference X) const 129 { return &X; } 130 131 /// standard construct - constructing an object on the location pointed by 132 // pPtr, and using its copy constructor to initialized its value to pValue. 133 // 134 // @param pPtr the address where the object to be constructed 135 // @param pValue the value to be constructed 136 void construct(pointer pPtr, const_reference pValue) 137 { chunk_type::construct(pPtr, pValue); } 138 139 /// default construct - constructing an object on the location pointed by 140 // pPtr, and using its default constructor to initialized its value to 141 // pValue. 142 // 143 // @param pPtr the address where the object to be constructed 144 void construct(pointer pPtr) 145 { chunk_type::construct(pPtr); } 146 147 /// standard destroy - destroy data on arbitrary address 148 // @para pPtr the address where the data to be destruected. 149 void destroy(pointer pPtr) 150 { chunk_type::destroy(pPtr); } 151 152 /// allocate - allocate N data in order. 153 // - Disallow to allocate a chunk whose size is bigger than a chunk. 154 // 155 // @param N the number of allocated data. 156 // @return the start address of the allocated memory 157 pointer allocate(size_type N) { 158 if (0 == N || N > chunk_type::size()) 159 return 0; 160 161 if (empty()) 162 initialize(); 163 164 size_type rest_num_elem = chunk_type::size() - m_pCurrent->bound; 165 pointer result = 0; 166 if (N > rest_num_elem) 167 getNewChunk(); 168 result = m_pCurrent->data + m_pCurrent->bound; 169 m_pCurrent->bound += N; 170 return result; 171 } 172 173 /// allocate - clone function of allocating one datum. 174 pointer allocate() { 175 if (empty()) 176 initialize(); 177 178 pointer result = 0; 179 if (chunk_type::size() == m_pCurrent->bound) 180 getNewChunk(); 181 result = m_pCurrent->data + m_pCurrent->bound; 182 ++m_pCurrent->bound; 183 return result; 184 } 185 186 /// deallocate - deallocate N data from the pPtr 187 // - if we can simply release some memory, then do it. Otherwise, do 188 // nothing. 189 void deallocate(pointer &pPtr, size_type N) { 190 if (0 == N || 191 N > chunk_type::size() || 192 0 == m_pCurrent->bound || 193 N >= m_pCurrent->bound) 194 return; 195 if (!isAvailable(pPtr)) 196 return; 197 m_pCurrent->bound -= N; 198 pPtr = 0; 199 } 200 201 /// deallocate - clone function of deallocating one datum 202 void deallocate(pointer &pPtr) { 203 if (0 == m_pCurrent->bound) 204 return; 205 if (!isAvailable(pPtr)) 206 return; 207 m_pCurrent->bound -= 1; 208 pPtr = 0; 209 } 210 211 /// isIn - whether the pPtr is in the current chunk? 212 bool isIn(pointer pPtr) const { 213 if (pPtr >= &(m_pCurrent->data[0]) && 214 pPtr <= &(m_pCurrent->data[chunk_type::size()-1])) 215 return true; 216 return false; 217 } 218 219 /// isIn - whether the pPtr is allocated, and can be constructed. 220 bool isAvailable(pointer pPtr) const { 221 if (pPtr >= &(m_pCurrent->data[m_pCurrent->bound]) && 222 pPtr <= &(m_pCurrent->data[chunk_type::size()-1])) 223 return true; 224 return false; 225 } 226 227 void reset() { 228 m_pRoot = 0; 229 m_pCurrent = 0; 230 m_AllocatedNum = 0; 231 } 232 233 /// clear - clear all chunks 234 void clear() { 235 chunk_type *cur = m_pRoot, *prev; 236 while (0 != cur) { 237 prev = cur; 238 cur = cur->next; 239 for (unsigned int idx = 0; idx != prev->bound; ++idx) 240 destroy(prev->data + idx); 241 delete prev; 242 } 243 reset(); 244 } 245 246 // ----- observers ----- // 247 bool empty() const { 248 return (0 == m_pRoot); 249 } 250 251 size_type max_size() const 252 { return m_AllocatedNum; } 253 254 protected: 255 inline void initialize() { 256 m_pRoot = new chunk_type(); 257 m_pCurrent = m_pRoot; 258 m_AllocatedNum += chunk_type::size(); 259 } 260 261 inline chunk_type *getNewChunk() { 262 chunk_type *result = new chunk_type(); 263 m_pCurrent->next = result; 264 m_pCurrent = result; 265 m_AllocatedNum += chunk_type::size(); 266 return result; 267 } 268 269 protected: 270 chunk_type *m_pRoot; 271 chunk_type *m_pCurrent; 272 size_type m_AllocatedNum; 273 }; 274 275 /** \class LinearAllocator 276 * \brief LinearAllocator is another bump pointer allocator which should be 277 * limited in use of two-phase memory allocation. 278 * 279 * Two-phase memory allocation clear separates the use of memory into 'claim' 280 * and 'release' phases. There are no interleaving allocation and 281 * deallocation. Interleaving 'allocate' and 'deallocate' increases the size 282 * of allocated memory, and causes bad locality. 283 * 284 * The underlying concept of LinearAllocator is a memory pool. LinearAllocator 285 * is a simple implementation of boost::pool's ordered_malloc() and 286 * ordered_free(). 287 * 288 * template argument DataType is the DataType to be allocated 289 * template argument ChunkSize is the number of bytes of a chunk 290 */ 291 template<typename DataType, size_t ChunkSize> 292 class LinearAllocator : public LinearAllocatorBase<Chunk<DataType, ChunkSize> > 293 { 294 public: 295 template<typename NewDataType> 296 struct rebind { 297 typedef LinearAllocator<NewDataType, ChunkSize> other; 298 }; 299 300 public: 301 LinearAllocator() 302 : LinearAllocatorBase<Chunk<DataType, ChunkSize> >() { 303 } 304 305 virtual ~LinearAllocator() 306 { } 307 }; 308 309 template<typename DataType> 310 class LinearAllocator<DataType, 0> : public LinearAllocatorBase<Chunk<DataType, 0> > 311 { 312 public: 313 template<typename NewDataType> 314 struct rebind { 315 typedef LinearAllocator<NewDataType, 0> other; 316 }; 317 318 public: 319 explicit LinearAllocator(size_t pNum) 320 : LinearAllocatorBase<Chunk<DataType, 0> >() { 321 Chunk<DataType, 0>::setSize(pNum); 322 } 323 324 virtual ~LinearAllocator() 325 { } 326 }; 327 328 template<typename DataType> 329 class MallocAllocator 330 { 331 public: 332 typedef size_t size_type; 333 typedef ptrdiff_t difference_type; 334 typedef DataType* pointer; 335 typedef const DataType* const_pointer; 336 typedef DataType& reference; 337 typedef const DataType& const_reference; 338 typedef DataType value_type; 339 340 template<typename OtherDataType> 341 struct rebind 342 { 343 typedef MallocAllocator<OtherDataType> other; 344 }; 345 346 public: 347 MallocAllocator() throw() 348 { } 349 350 MallocAllocator(const MallocAllocator&) throw() 351 { } 352 353 ~MallocAllocator() throw() 354 { } 355 356 pointer address(reference X) const 357 { return &X; } 358 359 const_pointer address(const_reference X) const 360 { return &X; } 361 362 pointer allocate(size_type pNumOfElements, const void* = 0) 363 { 364 return static_cast<DataType*>( 365 std::malloc(pNumOfElements*sizeof(DataType))); 366 } 367 368 void deallocate(pointer pObject, size_type) 369 { std::free(static_cast<void*>(pObject)); } 370 371 size_type max_size() const throw() 372 { return size_t(-1) / sizeof(DataType); } 373 374 void construct(pointer pObject, const DataType& pValue) 375 { ::new((void *)pObject) value_type(pValue); } 376 377 void destroy(pointer pObject) 378 { pObject->~DataType(); } 379 380 }; 381 382 template<> 383 class MallocAllocator<void> 384 { 385 public: 386 typedef size_t size_type; 387 typedef ptrdiff_t difference_type; 388 typedef void* pointer; 389 typedef const void* const_pointer; 390 typedef void* reference; 391 typedef const void* const_reference; 392 typedef void* value_type; 393 394 template<typename OtherDataType> 395 struct rebind 396 { 397 typedef MallocAllocator<OtherDataType> other; 398 }; 399 400 public: 401 MallocAllocator() throw() 402 { } 403 404 MallocAllocator(const MallocAllocator&) throw() 405 { } 406 407 ~MallocAllocator() throw() 408 { } 409 410 size_type max_size() const throw() 411 { return size_t(-1) / sizeof(void*); } 412 413 pointer address(reference X) const 414 { return X; } 415 416 const_pointer address(const_reference X) const 417 { return X; } 418 419 template<typename DataType> 420 DataType* allocate(size_type pNumOfElements, const void* = 0) { 421 return static_cast<DataType*>( 422 std::malloc(pNumOfElements*sizeof(DataType))); 423 } 424 425 pointer allocate(size_type pNumOfElements, const void* = 0) { 426 return std::malloc(pNumOfElements); 427 } 428 429 template<typename DataType> 430 void deallocate(DataType* pObject, size_type) 431 { std::free(static_cast<void*>(pObject)); } 432 433 void deallocate(pointer pObject, size_type) 434 { std::free(pObject); } 435 436 template<typename DataType> 437 void construct(DataType* pObject, const DataType& pValue) 438 { /* do nothing */ } 439 440 void construct(pointer pObject, const_reference pValue) 441 { /* do nothing */ } 442 443 template<typename DataType> 444 void destroy(DataType* pObject) 445 { /* do nothing */ } 446 447 void destroy(pointer pObject) 448 { /* do nothing */ } 449 }; 450 451 } // namespace mcld 452 453 #endif 454 455