allocators.h

Engine/source/persistence/rapidjson/allocators.h

More...

Classes:

class

C-runtime library allocator.

class

Default memory allocator used by the parser and DOM.

class

Chunk header for perpending to each chunk.

Public Defines

define

User-defined kDefaultChunkCapacity definition.

Detailed Description

Public Defines

RAPIDJSON_ALLOCATOR_DEFAULT_CHUNK_CAPACITY() (64 * 1024)

User-defined kDefaultChunkCapacity definition.

User can define this as any

size
that is a power of 2.

  1
  2// Tencent is pleased to support the open source community by making RapidJSON available.
  3// 
  4// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
  5//
  6// Licensed under the MIT License (the "License"); you may not use this file except
  7// in compliance with the License. You may obtain a copy of the License at
  8//
  9// http://opensource.org/licenses/MIT
 10//
 11// Unless required by applicable law or agreed to in writing, software distributed 
 12// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 
 13// CONDITIONS OF ANY KIND, either express or implied. See the License for the 
 14// specific language governing permissions and limitations under the License.
 15
 16#ifndef RAPIDJSON_ALLOCATORS_H_
 17#define RAPIDJSON_ALLOCATORS_H_
 18
 19#include "rapidjson.h"
 20
 21RAPIDJSON_NAMESPACE_BEGIN
 22
 23///////////////////////////////////////////////////////////////////////////////
 24// Allocator
 25
 26/*! \class rapidjson::Allocator
 27    \brief Concept for allocating, resizing and freeing memory block.
 28    
 29    Note that Malloc() and Realloc() are non-static but Free() is static.
 30    
 31    So if an allocator need to support Free(), it needs to put its pointer in 
 32    the header of memory block.
 33
 34\code
 35concept Allocator {
 36    static const bool kNeedFree;    //!< Whether this allocator needs to call Free().
 37
 38    // Allocate a memory block.
 39    // \param size of the memory block in bytes.
 40    // \returns pointer to the memory block.
 41    void* Malloc(size_t size);
 42
 43    // Resize a memory block.
 44    // \param originalPtr The pointer to current memory block. Null pointer is permitted.
 45    // \param originalSize The current size in bytes. (Design issue: since some allocator may not book-keep this, explicitly pass to it can save memory.)
 46    // \param newSize the new size in bytes.
 47    void* Realloc(void* originalPtr, size_t originalSize, size_t newSize);
 48
 49    // Free a memory block.
 50    // \param pointer to the memory block. Null pointer is permitted.
 51    static void Free(void *ptr);
 52};
 53\endcode
 54*/
 55
 56
 57/*! \def RAPIDJSON_ALLOCATOR_DEFAULT_CHUNK_CAPACITY
 58    \ingroup RAPIDJSON_CONFIG
 59    \brief User-defined kDefaultChunkCapacity definition.
 60
 61    User can define this as any \c size that is a power of 2.
 62*/
 63
 64#ifndef RAPIDJSON_ALLOCATOR_DEFAULT_CHUNK_CAPACITY
 65#define RAPIDJSON_ALLOCATOR_DEFAULT_CHUNK_CAPACITY (64 * 1024)
 66#endif
 67
 68
 69///////////////////////////////////////////////////////////////////////////////
 70// CrtAllocator
 71
 72//! C-runtime library allocator.
 73/*! This class is just wrapper for standard C library memory routines.
 74    \note implements Allocator concept
 75*/
 76class CrtAllocator {
 77public:
 78    static const bool kNeedFree = true;
 79    void* Malloc(size_t size) { 
 80        if (size) //  behavior of malloc(0) is implementation defined.
 81            return std::malloc(size);
 82        else
 83            return NULL; // standardize to returning NULL.
 84    }
 85    void* Realloc(void* originalPtr, size_t originalSize, size_t newSize) {
 86        (void)originalSize;
 87        if (newSize == 0) {
 88            std::free(originalPtr);
 89            return NULL;
 90        }
 91        return std::realloc(originalPtr, newSize);
 92    }
 93    static void Free(void *ptr) { std::free(ptr); }
 94};
 95
 96///////////////////////////////////////////////////////////////////////////////
 97// MemoryPoolAllocator
 98
 99//! Default memory allocator used by the parser and DOM.
