blob: 9ee85ebd35a885cc768703ff7a1c01f52ed6096a [file]
/*
* Copyright (C) 2026 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
* THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "config.h"
#include <wtf/SegmentedVector.h>
#include "MoveOnly.h"
namespace TestWebKitAPI {
struct DestructorCounter {
static unsigned s_destructorCount;
static void resetCount() { s_destructorCount = 0; }
int value;
DestructorCounter()
: value(0) { }
DestructorCounter(int v)
: value(v) { }
~DestructorCounter() { ++s_destructorCount; }
DestructorCounter(const DestructorCounter& other)
: value(other.value) { }
DestructorCounter& operator=(const DestructorCounter& other)
{
value = other.value;
return *this;
}
};
unsigned DestructorCounter::s_destructorCount = 0;
// ============================================================
// Section 1: Heap-only mode (InlineCapacity = 0)
// ============================================================
TEST(WTF_SegmentedVector, HeapOnlyEmpty)
{
SegmentedVector<int, 4> vector;
EXPECT_TRUE(vector.isEmpty());
EXPECT_EQ(0u, vector.size());
}
TEST(WTF_SegmentedVector, HeapOnlyAppendAndAccess)
{
SegmentedVector<int, 4> vector;
vector.append(1);
vector.append(2);
vector.append(3);
EXPECT_FALSE(vector.isEmpty());
EXPECT_EQ(3u, vector.size());
EXPECT_EQ(1, vector.at(0));
EXPECT_EQ(2, vector.at(1));
EXPECT_EQ(3, vector.at(2));
EXPECT_EQ(1, vector[0]);
EXPECT_EQ(2, vector[1]);
EXPECT_EQ(3, vector[2]);
}
TEST(WTF_SegmentedVector, HeapOnlyFirstAndLast)
{
SegmentedVector<int, 4> vector;
vector.append(10);
vector.append(20);
vector.append(30);
EXPECT_EQ(10, vector.first());
EXPECT_EQ(30, vector.last());
}
TEST(WTF_SegmentedVector, HeapOnlyMultipleSegments)
{
SegmentedVector<int, 4> vector;
// 12 elements = 3 segments of 4
for (int i = 0; i < 12; ++i)
vector.append(i);
EXPECT_EQ(12u, vector.size());
for (int i = 0; i < 12; ++i)
EXPECT_EQ(i, vector[i]);
}
TEST(WTF_SegmentedVector, HeapOnlyPointerStability)
{
SegmentedVector<int, 4> vector;
vector.append(100);
int* firstPtr = &vector[0];
// Add more elements, triggering additional segment allocations
for (int i = 1; i < 20; ++i)
vector.append(i * 100);
// Original pointer should still be valid
EXPECT_EQ(100, *firstPtr);
// Also verify pointers to later segments remain stable
int* laterPtr = &vector[10];
for (int i = 20; i < 40; ++i)
vector.append(i * 100);
EXPECT_EQ(1000, *laterPtr);
}
TEST(WTF_SegmentedVector, HeapOnlyRemoveLast)
{
SegmentedVector<int, 4> vector;
vector.append(1);
vector.append(2);
vector.append(3);
EXPECT_EQ(3u, vector.size());
vector.removeLast();
EXPECT_EQ(2u, vector.size());
EXPECT_EQ(2, vector.last());
vector.removeLast();
EXPECT_EQ(1u, vector.size());
EXPECT_EQ(1, vector.last());
vector.removeLast();
EXPECT_TRUE(vector.isEmpty());
}
TEST(WTF_SegmentedVector, HeapOnlyTakeLast)
{
SegmentedVector<int, 4> vector;
vector.append(10);
vector.append(20);
vector.append(30);
EXPECT_EQ(30, vector.takeLast());
EXPECT_EQ(2u, vector.size());
EXPECT_EQ(20, vector.takeLast());
EXPECT_EQ(1u, vector.size());
}
TEST(WTF_SegmentedVector, HeapOnlyClear)
{
SegmentedVector<int, 4> vector;
for (int i = 0; i < 20; ++i)
vector.append(i);
EXPECT_EQ(20u, vector.size());
vector.clear();
EXPECT_TRUE(vector.isEmpty());
EXPECT_EQ(0u, vector.size());
}
TEST(WTF_SegmentedVector, HeapOnlyGrow)
{
SegmentedVector<int, 4> vector;
vector.append(1);
vector.grow(10);
EXPECT_EQ(10u, vector.size());
EXPECT_EQ(1, vector[0]);
// Default-constructed elements should be 0
for (size_t i = 1; i < 10; ++i)
EXPECT_EQ(0, vector[i]);
}
TEST(WTF_SegmentedVector, HeapOnlyShrinkToFit)
{
SegmentedVector<int, 4> vector;
for (int i = 0; i < 20; ++i)
vector.append(i);
vector.shrinkToFit();
EXPECT_EQ(20u, vector.size());
for (int i = 0; i < 20; ++i)
EXPECT_EQ(i, vector[i]);
}
TEST(WTF_SegmentedVector, HeapOnlyIterator)
{
SegmentedVector<int, 4> vector;
vector.append(10);
vector.append(20);
vector.append(30);
auto it = vector.begin();
auto end = vector.end();
EXPECT_TRUE(it != end);
EXPECT_EQ(10, *it);
++it;
EXPECT_EQ(20, *it);
++it;
EXPECT_EQ(30, *it);
++it;
EXPECT_TRUE(it == end);
}
TEST(WTF_SegmentedVector, HeapOnlyIteratorMultipleSegments)
{
SegmentedVector<int, 4> vector;
for (int i = 0; i < 12; ++i)
vector.append(i * 10);
int expected = 0;
for (auto& value : vector) {
EXPECT_EQ(expected, value);
expected += 10;
}
EXPECT_EQ(120, expected);
}
TEST(WTF_SegmentedVector, HeapOnlyAlloc)
{
SegmentedVector<int, 4> vector;
int& ref1 = vector.alloc(100);
EXPECT_EQ(100, ref1);
EXPECT_EQ(1u, vector.size());
int& ref2 = vector.alloc(200);
EXPECT_EQ(200, ref2);
EXPECT_EQ(2u, vector.size());
// References should remain valid
EXPECT_EQ(100, ref1);
EXPECT_EQ(200, ref2);
}
TEST(WTF_SegmentedVector, HeapOnlyAllocMultipleSegments)
{
SegmentedVector<int, 4> vector;
int* refs[12];
for (int i = 0; i < 12; ++i)
refs[i] = &vector.alloc(i * 100);
// All references should remain valid
for (int i = 0; i < 12; ++i)
EXPECT_EQ(i * 100, *refs[i]);
}
TEST(WTF_SegmentedVector, HeapOnlyMoveOnly)
{
SegmentedVector<MoveOnly, 4> vector;
vector.append(MoveOnly(1));
vector.append(MoveOnly(2));
vector.append(MoveOnly(3));
EXPECT_EQ(1u, vector[0].value());
EXPECT_EQ(2u, vector[1].value());
EXPECT_EQ(3u, vector[2].value());
auto last = vector.takeLast();
EXPECT_EQ(3u, last.value());
EXPECT_EQ(2u, vector.size());
}
TEST(WTF_SegmentedVector, HeapOnlyMoveOnlyMultipleSegments)
{
SegmentedVector<MoveOnly, 4> vector;
for (unsigned i = 0; i < 12; ++i)
vector.append(MoveOnly(i));
EXPECT_EQ(12u, vector.size());
for (unsigned i = 0; i < 12; ++i)
EXPECT_EQ(i, vector[i].value());
}
// ============================================================
// Section 2: Inline + Heap mode (InlineCapacity > 0)
// ============================================================
TEST(WTF_SegmentedVector, InlineEmpty)
{
SegmentedVector<int, 4, 4> vector;
EXPECT_TRUE(vector.isEmpty());
EXPECT_EQ(0u, vector.size());
}
TEST(WTF_SegmentedVector, InlineAppendAndAccess)
{
SegmentedVector<int, 4, 4> vector;
vector.append(1);
vector.append(2);
vector.append(3);
EXPECT_FALSE(vector.isEmpty());
EXPECT_EQ(3u, vector.size());
EXPECT_EQ(1, vector.at(0));
EXPECT_EQ(2, vector.at(1));
EXPECT_EQ(3, vector.at(2));
EXPECT_EQ(1, vector[0]);
EXPECT_EQ(2, vector[1]);
EXPECT_EQ(3, vector[2]);
}
TEST(WTF_SegmentedVector, InlineFirstAndLast)
{
SegmentedVector<int, 4, 4> vector;
vector.append(10);
vector.append(20);
vector.append(30);
EXPECT_EQ(10, vector.first());
EXPECT_EQ(30, vector.last());
}
TEST(WTF_SegmentedVector, InlineOnlyStorage)
{
// Fill exactly to InlineCapacity — no heap allocation should occur
SegmentedVector<int, 4, 4> vector;
for (int i = 0; i < 4; ++i)
vector.append(i * 10);
EXPECT_EQ(4u, vector.size());
for (int i = 0; i < 4; ++i)
EXPECT_EQ(i * 10, vector[i]);
}
TEST(WTF_SegmentedVector, InlineToHeapTransition)
{
// Add InlineCapacity+1 elements to trigger the first heap allocation
SegmentedVector<int, 4, 4> vector;
for (int i = 0; i < 5; ++i)
vector.append(i * 10);
EXPECT_EQ(5u, vector.size());
for (int i = 0; i < 5; ++i)
EXPECT_EQ(i * 10, vector[i]);
}
TEST(WTF_SegmentedVector, InlineHeapMultipleSegments)
{
// Test that elements beyond inline storage go to heap segments
SegmentedVector<int, 4, 4> vector;
// 12 elements: 4 inline + 2 heap segments of 4
for (int i = 0; i < 12; ++i)
vector.append(i);
EXPECT_EQ(12u, vector.size());
for (int i = 0; i < 12; ++i)
EXPECT_EQ(i, vector[i]);
}
TEST(WTF_SegmentedVector, InlinePointerStability)
{
SegmentedVector<int, 4, 4> vector;
vector.append(100);
int* firstPtr = &vector[0];
// Add more elements, triggering heap segment allocation
for (int i = 1; i < 20; ++i)
vector.append(i * 100);
// Original pointer should still be valid
EXPECT_EQ(100, *firstPtr);
// Also verify pointers to heap elements remain stable
int* heapPtr = &vector[10];
for (int i = 20; i < 40; ++i)
vector.append(i * 100);
EXPECT_EQ(1000, *heapPtr);
}
TEST(WTF_SegmentedVector, InlinePointerStabilityAtBoundary)
{
SegmentedVector<int, 4, 4> vector;
// Fill inline storage
for (int i = 0; i < 4; ++i)
vector.append(i);
// Pointer to last inline element
int* lastInlinePtr = &vector[3];
// Add one more element — first heap element
vector.append(99);
int* firstHeapPtr = &vector[4];
// Add more elements to grow further
for (int i = 5; i < 20; ++i)
vector.append(i);
// Both pointers should remain valid
EXPECT_EQ(3, *lastInlinePtr);
EXPECT_EQ(99, *firstHeapPtr);
}
TEST(WTF_SegmentedVector, InlineRemoveLast)
{
SegmentedVector<int, 4, 4> vector;
vector.append(1);
vector.append(2);
vector.append(3);
EXPECT_EQ(3u, vector.size());
vector.removeLast();
EXPECT_EQ(2u, vector.size());
EXPECT_EQ(2, vector.last());
vector.removeLast();
EXPECT_EQ(1u, vector.size());
EXPECT_EQ(1, vector.last());
vector.removeLast();
EXPECT_TRUE(vector.isEmpty());
}
TEST(WTF_SegmentedVector, InlineRemoveLastAcrossBoundary)
{
SegmentedVector<int, 4, 4> vector;
// Fill past inline into heap
for (int i = 0; i < 6; ++i)
vector.append(i * 10);
EXPECT_EQ(6u, vector.size());
// Remove back into inline range
vector.removeLast(); // removes 50
vector.removeLast(); // removes 40
EXPECT_EQ(4u, vector.size());
EXPECT_EQ(30, vector.last());
// Remove within inline range
vector.removeLast(); // removes 30
EXPECT_EQ(3u, vector.size());
EXPECT_EQ(20, vector.last());
}
TEST(WTF_SegmentedVector, InlineTakeLast)
{
SegmentedVector<int, 4, 4> vector;
vector.append(10);
vector.append(20);
vector.append(30);
EXPECT_EQ(30, vector.takeLast());
EXPECT_EQ(2u, vector.size());
EXPECT_EQ(20, vector.takeLast());
EXPECT_EQ(1u, vector.size());
}
TEST(WTF_SegmentedVector, InlineClear)
{
SegmentedVector<int, 4, 4> vector;
for (int i = 0; i < 20; ++i)
vector.append(i);
EXPECT_EQ(20u, vector.size());
vector.clear();
EXPECT_TRUE(vector.isEmpty());
EXPECT_EQ(0u, vector.size());
}
TEST(WTF_SegmentedVector, InlineClearAndReuse)
{
SegmentedVector<int, 4, 4> vector;
// Fill past inline into heap
for (int i = 0; i < 10; ++i)
vector.append(i);
vector.clear();
EXPECT_TRUE(vector.isEmpty());
// Re-append — should reuse inline storage
for (int i = 0; i < 3; ++i)
vector.append(i * 100);
EXPECT_EQ(3u, vector.size());
EXPECT_EQ(0, vector[0]);
EXPECT_EQ(100, vector[1]);
EXPECT_EQ(200, vector[2]);
}
TEST(WTF_SegmentedVector, InlineGrowWithinInline)
{
SegmentedVector<int, 4, 4> vector;
vector.append(42);
vector.grow(4); // grow to exactly InlineCapacity
EXPECT_EQ(4u, vector.size());
EXPECT_EQ(42, vector[0]);
for (size_t i = 1; i < 4; ++i)
EXPECT_EQ(0, vector[i]);
}
TEST(WTF_SegmentedVector, InlineGrowAcrossBoundary)
{
SegmentedVector<int, 4, 4> vector;
vector.append(42);
vector.append(43);
vector.grow(7); // from 2 (inline) to 7 (inline + heap)
EXPECT_EQ(7u, vector.size());
EXPECT_EQ(42, vector[0]);
EXPECT_EQ(43, vector[1]);
for (size_t i = 2; i < 7; ++i)
EXPECT_EQ(0, vector[i]);
}
TEST(WTF_SegmentedVector, InlineGrow)
{
SegmentedVector<int, 4, 4> vector;
vector.append(1);
vector.grow(10);
EXPECT_EQ(10u, vector.size());
EXPECT_EQ(1, vector[0]);
// Default-constructed elements should be 0
for (size_t i = 1; i < 10; ++i)
EXPECT_EQ(0, vector[i]);
}
TEST(WTF_SegmentedVector, InlineShrinkToFit)
{
SegmentedVector<int, 4, 4> vector;
for (int i = 0; i < 20; ++i)
vector.append(i);
// shrinkToFit should not affect functionality
vector.shrinkToFit();
EXPECT_EQ(20u, vector.size());
for (int i = 0; i < 20; ++i)
EXPECT_EQ(i, vector[i]);
}
TEST(WTF_SegmentedVector, InlineIteratorInlineOnly)
{
// Iterator over exactly InlineCapacity elements (no heap)
SegmentedVector<int, 4, 4> vector;
for (int i = 0; i < 4; ++i)
vector.append(i * 10);
int expected = 0;
for (auto& value : vector) {
EXPECT_EQ(expected, value);
expected += 10;
}
EXPECT_EQ(40, expected);
}
TEST(WTF_SegmentedVector, InlineIteratorSpanningSegments)
{
SegmentedVector<int, 4, 4> vector;
for (int i = 0; i < 12; ++i)
vector.append(i * 10);
int expected = 0;
for (auto& value : vector) {
EXPECT_EQ(expected, value);
expected += 10;
}
EXPECT_EQ(120, expected);
}
TEST(WTF_SegmentedVector, InlineMoveOnly)
{
SegmentedVector<MoveOnly, 4, 4> vector;
vector.append(MoveOnly(1));
vector.append(MoveOnly(2));
vector.append(MoveOnly(3));
EXPECT_EQ(1u, vector[0].value());
EXPECT_EQ(2u, vector[1].value());
EXPECT_EQ(3u, vector[2].value());
auto last = vector.takeLast();
EXPECT_EQ(3u, last.value());
EXPECT_EQ(2u, vector.size());
}
TEST(WTF_SegmentedVector, InlineMoveOnlySpanningSegments)
{
SegmentedVector<MoveOnly, 4, 4> vector;
for (unsigned i = 0; i < 12; ++i)
vector.append(MoveOnly(i));
EXPECT_EQ(12u, vector.size());
for (unsigned i = 0; i < 12; ++i)
EXPECT_EQ(i, vector[i].value());
}
TEST(WTF_SegmentedVector, InlineAlloc)
{
SegmentedVector<int, 4, 4> vector;
int& ref1 = vector.alloc(100);
EXPECT_EQ(100, ref1);
EXPECT_EQ(1u, vector.size());
int& ref2 = vector.alloc(200);
EXPECT_EQ(200, ref2);
EXPECT_EQ(2u, vector.size());
// References should remain valid
EXPECT_EQ(100, ref1);
EXPECT_EQ(200, ref2);
}
TEST(WTF_SegmentedVector, InlineAllocSpanningSegments)
{
SegmentedVector<int, 4, 4> vector;
// Alloc elements across inline and heap segments
int* refs[12];
for (int i = 0; i < 12; ++i)
refs[i] = &vector.alloc(i * 100);
// All references should remain valid
for (int i = 0; i < 12; ++i)
EXPECT_EQ(i * 100, *refs[i]);
}
TEST(WTF_SegmentedVector, InlineConstructAndAppend)
{
SegmentedVector<int, 4, 4> vector;
vector.constructAndAppend(42);
vector.constructAndAppend(43);
EXPECT_EQ(2u, vector.size());
EXPECT_EQ(42, vector[0]);
EXPECT_EQ(43, vector[1]);
}
TEST(WTF_SegmentedVector, InlineDifferentSegmentSizes)
{
// Test with different inline and heap segment sizes
SegmentedVector<int, 8, 2> vector;
for (int i = 0; i < 20; ++i)
vector.append(i);
EXPECT_EQ(20u, vector.size());
for (int i = 0; i < 20; ++i)
EXPECT_EQ(i, vector[i]);
}
TEST(WTF_SegmentedVector, InlineLargeInlineSegment)
{
// Test with larger inline segment
SegmentedVector<int, 4, 16> vector;
for (int i = 0; i < 32; ++i)
vector.append(i);
EXPECT_EQ(32u, vector.size());
for (int i = 0; i < 32; ++i)
EXPECT_EQ(i, vector[i]);
}
// ============================================================
// Section 3: Destructor verification
// ============================================================
TEST(WTF_SegmentedVector, DestructorCalledOnRemoveLast)
{
SegmentedVector<DestructorCounter, 4> vector;
vector.append(DestructorCounter(1));
vector.append(DestructorCounter(2));
vector.append(DestructorCounter(3));
DestructorCounter::resetCount();
vector.removeLast();
EXPECT_EQ(1u, DestructorCounter::s_destructorCount);
vector.removeLast();
EXPECT_EQ(2u, DestructorCounter::s_destructorCount);
}
TEST(WTF_SegmentedVector, DestructorCalledOnClear)
{
SegmentedVector<DestructorCounter, 4> vector;
vector.append(DestructorCounter(1));
vector.append(DestructorCounter(2));
vector.append(DestructorCounter(3));
DestructorCounter::resetCount();
vector.clear();
EXPECT_EQ(3u, DestructorCounter::s_destructorCount);
}
TEST(WTF_SegmentedVector, DestructorCalledOnDestruction)
{
{
SegmentedVector<DestructorCounter, 4> vector;
vector.append(DestructorCounter(1));
vector.append(DestructorCounter(2));
vector.append(DestructorCounter(3));
// Reset count after construction
DestructorCounter::resetCount();
}
// Vector destructor should have destroyed all 3 elements
EXPECT_EQ(3u, DestructorCounter::s_destructorCount);
}
} // namespace TestWebKitAPI