blob: d0a89a87060d8443960bbb0ee1754198dae8ddb5 [file]
// Copyright 2026 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "util/bit_vector.h"
#include <algorithm>
#include "gtest/gtest.h"
#include "platform/base/span.h"
namespace openscreen {
namespace {
constexpr uint8_t kBitPatterns[] = {0b00000000, 0b11111111, 0b01010101,
0b10101010, 0b00100100, 0b01001001,
0b10010010, 0b00110110};
// These are used for testing various vector sizes, begins/ends of ranges, etc.
// They will exercise both the small case and the multiple integer case.
const size_t kTestSizes[] = {0, 1, 2, 3, 5, 7, 11, 13, 17, 19,
23, 29, 31, 37, 41, 43, 47, 53, 59, 61,
64, 67, 71, 73, 79, 83, 89, 97, 127, 128};
// Returns a subspan of `kTestSizes` that contains all values in the range
// [first,last].
Span<const size_t> GetTestSizesInRange(size_t first, size_t last) {
const auto begin =
std::lower_bound(std::cbegin(kTestSizes), std::cend(kTestSizes), first);
const auto end =
std::upper_bound(std::cbegin(kTestSizes), std::cend(kTestSizes), last);
return Span<const size_t>(&*begin, std::distance(begin, end));
}
// Returns true if an infinitely-repeating `pattern` has a bit set at the given
// `position`.
constexpr bool IsSetInPattern(uint8_t pattern, size_t position) {
constexpr size_t kRepeatPeriod = sizeof(pattern) * CHAR_BIT;
return !!((pattern >> (position % kRepeatPeriod)) & 1);
}
// Fills an infinitely-repeating `pattern` in `v`, but only modifies the bits at
// and after the given `from` position.
void FillWithPattern(uint8_t pattern, size_t from, BitVector* v) {
for (size_t i = from; i < v->size(); ++i) {
if (IsSetInPattern(pattern, i)) {
v->Set(i);
} else {
v->Clear(i);
}
}
}
// Tests that construction and resizes initialize the vector to the correct size
// and set or clear all of its bits, as requested.
TEST(BitVectorTest, ConstructsAndResizes) {
BitVector v;
ASSERT_EQ(v.size(), 0u);
for (int fill_set = 0; fill_set <= 1; ++fill_set) {
for (size_t size : kTestSizes) {
const bool all_bits_should_be_set = !!fill_set;
v.Resize(size,
all_bits_should_be_set ? BitVector::SET : BitVector::CLEARED);
ASSERT_EQ(size, v.size());
for (size_t i = 0; i < size; ++i) {
ASSERT_EQ(all_bits_should_be_set, v.IsSet(i));
}
}
}
}
// Tests that individual bits can be set and cleared for various vector sizes
// and bit patterns.
TEST(BitVectorTest, SetsAndClearsIndividualBits) {
BitVector v;
for (int fill_set = 0; fill_set <= 1; ++fill_set) {
for (size_t size : kTestSizes) {
v.Resize(size, fill_set ? BitVector::SET : BitVector::CLEARED);
for (uint8_t pattern : kBitPatterns) {
FillWithPattern(pattern, 0, &v);
for (size_t i = 0; i < size; ++i) {
ASSERT_EQ(IsSetInPattern(pattern, i), v.IsSet(i));
}
}
}
}
}
// Tests the FindFirstSet() operation, for various vector sizes and bit
// patterns.
TEST(BitVectorTest, FindsTheFirstBitSet) {
BitVector v;
// For various sizes of vector where no bits are set, the FFS operation should
// always return size().
for (size_t size : kTestSizes) {
v.Resize(size, BitVector::CLEARED);
ASSERT_EQ(size, v.FindFirstSet());
}
// For various sizes of vector where only one bit is set, the FFS operation
// should always return the position of that bit.
for (size_t size : kTestSizes) {
v.Resize(size, BitVector::CLEARED);
for (size_t position_plus_one : GetTestSizesInRange(1, size)) {
const size_t position = position_plus_one - 1;
v.Set(position);
ASSERT_EQ(position, v.FindFirstSet());
v.Clear(position);
}
}
// For various sizes of vector where a pattern of bits are set, the FFS
// operation should always return the first one set.
for (size_t size : kTestSizes) {
v.Resize(size, BitVector::CLEARED);
for (size_t position_plus_one : GetTestSizesInRange(1, size)) {
const size_t position = position_plus_one - 1;
v.Resize(size, BitVector::CLEARED);
v.Set(position);
for (uint8_t pattern : kBitPatterns) {
FillWithPattern(pattern, position_plus_one, &v);
ASSERT_EQ(position, v.FindFirstSet());
}
v.Clear(position);
}
}
}
} // namespace
} // namespace openscreen