You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
227 lines
7.7 KiB
227 lines
7.7 KiB
// Copyright 2019 The Chromium Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
// found in the LICENSE file.
|
|
|
|
#include "util/yet_another_bit_vector.h"
|
|
|
|
#include <algorithm>
|
|
|
|
#include "absl/algorithm/container.h"
|
|
#include "absl/types/span.h"
|
|
#include "gtest/gtest.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 "inlined storage" (size <= 64 case) and
|
|
// "heap-allocated storage" cases. These are all of the prime numbers less than
|
|
// 100, and also any non-negative multiples of 64 less than 192.
|
|
const int 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].
|
|
absl::Span<const int> GetTestSizesInRange(int first, int last) {
|
|
const auto begin = absl::c_lower_bound(kTestSizes, first);
|
|
const auto end = absl::c_upper_bound(kTestSizes, last);
|
|
return absl::Span<const int>(&*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, int position) {
|
|
constexpr int 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, int from, YetAnotherBitVector* v) {
|
|
for (int 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(YetAnotherBitVectorTest, ConstructsAndResizes) {
|
|
YetAnotherBitVector v;
|
|
ASSERT_EQ(v.size(), 0);
|
|
|
|
for (int fill_set = 0; fill_set <= 1; ++fill_set) {
|
|
for (int size : kTestSizes) {
|
|
const bool all_bits_should_be_set = !!fill_set;
|
|
v.Resize(size, all_bits_should_be_set ? YetAnotherBitVector::SET
|
|
: YetAnotherBitVector::CLEARED);
|
|
ASSERT_EQ(size, v.size());
|
|
for (int 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(YetAnotherBitVectorTest, SetsAndClearsIndividualBits) {
|
|
YetAnotherBitVector v;
|
|
for (int fill_set = 0; fill_set <= 1; ++fill_set) {
|
|
for (int size : kTestSizes) {
|
|
v.Resize(size, fill_set ? YetAnotherBitVector::SET
|
|
: YetAnotherBitVector::CLEARED);
|
|
|
|
for (uint8_t pattern : kBitPatterns) {
|
|
FillWithPattern(pattern, 0, &v);
|
|
for (int i = 0; i < size; ++i) {
|
|
ASSERT_EQ(IsSetInPattern(pattern, i), v.IsSet(i));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Tests that the vector shifts its bits right by various amounts, for various
|
|
// vector sizes and bit patterns.
|
|
TEST(YetAnotherBitVectorTest, ShiftsRight) {
|
|
YetAnotherBitVector v;
|
|
for (int size : kTestSizes) {
|
|
v.Resize(size, YetAnotherBitVector::CLEARED);
|
|
|
|
for (int steps_per_shift : GetTestSizesInRange(0, size)) {
|
|
for (uint8_t pattern : kBitPatterns) {
|
|
FillWithPattern(pattern, 0, &v);
|
|
|
|
if (size == 0 || steps_per_shift == 0) {
|
|
v.ShiftRight(0);
|
|
for (int i = 0; i < size; ++i) {
|
|
ASSERT_EQ(IsSetInPattern(pattern, i), v.IsSet(i));
|
|
}
|
|
} else {
|
|
const int num_shifts = 2 * size / steps_per_shift;
|
|
for (int iteration = 1; iteration <= num_shifts; ++iteration) {
|
|
v.ShiftRight(steps_per_shift);
|
|
const int total_shift_amount = iteration * steps_per_shift;
|
|
for (int i = 0; i < size; ++i) {
|
|
const int original_position = i + total_shift_amount;
|
|
if (original_position >= size) {
|
|
ASSERT_FALSE(v.IsSet(i));
|
|
} else {
|
|
ASSERT_EQ(IsSetInPattern(pattern, original_position),
|
|
v.IsSet(i));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Tests the FindFirstSet() operation, for various vector sizes and bit
|
|
// patterns.
|
|
TEST(YetAnotherBitVectorTest, FindsTheFirstBitSet) {
|
|
YetAnotherBitVector v;
|
|
|
|
// For various sizes of vector where no bits are set, the FFS operation should
|
|
// always return size().
|
|
for (int size : kTestSizes) {
|
|
v.Resize(size, YetAnotherBitVector::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 (int size : kTestSizes) {
|
|
v.Resize(size, YetAnotherBitVector::CLEARED);
|
|
|
|
for (int position_plus_one : GetTestSizesInRange(1, size)) {
|
|
const int 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 (int size : kTestSizes) {
|
|
v.Resize(size, YetAnotherBitVector::CLEARED);
|
|
|
|
for (int position_plus_one : GetTestSizesInRange(1, size)) {
|
|
const int position = position_plus_one - 1;
|
|
v.ClearAll();
|
|
v.Set(position);
|
|
for (uint8_t pattern : kBitPatterns) {
|
|
FillWithPattern(pattern, position_plus_one, &v);
|
|
ASSERT_EQ(position, v.FindFirstSet());
|
|
}
|
|
v.Clear(position);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Tests the CountBitsSet() operation, for various vector sizes, bit patterns,
|
|
// and ranges of bits being counted.
|
|
TEST(YetAnotherBitVector, CountsTheNumberOfBitsSet) {
|
|
YetAnotherBitVector v;
|
|
|
|
// For various sizes of vector where no bits are set, the operation should
|
|
// always return zero for any range.
|
|
for (int size : kTestSizes) {
|
|
v.Resize(size, YetAnotherBitVector::CLEARED);
|
|
for (int begin : GetTestSizesInRange(0, size)) {
|
|
for (int end : GetTestSizesInRange(begin, size)) {
|
|
ASSERT_EQ(0, v.CountBitsSet(begin, end));
|
|
}
|
|
}
|
|
}
|
|
|
|
// For various sizes of vector where all bits are set, the operation should
|
|
// always return the length of the range (or zero for invalid ranges).
|
|
for (int size : kTestSizes) {
|
|
v.Resize(size, YetAnotherBitVector::SET);
|
|
for (int begin : GetTestSizesInRange(0, size)) {
|
|
for (int end : GetTestSizesInRange(begin, size)) {
|
|
ASSERT_EQ(end - begin, v.CountBitsSet(begin, end));
|
|
}
|
|
}
|
|
}
|
|
|
|
// Test various sizes of vector where various patterns of bits are set.
|
|
for (int size : kTestSizes) {
|
|
v.Resize(size, YetAnotherBitVector::CLEARED);
|
|
for (uint8_t pattern : kBitPatterns) {
|
|
FillWithPattern(pattern, 0, &v);
|
|
|
|
for (int begin : GetTestSizesInRange(0, size)) {
|
|
for (int end : GetTestSizesInRange(begin, size)) {
|
|
// Note: The expected value being manually computed by examining each
|
|
// bit individually by calling IsSet(). Thus, this value is only good
|
|
// if IsSet() is working (which is tested by a different unit test).
|
|
int expected_popcount = 0;
|
|
for (int i = begin; i < end; ++i) {
|
|
if (v.IsSet(i)) {
|
|
++expected_popcount;
|
|
}
|
|
}
|
|
|
|
ASSERT_EQ(expected_popcount, v.CountBitsSet(begin, end));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
} // namespace
|
|
} // namespace openscreen
|