0032917: Coding Rules - eliminate MSVS warning C26440 on VS2019/C++20 (If your functi...
[occt.git] / src / NCollection / NCollection_AliasedArray.hxx
1 // Copyright (c) 2021 OPEN CASCADE SAS
2 //
3 // This file is part of Open CASCADE Technology software library.
4 //
5 // This library is free software; you can redistribute it and/or modify it under
6 // the terms of the GNU Lesser General Public License version 2.1 as published
7 // by the Free Software Foundation, with special exception defined in the file
8 // OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
9 // distribution for complete text of the license and disclaimer of any warranty.
10 //
11 // Alternatively, this file may be used under the terms of Open CASCADE
12 // commercial license or contractual agreement.
13
14 #ifndef _NCollection_AliasedArray_HeaderFile
15 #define _NCollection_AliasedArray_HeaderFile
16
17 #include <NCollection_DefineAlloc.hxx>
18 #include <NCollection_StlIterator.hxx>
19 #include <Standard_DimensionMismatch.hxx>
20 #include <Standard_OutOfMemory.hxx>
21 #include <Standard_OutOfRange.hxx>
22 #include <Standard_TypeMismatch.hxx>
23
24 //! Defines an array of values of configurable size.
25 //! For instance, this class allows defining an array of 32-bit or 64-bit integer values with bitness determined in runtime.
26 //! The element size in bytes (stride) should be specified at construction time.
27 //! Indexation starts from 0 index.
28 //! As actual type of element varies at runtime, element accessors are defined as templates.
29 //! Memory for array is allocated with the given alignment (template parameter).
30 template<int MyAlignSize = 16>
31 class NCollection_AliasedArray
32 {
33 public:
34   DEFINE_STANDARD_ALLOC
35 public:
36
37   //! Empty constructor.
38   NCollection_AliasedArray (Standard_Integer theStride)
39   : myData (NULL), myStride (theStride), mySize (0), myDeletable (false)
40   {
41     if (theStride <= 0) { throw Standard_RangeError ("NCollection_AliasedArray, stride should be positive"); }
42   }
43
44   //! Constructor
45   NCollection_AliasedArray (Standard_Integer theStride,
46                             Standard_Integer theLength)
47   : myData (NULL), myStride (theStride), mySize (theLength), myDeletable (true)
48   {
49     if (theLength <= 0 || myStride <= 0) { throw Standard_RangeError ("NCollection_AliasedArray, stride and length should be positive"); }
50     myData = (Standard_Byte* )Standard::AllocateAligned (SizeBytes(), MyAlignSize);
51     if (myData == NULL) { throw Standard_OutOfMemory ("NCollection_AliasedArray, allocation failed"); }
52   }
53
54   //! Copy constructor 
55   NCollection_AliasedArray (const NCollection_AliasedArray& theOther)
56   : myData (NULL), myStride (theOther.myStride), mySize (theOther.mySize), myDeletable (false)
57   {
58     if (mySize != 0)
59     {
60       myDeletable = true;
61       myData = (Standard_Byte* )Standard::AllocateAligned (SizeBytes(), MyAlignSize);
62       if (myData == NULL) { throw Standard_OutOfMemory ("NCollection_AliasedArray, allocation failed"); }
63       Assign (theOther);
64     }
65   }
66
67   //! Move constructor
68   NCollection_AliasedArray (NCollection_AliasedArray&& theOther) noexcept
69   : myData (theOther.myData), myStride (theOther.myStride), mySize (theOther.mySize), myDeletable (theOther.myDeletable)
70   {
71     theOther.myDeletable = false;
72   }
73
74   //! Constructor wrapping pre-allocated C-array of values without copying them.
75   template<typename Type_t>
76   NCollection_AliasedArray (const Type_t& theBegin,
77                             Standard_Integer theLength)
78   : myData ((Standard_Byte* )&theBegin), myStride ((int )sizeof(Type_t)), mySize (theLength), myDeletable (false)
79   {
80     if (theLength <= 0) { throw Standard_RangeError ("NCollection_AliasedArray, length should be positive"); }
81   }
82
83   //! Returns an element size in bytes.
84   Standard_Integer Stride() const { return myStride; }
85
86   //! Size query
87   Standard_Integer Size() const { return mySize; }
88
89   //! Length query (the same as Size())
90   Standard_Integer Length() const { return mySize; }
91
92   //! Return TRUE if array has zero length.
93   Standard_Boolean IsEmpty() const { return mySize == 0; }
94
95   //! Lower bound
96   Standard_Integer Lower() const { return 0; }
97
98   //! Upper bound
99   Standard_Integer Upper() const { return mySize - 1; }
100
101   //! myDeletable flag
102   Standard_Boolean IsDeletable() const { return myDeletable; }
103
104   //! IsAllocated flag - for naming compatibility
105   Standard_Boolean IsAllocated() const { return myDeletable; }
106
107   //! Return buffer size in bytes.
108   Standard_Size SizeBytes() const { return size_t(myStride) * size_t(mySize); }
109
110   //! Copies data of theOther array to this.
111   //! This array should be pre-allocated and have the same length as theOther;
112   //! otherwise exception Standard_DimensionMismatch is thrown.
113   NCollection_AliasedArray& Assign (const NCollection_AliasedArray& theOther)
114   {
115     if (&theOther != this)
116     {
117       if (myStride != theOther.myStride || mySize != theOther.mySize)
118       {
119         throw Standard_DimensionMismatch ("NCollection_AliasedArray::Assign(), arrays have different size");
120       }
121       if (myData != NULL)
122       {
123         memcpy (myData, theOther.myData, SizeBytes());
124       }
125     }
126     return *this;
127   }
128
129   //! Move assignment.
130   //! This array will borrow all the data from theOther.
131   //! The moved object will keep pointer to the memory buffer and
132   //! range, but it will not free the buffer on destruction.
133   NCollection_AliasedArray& Move (NCollection_AliasedArray& theOther)
134   {
135     if (&theOther != this)
136     {
137       if (myDeletable)
138       {
139         Standard::FreeAligned (myData);
140       }
141       myStride    = theOther.myStride;
142       mySize      = theOther.mySize;
143       myDeletable = theOther.myDeletable;
144       myData      = theOther.myData;
145       theOther.myDeletable = false;
146     }
147     return *this;
148   }
149
150   //! Assignment operator; @sa Assign()
151   NCollection_AliasedArray& operator= (const NCollection_AliasedArray& theOther)
152   { 
153     return Assign (theOther);
154   }
155
156   //! Move assignment operator; @sa Move()
157   NCollection_AliasedArray& operator= (NCollection_AliasedArray&& theOther)
158   {
159     return Move (theOther);
160   }
161
162   //! Resizes the array to specified bounds.
163   //! No re-allocation will be done if length of array does not change,
164   //! but existing values will not be discarded if theToCopyData set to FALSE.
165   //! @param theLength new length of array
166   //! @param theToCopyData flag to copy existing data into new array
167   void Resize (Standard_Integer theLength,
168                Standard_Boolean theToCopyData)
169   {
170     if (theLength <= 0) { throw Standard_RangeError ("NCollection_AliasedArray::Resize, length should be positive"); }
171     if (mySize == theLength)
172     {
173       return;
174     }
175
176     const Standard_Integer anOldLen  = mySize;
177     const Standard_Byte*   anOldData = myData;
178     mySize = theLength;
179     if (!theToCopyData && myDeletable)
180     {
181       Standard::FreeAligned (myData);
182     }
183     myData = (Standard_Byte* )Standard::AllocateAligned (SizeBytes(), MyAlignSize);
184     if (myData == NULL) { throw Standard_OutOfMemory ("NCollection_AliasedArray, allocation failed"); }
185     if (!theToCopyData)
186     {
187       myDeletable = true;
188       return;
189     }
190
191     const size_t aLenCopy = size_t(Min (anOldLen, theLength)) * size_t(myStride);
192     memcpy (myData, anOldData, aLenCopy);
193     if (myDeletable)
194     {
195       Standard::FreeAligned (anOldData);
196     }
197     myDeletable = true;
198   }
199
200   //! Destructor - releases the memory
201   ~NCollection_AliasedArray()
202   { 
203     if (myDeletable)
204     {
205       Standard::FreeAligned (myData);
206     }
207   }
208
209 public:
210
211   //! Access raw bytes of specified element.
212   const Standard_Byte* value (Standard_Integer theIndex) const
213   {
214     Standard_OutOfRange_Raise_if (theIndex < 0 || theIndex >= mySize, "NCollection_AliasedArray::value(), out of range index");
215     return myData + size_t(myStride) * size_t(theIndex);
216   }
217
218   //! Access raw bytes of specified element.
219   Standard_Byte* changeValue (Standard_Integer theIndex)
220   {
221     Standard_OutOfRange_Raise_if (theIndex < 0 || theIndex >= mySize, "NCollection_AliasedArray::changeValue(), out of range index");
222     return myData + size_t(myStride) * size_t(theIndex);
223   }
224
225   //! Initialize the items with theValue
226   template<typename Type_t> void Init (const Type_t& theValue)
227   {
228     for (Standard_Integer anIter = 0; anIter < mySize; ++anIter)
229     {
230       ChangeValue<Type_t> (anIter) = theValue;
231     }
232   }
233
234   //! Access element with specified position and type.
235   //! This method requires size of a type matching stride value.
236   template<typename Type_t> const Type_t& Value (Standard_Integer theIndex) const
237   {
238     Standard_TypeMismatch_Raise_if(size_t(myStride) != sizeof(Type_t), "NCollection_AliasedArray::Value(), wrong type");
239     return *reinterpret_cast<const Type_t*>(value (theIndex));
240   }
241
242   //! Access element with specified position and type.
243   //! This method requires size of a type matching stride value.
244   template<typename Type_t> void Value (Standard_Integer theIndex, Type_t& theValue) const
245   {
246     Standard_TypeMismatch_Raise_if(size_t(myStride) != sizeof(Type_t), "NCollection_AliasedArray::Value(), wrong type");
247     theValue = *reinterpret_cast<const Type_t*>(value (theIndex));
248   }
249
250   //! Access element with specified position and type.
251   //! This method requires size of a type matching stride value.
252   template<typename Type_t> Type_t& ChangeValue (Standard_Integer theIndex)
253   {
254     Standard_TypeMismatch_Raise_if(size_t(myStride) != sizeof(Type_t), "NCollection_AliasedArray::ChangeValue(), wrong type");
255     return *reinterpret_cast<Type_t* >(changeValue (theIndex));
256   }
257
258   //! Access element with specified position and type.
259   //! This method allows wrapping element into smaller type (e.g. to alias 2-components within 3-component vector).
260   template<typename Type_t> const Type_t& Value2 (Standard_Integer theIndex) const
261   {
262     Standard_TypeMismatch_Raise_if(size_t(myStride) < sizeof(Type_t), "NCollection_AliasedArray::Value2(), wrong type");
263     return *reinterpret_cast<const Type_t*>(value (theIndex));
264   }
265
266   //! Access element with specified position and type.
267   //! This method allows wrapping element into smaller type (e.g. to alias 2-components within 3-component vector).
268   template<typename Type_t> void Value2 (Standard_Integer theIndex, Type_t& theValue) const
269   {
270     Standard_TypeMismatch_Raise_if(size_t(myStride) < sizeof(Type_t), "NCollection_AliasedArray::Value2(), wrong type");
271     theValue = *reinterpret_cast<const Type_t*>(value (theIndex));
272   }
273
274   //! Access element with specified position and type.
275   //! This method allows wrapping element into smaller type (e.g. to alias 2-components within 3-component vector).
276   template<typename Type_t>
277   Type_t& ChangeValue2 (Standard_Integer theIndex)
278   {
279     Standard_TypeMismatch_Raise_if(size_t(myStride) < sizeof(Type_t), "NCollection_AliasedArray::ChangeValue2(), wrong type");
280     return *reinterpret_cast<Type_t* >(changeValue (theIndex));
281   }
282
283   //! Return first element
284   template<typename Type_t> const Type_t& First() const { return Value<Type_t> (0); }
285
286   //! Return first element
287   template<typename Type_t> Type_t& ChangeFirst() { return ChangeValue<Type_t> (0); }
288
289   //! Return last element
290   template<typename Type_t> const Type_t& Last() const { return Value<Type_t> (mySize - 1); }
291
292   //! Return last element
293   template<typename Type_t> Type_t& ChangeLast() { return Value<Type_t> (mySize - 1); }
294
295 protected:
296
297   Standard_Byte*   myData;      //!< data pointer
298   Standard_Integer myStride;    //!< element size
299   Standard_Integer mySize;      //!< number of elements
300   Standard_Boolean myDeletable; //!< flag showing who allocated the array
301
302 };
303
304 #endif // _NCollection_AliasedArray_HeaderFile