// Copyright (c) 2021 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_AliasedArray_HeaderFile #define _NCollection_AliasedArray_HeaderFile #include "Standard_DimensionMismatch.hxx" #include "Standard_OutOfMemory.hxx" #include "Standard_OutOfRange.hxx" #include "Standard_TypeMismatch.hxx" #include "Standard_Macro.hxx" #include //! Defines an array of values of configurable size. //! For instance, this class allows defining an array of 32-bit or 64-bit integer values with //! bitness determined in runtime. The element size in bytes (stride) should be specified at //! construction time. Indexation starts from 0 index. As actual type of element varies at runtime, //! element accessors are defined as templates. Memory for array is allocated with the given //! alignment (template parameter). template class NCollection_AliasedArray { public: DEFINE_STANDARD_ALLOC public: //! Empty constructor. NCollection_AliasedArray(int theStride) : myData(nullptr), myStride(theStride), mySize(0), myDeletable(false) { if (theStride <= 0) { throw Standard_RangeError("NCollection_AliasedArray, stride should be positive"); } } //! Constructor NCollection_AliasedArray(int theStride, int theLength) : myData(nullptr), myStride(theStride), mySize(theLength), myDeletable(true) { if (theLength <= 0 || myStride <= 0) { throw Standard_RangeError("NCollection_AliasedArray, stride and length should be positive"); } myData = (uint8_t*)Standard::AllocateAligned(SizeBytes(), MyAlignSize); if (myData == nullptr) { throw Standard_OutOfMemory("NCollection_AliasedArray, allocation failed"); } } //! Copy constructor NCollection_AliasedArray(const NCollection_AliasedArray& theOther) : myData(nullptr), myStride(theOther.myStride), mySize(theOther.mySize), myDeletable(false) { if (mySize != 0) { myDeletable = true; myData = (uint8_t*)Standard::AllocateAligned(SizeBytes(), MyAlignSize); if (myData == nullptr) { throw Standard_OutOfMemory("NCollection_AliasedArray, allocation failed"); } Assign(theOther); } } //! Move constructor NCollection_AliasedArray(NCollection_AliasedArray&& theOther) noexcept : myData(theOther.myData), myStride(theOther.myStride), mySize(theOther.mySize), myDeletable(theOther.myDeletable) { theOther.myDeletable = false; } //! Constructor wrapping pre-allocated C-array of values without copying them. template NCollection_AliasedArray(const Type_t& theBegin, int theLength) : myData((uint8_t*)&theBegin), myStride((int)sizeof(Type_t)), mySize(theLength), myDeletable(false) { if (theLength <= 0) { throw Standard_RangeError("NCollection_AliasedArray, length should be positive"); } } //! Returns an element size in bytes. int Stride() const { return myStride; } //! Size query int Size() const { return mySize; } //! Length query (the same as Size()) int Length() const { return mySize; } //! Return TRUE if array has zero length. bool IsEmpty() const { return mySize == 0; } //! Lower bound int Lower() const { return 0; } //! Upper bound int Upper() const { return mySize - 1; } //! myDeletable flag bool IsDeletable() const { return myDeletable; } //! IsAllocated flag - for naming compatibility bool IsAllocated() const { return myDeletable; } //! Return buffer size in bytes. size_t SizeBytes() const { return size_t(myStride) * size_t(mySize); } //! Copies data of theOther array to this. //! This array should be pre-allocated and have the same length as theOther; //! otherwise exception Standard_DimensionMismatch is thrown. NCollection_AliasedArray& Assign(const NCollection_AliasedArray& theOther) { if (&theOther != this) { if (myStride != theOther.myStride || mySize != theOther.mySize) { throw Standard_DimensionMismatch( "NCollection_AliasedArray::Assign(), arrays have different size"); } if (myData != nullptr) { memcpy(myData, theOther.myData, SizeBytes()); } } return *this; } //! Move assignment. //! This array will borrow all the data from theOther. //! The moved object will keep pointer to the memory buffer and //! range, but it will not free the buffer on destruction. NCollection_AliasedArray& Move(NCollection_AliasedArray& theOther) { if (&theOther != this) { if (myDeletable) { Standard::FreeAligned(myData); } myStride = theOther.myStride; mySize = theOther.mySize; myDeletable = theOther.myDeletable; myData = theOther.myData; theOther.myDeletable = false; } return *this; } //! Assignment operator; @sa Assign() NCollection_AliasedArray& operator=(const NCollection_AliasedArray& theOther) { return Assign(theOther); } //! Move assignment operator; @sa Move() NCollection_AliasedArray& operator=(NCollection_AliasedArray&& theOther) { return Move(theOther); } //! Resizes the array to specified bounds. //! No re-allocation will be done if length of array does not change, //! but existing values will not be discarded if theToCopyData set to FALSE. //! @param theLength new length of array //! @param theToCopyData flag to copy existing data into new array void Resize(int theLength, bool theToCopyData) { if (theLength <= 0) { throw Standard_RangeError("NCollection_AliasedArray::Resize, length should be positive"); } if (mySize == theLength) { return; } const int anOldLen = mySize; const uint8_t* anOldData = myData; mySize = theLength; if (!theToCopyData && myDeletable) { Standard::FreeAligned(myData); } myData = (uint8_t*)Standard::AllocateAligned(SizeBytes(), MyAlignSize); if (myData == nullptr) { throw Standard_OutOfMemory("NCollection_AliasedArray, allocation failed"); } if (!theToCopyData) { myDeletable = true; return; } const size_t aLenCopy = size_t((std::min)(anOldLen, theLength)) * size_t(myStride); memcpy(myData, anOldData, aLenCopy); if (myDeletable) { Standard::FreeAligned(anOldData); } myDeletable = true; } //! Destructor - releases the memory ~NCollection_AliasedArray() { if (myDeletable) { Standard::FreeAligned(myData); } } public: //! Access raw bytes of specified element. const uint8_t* value(int theIndex) const { Standard_OutOfRange_Raise_if(theIndex < 0 || theIndex >= mySize, "NCollection_AliasedArray::value(), out of range index"); return myData + size_t(myStride) * size_t(theIndex); } //! Access raw bytes of specified element. uint8_t* changeValue(int theIndex) { Standard_OutOfRange_Raise_if(theIndex < 0 || theIndex >= mySize, "NCollection_AliasedArray::changeValue(), out of range index"); return myData + size_t(myStride) * size_t(theIndex); } //! Initialize the items with theValue template void Init(const Type_t& theValue) { for (int anIter = 0; anIter < mySize; ++anIter) { ChangeValue(anIter) = theValue; } } //! Access element with specified position and type. //! This method requires size of a type matching stride value. template const Type_t& Value(int theIndex) const { Standard_TypeMismatch_Raise_if(size_t(myStride) != sizeof(Type_t), "NCollection_AliasedArray::Value(), wrong type"); return *reinterpret_cast(value(theIndex)); } //! Access element with specified position and type. //! This method requires size of a type matching stride value. template void Value(int theIndex, Type_t& theValue) const { Standard_TypeMismatch_Raise_if(size_t(myStride) != sizeof(Type_t), "NCollection_AliasedArray::Value(), wrong type"); theValue = *reinterpret_cast(value(theIndex)); } //! Access element with specified position and type. //! This method requires size of a type matching stride value. template Type_t& ChangeValue(int theIndex) { Standard_TypeMismatch_Raise_if(size_t(myStride) != sizeof(Type_t), "NCollection_AliasedArray::ChangeValue(), wrong type"); return *reinterpret_cast(changeValue(theIndex)); } //! Access element with specified position and type. //! This method allows wrapping element into smaller type (e.g. to alias 2-components within //! 3-component vector). template const Type_t& Value2(int theIndex) const { Standard_TypeMismatch_Raise_if(size_t(myStride) < sizeof(Type_t), "NCollection_AliasedArray::Value2(), wrong type"); return *reinterpret_cast(value(theIndex)); } //! Access element with specified position and type. //! This method allows wrapping element into smaller type (e.g. to alias 2-components within //! 3-component vector). template void Value2(int theIndex, Type_t& theValue) const { Standard_TypeMismatch_Raise_if(size_t(myStride) < sizeof(Type_t), "NCollection_AliasedArray::Value2(), wrong type"); theValue = *reinterpret_cast(value(theIndex)); } //! Access element with specified position and type. //! This method allows wrapping element into smaller type (e.g. to alias 2-components within //! 3-component vector). template Type_t& ChangeValue2(int theIndex) { Standard_TypeMismatch_Raise_if(size_t(myStride) < sizeof(Type_t), "NCollection_AliasedArray::ChangeValue2(), wrong type"); return *reinterpret_cast(changeValue(theIndex)); } //! Return first element template const Type_t& First() const { return Value(0); } //! Return first element template Type_t& ChangeFirst() { return ChangeValue(0); } //! Return last element template const Type_t& Last() const { return Value(mySize - 1); } //! Return last element template Type_t& ChangeLast() { return Value(mySize - 1); } protected: uint8_t* myData; //!< data pointer int myStride; //!< element size int mySize; //!< number of elements bool myDeletable; //!< flag showing who allocated the array }; #endif // _NCollection_AliasedArray_HeaderFile