lean cpp library
A lean C++ library providing efficient utility classes for high-performance C++ applications.
chunk_heap.h
00001 /*****************************************************/
00002 /* lean Memory                  (c) Tobias Zirr 2011 */
00003 /*****************************************************/
00004 
00005 #ifndef LEAN_MEMORY_CHUNK_HEAP
00006 #define LEAN_MEMORY_CHUNK_HEAP
00007 
00008 #include "../lean.h"
00009 #include "../tags/noncopyable.h"
00010 #include "alignment.h"
00011 #include "default_heap.h"
00012 
00013 namespace lean
00014 {
00015 namespace memory
00016 {
00017 
00019 template <size_t Size>
00020 struct optional_mem_block
00021 {
00022     char memory[Size];
00023 
00024     LEAN_INLINE char* get() { return memory; }
00025     LEAN_INLINE const char* get() const { return memory; }
00026 
00027     LEAN_INLINE operator char*() { return memory; }
00028     LEAN_INLINE operator const char*() const { return memory; }
00029 };
00030 template <>
00031 struct optional_mem_block<0>
00032 {
00033     LEAN_INLINE char* get() { return nullptr; }
00034     LEAN_INLINE const char* get() const { return nullptr; }
00035 
00036     LEAN_INLINE operator char*() { return nullptr; }
00037     LEAN_INLINE operator const char*() const { return nullptr; }
00038 };
00039 
00041 template <size_t ChunkSize, class Heap = default_heap, size_t StaticChunkSize = ChunkSize, size_t DefaultAlignment = sizeof(void*)>
00042 class chunk_heap : public lean::noncopyable
00043 {
00044 public:
00046     typedef Heap heap_type;
00048     typedef typename heap_type::size_type size_type;
00050     static const size_type chunk_size = ChunkSize;
00052     static const size_type default_alignment = DefaultAlignment;
00053 
00054 private:
00055     // Optional first static chunk
00056     optional_mem_block<StaticChunkSize> m_firstChunk;
00057     
00058     // Current chunk
00059     char *m_chunk;
00060     char *m_chunkOffset;
00061     char *m_chunkEnd;
00062 
00063     // Next chunk size
00064     size_type m_nextChunkSize;
00065 
00067     struct chunk_header
00068     {
00069         char *prev_chunk;
00070 
00072         chunk_header(char *prev_chunk)
00073             : prev_chunk(prev_chunk) { }
00074     };
00075     // Chunk alignment
00076     static const size_t chunk_alignment = alignof(chunk_header);
00077 
00079     LEAN_INLINE static chunk_header* to_chunk_header(char *chunk)
00080     {
00081         return reinterpret_cast<chunk_header*>(chunk) - 1;
00082     }
00083 
00085     template <size_t Alignment>
00086     char* allocate_aligned(size_type size)
00087     {
00088         // Get next free memory location
00089         char *aligned = align<Alignment>(m_chunkOffset);
00090 
00091         // Allocate new chunk, if old chunk too small
00092         if (size + static_cast<size_type>(aligned - m_chunkOffset) > static_cast<size_type>(m_chunkEnd - m_chunkOffset))
00093         {
00094             // Make sure new chunk is large enough for requested amount of memory + alignment
00095             size_type alignedSize = size + (Alignment - 1);
00096 
00097             size_type nextChunkSize = m_nextChunkSize;
00098             if (nextChunkSize < alignedSize)
00099                 nextChunkSize = alignedSize;
00100 
00101             nextChunkSize += sizeof(chunk_header);
00102 
00103             char *nextChunkBase = static_cast<char*>( Heap::allocate<chunk_alignment>(nextChunkSize) );
00104             new( static_cast<void*>(nextChunkBase) ) chunk_header(m_chunk);
00105 
00106             m_chunk = nextChunkBase + sizeof(chunk_header);
00107             m_chunkOffset = m_chunk;
00108             m_chunkEnd = nextChunkBase + nextChunkSize;
00109 
00110             // Reset chunk size
00111             if (chunk_size != 0)
00112                 m_nextChunkSize = chunk_size;
00113 
00114             // Get next free memory location
00115             aligned = align<Alignment>(m_chunkOffset);
00116         }
00117 
00118         // Memory now occupied
00119         m_chunkOffset = aligned + size;
00120 
00121         return aligned;
00122     }
00123 
00124 public:
00126     LEAN_INLINE chunk_heap(size_type chunkSize = ChunkSize)
00127         : m_chunk(m_firstChunk),
00128         m_chunkOffset(m_firstChunk),
00129         m_chunkEnd(m_firstChunk.get() + StaticChunkSize),
00130         m_nextChunkSize(chunkSize) { }
00132     LEAN_INLINE ~chunk_heap()
00133     {
00134         clear();
00135     }
00136 
00138     LEAN_INLINE void nextChunkSize(size_type nextSize) { m_nextChunkSize = nextSize; }
00140     LEAN_INLINE size_type nextChunkSize() const { return m_nextChunkSize; }
00141 
00143     LEAN_INLINE size_type capacity() const { return m_chunkEnd - m_chunkOffset; }
00144 
00150     LEAN_INLINE void reserve(size_type newCapacity, size_type minChunkSize = chunk_size)
00151     {
00152         size_type currentCapacity = capacity();
00153 
00154         if (newCapacity > currentCapacity) 
00155             nextChunkSize( max(newCapacity - currentCapacity, minChunkSize) );
00156     }
00157 
00159     void clear()
00160     {
00161         // Free as many chunks as possible
00162         while (m_chunk != m_firstChunk)
00163         {
00164             chunk_header *freeChunkBase = to_chunk_header(m_chunk);
00165 
00166             // Immediately store previous chunk in case clear is re-called after exception
00167             m_chunk = freeChunkBase->prev_chunk;
00168             m_chunkOffset = m_chunk;
00169             m_chunkEnd = m_chunk;
00170 
00171             Heap::free<chunk_alignment>(freeChunkBase);
00172         }
00173 
00174         // Re-initialize with first chunk
00175         m_chunkEnd = m_chunk + StaticChunkSize;
00176     }
00177 
00179     void clearButFirst()
00180     {
00181         if (m_chunk != m_firstChunk && to_chunk_header(m_chunk)->prev_chunk == m_firstChunk)
00182             // Re-initialize first dynamic chunk
00183             m_chunkOffset = m_chunk;
00184         else
00185             clear();
00186     }
00187 
00189     bool currentStatic()
00190     {
00191         return (m_chunk == m_firstChunk);
00192     }
00194     char* currentOffset()
00195     {
00196         return m_chunkOffset;
00197     }
00199     char* clearCurrent()
00200     {
00201         // Re-initialize with current chunk
00202         m_chunkOffset = m_chunk;
00203         return m_chunk;
00204     }
00206     char* clearFreeNext()
00207     {
00208         if (m_chunk != m_firstChunk)
00209         {
00210             chunk_header *freeChunkBase = to_chunk_header(m_chunk);
00211 
00212             // Immediately store previous chunk (exception-safe)
00213             m_chunk = freeChunkBase->prev_chunk;
00214             m_chunkOffset = m_chunk;
00215             m_chunkEnd = (m_chunk == m_firstChunk) ? m_chunk + StaticChunkSize : m_chunk;
00216 
00217             Heap::free<chunk_alignment>(freeChunkBase);
00218         }
00219 
00220         return m_chunk;
00221     }
00222 
00224     LEAN_INLINE void* allocate(size_type size) { return allocate<default_alignment>(size); }
00226     LEAN_INLINE void free(void *memory) { free<default_alignment>(memory); }
00227 
00229     template <size_t Alignment>
00230     LEAN_INLINE void* allocate(size_type size)
00231     {
00232         return allocate_aligned<Alignment>(size);
00233     }
00235     template <size_t Alignment>
00236     LEAN_INLINE void free(void *memory)
00237     {
00238         // Freeing of individual memory blocks unsupported
00239     }
00240 };
00241 
00242 } // namespace
00243 
00244 using memory::chunk_heap;
00245 
00246 } // namespace
00247 
00248 #endif