From c3da25d59718ab821446bf35edcea0577edbe2e1 Mon Sep 17 00:00:00 2001 From: Pasukhin Dmitry Date: Fri, 26 Dec 2025 00:25:05 +0000 Subject: [PATCH] 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. --- .../TKMath/GTests/math_Vector_Test.cxx | 160 ++++++++++++++++++ .../TKMath/math/math_VectorBase.hxx | 13 ++ .../TKMath/math/math_VectorBase.lxx | 30 ++++ 3 files changed, 203 insertions(+) diff --git a/src/FoundationClasses/TKMath/GTests/math_Vector_Test.cxx b/src/FoundationClasses/TKMath/GTests/math_Vector_Test.cxx index 0881c96847..d1d56cbe13 100644 --- a/src/FoundationClasses/TKMath/GTests/math_Vector_Test.cxx +++ b/src/FoundationClasses/TKMath/GTests/math_Vector_Test.cxx @@ -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(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(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(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(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(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(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(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(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(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(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(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(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(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(i)); + } +} diff --git a/src/FoundationClasses/TKMath/math/math_VectorBase.hxx b/src/FoundationClasses/TKMath/math/math_VectorBase.hxx index a38afd1a73..c421c431aa 100644 --- a/src/FoundationClasses/TKMath/math/math_VectorBase.hxx +++ b/src/FoundationClasses/TKMath/math/math_VectorBase.hxx @@ -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& 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); diff --git a/src/FoundationClasses/TKMath/math/math_VectorBase.lxx b/src/FoundationClasses/TKMath/math/math_VectorBase.lxx index 6e0314c010..9590c9d546 100644 --- a/src/FoundationClasses/TKMath/math/math_VectorBase.lxx +++ b/src/FoundationClasses/TKMath/math/math_VectorBase.lxx @@ -590,3 +590,33 @@ void math_VectorBase::Dump(Standard_OStream& theO) const theO << "math_Vector(" << Index << ") = " << Array(Index) << "\n"; } } + +template +void math_VectorBase::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(*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(*myBuffer.data(), theLower, theUpper); + } + else + { + // Stack -> Heap or Heap -> Heap: Array.Resize handles data copy + Array.Resize(theLower, theUpper, Standard_True); + } +}