Modeling - Implementation for math_Vector Resize functionality (#957)

math_VectorBase can be used with both stack and heap storage and resized when needed.
This is not often required, but when it is, the Resize method allows to do it efficiently,
  preserving existing data and optimizing memory usage.
This commit is contained in:
Pasukhin Dmitry
2025-12-26 00:25:05 +00:00
committed by GitHub
parent 0b9a5e0aab
commit c3da25d597
3 changed files with 203 additions and 0 deletions

View File

@@ -701,3 +701,163 @@ TEST(MathVectorTest, MoveSemantics)
EXPECT_EQ(aVecAssign1.Length(), 0);
}
// Tests for Resize operation
TEST(MathVectorTest, Resize_StackToStack_SameSize)
{
// Small vector that fits in stack buffer (THE_BUFFER_SIZE = 32)
math_Vector aVec(1, 10);
for (Standard_Integer i = 1; i <= 10; ++i)
{
aVec(i) = static_cast<Standard_Real>(i);
}
// Resize to same size - data should be preserved
aVec.Resize(10);
EXPECT_EQ(aVec.Length(), 10);
EXPECT_EQ(aVec.Lower(), 1);
EXPECT_EQ(aVec.Upper(), 10);
for (Standard_Integer i = 1; i <= 10; ++i)
{
EXPECT_DOUBLE_EQ(aVec(i), static_cast<Standard_Real>(i));
}
}
TEST(MathVectorTest, Resize_StackToStack_Grow)
{
// Small vector
math_Vector aVec(1, 5);
for (Standard_Integer i = 1; i <= 5; ++i)
{
aVec(i) = static_cast<Standard_Real>(i * 10);
}
// Grow but still within stack buffer
aVec.Resize(20);
EXPECT_EQ(aVec.Length(), 20);
EXPECT_EQ(aVec.Lower(), 1);
EXPECT_EQ(aVec.Upper(), 20);
// Original data should be preserved
for (Standard_Integer i = 1; i <= 5; ++i)
{
EXPECT_DOUBLE_EQ(aVec(i), static_cast<Standard_Real>(i * 10));
}
}
TEST(MathVectorTest, Resize_StackToStack_Shrink)
{
// Small vector
math_Vector aVec(1, 20);
for (Standard_Integer i = 1; i <= 20; ++i)
{
aVec(i) = static_cast<Standard_Real>(i);
}
// Shrink but still within stack buffer
aVec.Resize(10);
EXPECT_EQ(aVec.Length(), 10);
EXPECT_EQ(aVec.Lower(), 1);
EXPECT_EQ(aVec.Upper(), 10);
// Data within new range should be preserved
for (Standard_Integer i = 1; i <= 10; ++i)
{
EXPECT_DOUBLE_EQ(aVec(i), static_cast<Standard_Real>(i));
}
}
TEST(MathVectorTest, Resize_StackToHeap)
{
// Small vector that fits in stack
math_Vector aVec(1, 20);
for (Standard_Integer i = 1; i <= 20; ++i)
{
aVec(i) = static_cast<Standard_Real>(i);
}
// Resize to larger than stack buffer (>32)
aVec.Resize(50);
EXPECT_EQ(aVec.Length(), 50);
EXPECT_EQ(aVec.Lower(), 1);
EXPECT_EQ(aVec.Upper(), 50);
// Original data should be preserved
for (Standard_Integer i = 1; i <= 20; ++i)
{
EXPECT_DOUBLE_EQ(aVec(i), static_cast<Standard_Real>(i));
}
}
TEST(MathVectorTest, Resize_HeapToStack)
{
// Large vector on heap
math_Vector aVec(1, 50);
for (Standard_Integer i = 1; i <= 50; ++i)
{
aVec(i) = static_cast<Standard_Real>(i);
}
// Resize to fit in stack buffer
aVec.Resize(20);
EXPECT_EQ(aVec.Length(), 20);
EXPECT_EQ(aVec.Lower(), 1);
EXPECT_EQ(aVec.Upper(), 20);
// Data within new range should be preserved
for (Standard_Integer i = 1; i <= 20; ++i)
{
EXPECT_DOUBLE_EQ(aVec(i), static_cast<Standard_Real>(i));
}
}
TEST(MathVectorTest, Resize_HeapToHeap)
{
// Large vector on heap
math_Vector aVec(1, 50);
for (Standard_Integer i = 1; i <= 50; ++i)
{
aVec(i) = static_cast<Standard_Real>(i);
}
// Resize to different heap size
aVec.Resize(100);
EXPECT_EQ(aVec.Length(), 100);
EXPECT_EQ(aVec.Lower(), 1);
EXPECT_EQ(aVec.Upper(), 100);
// Original data should be preserved
for (Standard_Integer i = 1; i <= 50; ++i)
{
EXPECT_DOUBLE_EQ(aVec(i), static_cast<Standard_Real>(i));
}
}
TEST(MathVectorTest, Resize_NegativeLowerBound)
{
// Vector with negative lower bound
math_Vector aVec(-5, 5);
for (Standard_Integer i = -5; i <= 5; ++i)
{
aVec(i) = static_cast<Standard_Real>(i);
}
// Resize - lower bound preserved
aVec.Resize(8);
EXPECT_EQ(aVec.Length(), 8);
EXPECT_EQ(aVec.Lower(), -5);
EXPECT_EQ(aVec.Upper(), 2);
// Original data should be preserved
for (Standard_Integer i = -5; i <= 2; ++i)
{
EXPECT_DOUBLE_EQ(aVec(i), static_cast<Standard_Real>(i));
}
}