100/*! This allocator allocate memory blocks from pre-allocated memory chunks. 
101
102    It does not free memory blocks. And Realloc() only allocate new memory.
103
104    The memory chunks are allocated by BaseAllocator, which is CrtAllocator by default.
105
106    User may also supply a buffer as the first chunk.
107
108    If the user-buffer is full then additional chunks are allocated by BaseAllocator.
109
110    The user-buffer is not deallocated by this allocator.
111
112    \tparam BaseAllocator the allocator type for allocating memory chunks. Default is CrtAllocator.
113    \note implements Allocator concept
114*/
115template <typename BaseAllocator = CrtAllocator>
116class MemoryPoolAllocator {
117public:
118    static const bool kNeedFree = false;    //!< Tell users that no need to call Free() with this allocator. (concept Allocator)
119
120    //! Constructor with chunkSize.
121    /*! \param chunkSize The size of memory chunk. The default is kDefaultChunkSize.
122        \param baseAllocator The allocator for allocating memory chunks.
123    */
124    MemoryPoolAllocator(size_t chunkSize = kDefaultChunkCapacity, BaseAllocator* baseAllocator = 0) : 
125        chunkHead_(0), chunk_capacity_(chunkSize), userBuffer_(0), baseAllocator_(baseAllocator), ownBaseAllocator_(0)
126    {
127    }
128
129    //! Constructor with user-supplied buffer.
130    /*! The user buffer will be used firstly. When it is full, memory pool allocates new chunk with chunk size.
131
132        The user buffer will not be deallocated when this allocator is destructed.
133
134        \param buffer User supplied buffer.
135        \param size Size of the buffer in bytes. It must at least larger than sizeof(ChunkHeader).
136        \param chunkSize The size of memory chunk. The default is kDefaultChunkSize.
137        \param baseAllocator The allocator for allocating memory chunks.
138    */
139    MemoryPoolAllocator(void *buffer, size_t size, size_t chunkSize = kDefaultChunkCapacity, BaseAllocator* baseAllocator = 0) :
140        chunkHead_(0), chunk_capacity_(chunkSize), userBuffer_(buffer), baseAllocator_(baseAllocator), ownBaseAllocator_(0)
141    {
142        RAPIDJSON_ASSERT(buffer != 0);
143        RAPIDJSON_ASSERT(size > sizeof(ChunkHeader));
144        chunkHead_ = reinterpret_cast<ChunkHeader*>(buffer);
145        chunkHead_->capacity = size - sizeof(ChunkHeader);
146        chunkHead_->size = 0;
147        chunkHead_->next = 0;
148    }
149
150    //! Destructor.
151    /*! This deallocates all memory chunks, excluding the user-supplied buffer.
152    */
153    ~MemoryPoolAllocator() {
154        Clear();
155        RAPIDJSON_DELETE(ownBaseAllocator_);
156    }
157
158    //! Deallocates all memory chunks, excluding the user-supplied buffer.
159    void Clear() {
160        while (chunkHead_ && chunkHead_ != userBuffer_) {
161            ChunkHeader* next = chunkHead_->next;
162            baseAllocator_->Free(chunkHead_);
163            chunkHead_ = next;
164        }
165        if (chunkHead_ && chunkHead_ == userBuffer_)
166            chunkHead_->size = 0; // Clear user buffer
167    }
168
169    //! Computes the total capacity of allocated memory chunks.
170    /*! \return total capacity in bytes.
171    */
172    size_t Capacity() const {
173        size_t capacity = 0;
174        for (ChunkHeader* c = chunkHead_; c != 0; c = c->next)
175            capacity += c->capacity;
176        return capacity;
177    }
178
179    //! Computes the memory blocks allocated.
180    /*! \return total used bytes.
181    */
182    size_t Size() const {
183        size_t size = 0;
184        for (ChunkHeader* c = chunkHead_; c != 0; c = c->next)
185            size += c->size;
186        return size;
187    }
188
189    //! Allocates a memory block. (concept Allocator)
190    void* Malloc(size_t size) {
191        if (!size)
192            return NULL;
193
194        size = RAPIDJSON_ALIGN(size);
195        if (chunkHead_ == 0 || chunkHead_->size + size > chunkHead_->capacity)
196            if (!AddChunk(chunk_capacity_ > size ? chunk_capacity_ : size))
197                return NULL;
198
199        void *buffer = reinterpret_cast<char *>(chunkHead_) + RAPIDJSON_ALIGN(sizeof(ChunkHeader)) + chunkHead_->size;
200        chunkHead_->size += size;
201        return buffer;
202    }
203
204    //! Resizes a memory block (concept Allocator)
205    void* Realloc(void* originalPtr, size_t originalSize, size_t newSize) {
206        if (originalPtr == 0)
207            return Malloc(newSize);
208
209        if (newSize == 0)
210            return NULL;
211
212        originalSize = RAPIDJSON_ALIGN(originalSize);
213        newSize = RAPIDJSON_ALIGN(newSize);
214
215        // Do not shrink if new size is smaller than original
216        if (originalSize >= newSize)
217            return originalPtr;
218
219        // Simply expand it if it is the last allocation and there is sufficient space
220        if (originalPtr == reinterpret_cast<char *>(chunkHead_) + RAPIDJSON_ALIGN(sizeof(ChunkHeader)) + chunkHead_->size - originalSize) {
221            size_t increment = static_cast<size_t>(newSize - originalSize);
222            if (chunkHead_->size + increment <= chunkHead_->capacity) {
223                chunkHead_->size += increment;
224                return originalPtr;
225            }
226        }
227
228        // Realloc process: allocate and copy memory, do not free original buffer.
229        if (void* newBuffer = Malloc(newSize)) {
230            if (originalSize)
231                std::memcpy(newBuffer, originalPtr, originalSize);
232            return newBuffer;
233        }
234        else
235            return NULL;
236    }
237
238    //! Frees a memory block (concept Allocator)
239    static void Free(void *ptr) { (void)ptr; } // Do nothing
240
241private:
242    //! Copy constructor is not permitted.
243    MemoryPoolAllocator(const MemoryPoolAllocator& rhs) /* = delete */;
244    //! Copy assignment operator is not permitted.
245    MemoryPoolAllocator& operator=(const MemoryPoolAllocator& rhs) /* = delete */;
246
247    //! Creates a new chunk.
248    /*! \param capacity Capacity of the chunk in bytes.
249        \return true if success.
250    */
251    bool AddChunk(size_t capacity) {
252        if (!baseAllocator_)
253            ownBaseAllocator_ = baseAllocator_ = RAPIDJSON_NEW(BaseAllocator)();
254        if (ChunkHeader* chunk = reinterpret_cast<ChunkHeader*>(baseAllocator_->Malloc(RAPIDJSON_ALIGN(sizeof(ChunkHeader)) + capacity))) {
255            chunk->capacity = capacity;
256            chunk->size = 0;
257            chunk->next = chunkHead_;
258            chunkHead_ =  chunk;
259            return true;
260        }
261        else
262            return false;
263    }
264
265    static const int kDefaultChunkCapacity = RAPIDJSON_ALLOCATOR_DEFAULT_CHUNK_CAPACITY; //!< Default chunk capacity.
266
267    //! Chunk header for perpending to each chunk.
268    /*! Chunks are stored as a singly linked list.
269    */
270    struct ChunkHeader {
271        size_t capacity;    //!< Capacity of the chunk in bytes (excluding the header itself).
272        size_t size;        //!< Current size of allocated memory in bytes.
273        ChunkHeader *next;  //!< Next chunk in the linked list.
274    };
275
276    ChunkHeader *chunkHead_;    //!< Head of the chunk linked-list. Only the head chunk serves allocation.
277    size_t chunk_capacity_;     //!< The minimum capacity of chunk when they are allocated.
278    void *userBuffer_;          //!< User supplied buffer.
279    BaseAllocator* baseAllocator_;  //!< base allocator for allocating memory chunks.
280    BaseAllocator* ownBaseAllocator_;   //!< base allocator created by this object.
281};
282
283RAPIDJSON_NAMESPACE_END
284
285#endif // RAPIDJSON_ENCODINGS_H_
286