Allocators.h revision 22add6ff3426df1a85089fe6a6e1597ee3b6f300
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
20namespace mcld {
21
22/** \class Chunk
23 *  \brief Chunk is the basic unit of the storage of the LinearAllocator
24 *
25 *  @see LinearAllocator
26 */
27template<typename DataType, size_t ChunkSize>
28class Chunk
29{
30public:
31  typedef DataType value_type;
32public:
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
48public:
49  Chunk* next;
50  size_t bound;
51  DataType data[ChunkSize];
52};
53
54template<typename DataType>
55class Chunk<DataType, 0>
56{
57public:
58  typedef DataType value_type;
59
60public:
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
87public:
88  Chunk* next;
89  size_t bound;
90  DataType *data;
91  static size_t m_Size;
92};
93
94template<typename DataType>
95size_t Chunk<DataType, 0>::m_Size = 0;
96
97template<typename ChunkType>
98class LinearAllocatorBase : private Uncopyable
99{
100public:
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
111protected:
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
124public:
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
254protected:
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
269protected:
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 */
291template<typename DataType, size_t ChunkSize>
292class LinearAllocator : public LinearAllocatorBase<Chunk<DataType, ChunkSize> >
293{
294public:
295  template<typename NewDataType>
296  struct rebind {
297    typedef LinearAllocator<NewDataType, ChunkSize> other;
298  };
299
300public:
301  LinearAllocator()
302    : LinearAllocatorBase<Chunk<DataType, ChunkSize> >() {
303  }
304
305  virtual ~LinearAllocator()
306  { }
307};
308
309template<typename DataType>
310class LinearAllocator<DataType, 0> : public LinearAllocatorBase<Chunk<DataType, 0> >
311{
312public:
313  template<typename NewDataType>
314  struct rebind {
315    typedef LinearAllocator<NewDataType, 0> other;
316  };
317
318public:
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
328template<typename DataType>
329class MallocAllocator
330{
331public:
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
346public:
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
382template<>
383class MallocAllocator<void>
384{
385public:
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
400public:
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