• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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:
Chunk()33   Chunk()
34   : next(0), bound(0)
35   { }
36 
size()37   static size_t size() { return ChunkSize; }
38 
construct(value_type * pPtr)39   static void construct(value_type* pPtr)
40   { new (pPtr) value_type(); }
41 
construct(value_type * pPtr,const value_type & pValue)42   static void construct(value_type* pPtr, const value_type& pValue)
43   { new (pPtr) value_type(pValue); }
44 
destroy(value_type * pPtr)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:
Chunk()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 
~Chunk()69   ~Chunk() {
70     if (data)
71       free(data);
72   }
73 
size()74   static size_t size() { return m_Size; }
75 
setSize(size_t pSize)76   static void setSize(size_t pSize) { m_Size = pSize; }
77 
construct(value_type * pPtr)78   static void construct(value_type* pPtr)
79   { new (pPtr) value_type(); }
80 
construct(value_type * pPtr,const value_type & pValue)81   static void construct(value_type* pPtr, const value_type& pValue)
82   { new (pPtr) value_type(pValue); }
83 
destroy(value_type * pPtr)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:
LinearAllocatorBase()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.
~LinearAllocatorBase()121   virtual ~LinearAllocatorBase()
122   { }
123 
124 public:
address(reference X)125   pointer address(reference X) const
126   { return &X; }
127 
address(const_reference X)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
construct(pointer pPtr,const_reference pValue)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
construct(pointer pPtr)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.
destroy(pointer pPtr)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
allocate(size_type N)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.
allocate()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.
deallocate(pointer & pPtr,size_type N)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
deallocate(pointer & pPtr)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?
isIn(pointer pPtr)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.
isAvailable(pointer pPtr)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 
reset()227   void reset() {
228     m_pRoot = 0;
229     m_pCurrent = 0;
230     m_AllocatedNum = 0;
231   }
232 
233   /// clear - clear all chunks
clear()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  ----- //
empty()247   bool empty() const {
248     return (0 == m_pRoot);
249   }
250 
max_size()251   size_type max_size() const
252   { return m_AllocatedNum; }
253 
254 protected:
initialize()255   inline void initialize() {
256     m_pRoot = new chunk_type();
257     m_pCurrent = m_pRoot;
258     m_AllocatedNum += chunk_type::size();
259   }
260 
getNewChunk()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:
LinearAllocator()301   LinearAllocator()
302     : LinearAllocatorBase<Chunk<DataType, ChunkSize> >() {
303   }
304 
~LinearAllocator()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:
LinearAllocator(size_t pNum)319   explicit LinearAllocator(size_t pNum)
320     : LinearAllocatorBase<Chunk<DataType, 0> >() {
321     Chunk<DataType, 0>::setSize(pNum);
322   }
323 
~LinearAllocator()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:
MallocAllocator()347   MallocAllocator() throw()
348   { }
349 
throw()350   MallocAllocator(const MallocAllocator&) throw()
351   { }
352 
throw()353   ~MallocAllocator() throw()
354   { }
355 
address(reference X)356   pointer address(reference X) const
357   { return &X; }
358 
address(const_reference X)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 
deallocate(pointer pObject,size_type)368   void deallocate(pointer pObject, size_type)
369   { std::free(static_cast<void*>(pObject)); }
370 
max_size()371   size_type max_size() const throw()
372   { return size_t(-1) / sizeof(DataType); }
373 
construct(pointer pObject,const DataType & pValue)374   void construct(pointer pObject, const DataType& pValue)
375   { ::new((void *)pObject) value_type(pValue); }
376 
destroy(pointer pObject)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:
MallocAllocator()401   MallocAllocator() throw()
402   { }
403 
throw()404   MallocAllocator(const MallocAllocator&) throw()
405   { }
406 
throw()407   ~MallocAllocator() throw()
408   { }
409 
max_size()410   size_type max_size() const throw()
411   { return size_t(-1) / sizeof(void*); }
412 
address(reference X)413   pointer address(reference X) const
414   { return X; }
415 
address(const_reference X)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>
deallocate(DataType * pObject,size_type)430   void deallocate(DataType* pObject, size_type)
431   { std::free(static_cast<void*>(pObject)); }
432 
deallocate(pointer pObject,size_type)433   void deallocate(pointer pObject, size_type)
434   { std::free(pObject); }
435 
436   template<typename DataType>
construct(DataType * pObject,const DataType & pValue)437   void construct(DataType* pObject, const DataType& pValue)
438   { /* do nothing */ }
439 
construct(pointer pObject,const_reference pValue)440   void construct(pointer pObject, const_reference pValue)
441   { /* do nothing */ }
442 
443   template<typename DataType>
destroy(DataType * pObject)444   void destroy(DataType* pObject)
445   { /* do nothing */ }
446 
destroy(pointer pObject)447   void destroy(pointer pObject)
448   { /* do nothing */ }
449 };
450 
451 } // namespace mcld
452 
453 #endif
454 
455