mirror of
https://github.com/KytyPS5/KytyPS5.git
synced 2026-08-03 19:28:31 +00:00
311 lines
10 KiB
C++
311 lines
10 KiB
C++
#include "common/bitArray.h"
|
|
|
|
#include <array>
|
|
#include <cstdint>
|
|
#include <cstdio>
|
|
#include <cstdlib>
|
|
#include <utility>
|
|
|
|
namespace {
|
|
|
|
using Bits = Common::BitArray<128>;
|
|
|
|
static_assert(sizeof(Common::BitArray<1024>) == 128);
|
|
|
|
void Check(bool value, const char *message) {
|
|
if (!value) {
|
|
std::fprintf(stderr, "BitArrayTests: failed: %s\n", message);
|
|
std::abort();
|
|
}
|
|
}
|
|
|
|
void TestPointAndRangeOperations() {
|
|
Bits bits;
|
|
Check(bits.None() && !bits.Any(), "default state is not empty");
|
|
|
|
for (const auto index : {size_t{0}, size_t{63}, size_t{64}, size_t{127}}) {
|
|
bits.Set(index);
|
|
Check(bits.Get(index), "Set did not set a boundary bit");
|
|
bits.Unset(index);
|
|
Check(!bits.Get(index), "Unset did not clear a boundary bit");
|
|
}
|
|
|
|
bits.SetRange(60, 68);
|
|
for (size_t index = 0; index < 128; index++) {
|
|
Check(bits.Get(index) == (index >= 60 && index < 68),
|
|
"cross-word SetRange changed the wrong bits");
|
|
}
|
|
bits.Fill();
|
|
bits.UnsetRange(60, 68);
|
|
for (size_t index = 0; index < 128; index++) {
|
|
Check(bits.Get(index) == !(index >= 60 && index < 68),
|
|
"cross-word UnsetRange changed the wrong bits");
|
|
}
|
|
bits.Clear();
|
|
bits.SetRange(0, 128);
|
|
Check(!bits.None(), "full SetRange left the array empty");
|
|
bits.UnsetRange(0, 128);
|
|
Check(bits.None(), "full UnsetRange left set bits");
|
|
|
|
bits.Set(7);
|
|
bits.SetRange(9, 9);
|
|
bits.SetRange(0, 129);
|
|
bits.UnsetRange(9, 9);
|
|
bits.UnsetRange(0, 129);
|
|
Check(bits.Get(7), "invalid or empty range modified the array");
|
|
}
|
|
|
|
void TestMaskedConstructionAndBitwiseOperations() {
|
|
Bits source;
|
|
source.Fill();
|
|
const Bits masked(source, 31, 97);
|
|
for (size_t index = 0; index < 128; index++) {
|
|
Check(masked.Get(index) == (index >= 31 && index < 97),
|
|
"masked constructor retained a bit outside its range");
|
|
}
|
|
Check(Bits(source, 12, 12).None(), "empty masked constructor produced bits");
|
|
Check(Bits(source, 0, 129).None(),
|
|
"invalid masked constructor produced bits");
|
|
|
|
Bits left;
|
|
left.SetRange(0, 80);
|
|
Bits right;
|
|
right.SetRange(40, 120);
|
|
const auto exclusive = left ^ right;
|
|
for (size_t index = 0; index < 128; index++) {
|
|
const bool expected = (index < 80) != (index >= 40 && index < 120);
|
|
Check(exclusive.Get(index) == expected, "XOR produced the wrong bit");
|
|
}
|
|
const auto inverted = ~left;
|
|
for (size_t index = 0; index < 128; index++) {
|
|
Check(inverted.Get(index) == (index >= 80), "NOT produced the wrong bit");
|
|
}
|
|
}
|
|
|
|
void TestRangeDiscoveryAndIteration() {
|
|
Bits bits;
|
|
Check(bits.FirstRange() == Bits::Range{128, 128},
|
|
"empty FirstRange is wrong");
|
|
Check(bits.LastRange() == Bits::Range{0, 0}, "empty LastRange is wrong");
|
|
|
|
bits.SetRange(3, 8);
|
|
bits.SetRange(63, 70);
|
|
bits.Set(127);
|
|
Check(bits.FirstRange() == Bits::Range{3, 8}, "FirstRange is wrong");
|
|
Check(bits.FirstRangeFrom(5) == Bits::Range{5, 8},
|
|
"FirstRangeFrom inside a run is wrong");
|
|
Check(bits.FirstRangeFrom(8) == Bits::Range{63, 70},
|
|
"FirstRangeFrom gap is wrong");
|
|
Check(bits.LastRange() == Bits::Range{127, 128}, "LastRange is wrong");
|
|
Check(bits.LastRangeFrom(69) == Bits::Range{63, 69},
|
|
"LastRangeFrom inside a run is wrong");
|
|
Check(bits.LastRangeFrom(63) == Bits::Range{3, 8},
|
|
"LastRangeFrom gap is wrong");
|
|
|
|
constexpr std::array expected{Bits::Range{3, 8}, Bits::Range{63, 70},
|
|
Bits::Range{127, 128}};
|
|
size_t range_index = 0;
|
|
for (const auto range : bits) {
|
|
Check(range_index < expected.size() && range == expected[range_index],
|
|
"range iterator produced the wrong run");
|
|
range_index++;
|
|
}
|
|
Check(range_index == expected.size(), "range iterator omitted a run");
|
|
}
|
|
|
|
void TestRandomizedDifferential() {
|
|
Bits bits;
|
|
std::array<bool, 128> reference{};
|
|
uint64_t random = 0x53a9'7f11'ced4'29b5ull;
|
|
const auto next_random = [&random] {
|
|
random ^= random << 13;
|
|
random ^= random >> 7;
|
|
random ^= random << 17;
|
|
return random;
|
|
};
|
|
|
|
for (size_t operation = 0; operation < 10000; operation++) {
|
|
const auto first = static_cast<size_t>(next_random() % 128);
|
|
const auto last =
|
|
first + 1 + static_cast<size_t>(next_random() % (128 - first));
|
|
if ((next_random() & 1) != 0) {
|
|
bits.SetRange(first, last);
|
|
for (auto index = first; index < last; index++) {
|
|
reference[index] = true;
|
|
}
|
|
} else {
|
|
bits.UnsetRange(first, last);
|
|
for (auto index = first; index < last; index++) {
|
|
reference[index] = false;
|
|
}
|
|
}
|
|
|
|
bool any = false;
|
|
for (size_t index = 0; index < reference.size(); index++) {
|
|
Check(bits.Get(index) == reference[index],
|
|
"randomized bit state diverged");
|
|
any |= reference[index];
|
|
}
|
|
Check(bits.Any() == any && bits.None() == !any,
|
|
"randomized Any/None diverged");
|
|
|
|
const auto range_start = static_cast<size_t>(next_random() % 129);
|
|
auto expected_first_begin = range_start;
|
|
while (expected_first_begin < reference.size() &&
|
|
!reference[expected_first_begin]) {
|
|
expected_first_begin++;
|
|
}
|
|
if (expected_first_begin == reference.size()) {
|
|
Check(bits.FirstRangeFrom(range_start) == Bits::Range{128, 128},
|
|
"randomized FirstRangeFrom empty suffix diverged");
|
|
} else {
|
|
auto expected_first_end = expected_first_begin;
|
|
while (expected_first_end < reference.size() &&
|
|
reference[expected_first_end]) {
|
|
expected_first_end++;
|
|
}
|
|
Check(bits.FirstRangeFrom(range_start) ==
|
|
Bits::Range{expected_first_begin, expected_first_end},
|
|
"randomized FirstRangeFrom diverged");
|
|
}
|
|
|
|
const auto range_end = static_cast<size_t>(next_random() % 129);
|
|
auto expected_last_end = range_end;
|
|
while (expected_last_end != 0 && !reference[expected_last_end - 1]) {
|
|
expected_last_end--;
|
|
}
|
|
if (expected_last_end == 0) {
|
|
Check(bits.LastRangeFrom(range_end) == Bits::Range{0, 0},
|
|
"randomized LastRangeFrom empty prefix diverged");
|
|
} else {
|
|
auto expected_last_begin = expected_last_end;
|
|
while (expected_last_begin != 0 && reference[expected_last_begin - 1]) {
|
|
expected_last_begin--;
|
|
}
|
|
Check(bits.LastRangeFrom(range_end) ==
|
|
Bits::Range{expected_last_begin, expected_last_end},
|
|
"randomized LastRangeFrom diverged");
|
|
}
|
|
|
|
const auto masked_start = static_cast<size_t>(next_random() % 129);
|
|
const auto masked_end =
|
|
masked_start +
|
|
static_cast<size_t>(next_random() % (129 - masked_start));
|
|
const Bits masked(bits, masked_start, masked_end);
|
|
for (size_t index = 0; index < reference.size(); index++) {
|
|
Check(masked.Get(index) == (index >= masked_start && index < masked_end &&
|
|
reference[index]),
|
|
"randomized masked constructor diverged");
|
|
}
|
|
|
|
size_t first_begin = 0;
|
|
while (first_begin < reference.size() && !reference[first_begin]) {
|
|
first_begin++;
|
|
}
|
|
if (first_begin == reference.size()) {
|
|
Check(bits.FirstRange() == Bits::Range{128, 128},
|
|
"randomized empty FirstRange diverged");
|
|
Check(bits.LastRange() == Bits::Range{0, 0},
|
|
"randomized empty LastRange diverged");
|
|
} else {
|
|
auto first_end = first_begin;
|
|
while (first_end < reference.size() && reference[first_end]) {
|
|
first_end++;
|
|
}
|
|
Check(bits.FirstRange() == Bits::Range{first_begin, first_end},
|
|
"randomized FirstRange diverged");
|
|
|
|
auto last_end = reference.size();
|
|
while (!reference[last_end - 1]) {
|
|
last_end--;
|
|
}
|
|
auto last_begin = last_end;
|
|
while (last_begin != 0 && reference[last_begin - 1]) {
|
|
last_begin--;
|
|
}
|
|
Check(bits.LastRange() == Bits::Range{last_begin, last_end},
|
|
"randomized LastRange diverged");
|
|
}
|
|
|
|
size_t expected_begin = 0;
|
|
for (const auto [begin, end] : bits) {
|
|
while (expected_begin < reference.size() && !reference[expected_begin]) {
|
|
expected_begin++;
|
|
}
|
|
Check(begin == expected_begin, "randomized iterator run start diverged");
|
|
while (expected_begin < reference.size() && reference[expected_begin]) {
|
|
expected_begin++;
|
|
}
|
|
Check(end == expected_begin, "randomized iterator run end diverged");
|
|
}
|
|
while (expected_begin < reference.size() && !reference[expected_begin]) {
|
|
expected_begin++;
|
|
}
|
|
Check(expected_begin == reference.size(),
|
|
"randomized iterator omitted a run");
|
|
}
|
|
}
|
|
|
|
void TestTrackerSizedRandomizedDifferential() {
|
|
using TrackerBits = Common::BitArray<1024>;
|
|
TrackerBits bits;
|
|
std::array<bool, 1024> reference{};
|
|
uint64_t random = 0x9e37'79b9'7f4a'7c15ull;
|
|
const auto next_random = [&random] {
|
|
random ^= random << 13;
|
|
random ^= random >> 7;
|
|
random ^= random << 17;
|
|
return random;
|
|
};
|
|
|
|
for (size_t operation = 0; operation < 4096; operation++) {
|
|
const auto first = static_cast<size_t>(next_random() % reference.size());
|
|
const auto last =
|
|
first + 1 +
|
|
static_cast<size_t>(next_random() % (reference.size() - first));
|
|
const bool set = (next_random() & 1) != 0;
|
|
if (set) {
|
|
bits.SetRange(first, last);
|
|
} else {
|
|
bits.UnsetRange(first, last);
|
|
}
|
|
for (auto index = first; index < last; index++) {
|
|
reference[index] = set;
|
|
}
|
|
|
|
for (size_t index = 0; index < reference.size(); index++) {
|
|
Check(bits.Get(index) == reference[index],
|
|
"tracker-sized randomized bit state diverged");
|
|
}
|
|
|
|
size_t expected = 0;
|
|
for (const auto [begin, end] : bits) {
|
|
while (expected < reference.size() && !reference[expected]) {
|
|
expected++;
|
|
}
|
|
Check(begin == expected, "tracker-sized randomized range start diverged");
|
|
while (expected < reference.size() && reference[expected]) {
|
|
expected++;
|
|
}
|
|
Check(end == expected, "tracker-sized randomized range end diverged");
|
|
}
|
|
while (expected < reference.size() && !reference[expected]) {
|
|
expected++;
|
|
}
|
|
Check(expected == reference.size(),
|
|
"tracker-sized randomized iterator omitted a run");
|
|
}
|
|
}
|
|
|
|
} // namespace
|
|
|
|
int main() {
|
|
TestPointAndRangeOperations();
|
|
TestMaskedConstructionAndBitwiseOperations();
|
|
TestRangeDiscoveryAndIteration();
|
|
TestRandomizedDifferential();
|
|
TestTrackerSizedRandomizedDifferential();
|
|
std::puts("BitArrayTests: all cases passed");
|
|
return 0;
|
|
}
|