View File

@@ -306,6 +306,19 @@ public:
//! Is used to redefine the operator <<.
inline void Dump(Standard_OStream& theO) const;
//! Returns the underlying array for interoperability with legacy APIs.
//! Allows passing math_Vector data to functions expecting NCollection_Array1.
const NCollection_Array1<TheItemType>& Array1() const { return Array; }
//! Resizes the vector to a new size, keeping the same lower bound.
//! Existing data within the new range is preserved.
//! The method optimizes memory usage:
//! - If new size fits in stack buffer (<=32), uses stack allocation
//! - If new size requires heap and was already on heap, resizes in place
//! - Transitions between stack and heap as needed
//! @param theSize new size of the vector
inline void Resize(const Standard_Integer theSize);
friend inline Standard_OStream& operator<<(Standard_OStream& theO, const math_VectorBase& theVec)
{
theVec.Dump(theO);

View File

@@ -590,3 +590,33 @@ void math_VectorBase<TheItemType>::Dump(Standard_OStream& theO) const
theO << "math_Vector(" << Index << ") = " << Array(Index) << "\n";
}
}
template <typename TheItemType>
void math_VectorBase<TheItemType>::Resize(const Standard_Integer theSize)
{
const Standard_Integer theLower = Array.Lower();
const Standard_Integer theUpper = theLower + theSize - 1;
const Standard_Boolean aNewFitsStack = theSize <= THE_BUFFER_SIZE;
const Standard_Boolean aWasOnStack = !Array.IsDeletable();
if (aWasOnStack && aNewFitsStack)
{
// Stack -> Stack: data is already in myBuffer, just update Array bounds
Array = NCollection_Array1<TheItemType>(*myBuffer.data(), theLower, theUpper);
}
else if (aNewFitsStack)
{
// Heap -> Stack: copy data to stack buffer
const Standard_Integer aCopyLen = std::min(Array.Length(), theSize);
for (Standard_Integer i = 0; i < aCopyLen; ++i)
{
myBuffer[i] = Array.Value(theLower + i);
}
Array = NCollection_Array1<TheItemType>(*myBuffer.data(), theLower, theUpper);
}
else
{
// Stack -> Heap or Heap -> Heap: Array.Resize handles data copy
Array.Resize(theLower, theUpper, Standard_True);
}
}