// Copyright (c) 2002-2023 OPEN CASCADE SAS // // This file is part of Open CASCADE Technology software library. // // This library is free software; you can redistribute it and/or modify it under // the terms of the GNU Lesser General Public License version 2.1 as published // by the Free Software Foundation, with special exception defined in the file // OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT // distribution for complete text of the license and disclaimer of any warranty. // // Alternatively, this file may be used under the terms of Open CASCADE // commercial license or contractual agreement. #ifndef NCollection_DynamicArray_HeaderFile #define NCollection_DynamicArray_HeaderFile #include "NCollection_Allocator.hxx" #include "NCollection_LinearVector.hxx" #include "Standard_DimensionMismatch.hxx" #include "Standard_OutOfMemory.hxx" #include "Standard_NotImplemented.hxx" #include "Standard_OutOfRange.hxx" #include "NCollection_DefineAlloc.hxx" #include "NCollection_Iterator.hxx" #include "NCollection_OccAllocator.hxx" #include "StdFail_NotDone.hxx" #include #include #include #include //! Class NCollection_DynamicArray (dynamic array of objects) //! //! The array's indices always start at 0. //! //! The Vector is always created with 0 length. It can be enlarged by two means: //! 1. Calling the method Append (val) - then "val" is added to the end of the //! vector (the vector length is incremented) //! 2. Calling the method SetValue (i, val) - if "i" is greater than or equal //! to the current length of the vector, the vector is enlarged to accomo- //! date this index //! //! The methods Append and SetValue return a non-const reference to the copied //! object inside the vector. This reference is guaranteed to be valid until //! the vector is destroyed. It can be used to access the vector member directly //! or to pass its address to other data structures. //! //! The vector iterator remembers the length of the vector at the moment of the //! creation or initialisation of the iterator. Therefore the iteration begins //! at index 0 and stops at the index equal to (remembered_length-1). It is OK //! to enlarge the vector during the iteration. template class NCollection_DynamicArray { public: //! Memory allocation DEFINE_STANDARD_ALLOC; DEFINE_NCOLLECTION_ALLOC; public: typedef NCollection_OccAllocator allocator_type; typedef NCollection_LinearVector vector; public: // Define various type aliases for convenience using value_type = TheItemType; using size_type = size_t; using difference_type = size_t; using pointer = TheItemType*; using const_pointer = const TheItemType*; using reference = TheItemType&; using const_reference = const TheItemType&; public: template class DynamicIterator { public: using iterator_category = std::random_access_iterator_tag; using value_type = TheItemType; using difference_type = ptrdiff_t; using pointer = typename std::conditional::type; using reference = typename std::conditional::type; public: DynamicIterator() noexcept : myOwner(nullptr), myIndex(0), myUsedSize(0), myInternalSize(1), myBlockShift(0), myBlockMask(0), myBlockIndex(0), myCurrPtr(nullptr), myBlockEnd(nullptr) { } DynamicIterator(const NCollection_DynamicArray& theArray) noexcept : DynamicIterator(0, theArray) { } DynamicIterator(const size_t theIndex, const NCollection_DynamicArray& theArray) noexcept : myOwner(&theArray), myIndex(theIndex), myUsedSize(theArray.myUsedSize), myInternalSize(theArray.myInternalSize), myBlockShift(theArray.myBlockShift), myBlockMask(theArray.myBlockMask), myBlockIndex(0), myCurrPtr(nullptr), myBlockEnd(nullptr) { setIndex(theIndex); } DynamicIterator(const DynamicIterator& theOther) noexcept : myOwner(theOther.myOwner), myIndex(theOther.myIndex), myUsedSize(theOther.myUsedSize), myInternalSize(theOther.myInternalSize), myBlockShift(theOther.myBlockShift), myBlockMask(theOther.myBlockMask), myBlockIndex(theOther.myBlockIndex), myCurrPtr(theOther.myCurrPtr), myBlockEnd(theOther.myBlockEnd) { } DynamicIterator& operator=(const DynamicIterator& theOther) noexcept { myOwner = theOther.myOwner; myIndex = theOther.myIndex; myUsedSize = theOther.myUsedSize; myInternalSize = theOther.myInternalSize; myBlockShift = theOther.myBlockShift; myBlockMask = theOther.myBlockMask; myBlockIndex = theOther.myBlockIndex; myCurrPtr = theOther.myCurrPtr; myBlockEnd = theOther.myBlockEnd; return *this; } public: bool operator==(const DynamicIterator& theOther) const noexcept { return myOwner == theOther.myOwner && myIndex == theOther.myIndex; } template bool operator==(const DynamicIterator& theOther) const noexcept { return myOwner == theOther.myOwner && myIndex == theOther.myIndex; } template bool operator!=(const DynamicIterator& theOther) const noexcept { return myOwner != theOther.myOwner || myIndex != theOther.myIndex; } bool operator!=(const DynamicIterator& theOther) const noexcept { return !(*this == theOther); } reference operator*() const noexcept { return *myCurrPtr; } pointer operator->() const noexcept { return myCurrPtr; } DynamicIterator& operator++() noexcept { ++myIndex; ++myCurrPtr; if (myIndex >= myUsedSize) { myCurrPtr = nullptr; myBlockEnd = nullptr; } else if (myCurrPtr == myBlockEnd) { ++myBlockIndex; myCurrPtr = blockStart(myBlockIndex); myBlockEnd = myCurrPtr + myInternalSize; } return *this; } DynamicIterator operator++(int) noexcept { DynamicIterator theOld(*this); ++(*this); return theOld; } DynamicIterator& operator--() noexcept { if (myIndex == myUsedSize) { setIndex(myUsedSize - 1); return *this; } --myIndex; if (myCurrPtr > blockStart(myBlockIndex)) { --myCurrPtr; } else { --myBlockIndex; myCurrPtr = blockStart(myBlockIndex) + (myInternalSize - 1); myBlockEnd = blockStart(myBlockIndex) + myInternalSize; } return *this; } DynamicIterator operator--(int) noexcept { DynamicIterator theOld(*this); --(*this); return theOld; } DynamicIterator& operator+=(const difference_type theOffset) noexcept { setIndex(static_cast(static_cast(myIndex) + theOffset)); return *this; } DynamicIterator operator+(const difference_type theOffset) const noexcept { DynamicIterator aTemp(*this); aTemp += theOffset; return aTemp; } DynamicIterator& operator-=(const difference_type theOffset) noexcept { return *this += -theOffset; } DynamicIterator operator-(const difference_type theOffset) const noexcept { DynamicIterator aTemp(*this); aTemp += -theOffset; return aTemp; } difference_type operator-(const DynamicIterator& theOther) const noexcept { return static_cast(myIndex) - static_cast(theOther.myIndex); } reference operator[](const difference_type theOffset) const noexcept { return *(*this + theOffset); } bool operator<(const DynamicIterator& theOther) const noexcept { return (*this - theOther) < 0; } bool operator>(const DynamicIterator& theOther) const noexcept { return theOther < *this; } bool operator<=(const DynamicIterator& theOther) const noexcept { return !(theOther < *this); } bool operator>=(const DynamicIterator& theOther) const noexcept { return !(*this < theOther); } friend DynamicIterator operator+(const difference_type theOffset, const DynamicIterator& theIter) noexcept { return theIter + theOffset; } friend class DynamicIterator; private: void setIndex(const size_t theIndex) noexcept { myIndex = theIndex; if (myIndex >= myUsedSize || myOwner == nullptr) { myCurrPtr = nullptr; myBlockEnd = nullptr; myBlockIndex = 0; return; } myBlockIndex = myIndex >> myBlockShift; const size_t aLocalIndex = myIndex & myBlockMask; myCurrPtr = blockStart(myBlockIndex) + aLocalIndex; myBlockEnd = blockStart(myBlockIndex) + myInternalSize; } TheItemType* blockStart(const size_t theBlockIndex) const noexcept { return myOwner->getArray()[theBlockIndex]; } private: const NCollection_DynamicArray* myOwner; size_t myIndex; size_t myUsedSize; size_t myInternalSize; size_t myBlockShift; size_t myBlockMask; size_t myBlockIndex; TheItemType* myCurrPtr; TheItemType* myBlockEnd; }; using iterator = DynamicIterator; using const_iterator = DynamicIterator; using Iterator = NCollection_Iterator>; public: const_iterator begin() const noexcept { return const_iterator(*this); } iterator begin() noexcept { return iterator(*this); } const_iterator cbegin() const noexcept { return const_iterator(*this); } iterator end() noexcept { return iterator(myUsedSize, *this); } const_iterator end() const noexcept { return const_iterator(myUsedSize, *this); } const_iterator cend() const noexcept { return const_iterator(myUsedSize, *this); } public: //! @name public methods NCollection_DynamicArray(const size_t theIncrement) : myAlloc(), myInternalSize(roundUpPow2(theIncrement)), myBlockShift(log2Pow2(myInternalSize)), myBlockMask(myInternalSize - 1), myUsedSize(0) { } NCollection_DynamicArray(const int theIncrement = 256) : NCollection_DynamicArray(static_cast(theIncrement < 1 ? 1 : theIncrement)) { } // Constructor taking an allocator explicit NCollection_DynamicArray(const size_t theIncrement, const occ::handle& theAllocator) : myAlloc(allocator_type(theAllocator)), myInternalSize(roundUpPow2(theIncrement)), myBlockShift(log2Pow2(myInternalSize)), myBlockMask(myInternalSize - 1), myUsedSize(0) { } explicit NCollection_DynamicArray(const int theIncrement, const occ::handle& theAllocator) : NCollection_DynamicArray(static_cast(theIncrement < 1 ? 1 : theIncrement), theAllocator) { } // Constructor taking an allocator explicit NCollection_DynamicArray(const size_t theIncrement, const allocator_type& theAllocator) : myAlloc(theAllocator), myInternalSize(roundUpPow2(theIncrement)), myBlockShift(log2Pow2(myInternalSize)), myBlockMask(myInternalSize - 1), myUsedSize(0) { } explicit NCollection_DynamicArray(const int theIncrement, const allocator_type& theAllocator) : NCollection_DynamicArray(static_cast(theIncrement < 1 ? 1 : theIncrement), theAllocator) { } //! Copy constructor NCollection_DynamicArray(const NCollection_DynamicArray& theOther) : myContainer(theOther.myContainer), myAlloc(theOther.myAlloc), myInternalSize(theOther.myInternalSize), myBlockShift(theOther.myBlockShift), myBlockMask(theOther.myBlockMask), myUsedSize(theOther.myUsedSize) { copyDate(); } NCollection_DynamicArray(NCollection_DynamicArray&& theOther) noexcept : myContainer(std::move(theOther.myContainer)), myAlloc(theOther.myAlloc), myInternalSize(theOther.myInternalSize), myBlockShift(theOther.myBlockShift), myBlockMask(theOther.myBlockMask), myUsedSize(theOther.myUsedSize) { theOther.myUsedSize = 0; } ~NCollection_DynamicArray() { Clear(true); } //! Total number of items in the vector. size_t Size() const noexcept { return myUsedSize; } //! Total number of items (legacy int-returning API). int Length() const noexcept { return static_cast(myUsedSize); } //! Method for consistency with other collections. //! @return Lower bound (inclusive) for iteration. int Lower() const noexcept { return 0; } //! Method for consistency with other collections. //! @return Upper bound (inclusive) for iteration. int Upper() const noexcept { return static_cast(myUsedSize) - 1; } //! Empty query bool IsEmpty() const noexcept { return myUsedSize == 0; } //! Assignment to the collection of the same type NCollection_DynamicArray& Assign(const NCollection_DynamicArray& theOther, const bool theOwnAllocator = true) { if (&theOther == this) { return *this; } if (!theOwnAllocator) { Clear(myAlloc != theOther.myAlloc); myAlloc = theOther.myAlloc; } else { Clear(false); } myContainer = theOther.myContainer; myInternalSize = theOther.myInternalSize; myBlockShift = theOther.myBlockShift; myBlockMask = theOther.myBlockMask; myUsedSize = theOther.myUsedSize; copyDate(); return *this; } NCollection_DynamicArray& Assign(NCollection_DynamicArray&& theOther) { if (&theOther == this) { return *this; } Clear(true); myContainer = std::move(theOther.myContainer); myAlloc = theOther.myAlloc; myInternalSize = theOther.myInternalSize; myBlockShift = theOther.myBlockShift; myBlockMask = theOther.myBlockMask; myUsedSize = theOther.myUsedSize; theOther.myUsedSize = 0; return *this; } //! Assignment operator NCollection_DynamicArray& operator=(const NCollection_DynamicArray& theOther) { return Assign(theOther, false); } //! Assignment operator NCollection_DynamicArray& operator=(NCollection_DynamicArray&& theOther) noexcept { return Assign(std::forward(theOther)); } //! Append reference Append(const TheItemType& theValue) { if (myUsedSize >= availableSize()) { expandArray(); } pointer aPnt = &at(myUsedSize++); myAlloc.construct(aPnt, theValue); return *aPnt; } //! Append reference Append(TheItemType&& theValue) { if (myUsedSize >= availableSize()) { expandArray(); } pointer aPnt = &at(myUsedSize++); myAlloc.construct(aPnt, std::forward(theValue)); return *aPnt; } //! Insert a value after the element at theIndex, shifting subsequent elements right. //! @param theIndex index after which to insert (must be in [0, Size()-1]) //! @param theValue value to insert //! @return reference to the inserted element reference InsertAfter(const size_t theIndex, const TheItemType& theValue) { Standard_OutOfRange_Raise_if(theIndex >= myUsedSize, "NCollection_DynamicArray::InsertAfter: index out of range"); Appended(); for (size_t i = myUsedSize - 1; i > theIndex + 1; --i) { at(i) = std::move(at(i - 1)); } at(theIndex + 1) = theValue; return at(theIndex + 1); } //! Insert a value after the element at theIndex (move version). reference InsertAfter(const size_t theIndex, TheItemType&& theValue) { Standard_OutOfRange_Raise_if(theIndex >= myUsedSize, "NCollection_DynamicArray::InsertAfter: index out of range"); Appended(); for (size_t i = myUsedSize - 1; i > theIndex + 1; --i) { at(i) = std::move(at(i - 1)); } at(theIndex + 1) = std::forward(theValue); return at(theIndex + 1); } reference InsertAfter(const int theIndex, const TheItemType& theValue) { Standard_OutOfRange_Raise_if(theIndex < 0, "NCollection_DynamicArray::InsertAfter: index out of range"); return InsertAfter(static_cast(theIndex), theValue); } reference InsertAfter(const int theIndex, TheItemType&& theValue) { Standard_OutOfRange_Raise_if(theIndex < 0, "NCollection_DynamicArray::InsertAfter: index out of range"); return InsertAfter(static_cast(theIndex), std::forward(theValue)); } //! Insert a value before the element at theIndex, shifting it and subsequent elements right. //! @param theIndex index before which to insert (must be in [0, Size()-1]) //! @param theValue value to insert //! @return reference to the inserted element reference InsertBefore(const size_t theIndex, const TheItemType& theValue) { Standard_OutOfRange_Raise_if(theIndex >= myUsedSize, "NCollection_DynamicArray::InsertBefore: index out of range"); Appended(); for (size_t i = myUsedSize - 1; i > theIndex; --i) { at(i) = std::move(at(i - 1)); } at(theIndex) = theValue; return at(theIndex); } //! Insert a value before the element at theIndex (move version). reference InsertBefore(const size_t theIndex, TheItemType&& theValue) { Standard_OutOfRange_Raise_if(theIndex >= myUsedSize, "NCollection_DynamicArray::InsertBefore: index out of range"); Appended(); for (size_t i = myUsedSize - 1; i > theIndex; --i) { at(i) = std::move(at(i - 1)); } at(theIndex) = std::forward(theValue); return at(theIndex); } reference InsertBefore(const int theIndex, const TheItemType& theValue) { Standard_OutOfRange_Raise_if(theIndex < 0, "NCollection_DynamicArray::InsertBefore: index out of range"); return InsertBefore(static_cast(theIndex), theValue); } reference InsertBefore(const int theIndex, TheItemType&& theValue) { Standard_OutOfRange_Raise_if(theIndex < 0, "NCollection_DynamicArray::InsertBefore: index out of range"); return InsertBefore(static_cast(theIndex), std::forward(theValue)); } void EraseLast() { if (myUsedSize == 0) { return; } if constexpr (!std::is_trivially_destructible_v) { TheItemType* aLastElem = &ChangeLast(); myAlloc.destroy(aLastElem); } myUsedSize--; } //! Appends an empty value and returns the reference to it reference Appended() { if (myUsedSize >= availableSize()) { expandArray(); } pointer aPnt = &at(myUsedSize++); myAlloc.construct(aPnt); return *aPnt; } //! Emplace one item at the end, constructing it in-place //! @param theArgs arguments forwarded to TheItemType constructor //! @return reference to the newly constructed item template reference EmplaceAppend(Args&&... theArgs) { if (myUsedSize >= availableSize()) { expandArray(); } pointer aPnt = &at(myUsedSize++); myAlloc.construct(aPnt, std::forward(theArgs)...); return *aPnt; } //! Emplace value at the specified index, constructing it in-place //! If the index is beyond current size, default-constructs intermediate elements //! @param theIndex index at which to emplace the value //! @param theArgs arguments forwarded to TheItemType constructor //! @return reference to the newly constructed item template reference EmplaceValue(const size_t theIndex, Args&&... theArgs) { ensureStorageForIndex(theIndex); const bool isExisting = theIndex < myUsedSize; if (!isExisting) { for (; myUsedSize < theIndex; myUsedSize++) { pointer aPnt = &at(myUsedSize); myAlloc.construct(aPnt); } myUsedSize++; } pointer aPnt = &at(theIndex); if (isExisting) { if constexpr (!std::is_trivially_destructible_v) { myAlloc.destroy(aPnt); } } myAlloc.construct(aPnt, std::forward(theArgs)...); return *aPnt; } template reference EmplaceValue(const int theIndex, Args&&... theArgs) { Standard_OutOfRange_Raise_if(theIndex < 0, "NCollection_DynamicArray::EmplaceValue: index out of range"); return EmplaceValue(static_cast(theIndex), std::forward(theArgs)...); } //! Operator() - query the const value const_reference operator()(const size_t theIndex) const noexcept { return at(theIndex); } const_reference operator()(const int theIndex) const noexcept { return at(static_cast(theIndex)); } //! Operator[] - query the const value const_reference operator[](const size_t theIndex) const noexcept { return at(theIndex); } const_reference operator[](const int theIndex) const noexcept { return at(static_cast(theIndex)); } const_reference Value(const size_t theIndex) const noexcept { return at(theIndex); } const_reference Value(const int theIndex) const noexcept { return at(static_cast(theIndex)); } //! @return first element const_reference First() const noexcept { return getArray()[0][0]; } //! @return first element reference ChangeFirst() noexcept { return getArray()[0][0]; } //! @return last element const_reference Last() const noexcept { return at(myUsedSize - 1); } //! @return last element reference ChangeLast() noexcept { return at(myUsedSize - 1); } //! Operator() - query the value reference operator()(const size_t theIndex) noexcept { return at(theIndex); } reference operator()(const int theIndex) noexcept { return at(static_cast(theIndex)); } //! Operator[] - query the value reference operator[](const size_t theIndex) noexcept { return at(theIndex); } reference operator[](const int theIndex) noexcept { return at(static_cast(theIndex)); } reference ChangeValue(const size_t theIndex) noexcept { return at(theIndex); } reference ChangeValue(const int theIndex) noexcept { return at(static_cast(theIndex)); } //! SetValue () - set or append a value reference SetValue(const size_t theIndex, const TheItemType& theValue) { ensureStorageForIndex(theIndex); const bool isExisting = theIndex < myUsedSize; if (!isExisting) { for (; myUsedSize < theIndex; myUsedSize++) { pointer aPnt = &at(myUsedSize); myAlloc.construct(aPnt); } myUsedSize++; } pointer aPnt = &at(theIndex); if (isExisting) { if constexpr (!std::is_trivially_destructible_v) { myAlloc.destroy(aPnt); } } myAlloc.construct(aPnt, theValue); return *aPnt; } //! SetValue () - set or append a value reference SetValue(const size_t theIndex, TheItemType&& theValue) { ensureStorageForIndex(theIndex); const bool isExisting = theIndex < myUsedSize; if (!isExisting) { for (; myUsedSize < theIndex; myUsedSize++) { pointer aPnt = &at(myUsedSize); myAlloc.construct(aPnt); } myUsedSize++; } pointer aPnt = &at(theIndex); if (isExisting) { if constexpr (!std::is_trivially_destructible_v) { myAlloc.destroy(aPnt); } } myAlloc.construct(aPnt, std::forward(theValue)); return *aPnt; } reference SetValue(const int theIndex, const TheItemType& theValue) { Standard_OutOfRange_Raise_if(theIndex < 0, "NCollection_DynamicArray::SetValue: index out of range"); return SetValue(static_cast(theIndex), theValue); } reference SetValue(const int theIndex, TheItemType&& theValue) { Standard_OutOfRange_Raise_if(theIndex < 0, "NCollection_DynamicArray::SetValue: index out of range"); return SetValue(static_cast(theIndex), std::forward(theValue)); } void Clear(const bool theReleaseMemory = false) { for (size_t aBlockInd = 0; aBlockInd < myContainer.Size(); aBlockInd++) { TheItemType* aCurStart = getArray()[aBlockInd]; if constexpr (!std::is_trivially_destructible_v) { const size_t aBlockStart = aBlockInd * myInternalSize; const size_t aCount = (myUsedSize > aBlockStart + myInternalSize) ? myInternalSize : (myUsedSize > aBlockStart ? myUsedSize - aBlockStart : 0); for (size_t anElemInd = 0; anElemInd < aCount; anElemInd++) { aCurStart[anElemInd].~TheItemType(); } } if (theReleaseMemory) { myAlloc.deallocate(aCurStart, myInternalSize); } } if (theReleaseMemory) { myContainer.Clear(theReleaseMemory); } myUsedSize = 0; } void SetIncrement(const size_t theIncrement) noexcept { if (myUsedSize != 0) { return; } myInternalSize = roundUpPow2(theIncrement); myBlockShift = log2Pow2(myInternalSize); myBlockMask = myInternalSize - 1; } void SetIncrement(const int theIncrement) noexcept { SetIncrement(static_cast(theIncrement < 1 ? 1 : theIncrement)); } friend iterator; friend const_iterator; protected: size_t availableSize() const noexcept { return static_cast(myContainer.Size()) << myBlockShift; } //! Ensure storage blocks exist to access theIndex. void ensureStorageForIndex(const size_t theIndex) { const size_t aRequiredBlocks = (theIndex >> myBlockShift) + 1; ensureBlockCount(aRequiredBlocks); } //! Ensure at least theBlockCount blocks are allocated in myContainer. void ensureBlockCount(const size_t theBlockCount) { if (theBlockCount > myContainer.Capacity()) { myContainer.Reserve(theBlockCount); } while (myContainer.Size() < theBlockCount) { expandArray(); } } TheItemType* expandArray() { TheItemType* aNewBlock = myAlloc.allocate(myInternalSize); myContainer.Append(aNewBlock); return aNewBlock; } reference at(const size_t theInd) noexcept { return getArray()[theInd >> myBlockShift][theInd & myBlockMask]; } const_reference at(const size_t theInd) const noexcept { return getArray()[theInd >> myBlockShift][theInd & myBlockMask]; } void copyDate() { size_t aUsedSize = 0; for (size_t aBlockInd = 0; aBlockInd < myContainer.Size(); aBlockInd++) { TheItemType* aCurStart = getArray()[aBlockInd]; TheItemType* aNewBlock = myAlloc.allocate(myInternalSize); if constexpr (std::is_trivially_copyable_v) { const size_t aCount = (myUsedSize - aUsedSize < myInternalSize) ? myUsedSize - aUsedSize : myInternalSize; std::memcpy(aNewBlock, aCurStart, aCount * sizeof(TheItemType)); aUsedSize += aCount; } else { for (size_t anElemInd = 0; anElemInd < myInternalSize && aUsedSize < myUsedSize; anElemInd++, aUsedSize++) { pointer aPnt = &aNewBlock[anElemInd]; myAlloc.construct(aPnt, aCurStart[anElemInd]); } } getArray()[aBlockInd] = aNewBlock; } } //! Wrapper to extract array of block pointers. TheItemType** getArray() noexcept { return myContainer.IsEmpty() ? nullptr : &myContainer[0]; } //! Wrapper to extract array of block pointers (const overload). TheItemType* const* getArray() const noexcept { return myContainer.IsEmpty() ? nullptr : &myContainer[0]; } //! Round up to the nearest power of 2 (returns theValue if already power of 2). //! Works correctly for both 32-bit and 64-bit size_t. static constexpr size_t roundUpPow2(const size_t theValue) noexcept { size_t v = (theValue < 1 ? 1 : theValue); v--; v |= v >> 1; v |= v >> 2; v |= v >> 4; v |= v >> 8; v |= v >> 16; if constexpr (sizeof(size_t) > 4) { v |= v >> 32; } v++; return v; } //! Compute log2 of a power-of-2 value. static constexpr size_t log2Pow2(const size_t theValue) noexcept { size_t aShift = 0; size_t v = theValue; while (v > 1) { v >>= 1; ++aShift; } return aShift; } protected: vector myContainer; allocator_type myAlloc; size_t myInternalSize; size_t myBlockShift; //!< log2(myInternalSize) for fast index-to-block mapping size_t myBlockMask; //!< myInternalSize - 1 for fast index-within-block mapping size_t myUsedSize; }; #endif // NCollection_DynamicArray_HeaderFile