blob: 7aceff11cd419e7f281c6bad4642e3f23eaeb9e7 [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/BitVector.h>
#include <wtf/MathExtras.h>
#include <wtf/Vector.h>
namespace TestWebKitAPI {
// Bug: mergeSlow iterates a.size() words but indexes into b which may have
// fewer words, causing an out-of-bounds read. This happens when `this` is
// already larger than `other` (both out-of-line) so ensureSize() is a no-op.
TEST(WTF_BitVector, MergeLargerIntoSmaller)
{
// Create a large BitVector (256 bits = 4 words on 64-bit).
BitVector large(256);
large.quickSet(0);
large.quickSet(200);
// Create a smaller BitVector (128 bits = 2 words on 64-bit).
BitVector small(128);
small.quickSet(42);
small.quickSet(100);
// large.merge(small) should OR small's bits into large.
// Bug: the loop uses large's word count as the bound but indexes into
// small's word span, reading words out of bounds.
large.merge(small);
// Bits originally in large must survive.
EXPECT_TRUE(large.get(0));
EXPECT_TRUE(large.get(200));
// Bits from small must be merged in.
EXPECT_TRUE(large.get(42));
EXPECT_TRUE(large.get(100));
// Bits that were never set must remain clear.
EXPECT_FALSE(large.get(1));
EXPECT_FALSE(large.get(64));
EXPECT_FALSE(large.get(150));
EXPECT_FALSE(large.get(255));
// Total set bits should be exactly 4.
EXPECT_EQ(large.bitCount(), 4u);
}
TEST(WTF_BitVector, MoveConstructInline)
{
BitVector a;
a.set(0);
a.set(5);
a.set(62);
BitVector b(WTF::move(a));
EXPECT_TRUE(b.get(0));
EXPECT_TRUE(b.get(5));
EXPECT_TRUE(b.get(62));
EXPECT_EQ(b.bitCount(), 3u);
// Source should be empty after move.
EXPECT_TRUE(a.isEmpty());
EXPECT_EQ(a.bitCount(), 0u);
}
TEST(WTF_BitVector, MoveConstructOutOfLine)
{
BitVector a(256);
a.quickSet(0);
a.quickSet(100);
a.quickSet(200);
BitVector b(WTF::move(a));
EXPECT_TRUE(b.get(0));
EXPECT_TRUE(b.get(100));
EXPECT_TRUE(b.get(200));
EXPECT_EQ(b.bitCount(), 3u);
EXPECT_GE(b.size(), 256u);
// Source should be empty inline after move.
EXPECT_TRUE(a.isEmpty());
EXPECT_EQ(a.bitCount(), 0u);
}
TEST(WTF_BitVector, MoveAssignInline)
{
BitVector a;
a.set(3);
a.set(10);
BitVector b;
b.set(7);
b = WTF::move(a);
EXPECT_TRUE(b.get(3));
EXPECT_TRUE(b.get(10));
EXPECT_FALSE(b.get(7));
EXPECT_EQ(b.bitCount(), 2u);
EXPECT_TRUE(a.isEmpty());
}
TEST(WTF_BitVector, MoveAssignOutOfLine)
{
BitVector a(256);
a.quickSet(50);
a.quickSet(150);
BitVector b(128);
b.quickSet(0);
b = WTF::move(a);
EXPECT_TRUE(b.get(50));
EXPECT_TRUE(b.get(150));
EXPECT_FALSE(b.get(0));
EXPECT_EQ(b.bitCount(), 2u);
EXPECT_GE(b.size(), 256u);
EXPECT_TRUE(a.isEmpty());
}
TEST(WTF_BitVector, MoveAssignOutOfLineToOutOfLine)
{
// Both source and destination are out-of-line. The destination's
// allocation should be freed.
BitVector a(200);
a.quickSet(100);
BitVector b(300);
b.quickSet(250);
b = WTF::move(a);
EXPECT_TRUE(b.get(100));
EXPECT_FALSE(b.get(250));
EXPECT_EQ(b.bitCount(), 1u);
EXPECT_TRUE(a.isEmpty());
}
TEST(WTF_BitVector, MoveInVector)
{
Vector<BitVector> vec;
for (unsigned i = 0; i < 100; ++i) {
BitVector bv(256);
bv.quickSet(i);
vec.append(WTF::move(bv));
}
for (unsigned i = 0; i < 100; ++i) {
EXPECT_TRUE(vec[i].get(i));
EXPECT_EQ(vec[i].bitCount(), 1u);
}
}
TEST(WTF_BitVector, MoveAssignSelf)
{
BitVector a(256);
a.quickSet(42);
auto& ref = a;
a = WTF::move(ref);
// Should survive self-move-assignment.
EXPECT_TRUE(a.get(42));
EXPECT_EQ(a.bitCount(), 1u);
}
TEST(WTF_BitVector, FilterOutOfLineWithInline)
{
// Create an out-of-line BitVector (256 bits = 4 words on 64-bit)
// with bits set across multiple words.
BitVector large(256);
large.quickSet(5);
large.quickSet(42);
large.quickSet(100); // word 1 (bits 64-127).
large.quickSet(200); // word 3 (bits 192-255).
// Create an inline BitVector (fits in maxInlineBits = 63 bits)
// with only bit 5 set.
BitVector small;
small.set(5);
// filter is AND: only bits set in both should survive.
// Bit 5 is in both, so it survives. Bits 42, 100, 200 are not in
// small (which has no bits beyond 62), so they must be cleared.
large.filter(small);
EXPECT_TRUE(large.get(5));
EXPECT_FALSE(large.get(42));
EXPECT_FALSE(large.get(100));
EXPECT_FALSE(large.get(200));
EXPECT_EQ(large.bitCount(), 1u);
}
} // namespace TestWebKitAPI