C++程序  |  455行  |  11.06 KB

//===- Allocators.h ---------------------------------------------------------===//
//
//                     The MCLinker Project
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#ifndef MCLD_SUPPORT_ALLOCATORS_H
#define MCLD_SUPPORT_ALLOCATORS_H
#ifdef ENABLE_UNITTEST
#include <gtest.h>
#endif
#include <mcld/ADT/Uncopyable.h>
#include <mcld/ADT/TypeTraits.h>

#include <cstddef>
#include <cstdlib>

namespace mcld {

/** \class Chunk
 *  \brief Chunk is the basic unit of the storage of the LinearAllocator
 *
 *  @see LinearAllocator
 */
template<typename DataType, size_t ChunkSize>
class Chunk
{
public:
  typedef DataType value_type;
public:
  Chunk()
  : next(0), bound(0)
  { }

  static size_t size() { return ChunkSize; }

  static void construct(value_type* pPtr)
  { new (pPtr) value_type(); }

  static void construct(value_type* pPtr, const value_type& pValue)
  { new (pPtr) value_type(pValue); }

  static void destroy(value_type* pPtr)
  { }

public:
  Chunk* next;
  size_t bound;
  DataType data[ChunkSize];
};

template<typename DataType>
class Chunk<DataType, 0>
{
public:
  typedef DataType value_type;

public:
  Chunk()
  : next(0), bound(0) {
    if (0 != m_Size)
      data = (DataType*)malloc(sizeof(DataType)*m_Size);
    else
      data = 0;
  }

  ~Chunk() {
    if (data)
      free(data);
  }

  static size_t size() { return m_Size; }

  static void setSize(size_t pSize) { m_Size = pSize; }

  static void construct(value_type* pPtr)
  { new (pPtr) value_type(); }

  static void construct(value_type* pPtr, const value_type& pValue)
  { new (pPtr) value_type(pValue); }

  static void destroy(value_type* pPtr)
  { pPtr->~value_type(); }

public:
  Chunk* next;
  size_t bound;
  DataType *data;
  static size_t m_Size;
};

template<typename DataType>
size_t Chunk<DataType, 0>::m_Size = 0;

template<typename ChunkType>
class LinearAllocatorBase : private Uncopyable
{
public:
  typedef ChunkType                             chunk_type;
  typedef typename ChunkType::value_type        value_type;
  typedef typename ChunkType::value_type*       pointer;
  typedef typename ChunkType::value_type&       reference;
  typedef const typename ChunkType::value_type* const_pointer;
  typedef const typename ChunkType::value_type& const_reference;
  typedef size_t                                size_type;
  typedef ptrdiff_t                             difference_type;
  typedef unsigned char                         byte_type;

protected:
  LinearAllocatorBase()
    : m_pRoot(0),
      m_pCurrent(0),
      m_AllocatedNum(0) {
  }

  // LinearAllocatorBase does NOT mean to destroy the allocated memory.
  // If you want a memory allocator to release memory at destruction, please
  // use GCFactory series.
  virtual ~LinearAllocatorBase()
  { }

public:
  pointer address(reference X) const
  { return &X; }

  const_pointer address(const_reference X) const
  { return &X; }

  /// standard construct - constructing an object on the location pointed by
  //  pPtr, and using its copy constructor to initialized its value to pValue.
  //
  //  @param pPtr the address where the object to be constructed
  //  @param pValue the value to be constructed
  void construct(pointer pPtr, const_reference pValue)
  { chunk_type::construct(pPtr, pValue); }

  /// default construct - constructing an object on the location pointed by
  //  pPtr, and using its default constructor to initialized its value to
  //  pValue.
  //
  //  @param pPtr the address where the object to be constructed
  void construct(pointer pPtr)
  { chunk_type::construct(pPtr); }

  /// standard destroy - destroy data on arbitrary address
  //  @para pPtr the address where the data to be destruected.
  void destroy(pointer pPtr)
  { chunk_type::destroy(pPtr); }

