Home | History | Annotate | Download | only in Support
      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