| 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 fbxarray.h |
| 13 | #ifndef _FBXSDK_CORE_BASE_ARRAY_H_ |
| 14 | #define _FBXSDK_CORE_BASE_ARRAY_H_ |
| 15 | |
| 16 | #include <fbxsdk/fbxsdk_def.h> |
| 17 | |
| 18 | #include <fbxsdk/fbxsdk_nsbegin.h> |
| 19 | |
| 20 | /** Class for array of basic elements such as pointers and basic types. This class will not |
| 21 | * call constructor and destructor for elements, thus it is not suitable for object references. |
| 22 | * Memory allocations are always done in a single contiguous memory region. */ |
| 23 | template <class T> class FbxArray |
| 24 | { |
| 25 | public: |
| 26 | //! Element compare function pointer definition |
| 27 | typedef int (*CompareFunc)(const void*, const void*); |
| 28 | |
| 29 | //! Constructor. |
| 30 | FbxArray() : mSize(0), mCapacity(0), mArray(NULL){} |
| 31 | |
| 32 | //! Reserve constructor. |
| 33 | FbxArray(const int pCapacity) : mSize(0), mCapacity(0), mArray(NULL){ if( pCapacity > 0 ) Reserve(pCapacity); } |
| 34 | |
| 35 | //! Copy constructor. |
| 36 | FbxArray(const FbxArray& pArray) : mSize(0), mCapacity(0), mArray(NULL){ *this = pArray; } |
| 37 | |
| 38 | /** Destructor. |
| 39 | * \remark The destructor for each element will not be called. */ |
| 40 | ~FbxArray(){ Clear(); } |
| 41 | |
| 42 | /** Insert an element at the given position, growing the array if capacity is not sufficient. |
| 43 | * \param pIndex Position where to insert the element. Must be a positive value. |
| 44 | * \param pElement Element to insert in the array. |
| 45 | * \param pCompact If \c true and capacity is exceeded, grow capacity by one, otherwise double capacity (default). |
| 46 | * \return -1 if insert failed, otherwise the position of the inserted element in the array. |
| 47 | * \remark If the given index is greater than Size(), the element is appended at the end. Use compact mode only if you need to save memory. */ |
| 48 | inline int InsertAt(const int pIndex, const T& pElement, bool pCompact=false) |
| 49 | { |
| 50 | FBX_ASSERT_RETURN_VALUE(pIndex >= 0, -1); |
| 51 | int lIndex = FbxMin(pIndex, mSize); |
| 52 | if( mSize >= mCapacity ) |
| 53 | { |
| 54 | T lElement = pElement; //Copy element because we might move memory |
| 55 | int lNewCapacity = FbxMax(pCompact ? mCapacity + 1 : mCapacity * 2, 1); //We always double capacity when not compacting |
| 56 | T* lArray = Allocate(lNewCapacity); |
| 57 | FBX_ASSERT_RETURN_VALUE(lArray, -1); |
| 58 | mArray = lArray; |
| 59 | mCapacity = lNewCapacity; |
| 60 | return InsertAt(pIndex, lElement); //Insert copied element because reference might be moved |
| 61 | } |
| 62 | |
| 63 | if( lIndex < mSize ) //Move elements to leave a space open to insert the new element |
| 64 | { |
| 65 | //If pElement is inside memmove range, copy element and insert copy instead |
| 66 | if( (&pElement >= &mArray[lIndex]) && (&pElement < &mArray[mSize]) ) |
| 67 | { |
| 68 | T lElement = pElement; |
| 69 | return InsertAt(pIndex, lElement); |
| 70 | } |
| 71 | memmove(&mArray[lIndex + 1], &mArray[lIndex], (mSize - lIndex) * sizeof(T)); |
| 72 | } |
| 73 | |
| 74 | memcpy(&mArray[lIndex], &pElement, sizeof(T)); |
| 75 | mSize++; |
| 76 | |
| 77 | return lIndex; |
| 78 | } |
| 79 | |
| 80 | /** Append an element at the end of the array, doubling the array if capacity is not sufficient. |
| 81 | * \param pElement Element to append to the array. |
| 82 | * \return -1 if add failed, otherwise the position of the added element in the array. */ |
| 83 | inline int Add(const T& pElement) |
| 84 | { |
| 85 | return InsertAt(mSize, pElement); |
| 86 | } |
| 87 | |
| 88 | /** Append an element at the end of array, if not already present, doubling the array if capacity is not sufficient. |
| 89 | * \param pElement Element to append to the array. |
| 90 | * \return -1 if add failed, otherwise the position of the added element in the array. */ |
| 91 | inline int AddUnique(const T& pElement) |
| 92 | { |
| 93 | int lIndex = Find(pElement); |
| 94 | return ( lIndex == -1 ) ? Add(pElement) : lIndex; |
| 95 | } |
| 96 | |
| 97 | /** Append an element at the end of the array, growing the array by one element if capacity is not sufficient. |
| 98 | * \param pElement Element to append to the array. |
| 99 | * \return -1 if add failed, otherwise the position of the added element in the array. */ |
| 100 | inline int AddCompact(const T& pElement) |
| 101 | { |
| 102 | return InsertAt(mSize, pElement, true); |
| 103 | } |
| 104 | |
| 105 | /** Retrieve the number of element contained in the array. To increase the capacity without increasing the size, please use Reserve(). |
| 106 | * \return The number of element in the array. |
| 107 | * \remark The size of the array cannot exceed its capacity. */ |
| 108 | inline int Size() const { return mSize; } |
| 109 | |
| 110 | /** Retrieve the current allocated memory capacity of the array. |
| 111 | * \return The capacity of the array in number of element. |
| 112 | * \remark The capacity will always be greater or equal to its size. */ |
| 113 | inline int Capacity() const { return mCapacity; } |
| 114 | |
| 115 | /** Retrieve a reference of the element at given index position in the array. |
| 116 | * \param pIndex Position of element in the array. |
| 117 | * \return A reference to the element at the specified position in the array. |
| 118 | * \remark No error will be thrown if the index is out of bounds. */ |
| 119 | inline T& operator[](const int pIndex) const |
| 120 | { |
| 121 | #ifdef _DEBUG |
| 122 | FBX_ASSERT_MSG(pIndex >= 0, "Index is out of range!" ); |
| 123 | if( pIndex >= mSize ) |
| 124 | { |
| 125 | if( pIndex < mCapacity ) |
| 126 | { |
| 127 | FBX_ASSERT_NOW("Index is out of range, but not outside of capacity! Call SetAt() to use reserved memory." ); |
| 128 | } |
| 129 | else FBX_ASSERT_NOW("Index is out of range!" ); |
| 130 | } |
| 131 | #endif |
| 132 | return (T&)mArray[pIndex]; |
| 133 | } |
| 134 | |
| 135 | /** Retrieve a copy of the element at given index position in the array. |
| 136 | * \param pIndex Position of element in the array. |
| 137 | * \return The value of the element at the specified position in the array. |
| 138 | * \remark No error will be thrown if the index is out of bounds. */ |
| 139 | inline T GetAt(const int pIndex) const |
| 140 | { |
| 141 | return operator[](pIndex); |
| 142 | } |
| 143 | |
| 144 | /** Retrieve a copy of the first element. |
| 145 | * \return Copy of the first element. |
| 146 | * \remark The array should have at least one element and no error will be thrown if the array is empty. */ |
| 147 | inline T GetFirst() const |
| 148 | { |
| 149 | return GetAt(0); |
| 150 | } |
| 151 | |
| 152 | /** Retrieve a copy of the last element. |
| 153 | * \return Copy of the last element. |
| 154 | * \remark The array should have at least one element and no error will be thrown if the array is empty. */ |
| 155 | inline T GetLast() const |
| 156 | { |
| 157 | return GetAt(mSize-1); |
| 158 | } |
| 159 | |
| 160 | /** Find first matching element, from first to last. |
| 161 | * \param pElement The element to be compared to each of the elements. |
| 162 | * \param pStartIndex The position to start searching from. |
| 163 | * \return Position of first matching element or -1 if there is no matching element. */ |
| 164 | inline int Find(const T& pElement, const int pStartIndex=0) const |
| 165 | { |
| 166 | FBX_ASSERT_RETURN_VALUE(pStartIndex >= 0, -1); |
| 167 | for( int i = pStartIndex; i < mSize; ++i ) |
| 168 | { |
| 169 | if( operator[](i) == pElement ) return i; |
| 170 | } |
| 171 | return -1; |
| 172 | } |
| 173 | |
| 174 | /** Find first matching element, from last to first. |
| 175 | * \param pElement The element to be compared to each of the elements. |
| 176 | * \param pStartIndex The position to start searching from. |
| 177 | * \return Position of first matching element or -1 if there is no matching element. */ |
| 178 | inline int FindReverse(const T& pElement, const int pStartIndex=FBXSDK_INT_MAX) const |
| 179 | { |
| 180 | for( int i = FbxMin(pStartIndex, mSize-1); i >= 0; --i ) |
| 181 | { |
| 182 | if( operator[](i) == pElement ) return i; |
| 183 | } |
| 184 | return -1; |
| 185 | } |
| 186 | |
| 187 | /** Request for allocation of additional memory without inserting new elements. After the memory has been reserved, please use SetAt() to initialize elements. |
| 188 | * \param pCapacity The number of additional element memory allocation requested. |
| 189 | * \return \c true if the memory allocation succeeded or if the capacity is unchanged, \c false otherwise. |
| 190 | * \remark If the requested capacity is less than or equal to the current capacity, this call has no effect. In either case, Size() is unchanged. */ |
| 191 | inline bool Reserve(const int pCapacity) |
| 192 | { |
| 193 | FBX_ASSERT_RETURN_VALUE(pCapacity > 0, false); |
| 194 | if( pCapacity > mCapacity ) |
| 195 | { |
| 196 | T* lArray = Allocate(pCapacity); |
| 197 | FBX_ASSERT_RETURN_VALUE(lArray, false); |
| 198 | mArray = lArray; |
| 199 | mCapacity = pCapacity; |
| 200 | |
| 201 | //Initialize new memory to zero |
| 202 | memset(&mArray[mSize], 0, (mCapacity - mSize) * sizeof(T)); |
| 203 | } |
| 204 | return true; |
| 205 | } |
| 206 | |
| 207 | /** Set the element at given position in the array. |
| 208 | * \param pIndex Position of element in the array. |
| 209 | * \param pElement The new element. |
| 210 | * \remark If the index is outside range, and outside capacity, this call has no effect. However, if index is |
| 211 | * within capacity range, element count is increased such that Size() will become pIndex + 1. */ |
| 212 | inline void SetAt(const int pIndex, const T& pElement) |
| 213 | { |
| 214 | FBX_ASSERT_RETURN(pIndex < mCapacity); |
| 215 | if( pIndex >= mSize ) mSize = pIndex + 1; |
| 216 | if( mArray ) memcpy(&mArray[pIndex], &pElement, sizeof(T)); |
| 217 | } |
| 218 | |
| 219 | /** Set the value of the first element. |
| 220 | * \param pElement The new value of the last element. |
| 221 | * \remark The array should have at least one element and no error will be thrown if the array is empty. */ |
| 222 | inline void SetFirst(const T& pElement) |
| 223 | { |
| 224 | SetAt(0, pElement); |
| 225 | } |
| 226 | |
| 227 | /** Set the value of the last element. |
| 228 | * \param pElement The new value of the last element. |
| 229 | * \remark The array should have at least one element and no error will be thrown if the array is empty. */ |
| 230 | inline void SetLast(const T& pElement) |
| 231 | { |
| 232 | SetAt(mSize-1, pElement); |
| 233 | } |
| 234 | |
| 235 | /** Remove an element at the given position in the array. |
| 236 | * \param pIndex Position of the element to remove. |
| 237 | * \return Removed element. |
| 238 | * \remark No error will be thrown if the index is out of bounds. */ |
| 239 | inline T RemoveAt(const int pIndex) |
| 240 | { |
| 241 | T lElement = GetAt(pIndex); |
| 242 | if( pIndex + 1 < mSize ) |
| 243 | { |
| 244 | memmove(&mArray[pIndex], &mArray[pIndex + 1], (mSize - pIndex - 1) * sizeof(T)); |
| 245 | } |
| 246 | mSize--; |
| 247 | return lElement; |
| 248 | } |
| 249 | |
| 250 | /** Remove the first element in the array. |
| 251 | * \return Removed element. |
| 252 | * \remark The array should have at least one element and no error will be thrown if the array is empty. */ |
| 253 | inline T RemoveFirst() |
| 254 | { |
| 255 | return RemoveAt(0); |
| 256 | } |
| 257 | |
| 258 | /** Remove the last element in the array. |
| 259 | * \return Removed element. |
| 260 | * \remark The array should have at least one element and no error will be thrown if the array is empty. */ |
| 261 | inline T RemoveLast() |
| 262 | { |
| 263 | return RemoveAt(mSize-1); |
| 264 | } |
| 265 | |
| 266 | /** Remove first matching element in the array. |
| 267 | * \param pElement Element to be removed. |
| 268 | * \return \c true if a matching element is found and removed, \c false otherwise. */ |
| 269 | inline bool RemoveIt(const T& pElement) |
| 270 | { |
| 271 | int Index = Find(pElement); |
| 272 | if( Index >= 0 ) |
| 273 | { |
| 274 | RemoveAt(Index); |
| 275 | return true; |
| 276 | } |
| 277 | return false; |
| 278 | } |
| 279 | |
| 280 | /** Remove a range of elements at the given position in the array. |
| 281 | * \param pIndex Begin position of the elements to remove. |
| 282 | * \param pCount The count of elements to remove. |
| 283 | * \return \c true if successful, otherwise \c false. */ |
| 284 | inline void RemoveRange(const int pIndex, const int pCount) |
| 285 | { |
| 286 | if( pIndex + pCount < mSize ) |
| 287 | { |
| 288 | memmove(&mArray[pIndex], &mArray[pIndex + pCount], (mSize - pIndex - pCount) * sizeof(T)); |
| 289 | } |
| 290 | mSize -= pCount; |
| 291 | } |
| 292 | |
| 293 | /** Inserts or erases elements at the end such that Size() becomes pSize, increasing capacity if needed. Please use SetAt() to initialize any new elements. |
| 294 | * \param pSize The new count of elements to set the array to. Must be greater or equal to zero. |
| 295 | * \return \c true if the memory (re)allocation succeeded, \c false otherwise. |
| 296 | * \remark If the requested element count is less than or equal to the current count, elements are freed from memory. Otherwise, the array grows and elements are unchanged. */ |
| 297 | inline bool Resize(const int pSize) |
| 298 | { |
| 299 | if( pSize == mSize && mSize == mCapacity ) return true; |
| 300 | |
| 301 | if( pSize == 0 ) |
| 302 | { |
| 303 | Clear(); |
| 304 | return true; |
| 305 | } |
| 306 | |
| 307 | FBX_ASSERT_RETURN_VALUE(pSize > 0, false); |
| 308 | if( pSize != mCapacity ) |
| 309 | { |
| 310 | T* lArray = Allocate(pSize); |
| 311 | FBX_ASSERT_RETURN_VALUE(lArray, false); |
| 312 | mArray = lArray; |
| 313 | } |
| 314 | |
| 315 | if( pSize > mCapacity ) //Initialize new memory to zero |
| 316 | { |
| 317 | memset(&mArray[mSize], 0, (pSize - mSize) * sizeof(T)); |
| 318 | } |
| 319 | |
| 320 | mSize = pSize; |
| 321 | mCapacity = pSize; |
| 322 | return true; |
| 323 | } |
| 324 | |
| 325 | /** Increase size of array by the specified size. |
| 326 | * \param pSize The size to add to the array size. |
| 327 | * \return \c true if operation succeeded, \c false otherwise. */ |
| 328 | inline bool Grow(const int pSize) |
| 329 | { |
| 330 | return Resize(mSize + pSize); |
| 331 | } |
| 332 | |
| 333 | /** Reduce size of array by the specified size. |
| 334 | * \param pSize The size to remove from the array size. |
| 335 | * \return \c true if operation succeeded, \c false otherwise. */ |
| 336 | inline bool Shrink(const int pSize) |
| 337 | { |
| 338 | return Resize(mSize - pSize); |
| 339 | } |
| 340 | |
| 341 | /** Compact the array so that its capacity is the same as its size. |
| 342 | * \return \c true if operation succeeded, \c false otherwise. */ |
| 343 | inline bool Compact() |
| 344 | { |
| 345 | return Resize(mSize); |
| 346 | } |
| 347 | |
| 348 | /** Reset the number of element to zero and free the memory allocated. |
| 349 | * \remark This only free the memory allocated by the array, and doesn't call the destructor of each element. */ |
| 350 | inline void Clear() |
| 351 | { |
| 352 | if( mArray != NULL ) |
| 353 | { |
| 354 | mSize = 0; |
| 355 | mCapacity = 0; |
| 356 | FbxFree(mArray); |
| 357 | mArray = NULL; |
| 358 | } |
| 359 | } |
| 360 | |
| 361 | /** Sort the array using the specified compare function pointer |
| 362 | * \param pCompareFunc The compare function to use to sort elements. */ |
| 363 | inline void Sort(CompareFunc pCompareFunc) |
| 364 | { |
| 365 | qsort(mArray, mSize, sizeof(T), pCompareFunc); |
| 366 | } |
| 367 | |
| 368 | //! Get pointer to internal array of elements. |
| 369 | inline T* GetArray() const { return mArray ? (T*)mArray : NULL; } |
| 370 | |
| 371 | //! Cast operator. |
| 372 | inline operator T* (){ return mArray ? (T*)mArray : NULL; } |
| 373 | |
| 374 | /** Append another array at the end of this array. |
| 375 | * \param pOther The other array to append to this array. */ |
| 376 | inline void AddArray(const FbxArray<T>& pOther) |
| 377 | { |
| 378 | if( Grow(pOther.mSize) ) |
| 379 | { |
| 380 | memcpy(&mArray[mSize - pOther.mSize], pOther.mArray, pOther.mSize * sizeof(T)); |
| 381 | } |
| 382 | } |
| 383 | |
| 384 | /** Append the elements of another array at the end of this array if they are not present. |
| 385 | * \param pOther Another array. */ |
| 386 | inline void AddArrayNoDuplicate(const FbxArray<T>& pOther) |
| 387 | { |
| 388 | for( int i = 0, c = pOther.mSize; i < c; ++i ) |
| 389 | { |
| 390 | AddUnique(pOther[i]); |
| 391 | } |
| 392 | } |
| 393 | |
| 394 | /** Remove the elements of another array from this array is they are present. |
| 395 | * \param pOther Another array. */ |
| 396 | inline void RemoveArray(const FbxArray<T>& pOther) |
| 397 | { |
| 398 | for( int i = 0, c = pOther.mSize; i < c; ++i ) |
| 399 | { |
| 400 | RemoveIt(pOther[i]); |
| 401 | } |
| 402 | } |
| 403 | |
| 404 | /** Operator to copy elements of an array. |
| 405 | * \return this array containing a copy of pOther elements. */ |
| 406 | inline FbxArray<T>& operator=(const FbxArray<T>& pOther) |
| 407 | { |
| 408 | if( this != &pOther ) |
| 409 | { |
| 410 | if( Resize(pOther.mSize) ) |
| 411 | { |
| 412 | memcpy(mArray, pOther.mArray, pOther.mSize * sizeof(T)); |
| 413 | } |
| 414 | } |
| 415 | return *this; |
| 416 | } |
| 417 | |
| 418 | /** Operator to compare elements of an array. |
| 419 | * \return \c true if the two arrays are equal, otherwise \c false. */ |
| 420 | inline bool operator==(const FbxArray<T>& pOther) const |
| 421 | { |
| 422 | if( this == &pOther ) return true; |
| 423 | if( mSize != pOther.mSize ) return false; |
| 424 | return memcmp(mArray, pOther.mArray, sizeof(T) * mSize) == 0; |
| 425 | } |
| 426 | |
| 427 | /***************************************************************************************************************************** |
| 428 | ** WARNING! Anything beyond these lines is for internal use, may not be documented and is subject to change without notice! ** |
| 429 | *****************************************************************************************************************************/ |
| 430 | #ifndef DOXYGEN_SHOULD_SKIP_THIS |
| 431 | inline int GetCount() const { return mSize; } |
| 432 | |
| 433 | private: |
| 434 | inline T* Allocate(const int pCapacity) |
| 435 | { |
| 436 | return (T*)FbxRealloc(mArray, pCapacity * sizeof(T)); |
| 437 | } |
| 438 | |
| 439 | int mSize; |
| 440 | int mCapacity; |
| 441 | T* mArray; |
| 442 | |
| 443 | #if defined(FBXSDK_COMPILER_MSC) |
| 444 | //Previously class FbxArray is for pointers. Somehow, it's used to store other types. Here's a compile-time checking for known incompatible classes. |
| 445 | //If it happens you find new incompatible ones, declare them with macro FBXSDK_INCOMPATIBLE_WITH_ARRAY. Also see file fbxstring.h. |
| 446 | FBX_ASSERT_STATIC(FBXSDK_IS_SIMPLE_TYPE(T) || __is_enum(T) || (__has_trivial_constructor(T)&&__has_trivial_destructor(T)) || !FBXSDK_IS_INCOMPATIBLE_WITH_ARRAY(T)); |
| 447 | #endif |
| 448 | |
| 449 | #endif /* !DOXYGEN_SHOULD_SKIP_THIS *****************************************************************************************/ |
| 450 | }; |
| 451 | |
| 452 | //! Call FbxFree on each element of the array, and then clear it. |
| 453 | template <class T> inline void FbxArrayFree(FbxArray<T>& pArray) |
| 454 | { |
| 455 | for( int i = 0, c = pArray.Size(); i < c; ++i ) |
| 456 | { |
| 457 | FbxFree(pArray[i]); |
| 458 | } |
| 459 | pArray.Clear(); |
| 460 | } |
| 461 | |
| 462 | //! Call FbxDelete on each element of the array, and then clear it. |
| 463 | template <class T> inline void FbxArrayDelete(FbxArray<T>& pArray) |
| 464 | { |
| 465 | for( int i = 0, c = pArray.Size(); i < c; ++i ) |
| 466 | { |
| 467 | FbxDelete(pArray[i]); |
| 468 | } |
| 469 | pArray.Clear(); |
| 470 | } |
| 471 | |
| 472 | //! Call Destroy on each element of the array, and then clear it. |
| 473 | template <class T> inline void FbxArrayDestroy(FbxArray<T>& pArray) |
| 474 | { |
| 475 | for( int i = 0, c = pArray.Size(); i < c; ++i ) |
| 476 | { |
| 477 | (pArray[i])->Destroy(); |
| 478 | } |
| 479 | pArray.Clear(); |
| 480 | } |
| 481 | |
| 482 | //! Make sure to break build if someone try to make FbxArray<FbxArray<T>>, which is not supported. |
| 483 | template <class T> FBXSDK_INCOMPATIBLE_WITH_ARRAY_TEMPLATE(FbxArray<T>); |
| 484 | |
| 485 | #include <fbxsdk/fbxsdk_nsend.h> |
| 486 | |
| 487 | #endif /* _FBXSDK_CORE_BASE_ARRAY_H_ */ |
| 488 | |