  /// allocate - allocate N data in order.
  //  - Disallow to allocate a chunk whose size is bigger than a chunk.
  //
  //  @param N the number of allocated data.
  //  @return the start address of the allocated memory
  pointer allocate(size_type N) {
    if (0 == N || N > chunk_type::size())
      return 0;

    if (empty())
      initialize();

    size_type rest_num_elem = chunk_type::size() - m_pCurrent->bound;
    pointer result = 0;
    if (N > rest_num_elem)
      getNewChunk();
    result = m_pCurrent->data + m_pCurrent->bound;
    m_pCurrent->bound += N;
    return result;
  }

  /// allocate - clone function of allocating one datum.
  pointer allocate() {
    if (empty())
      initialize();

    pointer result = 0;
    if (chunk_type::size() == m_pCurrent->bound)
      getNewChunk();
    result = m_pCurrent->data + m_pCurrent->bound;
    ++m_pCurrent->bound;
    return result;
  }

  /// deallocate - deallocate N data from the pPtr
  //  - if we can simply release some memory, then do it. Otherwise, do
  //    nothing.
  void deallocate(pointer &pPtr, size_type N) {
    if (0 == N ||
        N > chunk_type::size() ||
        0 == m_pCurrent->bound ||
        N >= m_pCurrent->bound)
      return;
    if (!isAvailable(pPtr))
      return;
    m_pCurrent->bound -= N;
    pPtr = 0;
  }

  /// deallocate - clone function of deallocating one datum
  void deallocate(pointer &pPtr) {
    if (0 == m_pCurrent->bound)
      return;
    if (!isAvailable(pPtr))
      return;
    m_pCurrent->bound -= 1;
    pPtr = 0;
  }

  /// isIn - whether the pPtr is in the current chunk?
  bool isIn(pointer pPtr) const {
    if (pPtr >= &(m_pCurrent->data[0]) &&
        pPtr <= &(m_pCurrent->data[chunk_type::size()-1]))
      return true;
    return false;
  }

  /// isIn - whether the pPtr is allocated, and can be constructed.
  bool isAvailable(pointer pPtr) const {
    if (pPtr >= &(m_pCurrent->data[m_pCurrent->bound]) &&
        pPtr <= &(m_pCurrent->data[chunk_type::size()-1]))
      return true;
    return false;
  }

  void reset() {
    m_pRoot = 0;
    m_pCurrent = 0;
    m_AllocatedNum = 0;
  }

  /// clear - clear all chunks
  void clear() {
    chunk_type *cur = m_pRoot, *prev;
    while (0 != cur) {
      prev = cur;
      cur = cur->next;
      for (unsigned int idx = 0; idx != prev->bound; ++idx)
        destroy(prev->data + idx);
      delete prev;
    }
    reset();
  }

  // -----  observers  ----- //
  bool empty() const {
    return (0 == m_pRoot);
  }

  size_type max_size() const
  { return m_AllocatedNum; }

protected:
  inline void initialize() {
    m_pRoot = new chunk_type();
    m_pCurrent = m_pRoot;
    m_AllocatedNum += chunk_type::size();
  }

  inline chunk_type *getNewChunk() {
    chunk_type *result = new chunk_type();
    m_pCurrent->next = result;
    m_pCurrent = result;
    m_AllocatedNum += chunk_type::size();
    return result;
  }

protected:
  chunk_type *m_pRoot;
  chunk_type *m_pCurrent;
  size_type   m_AllocatedNum;
};

/** \class LinearAllocator
 *  \brief LinearAllocator is another bump pointer allocator which should be
 *  limited in use of two-phase memory allocation.
 *
 *  Two-phase memory allocation clear separates the use of memory into 'claim'
 *  and 'release' phases. There are no interleaving allocation and
 *  deallocation. Interleaving 'allocate' and 'deallocate' increases the size
 *  of allocated memory, and causes bad locality.
 *
 *  The underlying concept of LinearAllocator is a memory pool. LinearAllocator
 *  is a simple implementation of boost::pool's ordered_malloc() and
 *  ordered_free().
 *
 *  template argument DataType is the DataType to be allocated
 *  template argument ChunkSize is the number of bytes of a chunk
 */
template<typename DataType, size_t ChunkSize>
class LinearAllocator : public LinearAllocatorBase<Chunk<DataType, ChunkSize> >
{
public:
  template<typename NewDataType>
  struct rebind {
    typedef LinearAllocator<NewDataType, ChunkSize> other;
  };

public:
  LinearAllocator()
    : LinearAllocatorBase<Chunk<DataType, ChunkSize> >() {
  }

