From abb18bf47cb01ceeec9cba0d2988b48ed5cf53dd Mon Sep 17 00:00:00 2001 From: Dor Forer Date: Wed, 5 Aug 2026 12:31:46 +0300 Subject: [PATCH] Add a range constructor to vecsim_stl::vector vecsim_stl::vector could only be built empty, with a size, or with a size and a fill value, so building one from an existing range meant constructing it empty and then copying elements in by hand. Add an iterator-pair constructor that forwards to the underlying std::vector, keeping the allocator as the trailing argument like the other constructors. It is constrained to std::input_iterator (the same idiom std::vector uses) so that calls such as vector(10, 5, alloc) keep resolving to the (count, value) constructor instead of being deduced as a range of ints. Co-Authored-By: Claude Opus 5 (1M context) --- src/VecSim/utils/vecsim_stl.h | 7 +++++++ tests/unit/test_allocator.cpp | 37 +++++++++++++++++++++++++++++++++++ 2 files changed, 44 insertions(+) diff --git a/src/VecSim/utils/vecsim_stl.h b/src/VecSim/utils/vecsim_stl.h index 72131a818..823b78952 100644 --- a/src/VecSim/utils/vecsim_stl.h +++ b/src/VecSim/utils/vecsim_stl.h @@ -11,6 +11,7 @@ #include "VecSim/memory/vecsim_base.h" #include #include +#include #include #include #include @@ -33,6 +34,12 @@ class vector : public VecsimBaseObject, public std::vector>(cap, alloc) {} explicit vector(size_t cap, T val, const std::shared_ptr &alloc) : VecsimBaseObject(alloc), std::vector>(cap, val, alloc) {} + // Range constructor. Constrained to input iterators (same idiom std::vector uses) so that + // calls like vector(10, 5, alloc) resolve to the fill constructor above instead of + // being deduced as a range of ints. + template + explicit vector(Iter first, Iter last, const std::shared_ptr &alloc) + : VecsimBaseObject(alloc), std::vector>(first, last, alloc) {} bool remove(T element) { auto it = std::find(this->begin(), this->end(), element); diff --git a/tests/unit/test_allocator.cpp b/tests/unit/test_allocator.cpp index b5e4f76fb..cfe251098 100644 --- a/tests/unit/test_allocator.cpp +++ b/tests/unit/test_allocator.cpp @@ -15,7 +15,9 @@ #include "VecSim/algorithms/hnsw/hnsw_single.h" #include "unit_test_utils.h" #include "VecSim/utils/serializer.h" +#include "VecSim/utils/vecsim_stl.h" #include "VecSim/index_factories/hnsw_factory.h" +#include const size_t vecsimAllocationOverhead = VecSimAllocator::getAllocationOverheadSize(); @@ -83,6 +85,41 @@ TEST_F(AllocatorTest, test_nested_object) { delete obj; } +TEST_F(AllocatorTest, test_vector_range_constructor) { + std::shared_ptr allocator = VecSimAllocator::newVecsimAllocator(); + const int src[] = {1, 2, 3, 4}; + + // A range of random access iterators: the exact size is known upfront, so a single allocation + // of the vector's data is expected, and it goes through the vecsim allocator. + const size_t expectedAllocationSize = allocator->getAllocationSize(); + { + vecsim_stl::vector fromArray(src, src + 4, allocator); + ASSERT_EQ(allocator->getAllocationSize(), + expectedAllocationSize + sizeof(src) + vecsimAllocationOverhead); + ASSERT_EQ(fromArray.size(), 4); + ASSERT_TRUE(std::equal(fromArray.begin(), fromArray.end(), src)); + + // A range of another vecsim_stl::vector's iterators. + vecsim_stl::vector copy(fromArray.begin(), fromArray.end(), allocator); + ASSERT_EQ(copy.size(), fromArray.size()); + ASSERT_TRUE(std::equal(copy.begin(), copy.end(), src)); + } + // Both vectors were destroyed, so their data was returned to the allocator. + ASSERT_EQ(allocator->getAllocationSize(), expectedAllocationSize); + + // A range of non-contiguous iterators (the size is not known upfront). + std::list list{7, 8, 9}; + vecsim_stl::vector fromList(list.begin(), list.end(), allocator); + ASSERT_EQ(fromList.size(), list.size()); + ASSERT_TRUE(std::equal(fromList.begin(), fromList.end(), list.begin())); + + // The range constructor is constrained to input iterators, so it must not be preferred over + // the (count, value) constructor when T is an integral type. + vecsim_stl::vector fill(10, 5, allocator); + ASSERT_EQ(fill.size(), 10); + ASSERT_EQ(std::count(fill.begin(), fill.end(), 5UL), 10); +} + template class IndexAllocatorTest : public ::testing::Test {};