Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2026-09-13 09:17:14

0001 // Copyright (c) 2026 OPEN CASCADE SAS
0002 //
0003 // This file is part of Open CASCADE Technology software library.
0004 //
0005 // This library is free software; you can redistribute it and/or modify it under
0006 // the terms of the GNU Lesser General Public License version 2.1 as published
0007 // by the Free Software Foundation, with special exception defined in the file
0008 // OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
0009 // distribution for complete text of the license and disclaimer of any warranty.
0010 //
0011 // Alternatively, this file may be used under the terms of Open CASCADE
0012 // commercial license or contractual agreement.
0013 
0014 #ifndef NCollection_LinearVector_HeaderFile
0015 #define NCollection_LinearVector_HeaderFile
0016 
0017 #include <NCollection_Allocator.hxx>
0018 #include <NCollection_Array1.hxx>
0019 #include <Standard_OutOfMemory.hxx>
0020 #include <Standard_OutOfRange.hxx>
0021 
0022 #include <algorithm>
0023 #include <cstddef>
0024 #include <cstring>
0025 #include <limits>
0026 #include <type_traits>
0027 #include <utility>
0028 
0029 //! Contiguous dynamic array using a flat memory buffer.
0030 //!
0031 //! Unlike NCollection_DynamicArray which uses segmented block storage,
0032 //! this container stores all elements in a single contiguous allocation,
0033 //! providing O(1) element access with a single pointer dereference.
0034 //!
0035 //! For trivially copyable types, growth uses Standard::Reallocate which
0036 //! can extend the buffer in-place without copying elements. For non-trivial
0037 //! types, growth allocates a new buffer and move-constructs elements.
0038 //!
0039 //! Indices are always 0-based.
0040 //!
0041 //! @warning Any operation that may grow the buffer - Append, Appended,
0042 //!          EmplaceAppend, SetValue past end, Resize, Reserve, InsertBefore,
0043 //!          InsertAfter, copy/move assignment - invalidates all iterators,
0044 //!          references, and raw pointers into the vector whenever it
0045 //!          actually reallocates. Erase/EraseLast also invalidate references
0046 //!          at or beyond the removed position.
0047 template <typename TheItemType>
0048 class NCollection_LinearVector
0049 {
0050 public:
0051   using value_type      = TheItemType;
0052   using size_type       = size_t;
0053   using pointer         = TheItemType*;
0054   using const_pointer   = const TheItemType*;
0055   using reference       = TheItemType&;
0056   using const_reference = const TheItemType&;
0057   using iterator        = TheItemType*;
0058   using const_iterator  = const TheItemType*;
0059   using allocator_type  = NCollection_Allocator<TheItemType>;
0060 
0061 public:
0062   //! Empty constructor.
0063   NCollection_LinearVector() noexcept = default;
0064 
0065   //! Constructor with pre-allocated capacity.
0066   //! Unlike std::vector(n), this constructor does not create elements.
0067   //! Use Resize() or NCollection_LinearVector(theSize, theValue) to construct items.
0068   //! @param[in] theCapacity number of elements to pre-allocate
0069   explicit NCollection_LinearVector(const size_t theCapacity)
0070   {
0071     if (theCapacity > 0)
0072     {
0073       myData     = myAlloc.allocate(theCapacity);
0074       myCapacity = theCapacity;
0075     }
0076   }
0077 
0078   //! Constructor creating theSize elements initialized to theValue.
0079   //! Equivalent to std::vector(n, val).
0080   //! @param[in] theSize   number of elements to construct
0081   //! @param[in] theValue  value to initialize each element with
0082   NCollection_LinearVector(const size_t theSize, const TheItemType& theValue)
0083   {
0084     Resize(theSize, theValue);
0085   }
0086 
0087   //! Copy constructor.
0088   NCollection_LinearVector(const NCollection_LinearVector& theOther)
0089   {
0090     if (theOther.mySize > 0)
0091     {
0092       myData     = myAlloc.allocate(theOther.mySize);
0093       myCapacity = theOther.mySize;
0094       mySize     = theOther.mySize;
0095       if constexpr (std::is_trivially_copyable_v<TheItemType>)
0096       {
0097         std::memcpy(myData, theOther.myData, mySize * sizeof(TheItemType));
0098       }
0099       else
0100       {
0101         for (size_t i = 0; i < mySize; ++i)
0102         {
0103           myAlloc.construct(myData + i, theOther.myData[i]);
0104         }
0105       }
0106     }
0107   }
0108 
0109   //! Move constructor.
0110   NCollection_LinearVector(NCollection_LinearVector&& theOther) noexcept
0111       : myData(theOther.myData),
0112         mySize(theOther.mySize),
0113         myCapacity(theOther.myCapacity)
0114   {
0115     theOther.myData     = nullptr;
0116     theOther.mySize     = 0;
0117     theOther.myCapacity = 0;
0118   }
0119 
0120   //! Destructor.
0121   ~NCollection_LinearVector() { Clear(true); }
0122 
0123   //! Copy assignment.
0124   NCollection_LinearVector& operator=(const NCollection_LinearVector& theOther)
0125   {
0126     if (this != &theOther)
0127     {
0128       if (theOther.mySize > myCapacity)
0129       {
0130         NCollection_LinearVector aTmp(theOther);
0131         *this = std::move(aTmp);
0132         return *this;
0133       }
0134 
0135       if constexpr (std::is_trivially_copyable_v<TheItemType>)
0136       {
0137         if (theOther.mySize > 0)
0138         {
0139           std::memcpy(myData, theOther.myData, theOther.mySize * sizeof(TheItemType));
0140         }
0141       }
0142       else
0143       {
0144         const size_t aCommonSize = std::min(mySize, theOther.mySize);
0145         for (size_t i = 0; i < aCommonSize; ++i)
0146         {
0147           myData[i] = theOther.myData[i];
0148         }
0149         for (size_t i = aCommonSize; i < theOther.mySize; ++i)
0150         {
0151           myAlloc.construct(myData + i, theOther.myData[i]);
0152         }
0153         destroyRange(theOther.mySize, mySize);
0154       }
0155       mySize = theOther.mySize;
0156     }
0157     return *this;
0158   }
0159 
0160   //! Move assignment.
0161   NCollection_LinearVector& operator=(NCollection_LinearVector&& theOther) noexcept
0162   {
0163     if (this != &theOther)
0164     {
0165       Clear(true);
0166       myData              = theOther.myData;
0167       mySize              = theOther.mySize;
0168       myCapacity          = theOther.myCapacity;
0169       theOther.myData     = nullptr;
0170       theOther.mySize     = 0;
0171       theOther.myCapacity = 0;
0172     }
0173     return *this;
0174   }
0175 
0176   //! @return raw data pointer.
0177   TheItemType* Data() noexcept { return myData; }
0178 
0179   //! @return raw data pointer.
0180   const TheItemType* Data() const noexcept { return myData; }
0181 
0182   //! @return true if the vector has allocated storage.
0183   bool HasData() const noexcept { return myData != nullptr; }
0184 
0185   //! @return true if the vector contains no elements.
0186   bool Empty() const noexcept { return IsEmpty(); }
0187 
0188   //! @return current max supported size.
0189   static constexpr size_t MaxSize() noexcept { return std::numeric_limits<size_t>::max(); }
0190 
0191   //! @return number of elements.
0192   size_t Size() const noexcept { return mySize; }
0193 
0194   //! @return true if the vector contains no elements.
0195   bool IsEmpty() const noexcept { return mySize == 0; }
0196 
0197   //! @return current allocated capacity.
0198   size_t Capacity() const noexcept { return myCapacity; }
0199 
0200   //! Pre-allocate memory for at least theCapacity elements without changing size.
0201   //! @param[in] theCapacity minimum capacity to ensure
0202   void Reserve(const size_t theCapacity)
0203   {
0204     if (theCapacity > myCapacity)
0205     {
0206       grow(theCapacity);
0207     }
0208   }
0209 
0210   //! Change the number of elements.
0211   //! If theSize > Size(), new elements are default-constructed.
0212   //! If theSize < Size(), excess elements are destroyed.
0213   //! @param[in] theSize new number of elements
0214   void Resize(const size_t theSize) { Resize(theSize, TheItemType()); }
0215 
0216   //! Change the number of elements, filling new slots with theValue.
0217   //! If theSize > Size(), new elements are copy-constructed from theValue.
0218   //! If theSize < Size(), excess elements are destroyed.
0219   //! @param[in] theSize  new number of elements
0220   //! @param[in] theValue value to fill new elements with
0221   void Resize(const size_t theSize, const TheItemType& theValue)
0222   {
0223     if (theSize > mySize)
0224     {
0225       if (theSize > myCapacity)
0226       {
0227         grow(theSize);
0228       }
0229       for (size_t i = mySize; i < theSize; ++i)
0230       {
0231         myAlloc.construct(myData + i, theValue);
0232       }
0233     }
0234     else if (theSize < mySize)
0235     {
0236       destroyRange(theSize, mySize);
0237     }
0238     mySize = theSize;
0239   }
0240 
0241   //! @return const reference to element at theIndex.
0242   //! @param[in] theIndex element index (0-based)
0243   const TheItemType& Value(const size_t theIndex) const
0244   {
0245     Standard_OutOfRange_Raise_if(theIndex >= mySize, "NCollection_LinearVector::Value");
0246     return myData[theIndex];
0247   }
0248 
0249   //! @return mutable reference to element at theIndex.
0250   //! @param[in] theIndex element index (0-based)
0251   TheItemType& ChangeValue(const size_t theIndex)
0252   {
0253     Standard_OutOfRange_Raise_if(theIndex >= mySize, "NCollection_LinearVector::ChangeValue");
0254     return myData[theIndex];
0255   }
0256 
0257   //! @return const reference to element at theIndex.
0258   const TheItemType& operator()(const size_t theIndex) const { return myData[theIndex]; }
0259 
0260   //! @return mutable reference to element at theIndex.
0261   TheItemType& operator()(const size_t theIndex) { return myData[theIndex]; }
0262 
0263   //! @return const reference to element at theIndex.
0264   const TheItemType& operator[](const size_t theIndex) const { return myData[theIndex]; }
0265 
0266   //! @return mutable reference to element at theIndex.
0267   TheItemType& operator[](const size_t theIndex) { return myData[theIndex]; }
0268 
0269   //! @return const reference to the first element.
0270   const TheItemType& First() const
0271   {
0272     Standard_OutOfRange_Raise_if(mySize == 0, "NCollection_LinearVector::First");
0273     return myData[0];
0274   }
0275 
0276   //! @return mutable reference to the first element.
0277   TheItemType& ChangeFirst()
0278   {
0279     Standard_OutOfRange_Raise_if(mySize == 0, "NCollection_LinearVector::ChangeFirst");
0280     return myData[0];
0281   }
0282 
0283   //! @return const reference to the last element.
0284   const TheItemType& Last() const
0285   {
0286     Standard_OutOfRange_Raise_if(mySize == 0, "NCollection_LinearVector::Last");
0287     return myData[mySize - 1];
0288   }
0289 
0290   //! @return mutable reference to the last element.
0291   TheItemType& ChangeLast()
0292   {
0293     Standard_OutOfRange_Raise_if(mySize == 0, "NCollection_LinearVector::ChangeLast");
0294     return myData[mySize - 1];
0295   }
0296 
0297   //! Append a copy of theValue to the end.
0298   //! @param[in] theValue element to append
0299   //! @return reference to the appended element
0300   TheItemType& Append(const TheItemType& theValue)
0301   {
0302     if (mySize == myCapacity)
0303     {
0304       grow(mySize + 1);
0305     }
0306     myAlloc.construct(myData + mySize, theValue);
0307     return myData[mySize++];
0308   }
0309 
0310   //! Append theValue by move to the end.
0311   //! @param[in] theValue element to move-append
0312   //! @return reference to the appended element
0313   TheItemType& Append(TheItemType&& theValue)
0314   {
0315     if (mySize == myCapacity)
0316     {
0317       grow(mySize + 1);
0318     }
0319     myAlloc.construct(myData + mySize, std::move(theValue));
0320     return myData[mySize++];
0321   }
0322 
0323   //! Append a default-constructed element.
0324   //! @return reference to the appended element
0325   TheItemType& Appended()
0326   {
0327     if (mySize == myCapacity)
0328     {
0329       grow(mySize + 1);
0330     }
0331     myAlloc.construct(myData + mySize, TheItemType());
0332     return myData[mySize++];
0333   }
0334 
0335   //! Append an element constructed in-place with the given arguments.
0336   //! @param[in] theArgs constructor arguments
0337   //! @return reference to the appended element
0338   template <class... Args>
0339   TheItemType& EmplaceAppend(Args&&... theArgs)
0340   {
0341     if (mySize == myCapacity)
0342     {
0343       grow(mySize + 1);
0344     }
0345     myAlloc.construct(myData + mySize, std::forward<Args>(theArgs)...);
0346     return myData[mySize++];
0347   }
0348 
0349   //! Set value at theIndex. If theIndex >= Size(), the vector is extended.
0350   //! @param[in] theIndex element index (0-based)
0351   //! @param[in] theValue value to set
0352   //! @return reference to the element
0353   TheItemType& SetValue(const size_t theIndex, const TheItemType& theValue)
0354   {
0355     if (theIndex >= mySize)
0356     {
0357       Resize(theIndex + 1);
0358     }
0359     myData[theIndex] = theValue;
0360     return myData[theIndex];
0361   }
0362 
0363   //! Set value at theIndex by move. If theIndex >= Size(), the vector is extended.
0364   //! @param[in] theIndex element index (0-based)
0365   //! @param[in] theValue value to set
0366   //! @return reference to the element
0367   TheItemType& SetValue(const size_t theIndex, TheItemType&& theValue)
0368   {
0369     if (theIndex >= mySize)
0370     {
0371       Resize(theIndex + 1);
0372     }
0373     myData[theIndex] = std::move(theValue);
0374     return myData[theIndex];
0375   }
0376 
0377   //! Insert theValue before theIndex, shifting elements right.
0378   //! @param[in] theIndex insertion position (0-based)
0379   //! @param[in] theValue element to insert
0380   void InsertBefore(const size_t theIndex, const TheItemType& theValue)
0381   {
0382     Standard_OutOfRange_Raise_if(theIndex > mySize, "NCollection_LinearVector::InsertBefore");
0383     if (mySize == myCapacity)
0384     {
0385       grow(mySize + 1);
0386     }
0387     if (theIndex < mySize)
0388     {
0389       shiftRight(theIndex, 1);
0390     }
0391     myAlloc.construct(myData + theIndex, theValue);
0392     ++mySize;
0393   }
0394 
0395   //! Insert theValue after theIndex, shifting elements right.
0396   //! @param[in] theIndex position after which to insert (0-based)
0397   //! @param[in] theValue element to insert
0398   void InsertAfter(const size_t theIndex, const TheItemType& theValue)
0399   {
0400     Standard_OutOfRange_Raise_if(theIndex >= mySize, "NCollection_LinearVector::InsertAfter");
0401     InsertBefore(theIndex + 1, theValue);
0402   }
0403 
0404   //! Insert theValue before theIndex, shifting elements right.
0405   //! @param[in] theIndex insertion position (0-based)
0406   //! @param[in] theValue element to move-insert
0407   void InsertBefore(const size_t theIndex, TheItemType&& theValue)
0408   {
0409     Standard_OutOfRange_Raise_if(theIndex > mySize, "NCollection_LinearVector::InsertBefore");
0410     if (mySize == myCapacity)
0411     {
0412       grow(mySize + 1);
0413     }
0414     if (theIndex < mySize)
0415     {
0416       shiftRight(theIndex, 1);
0417     }
0418     myAlloc.construct(myData + theIndex, std::move(theValue));
0419     ++mySize;
0420   }
0421 
0422   //! Insert theValue after theIndex, shifting elements right.
0423   //! @param[in] theIndex position after which to insert (0-based)
0424   //! @param[in] theValue element to move-insert
0425   void InsertAfter(const size_t theIndex, TheItemType&& theValue)
0426   {
0427     Standard_OutOfRange_Raise_if(theIndex >= mySize, "NCollection_LinearVector::InsertAfter");
0428     InsertBefore(theIndex + 1, std::move(theValue));
0429   }
0430 
0431   //! Remove the last element.
0432   void EraseLast()
0433   {
0434     if (mySize > 0)
0435     {
0436       --mySize;
0437       destroyRange(mySize, mySize + 1);
0438     }
0439   }
0440 
0441   //! Remove element at theIndex, shifting subsequent elements left.
0442   //! @param[in] theIndex element index (0-based)
0443   void Erase(const size_t theIndex)
0444   {
0445     Standard_OutOfRange_Raise_if(theIndex >= mySize, "NCollection_LinearVector::Erase");
0446     // Shift first (move-assign into still-live slots), then destroy the
0447     // vacated tail slot.  Destroying before shifting would leave
0448     // myData[theIndex] uninitialized; a non-trivial move-assignment (e.g.
0449     // TCollection_AsciiString's, which inspects its current buffer before
0450     // freeing) would then read garbage from the destructed slot and crash.
0451     if (theIndex + 1 < mySize)
0452     {
0453       shiftLeft(theIndex, theIndex + 1, mySize);
0454     }
0455     destroyRange(mySize - 1, mySize);
0456     --mySize;
0457   }
0458 
0459   //! Remove elements in range [theFrom, theTo), shifting subsequent elements left.
0460   //! @param[in] theFrom start index (inclusive, 0-based)
0461   //! @param[in] theTo   end index (exclusive, 0-based)
0462   void Erase(const size_t theFrom, const size_t theTo)
0463   {
0464     Standard_OutOfRange_Raise_if(theTo > mySize || theFrom >= theTo,
0465                                  "NCollection_LinearVector::Erase");
0466     const size_t aCount = theTo - theFrom;
0467     // Shift first (move-assign over still-live slots), then destroy the
0468     // vacated tail - see Erase(size_t) above for the rationale.
0469     if (theTo < mySize)
0470     {
0471       shiftLeft(theFrom, theTo, mySize);
0472     }
0473     destroyRange(mySize - aCount, mySize);
0474     mySize -= aCount;
0475   }
0476 
0477   //! Remove all elements.
0478   //! @param[in] theReleaseMemory if true, deallocate the buffer
0479   void Clear(const bool theReleaseMemory = false)
0480   {
0481     destroyRange(0, mySize);
0482     mySize = 0;
0483     if (theReleaseMemory && myData != nullptr)
0484     {
0485       myAlloc.deallocate(myData, myCapacity);
0486       myData     = nullptr;
0487       myCapacity = 0;
0488     }
0489   }
0490 
0491   //! Returns a span as Array1 with shared memory.
0492   //! Modifying the vector or the array may invalidate the shared buffer.
0493   //! @return array view of the vector data
0494   NCollection_Array1<TheItemType> ToArray1() const
0495   {
0496     return NCollection_Array1<TheItemType>(myData, mySize);
0497   }
0498 
0499   //! @return iterator to the first element.
0500   iterator begin() noexcept { return myData; }
0501 
0502   //! @return iterator past the last element.
0503   iterator end() noexcept { return myData + mySize; }
0504 
0505   //! @return const iterator to the first element.
0506   const_iterator begin() const noexcept { return myData; }
0507 
0508   //! @return const iterator past the last element.
0509   const_iterator end() const noexcept { return myData + mySize; }
0510 
0511   //! @return const iterator to the first element.
0512   const_iterator cbegin() const noexcept { return myData; }
0513 
0514   //! @return const iterator past the last element.
0515   const_iterator cend() const noexcept { return myData + mySize; }
0516 
0517 private:
0518   //! Grow the buffer to accommodate at least theMinCapacity elements.
0519   void grow(const size_t theMinCapacity)
0520   {
0521     Standard_OutOfMemory_Raise_if(theMinCapacity > MaxSize(), "NCollection_LinearVector::grow");
0522     size_t aNewCap = myCapacity > 0 ? myCapacity * 2 : 2;
0523     if (myCapacity > MaxSize() / 2)
0524     {
0525       aNewCap = MaxSize();
0526     }
0527     if (aNewCap < theMinCapacity)
0528     {
0529       aNewCap = theMinCapacity;
0530     }
0531     if constexpr (std::is_trivially_copyable_v<TheItemType>)
0532     {
0533       myData = myAlloc.reallocate(myData, aNewCap);
0534     }
0535     else
0536     {
0537       TheItemType* aNewData = myAlloc.allocate(aNewCap);
0538       for (size_t i = 0; i < mySize; ++i)
0539       {
0540         myAlloc.construct(aNewData + i, std::move(myData[i]));
0541         myData[i].~TheItemType();
0542       }
0543       if (myData != nullptr)
0544       {
0545         myAlloc.deallocate(myData, myCapacity);
0546       }
0547       myData = aNewData;
0548     }
0549     myCapacity = aNewCap;
0550   }
0551 
0552   //! Destroy elements in range [theFrom, theTo).
0553   void destroyRange(const size_t theFrom, const size_t theTo)
0554   {
0555     if constexpr (!std::is_trivially_destructible_v<TheItemType>)
0556     {
0557       for (size_t i = theFrom; i < theTo; ++i)
0558       {
0559         myData[i].~TheItemType();
0560       }
0561     }
0562   }
0563 
0564   //! Shift elements right starting at theIndex by theCount positions.
0565   //! Caller must ensure capacity is sufficient. Does NOT construct at theIndex.
0566   void shiftRight(const size_t theIndex, const size_t theCount)
0567   {
0568     if constexpr (std::is_trivially_copyable_v<TheItemType>)
0569     {
0570       std::memmove(myData + theIndex + theCount,
0571                    myData + theIndex,
0572                    (mySize - theIndex) * sizeof(TheItemType));
0573     }
0574     else
0575     {
0576       // Move-construct last element into uninitialized space
0577       for (size_t i = mySize; i-- > theIndex;)
0578       {
0579         myAlloc.construct(myData + i + theCount, std::move(myData[i]));
0580         myData[i].~TheItemType();
0581       }
0582     }
0583   }
0584 
0585   //! Shift live elements [theSrcFrom, theSrcTo) left so they start at theDstFrom.
0586   //! Requires theDstFrom < theSrcFrom and assumes both source and destination
0587   //! slots currently hold live, constructed objects (move-assignment is used).
0588   //! The caller is responsible for destroying the vacated tail - see Erase().
0589   void shiftLeft(const size_t theDstFrom, const size_t theSrcFrom, const size_t theSrcTo)
0590   {
0591     const size_t aCount = theSrcTo - theSrcFrom;
0592     if constexpr (std::is_trivially_copyable_v<TheItemType>)
0593     {
0594       std::memmove(myData + theDstFrom, myData + theSrcFrom, aCount * sizeof(TheItemType));
0595     }
0596     else
0597     {
0598       for (size_t i = 0; i < aCount; ++i)
0599       {
0600         myData[theDstFrom + i] = std::move(myData[theSrcFrom + i]);
0601       }
0602     }
0603   }
0604 
0605 private:
0606   allocator_type myAlloc;
0607   TheItemType*   myData     = nullptr;
0608   size_t         mySize     = 0;
0609   size_t         myCapacity = 0;
0610 };
0611 
0612 #endif // NCollection_LinearVector_HeaderFile