  virtual ~LinearAllocator()
  { }
};

template<typename DataType>
class LinearAllocator<DataType, 0> : public LinearAllocatorBase<Chunk<DataType, 0> >
{
public:
  template<typename NewDataType>
  struct rebind {
    typedef LinearAllocator<NewDataType, 0> other;
  };

public:
  explicit LinearAllocator(size_t pNum)
    : LinearAllocatorBase<Chunk<DataType, 0> >() {
    Chunk<DataType, 0>::setSize(pNum);
  }

  virtual ~LinearAllocator()
  { }
};

template<typename DataType>
class MallocAllocator
{
public:
  typedef size_t          size_type;
  typedef ptrdiff_t       difference_type;
  typedef DataType*       pointer;
  typedef const DataType* const_pointer;
  typedef DataType&       reference;
  typedef const DataType& const_reference;
  typedef DataType        value_type;

  template<typename OtherDataType>
  struct rebind
  {
    typedef MallocAllocator<OtherDataType> other;
  };

public:
  MallocAllocator() throw()
  { }

  MallocAllocator(const MallocAllocator&) throw()
  { }

  ~MallocAllocator() throw()
  { }

  pointer address(reference X) const
  { return &X; }

  const_pointer address(const_reference X) const
  { return &X; }

  pointer allocate(size_type pNumOfElements, const void* = 0)
  {
    return static_cast<DataType*>(
                       std::malloc(pNumOfElements*sizeof(DataType)));
  }

  void deallocate(pointer pObject, size_type)
  { std::free(static_cast<void*>(pObject)); }

  size_type max_size() const throw()
  { return size_t(-1) / sizeof(DataType); }

  void construct(pointer pObject, const DataType& pValue)
  { ::new((void *)pObject) value_type(pValue); }

  void destroy(pointer pObject)
  { pObject->~DataType(); }

};

template<>
class MallocAllocator<void>
{
public:
  typedef size_t      size_type;
  typedef ptrdiff_t   difference_type;
  typedef void*       pointer;
  typedef const void* const_pointer;
  typedef void*       reference;
  typedef const void* const_reference;
  typedef void*       value_type;

  template<typename OtherDataType>
  struct rebind
  {
    typedef MallocAllocator<OtherDataType> other;
  };

public:
  MallocAllocator() throw()
  { }

  MallocAllocator(const MallocAllocator&) throw()
  { }

  ~MallocAllocator() throw()
  { }

  size_type max_size() const throw()
  { return size_t(-1) / sizeof(void*); }

  pointer address(reference X) const
  { return X; }

  const_pointer address(const_reference X) const
  { return X; }

  template<typename DataType>
  DataType* allocate(size_type pNumOfElements, const void* = 0) {
    return static_cast<DataType*>(
                       std::malloc(pNumOfElements*sizeof(DataType)));
  }

  pointer allocate(size_type pNumOfElements, const void* = 0) {
    return std::malloc(pNumOfElements);
  }

  template<typename DataType>
  void deallocate(DataType* pObject, size_type)
  { std::free(static_cast<void*>(pObject)); }

  void deallocate(pointer pObject, size_type)
  { std::free(pObject); }

  template<typename DataType>
  void construct(DataType* pObject, const DataType& pValue)
  { /* do nothing */ }

  void construct(pointer pObject, const_reference pValue)
  { /* do nothing */ }

  template<typename DataType>
  void destroy(DataType* pObject)
  { /* do nothing */ }

  void destroy(pointer pObject)
  { /* do nothing */ }
};

} // namespace mcld

#endif