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 fbxdynamicarray.h |
13 | #ifndef _FBXSDK_CORE_BASE_DYNAMICARRAY_H_ |
14 | #define _FBXSDK_CORE_BASE_DYNAMICARRAY_H_ |
15 | |
16 | #include <fbxsdk/fbxsdk_def.h> |
17 | |
18 | #include <fbxsdk/core/base/fbxcontainerallocators.h> |
19 | |
20 | #include <fbxsdk/fbxsdk_nsbegin.h> |
21 | |
22 | /** Template class for dynamic array holding objects. |
23 | * \nosubgrouping |
24 | * \see FbxStaticArray |
25 | */ |
26 | template <typename Type, typename Allocator=FbxBaseAllocator> class FbxDynamicArray |
27 | { |
28 | public: |
29 | //! Default constructor. |
30 | FbxDynamicArray() : |
31 | mArray(NULL), |
32 | mCapacity(0), |
33 | mSize(0), |
34 | mAllocator(sizeof(Type)) |
35 | { |
36 | } |
37 | |
38 | /** Constructor. |
39 | * \param pInitialSize initial capacity of this array */ |
40 | FbxDynamicArray(const size_t pInitialSize) : |
41 | mArray(NULL), |
42 | mCapacity(0), |
43 | mSize(0), |
44 | mAllocator(sizeof(Type)) |
45 | { |
46 | Reserve(pInitialSize); |
47 | } |
48 | |
49 | /** Copy constructor. |
50 | * \remarks The copy constructor of \c Type will be |
51 | * invoked in order to copy the value of elements to the |
52 | * new array. |
53 | */ |
54 | FbxDynamicArray(const FbxDynamicArray& pArray) : |
55 | mArray(NULL), |
56 | mCapacity(0), |
57 | mSize(0), |
58 | mAllocator(sizeof(Type)) |
59 | { |
60 | Reserve(pArray.mCapacity); |
61 | CopyArray(mArray, pArray.mArray, pArray.mSize); |
62 | mSize = pArray.mSize; |
63 | } |
64 | |
65 | //! Destructor. |
66 | ~FbxDynamicArray() |
67 | { |
68 | for( size_t i = 0; i < mSize; ++i ) |
69 | { |
70 | mArray[i].~Type(); |
71 | } |
72 | mAllocator.FreeMemory(mArray); |
73 | } |
74 | |
75 | //! Gets the current capacity of the array. |
76 | size_t Capacity() const |
77 | { |
78 | return mCapacity; |
79 | } |
80 | |
81 | //! Gets the size of the array. |
82 | size_t Size() const |
83 | { |
84 | return mSize; |
85 | } |
86 | |
87 | /** Assures that sufficient memory is allocated to hold n objects in the array, and increases the capacity if necessary. |
88 | * \param pCount Number of objects to reserve */ |
89 | void Reserve(const size_t pCount) |
90 | { |
91 | if( pCount > mCapacity ) |
92 | { |
93 | //We don't use mAllocator.PreAllocate, because we want our array to be continuous in memory. |
94 | Type* lNewArray = (Type*)mAllocator.AllocateRecords(pCount); |
95 | MoveArray(lNewArray, mArray, mSize); |
96 | mAllocator.FreeMemory(mArray); |
97 | mArray = lNewArray; |
98 | mCapacity = pCount; |
99 | } |
100 | } |
101 | |
102 | /** Appends n objects at the end of the array. |
103 | * \param pItem object to append |
104 | * \param pNCopies number of copies to append */ |
105 | void PushBack(const Type& pItem, const size_t pNCopies = 1) |
106 | { |
107 | if( mSize + pNCopies > mCapacity ) |
108 | { |
109 | size_t lNewSize = mCapacity + mCapacity / 2; //grow by 50% |
110 | if( mSize + pNCopies > lNewSize ) |
111 | { |
112 | lNewSize = mSize + pNCopies; |
113 | } |
114 | Reserve(lNewSize); |
115 | } |
116 | FBX_ASSERT(mSize + pNCopies <= mCapacity); |
117 | Fill(mArray + mSize, pItem, pNCopies); |
118 | mSize += pNCopies; |
119 | } |
120 | |
121 | /** Inserts n objects at the specified position. |
122 | * \param pIndex position index |
123 | * \param pItem object to insert |
124 | * \param pNCopies number of copies to append */ |
125 | void Insert(const size_t pIndex, const Type& pItem, const size_t pNCopies=1) |
126 | { |
127 | FBX_ASSERT(pIndex >= 0); |
128 | FBX_ASSERT(pIndex <= mSize); |
129 | Type lValue = pItem; // in case pItem is in array |
130 | if( pNCopies == 0 ) |
131 | { |
132 | } |
133 | else if( pIndex >= mSize ) |
134 | { |
135 | PushBack(pItem, pNCopies); |
136 | } |
137 | else if( mSize + pNCopies > mCapacity ) |
138 | { |
139 | size_t lNewSize = mCapacity + mCapacity / 2; //not enough room, grow by 50% |
140 | if( mSize + pNCopies > lNewSize ) |
141 | { |
142 | lNewSize = mSize + pNCopies; |
143 | } |
144 | |
145 | Type* lNewArray = (Type*)mAllocator.AllocateRecords(lNewSize); |
146 | MoveArray(lNewArray, mArray, pIndex); // copy prefix |
147 | Fill(lNewArray + pIndex, pItem, pNCopies); // copy values |
148 | MoveArray(lNewArray + pIndex + pNCopies, mArray + pIndex, mSize - pIndex); // copy suffix |
149 | mAllocator.FreeMemory(mArray); |
150 | mArray = lNewArray; |
151 | mSize += pNCopies; |
152 | mCapacity = lNewSize; |
153 | } |
154 | else |
155 | { |
156 | // copy suffix backwards |
157 | MoveArrayBackwards(mArray + pIndex + pNCopies, mArray + pIndex, mSize - pIndex); |
158 | Fill(mArray + pIndex, pItem, pNCopies); // copy values |
159 | mSize += pNCopies; |
160 | } |
161 | } |
162 | |
163 | /** Removes n objects at the end. |
164 | * \param pNElements number of objects to remove */ |
165 | void PopBack(size_t pNElements=1) |
166 | { |
167 | FBX_ASSERT(pNElements <= mSize); |
168 | for( size_t i = mSize - pNElements; i < mSize; ++i ) |
169 | { |
170 | mArray[i].~Type(); |
171 | } |
172 | mSize -= pNElements; |
173 | } |
174 | |
175 | /** Removes n objects at the specified position. |
176 | * \param pIndex position index |
177 | * \param pNElements number of objects to remove */ |
178 | void Remove(const size_t pIndex, size_t pNElements=1) |
179 | { |
180 | FBX_ASSERT(pIndex >= 0); |
181 | FBX_ASSERT(pIndex <= mSize); |
182 | FBX_ASSERT(pIndex + pNElements <= mSize); |
183 | if( pIndex + pNElements >= mSize ) |
184 | { |
185 | PopBack(pNElements); |
186 | } |
187 | else |
188 | { |
189 | for( size_t i = pIndex; i < pIndex + pNElements; ++i ) |
190 | { |
191 | mArray[i].~Type(); |
192 | } |
193 | MoveOverlappingArray(&mArray[pIndex], &mArray[pIndex + pNElements], mSize - pIndex - pNElements); |
194 | mSize -= pNElements; |
195 | } |
196 | } |
197 | |
198 | /** Gets nth object in the array. |
199 | * \param pIndex position index */ |
200 | Type& operator[](const size_t pIndex) |
201 | { |
202 | return mArray[pIndex]; |
203 | } |
204 | |
205 | /** Gets nth object in the array. |
206 | * \param pIndex position index */ |
207 | const Type& operator[](const size_t pIndex) const |
208 | { |
209 | return mArray[pIndex]; |
210 | } |
211 | |
212 | /** Retrieve the first item in the array. |
213 | * \return The first item in the array. */ |
214 | Type& First() |
215 | { |
216 | return operator[](0); |
217 | } |
218 | |
219 | /** Retrieve the first item in the array. |
220 | * \return The first item in the array. */ |
221 | const Type& First() const |
222 | { |
223 | return operator[](0); |
224 | } |
225 | |
226 | /** Retrieve the last item in the array. |
227 | * \return The last item in the array. */ |
228 | Type& Last() |
229 | { |
230 | return operator[](mSize-1); |
231 | } |
232 | |
233 | /** Retrieve the last item in the array. |
234 | * \return The last item in the array. */ |
235 | const Type& Last() const |
236 | { |
237 | return operator[](mSize-1); |
238 | } |
239 | |
240 | /** Find first matching element, from first to last. |
241 | * \param pItem The item to try to find in the array. |
242 | * \param pStartIndex The index to start searching from. |
243 | * \return Index of the first matching item, otherwise returns -1 (equivalent of SIZE_MAX for size_t). */ |
244 | size_t Find(const Type& pItem, const size_t pStartIndex=0) const |
245 | { |
246 | for( size_t i = pStartIndex; i < mSize; ++i ) |
247 | { |
248 | if( operator[](i) == pItem ) return i; |
249 | } |
250 | return -1; |
251 | } |
252 | |
253 | /** Assignment operator. |
254 | * \remarks The copy constructor of \c Type will be invoked in order to copy the value of elements to the new array. */ |
255 | FbxDynamicArray& operator=(const FbxDynamicArray& pArray) |
256 | { |
257 | Reserve(pArray.mCapacity); |
258 | CopyArray(mArray, pArray.mArray, pArray.mSize); |
259 | mSize = pArray.mSize; |
260 | return *this; |
261 | } |
262 | |
263 | /***************************************************************************************************************************** |
264 | ** WARNING! Anything beyond these lines is for internal use, may not be documented and is subject to change without notice! ** |
265 | *****************************************************************************************************************************/ |
266 | #ifndef DOXYGEN_SHOULD_SKIP_THIS |
267 | private: |
268 | static void CopyArray(Type* pDest, const Type* pSrc, size_t pCount) |
269 | { |
270 | for( int i = 0; i < int(pCount); i++ ) |
271 | { |
272 | new(&(pDest[i])) Type(pSrc[i]); //in-place new won't allocate memory, so it is safe |
273 | } |
274 | } |
275 | |
276 | static void MoveArray(Type* pDest, const Type* pSrc, size_t pCount) |
277 | { |
278 | for( int i = 0; i < int(pCount); i++ ) |
279 | { |
280 | new(&(pDest[i])) Type(pSrc[i]); //in-place new won't allocate memory, so it is safe |
281 | } |
282 | |
283 | for( int i = 0; i < int(pCount); i++ ) |
284 | { |
285 | pSrc[i].~Type(); |
286 | } |
287 | } |
288 | |
289 | static void MoveOverlappingArray(Type* pDest, const Type* pSrc, size_t pCount) |
290 | { |
291 | for( int i = 0; i < int(pCount); i++ ) |
292 | { |
293 | new(&(pDest[i])) Type(pSrc[i]); //in-place new won't allocate memory, so it is safe |
294 | pSrc[i].~Type(); |
295 | } |
296 | } |
297 | |
298 | static void MoveArrayBackwards(Type* pDest, const Type* pSrc, size_t pCount) |
299 | { |
300 | for( int i = 0; i < int(pCount); ++i ) |
301 | { |
302 | new(&(pDest[pCount-1-i])) Type(pSrc[pCount-1-i]); //in-place new won't allocate memory, so it is safe |
303 | pSrc[pCount-1-i].~Type(); |
304 | } |
305 | } |
306 | |
307 | static void Fill(Type* pDest, const Type& pItem, size_t pCount) |
308 | { |
309 | for( int i = 0; i < int(pCount); i++ ) |
310 | { |
311 | new(&(pDest[i])) Type(pItem); //in-place new won't allocate memory, so it is safe |
312 | } |
313 | } |
314 | |
315 | Type* mArray; |
316 | size_t mCapacity; |
317 | size_t mSize; |
318 | Allocator mAllocator; |
319 | #endif /* !DOXYGEN_SHOULD_SKIP_THIS *****************************************************************************************/ |
320 | }; |
321 | |
322 | #include <fbxsdk/fbxsdk_nsend.h> |
323 | |
324 | #endif /* _FBXSDK_CORE_BASE_DYNAMICARRAY_H_ */ |
325 | |