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