From 1b630bc07fe355822e713a40ab5a3fecc8034a0c Mon Sep 17 00:00:00 2001 From: Green Sky Date: Mon, 24 Jun 2024 12:14:51 +0200 Subject: [PATCH] impl and test bitset util --- CMakeLists.txt | 19 ++ solanaceae/ngc_ft1_sha1/bitset.hpp | 104 ++++++++++ solanaceae/ngc_ft1_sha1/bitset_tests.cpp | 245 +++++++++++++++++++++++ 3 files changed, 368 insertions(+) create mode 100644 solanaceae/ngc_ft1_sha1/bitset.hpp create mode 100644 solanaceae/ngc_ft1_sha1/bitset_tests.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index ccfb116..42082ab 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -69,3 +69,22 @@ target_link_libraries(solanaceae_sha1_ngcft1 PUBLIC solanaceae_file2 ) +######################################## + +option(SOLANACEAE_NGCFT1_SHA1_BUILD_TESTING "Build the solanaceae_ngcft1_sha1 tests" OFF) +message("II SOLANACEAE_NGCFT1_SHA1_BUILD_TESTING " ${SOLANACEAE_NGCFT1_SHA1_BUILD_TESTING}) + +# TODO: proper options n shit +if (SOLANACEAE_NGCFT1_SHA1_BUILD_TESTING) + include(CTest) + + add_executable(bitset_tests + ./solanaceae/ngc_ft1_sha1/bitset_tests.cpp + ) + + target_link_libraries(bitset_tests PUBLIC + solanaceae_sha1_ngcft1 + ) + +endif() + diff --git a/solanaceae/ngc_ft1_sha1/bitset.hpp b/solanaceae/ngc_ft1_sha1/bitset.hpp new file mode 100644 index 0000000..3da2383 --- /dev/null +++ b/solanaceae/ngc_ft1_sha1/bitset.hpp @@ -0,0 +1,104 @@ +#pragma once + +#include +#include + + +#include + +// runtime sized bitset (stl so sad) +// size is round up to bytes +struct BitSet { + std::vector _bytes; + + BitSet(void) = delete; + BitSet(const BitSet&) = default; + BitSet(BitSet&&) = default; + BitSet(size_t size) { + _bytes.resize((size+7)/8); + } + + bool operator[](size_t pos) const { + assert(pos < size_bits()); + if (pos >= size_bits()) { + return false; + } + + const size_t pos_in_bytes = pos/8; + + assert(pos_in_bytes < size_bytes()); + if (pos_in_bytes >= size_bytes()) { + return false; + } + + const size_t pos_rest_bits = pos%8; + + // bits are ordered high to low + return _bytes[pos_in_bytes] & ((0x1 << 7) >> pos_rest_bits); + } + + void set(size_t pos) { + assert(pos < _bytes.size()*8); + const size_t pos_in_bytes = pos/8; + assert(pos_in_bytes < _bytes.size()); + + const size_t pos_rest_bits = pos%8; + + // bits are ordered high to low + _bytes[pos_in_bytes] |= ((0x1 << 7) >> pos_rest_bits); + } + + void unset(size_t pos) { + assert(pos < _bytes.size()*8); + const size_t pos_in_bytes = pos/8; + assert(pos_in_bytes < _bytes.size()); + + const size_t pos_rest_bits = pos%8; + + // bits are ordered high to low + _bytes[pos_in_bytes] &= ~((0x1 << 7) >> pos_rest_bits); + } + + uint8_t* data(void) { + return _bytes.data(); + } + + size_t size_bits(void) const { + return _bytes.size()*8; + } + + size_t size_bytes(void) const { + return _bytes.size(); + } + + BitSet& merge(const BitSet& other) { + if (other.size_bytes() > size_bytes()) { + _bytes.resize(other.size_bytes()); + } + + for (size_t i = 0; i < size_bytes() && i < other.size_bytes(); i++) { + _bytes[i] |= other._bytes[i]; + } + + return *this; + } + + // start is the first bit in other relative to self + BitSet& merge(const BitSet& other, size_t start) { + // TODO: efficent implementation + + size_t need_size_bits = other.size_bits() + start; + if (need_size_bits > size_bits()) { + _bytes.resize((need_size_bits+7)/8); + } + + for (size_t i = 0; i < other.size_bits(); i++) { + if (other[i]) { + set(start+i); + } + } + + return *this; + } +}; + diff --git a/solanaceae/ngc_ft1_sha1/bitset_tests.cpp b/solanaceae/ngc_ft1_sha1/bitset_tests.cpp new file mode 100644 index 0000000..296f5b9 --- /dev/null +++ b/solanaceae/ngc_ft1_sha1/bitset_tests.cpp @@ -0,0 +1,245 @@ +#include "./bitset.hpp" + +#include +#include +#include + +int main(void) { + + // #################### + + for (size_t i = 1; i <= 8; i++) { + BitSet bs(i); + assert(bs._bytes.size() == 1); + } + + { + BitSet bs(9); + assert(bs._bytes.size() == 2); + } + + { + BitSet bs(24); + assert(bs._bytes.size() == 3); + } + + // #################### + + { // simple bit sets in 1 byte + BitSet bs(8); + assert(bs._bytes.size() == 1); + assert(bs._bytes.at(0) == 0x00); + + bs.set(0); + assert(bs._bytes.at(0) == 0b10000000); + assert(bs[0]); + assert(!bs[1]); + + bs.set(7); + assert(bs._bytes.at(0) == 0b10000001); + assert(bs[7]); + + bs.unset(0); + assert(bs._bytes.at(0) == 0b00000001); + assert(!bs[0]); + + bs.set(6); + assert(bs._bytes.at(0) == 0b00000011); + assert(bs[6]); + + // useless unset + bs.unset(0); + assert(bs._bytes.at(0) == 0b00000011); + assert(!bs[0]); + + // useless set + bs.set(7); + assert(bs._bytes.at(0) == 0b00000011); + assert(bs[7]); + } + + { // simple bit sets in 2 bytes + BitSet bs(16); + assert(bs._bytes.size() == 2); + assert(bs._bytes.at(0) == 0x00); + assert(bs._bytes.at(1) == 0x00); + + // first same as before, making sure no side effects happen + + bs.set(0); + assert(bs._bytes.at(0) == 0b10000000); + assert(bs._bytes.at(1) == 0b00000000); + assert(bs[0]); + + bs.set(7); + assert(bs._bytes.at(0) == 0b10000001); + assert(bs._bytes.at(1) == 0b00000000); + assert(bs[7]); + + bs.unset(0); + assert(bs._bytes.at(0) == 0b00000001); + assert(bs._bytes.at(1) == 0b00000000); + + bs.set(6); + assert(bs._bytes.at(0) == 0b00000011); + assert(bs._bytes.at(1) == 0b00000000); + + // useless unset + bs.unset(0); + assert(bs._bytes.at(0) == 0b00000011); + assert(bs._bytes.at(1) == 0b00000000); + + // useless set + bs.set(7); + assert(bs._bytes.at(0) == 0b00000011); + assert(bs._bytes.at(1) == 0b00000000); + + // now in second byte + + bs.set(8); + assert(bs._bytes.at(0) == 0b00000011); + assert(bs._bytes.at(1) == 0b10000000); + assert(bs[8]); + + bs.set(9); + assert(bs._bytes.at(0) == 0b00000011); + assert(bs._bytes.at(1) == 0b11000000); + assert(bs[9]); + + bs.set(15); + assert(bs._bytes.at(0) == 0b00000011); + assert(bs._bytes.at(1) == 0b11000001); + assert(bs[15]); + + bs.unset(9); + assert(bs._bytes.at(0) == 0b00000011); + assert(bs._bytes.at(1) == 0b10000001); + } + + // random long range set tests + for (size_t iml = 0; iml < 1000; iml++) { + std::default_random_engine rng(1337*17); + + size_t bit_pos = rng()%16384; + size_t total_bit_count = bit_pos + rng()%16384; // so max 32768 + + BitSet bs(total_bit_count); + assert(bs.size_bytes() == (total_bit_count+7)/8); + + for (size_t i = 0; i < total_bit_count; i++) { + assert(!bs[i]); + } + + bs.set(bit_pos); + assert(bs[bit_pos]); + + for (size_t i = 0; i < total_bit_count; i++) { + if (i == bit_pos) { + assert(bs[i]); + } else { + assert(!bs[i]); + } + } + } + + { // merging lets start simple + BitSet bs1(8); + BitSet bs2(8); + assert(bs1._bytes.at(0) == 0b00000000); + assert(bs2._bytes.at(0) == 0b00000000); + + bs2.set(4); + assert(bs2._bytes.at(0) == 0b00001000); + + bs1.merge(bs2); + assert(bs2._bytes.at(0) == 0b00001000); + assert(bs1._bytes.at(0) == 0b00001000); + } + + { // merging + BitSet bs1(8); + BitSet bs2(8); + assert(bs1._bytes.at(0) == 0b00000000); + assert(bs2._bytes.at(0) == 0b00000000); + + bs1.set(1); + bs2.set(4); + assert(bs1._bytes.at(0) == 0b01000000); + assert(bs2._bytes.at(0) == 0b00001000); + + bs1.merge(bs2); + assert(bs2._bytes.at(0) == 0b00001000); + assert(bs1._bytes.at(0) == 0b01001000); + } + + { // merging 2 bytes + BitSet bs1(16); + BitSet bs2(8); + assert(bs1._bytes.at(0) == 0b00000000); + assert(bs1._bytes.at(1) == 0b00000000); + assert(bs2._bytes.at(0) == 0b00000000); + + bs1.set(1); + bs1.set(8); + assert(bs1._bytes.at(0) == 0b01000000); + assert(bs1._bytes.at(1) == 0b10000000); + + bs2.set(4); + assert(bs2._bytes.at(0) == 0b00001000); + + bs1.merge(bs2); + assert(bs2._bytes.at(0) == 0b00001000); + assert(bs1._bytes.at(0) == 0b01001000); + assert(bs1._bytes.at(1) == 0b10000000); + } + + { // merging larger in smaller + BitSet bs1(8); + BitSet bs2(16); + assert(bs1._bytes.at(0) == 0b00000000); + assert(bs2._bytes.at(0) == 0b00000000); + assert(bs2._bytes.at(1) == 0b00000000); + + assert(bs1.size_bytes() != bs2.size_bytes()); + + bs1.set(1); + assert(bs1._bytes.at(0) == 0b01000000); + + bs2.set(4); + bs2.set(8); + assert(bs2._bytes.at(0) == 0b00001000); + assert(bs2._bytes.at(1) == 0b10000000); + + bs1.merge(bs2); + assert(bs2._bytes.at(0) == 0b00001000); + assert(bs2._bytes.at(1) == 0b10000000); + assert(bs1._bytes.at(0) == 0b01001000); + assert(bs1._bytes.at(1) == 0b10000000); + + assert(bs1.size_bytes() == bs2.size_bytes()); + } + + { // offset merge simple + BitSet bs1(16); + BitSet bs2(8); + assert(bs1._bytes.at(0) == 0b00000000); + assert(bs1._bytes.at(1) == 0b00000000); + assert(bs2._bytes.at(0) == 0b00000000); + + bs1.set(1); + bs1.set(8); + assert(bs1._bytes.at(0) == 0b01000000); + assert(bs1._bytes.at(1) == 0b10000000); + + bs2.set(4); + assert(bs2._bytes.at(0) == 0b00001000); + + bs1.merge(bs2, 8); + assert(bs2._bytes.at(0) == 0b00001000); + assert(bs1._bytes.at(0) == 0b01000000); + assert(bs1._bytes.at(1) == 0b10001000); + } + + return 0; +} +