1/****************************************************************************************
2
3 Copyright (C) 2015 Autodesk, Inc.
4 All rights reserved.
5
6 Use of this software is subject to the terms of the Autodesk license agreement
7 provided at the time of installation or download, or which otherwise accompanies
8 this software in either electronic or hard copy form.
9
10****************************************************************************************/
11
12//! \file fbxcontainerallocators.h
13#ifndef _FBXSDK_CORE_BASE_CONTAINER_ALLOCATORS_H_
14#define _FBXSDK_CORE_BASE_CONTAINER_ALLOCATORS_H_
15
16#include <fbxsdk/fbxsdk_def.h>
17
18#include <fbxsdk/fbxsdk_nsbegin.h>
19
20/** An allocator class for use as a template parameter to one of the
21 * container class (FbxMap, FbxSet, FbxDynamicArray...) must implement these.
22 */
23class FBXSDK_DLL FbxBaseAllocator
24{
25public:
26 /** The class constructor.
27 * \param pRecordSize the size of one record held by the container.
28 * \remarks The parameter pRecordSize is not necessarily the same
29 * size as of the value type, since the
30 * container may wrap the value into a private class.
31 */
32 FbxBaseAllocator(const size_t pRecordSize) :
33 mRecordSize(pRecordSize)
34 {
35 }
36
37 /** This tells the allocator that we are about to call AllocateRecords
38 * one or many times to allocate pRecordCount records.
39 * \param pRecordCount
40 * \remarks This gives the allocator a chance to do whatever it deems necessary
41 * to optimize subsequent allocations, for example, by preallocating a
42 * sufficiently large pool of memory.
43 */
44 void Reserve(const size_t /*pRecordCount*/)
45 {
46 // By default, ignore all preallocating requests.
47 }
48
49 /** Returns a pointer to a uninitialized continuous block of memory
50 * able to hold pRecordCount * pRecordSize bytes.
51 * \param pRecordCount
52 * \remarks pRecordSize was defined in the Constructor description, above.
53 */
54 void* AllocateRecords(const size_t pRecordCount=1)
55 {
56 return FbxMalloc(pRecordCount * mRecordSize);
57 }
58
59 /** Frees a block of memory returned by AllocateRecords.
60 * \param pRecord
61 */
62 void FreeMemory(void* pRecord)
63 {
64 FbxFree(pRecord);
65 }
66
67 /** \return the size of each record allocated.
68 */
69 size_t GetRecordSize() const
70 {
71 return mRecordSize;
72 }
73
74private:
75 size_t mRecordSize;
76};
77
78/** This allocator only frees the allocated memory when it is deleted.
79 * This is a good allocator for building dictionaries, where we only
80 * add things to a container, but never remove them.
81 */
82class FbxHungryAllocator
83{
84public:
85 FbxHungryAllocator(size_t pRecordSize) :
86 mRecordSize(pRecordSize),
87 mRecordPoolSize(0),
88 mData(NULL)
89 {
90 }
91
92 FbxHungryAllocator(const FbxHungryAllocator& pOther) :
93 mRecordSize(pOther.mRecordSize),
94 mRecordPoolSize(pOther.mRecordPoolSize),
95 mData(NULL)
96 {
97 }
98
99 ~FbxHungryAllocator()
100 {
101 MemoryBlock* lCurrent = mData;
102 MemoryBlock* lNext = lCurrent ? lCurrent->mNextBlock : 0;
103 while (lCurrent)
104 {
105 FbxDelete(lCurrent);
106 lCurrent = lNext;
107 lNext = lCurrent ? lCurrent->mNextBlock : 0;
108 }
109 }
110
111 void Reserve(const size_t pRecordCount)
112 {
113 MemoryBlock* lMem = FbxNew< MemoryBlock >(pRecordCount* mRecordSize);
114 lMem->mNextBlock = mData;
115 mData = lMem;
116 mRecordPoolSize += pRecordCount;
117 }
118
119 void* AllocateRecords(const size_t pRecordCount = 1)
120 {
121 MemoryBlock* lBlock = mData;
122 void* lRecord = NULL;
123
124 while( (lBlock != NULL) && ((lRecord = lBlock->GetChunk(pRecordCount * mRecordSize)) == NULL) )
125 {
126 lBlock = lBlock->mNextBlock;
127 }
128
129 if( lRecord == NULL )
130 {
131 size_t lNumRecordToAllocate = mRecordPoolSize / 8 == 0 ? 2 : mRecordPoolSize / 8;
132 if( lNumRecordToAllocate < pRecordCount )
133 {
134 lNumRecordToAllocate = pRecordCount;
135 }
136 Reserve(lNumRecordToAllocate);
137 lRecord = AllocateRecords(pRecordCount);
138 }
139 return lRecord;
140 }
141
142 void FreeMemory(void* /*pRecord*/)
143 {
144 // "Hungry": release memory only when the allocator is destroyed.
145 }
146
147 size_t GetRecordSize() const
148 {
149 return mRecordSize;
150 }
151
152 FbxHungryAllocator& operator=(const FbxHungryAllocator& pOther)
153 {
154 if( this != &pOther )
155 {
156 // The next call to AllocateRecords() may skip over currently reserved
157 // records if the size changes drastically, but otherwise GetChunk()
158 // is size-oblivious.
159 if( mRecordSize < pOther.mRecordSize )
160 {
161 mRecordPoolSize = 0;
162 }
163
164 mRecordSize = pOther.mRecordSize;
165 }
166 return(*this);
167 }
168
169private:
170 class MemoryBlock
171 {
172 public:
173 MemoryBlock(size_t pSize) :
174 mNextBlock(NULL),
175 mData(NULL),
176 mFreeData(NULL),
177 mEnd(NULL)
178 {
179 mData = FbxMalloc(pSize);
180 mFreeData = mData;
181 mEnd = reinterpret_cast<char*>(mData) + pSize;
182 }
183
184 ~MemoryBlock()
185 {
186 FbxFree(mData);
187 }
188
189 void* GetChunk(const size_t pSize)
190 {
191 if( reinterpret_cast<char*>(mFreeData) + pSize < mEnd )
192 {
193 void* lChunk = mFreeData;
194 mFreeData = reinterpret_cast<char*>(mFreeData) + pSize;
195 return lChunk;
196 }
197 return NULL;
198 }
199
200 MemoryBlock* mNextBlock;
201 void* mData;
202 void* mFreeData;
203 void* mEnd;
204 };
205
206 size_t mRecordSize;
207 size_t mRecordPoolSize;
208 MemoryBlock* mData;
209};
210
211#include <fbxsdk/fbxsdk_nsend.h>
212
213#endif /* _FBXSDK_CORE_BASE_CONTAINER_ALLOCATORS_H_ */
214