From 586f4512e2997517f321b5fe688f3974d1509288 Mon Sep 17 00:00:00 2001 From: Peter McLean Date: Mon, 19 May 2025 22:27:29 -0400 Subject: [PATCH 1/5] Add a bounds type for SystemVerilog style slicing --- CMakeLists.txt | 11 ++- include/bitlib/bit-containers/bit_array.hpp | 1 + .../bitlib/bit-containers/bit_array_base.hpp | 15 +++- include/bitlib/bit-containers/bounds.hpp | 70 +++++++++++++++++++ test/src/test-array.cpp | 16 +++++ 5 files changed, 109 insertions(+), 4 deletions(-) create mode 100644 include/bitlib/bit-containers/bounds.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 061f11a9..d3c788c0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,7 +1,12 @@ # specify the C++ standard cmake_minimum_required(VERSION 3.14) -cmake_policy(SET CMP0168 NEW) -cmake_policy(SET CMP0167 NEW) +if(POLICY CMP0168) + cmake_policy(SET CMP0168 NEW) +endif() + +if(POLICY CMP0167) + cmake_policy(SET CMP0167 NEW) +endif() # set the project name project(BitLib VERSION 0.3.0) @@ -41,6 +46,7 @@ target_sources(bitlib INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/include/bitlib/bit-algorithms/transform.hpp ${CMAKE_CURRENT_SOURCE_DIR}/include/bitlib/bit-algorithms/type_traits.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/include/bitlib/bit-containers/bit_array_base.hpp ${CMAKE_CURRENT_SOURCE_DIR}/include/bitlib/bit-containers/bit_array_dynamic_extent.hpp ${CMAKE_CURRENT_SOURCE_DIR}/include/bitlib/bit-containers/bit_array.hpp ${CMAKE_CURRENT_SOURCE_DIR}/include/bitlib/bit-containers/bit_bitsof.hpp @@ -48,6 +54,7 @@ target_sources(bitlib INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/include/bitlib/bit-containers/bit_mdspan_accessor.hpp ${CMAKE_CURRENT_SOURCE_DIR}/include/bitlib/bit-containers/bit_span.hpp ${CMAKE_CURRENT_SOURCE_DIR}/include/bitlib/bit-containers/bit_vector.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/include/bitlib/bit-containers/bounds.hpp ${CMAKE_CURRENT_SOURCE_DIR}/include/bitlib/bit-iterator/bit_details.hpp ${CMAKE_CURRENT_SOURCE_DIR}/include/bitlib/bit-iterator/bit.hpp diff --git a/include/bitlib/bit-containers/bit_array.hpp b/include/bitlib/bit-containers/bit_array.hpp index 53e32e1f..b143ba1e 100644 --- a/include/bitlib/bit-containers/bit_array.hpp +++ b/include/bitlib/bit-containers/bit_array.hpp @@ -26,6 +26,7 @@ #include "bitlib/bit-containers/bit_array_base.hpp" #include "bitlib/bit-containers/bit_bitsof.hpp" #include "bitlib/bit-containers/bit_span.hpp" +#include "bitlib/bit-containers/bounds.hpp" #include "bitlib/bit-iterator/bit.hpp" #include "bitlib/bit_concepts.hpp" diff --git a/include/bitlib/bit-containers/bit_array_base.hpp b/include/bitlib/bit-containers/bit_array_base.hpp index 009a29e2..850dca9c 100644 --- a/include/bitlib/bit-containers/bit_array_base.hpp +++ b/include/bitlib/bit-containers/bit_array_base.hpp @@ -25,6 +25,7 @@ #include "bitlib/bit-algorithms/bit_algorithm.hpp" #include "bitlib/bit-containers/bit_bitsof.hpp" #include "bitlib/bit-containers/bit_span.hpp" +#include "bitlib/bit-containers/bounds.hpp" #include "bitlib/bit-iterator/bit.hpp" namespace bit { @@ -163,17 +164,27 @@ class bit_array_base { /** * @brief Slice operations - returns a bit_array_ref */ - constexpr auto operator()(size_type offset, size_type right) const noexcept { + constexpr auto operator()(const size_type offset, const size_type right) const noexcept { return bit_array_ref(&this->at(offset), right - offset); } + template + constexpr auto operator()(const bounds& slice) const noexcept { + return bit_array_ref(&this->at(slice.begin), slice.size()); + } + /** * @brief Slice operations - returns a bit_array_ref */ - constexpr auto operator()(size_type offset, size_type right) noexcept { + constexpr auto operator()(const size_type offset, const size_type right) noexcept { return bit_array_ref(&this->at(offset), right - offset); } + template + constexpr auto operator()(const bounds& slice) noexcept { + return bit_array_ref(&this->at(slice.begin), slice.size()); + } + // Common operations constexpr void fill(value_type bit_val) noexcept { std::fill(derived().begin(), derived().end(), bit_val); diff --git a/include/bitlib/bit-containers/bounds.hpp b/include/bitlib/bit-containers/bounds.hpp new file mode 100644 index 00000000..f7842bb2 --- /dev/null +++ b/include/bitlib/bit-containers/bounds.hpp @@ -0,0 +1,70 @@ +// ================================= BIT_SLICE =================================== // +// Project: The Experimental Bit Algorithms Library +// \file bit_array.hpp +// Description: Implementation of bit_array +// Creator: Vincent Reverdy +// Contributor: Peter McLean [2025] +// License: BSD 3-Clause License +// ========================================================================== // +#include + +#ifndef _BIT_SLICE_HPP_INCLUDED +#define _BIT_SLICE_HPP_INCLUDED + +namespace bit { + +template +class bit_array_base; + +template +class bounds { + template + friend class bit_array_base; + private: + size_type begin; + size_type end; + public: + constexpr bounds() = delete; + constexpr bounds(const bounds& other) = default; + constexpr bounds(const bounds&& other) : begin(other.begin), end(other.end) { + + } + constexpr bounds(const size_type& pos) : begin(pos), end(pos + 1) { + } + constexpr bounds(const size_type& begin, const size_type& end) : + begin(begin), end(end) { + } + constexpr bounds(std::initializer_list dims) { + if (dims.size() > 2) { + throw std::invalid_argument("Initializer list must have at most 2 elements"); + } + auto it = dims.begin(); + if (dims.size() >= 1) { + begin = *it; + end = begin + 1; + } + if (dims.size() >= 2) { + it++; + end = *it; + } + } + + constexpr bool operator==(const bounds& other) const = default; + constexpr auto operator<=>(const bounds& other) const = default; + + constexpr bounds& operator+=(const size_type& size) { + end = begin + size; + return *this; + } + constexpr bounds& operator-=(const size_type& size) { + begin = end - size; + return *this; + } + constexpr size_type size() const { + return end - begin; + } +}; + +} // namespace bit + +#endif diff --git a/test/src/test-array.cpp b/test/src/test-array.cpp index 578ce026..ebaf6c76 100644 --- a/test/src/test-array.cpp +++ b/test/src/test-array.cpp @@ -496,6 +496,22 @@ TEST(BitArrayTest, Slice) { EXPECT_EQ(arr, 0x20'DEADBEAF_b); } +TEST(BitArrayTest, SliceWithClass) { + auto arr = 0x20'DEADBEEF_b; + auto span2 = arr(bit::bounds{4} += 4); + EXPECT_EQ(span2.size(), 4); + EXPECT_EQ(span2[0], (0xE & (1 << 0)) ? bit::bit1 : bit::bit0); + EXPECT_EQ(span2[1], (0xE & (1 << 1)) ? bit::bit1 : bit::bit0); + EXPECT_EQ(span2[2], (0xE & (1 << 2)) ? bit::bit1 : bit::bit0); + EXPECT_EQ(span2[3], (0xE & (1 << 3)) ? bit::bit1 : bit::bit0); + span2 = 0x4'A_b; + EXPECT_EQ(arr, 0x20'DEADBEAF_b); + auto span3 = arr({4, 8}); + EXPECT_EQ(span3, span2); + auto span4 = arr(bit::bounds{7} -= 4); + EXPECT_EQ(span4, span2); +} + TEST(BitArrayTest, SliceModify) { auto arr = 0x24'DEADBEEF_b; auto span2 = arr(4, 8); From 53042d6879dfad38afefaff13e76ff72472861b3 Mon Sep 17 00:00:00 2001 From: Peter McLean Date: Mon, 19 May 2025 22:44:45 -0400 Subject: [PATCH 2/5] Add missing files to sources list --- CMakeLists.txt | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index d3c788c0..b756d6dc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -46,11 +46,13 @@ target_sources(bitlib INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/include/bitlib/bit-algorithms/transform.hpp ${CMAKE_CURRENT_SOURCE_DIR}/include/bitlib/bit-algorithms/type_traits.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/include/bitlib/bit-containers/bit-containers.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/include/bitlib/bit-containers/bit_array.hpp ${CMAKE_CURRENT_SOURCE_DIR}/include/bitlib/bit-containers/bit_array_base.hpp ${CMAKE_CURRENT_SOURCE_DIR}/include/bitlib/bit-containers/bit_array_dynamic_extent.hpp - ${CMAKE_CURRENT_SOURCE_DIR}/include/bitlib/bit-containers/bit_array.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/include/bitlib/bit-containers/bit_array_ref.hpp ${CMAKE_CURRENT_SOURCE_DIR}/include/bitlib/bit-containers/bit_bitsof.hpp - ${CMAKE_CURRENT_SOURCE_DIR}/include/bitlib/bit-containers/bit-containers.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/include/bitlib/bit-containers/bit_literal.hpp ${CMAKE_CURRENT_SOURCE_DIR}/include/bitlib/bit-containers/bit_mdspan_accessor.hpp ${CMAKE_CURRENT_SOURCE_DIR}/include/bitlib/bit-containers/bit_span.hpp ${CMAKE_CURRENT_SOURCE_DIR}/include/bitlib/bit-containers/bit_vector.hpp From 3387d24347dc64150148c6da7f28322e953a3bfa Mon Sep 17 00:00:00 2001 From: Peter McLean Date: Mon, 19 May 2025 23:35:43 -0400 Subject: [PATCH 3/5] Mimicking None ala Python. WIP --- include/bitlib/bit-containers/bounds.hpp | 56 ++++++++++++++++++------ test/src/test-array.cpp | 4 ++ 2 files changed, 46 insertions(+), 14 deletions(-) diff --git a/include/bitlib/bit-containers/bounds.hpp b/include/bitlib/bit-containers/bounds.hpp index f7842bb2..395ef2a4 100644 --- a/include/bitlib/bit-containers/bounds.hpp +++ b/include/bitlib/bit-containers/bounds.hpp @@ -7,6 +7,8 @@ // License: BSD 3-Clause License // ========================================================================== // #include +#include +#include #ifndef _BIT_SLICE_HPP_INCLUDED #define _BIT_SLICE_HPP_INCLUDED @@ -23,29 +25,55 @@ class bounds { private: size_type begin; size_type end; - public: + size_type step; + + public: + using None = std::tuple<>; constexpr bounds() = delete; constexpr bounds(const bounds& other) = default; - constexpr bounds(const bounds&& other) : begin(other.begin), end(other.end) { - + constexpr bounds(const bounds&& other) + : begin(other.begin), end(other.end), step(other.step) { + } + constexpr bounds(const size_type& pos) + : begin(pos), end(pos + 1), step(1) { } - constexpr bounds(const size_type& pos) : begin(pos), end(pos + 1) { + constexpr bounds(const size_type& begin, const size_type& end, const size_type& step = 1) + : begin(begin), end(end), step(step) { } - constexpr bounds(const size_type& begin, const size_type& end) : - begin(begin), end(end) { + constexpr bounds(const None begin, const size_type& end, const size_type& step = 1) + : begin(0), end(end), step(step) { } - constexpr bounds(std::initializer_list dims) { - if (dims.size() > 2) { + constexpr bounds(std::initializer_list> components) { + if (components.size() > 3) { throw std::invalid_argument("Initializer list must have at most 2 elements"); } - auto it = dims.begin(); - if (dims.size() >= 1) { - begin = *it; - end = begin + 1; + auto it = components.begin(); + begin = 0; + end = 1; + step = 1; + if (components.size() >= 1) { + if (std::holds_alternative(*it)) { + begin = std::get(*it); + end = begin + 1; + } else { + std::cout << "None for [0]" << std::endl; + begin = 0; + end = 1; + } + } + if (components.size() >= 2) { + it++; + if (std::holds_alternative(*it)) { + end = std::get(*it); + } } - if (dims.size() >= 2) { + if (components.size() >= 3) { it++; - end = *it; + if (std::holds_alternative(*it)) { + step = std::get(*it); + } else { + step = 1; + } } } diff --git a/test/src/test-array.cpp b/test/src/test-array.cpp index ebaf6c76..69069038 100644 --- a/test/src/test-array.cpp +++ b/test/src/test-array.cpp @@ -510,6 +510,10 @@ TEST(BitArrayTest, SliceWithClass) { EXPECT_EQ(span3, span2); auto span4 = arr(bit::bounds{7} -= 4); EXPECT_EQ(span4, span2); + bit::bounds bounds({}, 5); + EXPECT_EQ(bounds.size(), 5); + bit::bounds bounds2{{}, 5}; + EXPECT_EQ(bounds, bounds2); } TEST(BitArrayTest, SliceModify) { From a1fd3f6c67942e24ad14ccaa6c349a39ae7d36f4 Mon Sep 17 00:00:00 2001 From: Peter McLean Date: Tue, 20 May 2025 21:54:51 -0400 Subject: [PATCH 4/5] Remove step as bit_array_ref cannot accomodate --- .../bitlib/bit-containers/bit_array_base.hpp | 10 +- include/bitlib/bit-containers/bounds.hpp | 163 ++++++++++++------ test/src/test-array.cpp | 8 +- 3 files changed, 120 insertions(+), 61 deletions(-) diff --git a/include/bitlib/bit-containers/bit_array_base.hpp b/include/bitlib/bit-containers/bit_array_base.hpp index 850dca9c..d0586ab9 100644 --- a/include/bitlib/bit-containers/bit_array_base.hpp +++ b/include/bitlib/bit-containers/bit_array_base.hpp @@ -170,7 +170,10 @@ class bit_array_base { template constexpr auto operator()(const bounds& slice) const noexcept { - return bit_array_ref(&this->at(slice.begin), slice.size()); + auto bounds_resolved = slice.resolve(derived().size()); + assert(bounds_resolved.first <= bounds_resolved.second); + auto bounds_size = bounds_resolved.second - bounds_resolved.first; + return bit_array_ref(&this->at(bounds_resolved.first), bounds_size); } /** @@ -182,7 +185,10 @@ class bit_array_base { template constexpr auto operator()(const bounds& slice) noexcept { - return bit_array_ref(&this->at(slice.begin), slice.size()); + auto bounds_resolved = slice.resolve(derived().size()); + assert(bounds_resolved.first <= bounds_resolved.second); + auto bounds_size = bounds_resolved.second - bounds_resolved.first; + return bit_array_ref(&this->at(bounds_resolved.first), bounds_size); } // Common operations diff --git a/include/bitlib/bit-containers/bounds.hpp b/include/bitlib/bit-containers/bounds.hpp index 395ef2a4..2d77d259 100644 --- a/include/bitlib/bit-containers/bounds.hpp +++ b/include/bitlib/bit-containers/bounds.hpp @@ -18,78 +18,129 @@ namespace bit { template class bit_array_base; -template +template class bounds { template friend class bit_array_base; - private: - size_type begin; - size_type end; - size_type step; - - public: - using None = std::tuple<>; - constexpr bounds() = delete; - constexpr bounds(const bounds& other) = default; - constexpr bounds(const bounds&& other) - : begin(other.begin), end(other.end), step(other.step) { + + public: + using None_t = std::tuple<>; + static constexpr None_t None = None_t{}; + + private: + using var_t = std::variant; + var_t begin; + var_t end; + + public: + constexpr bounds() : begin(None), end(None) { + } + constexpr bounds(const bounds& other) = default; + constexpr bounds(const bounds&& other) + : begin(other.begin), end(other.end) { + } + constexpr bounds(const size_type pos) + : begin(pos), end(None) { + } + constexpr bounds(const size_type begin, const size_type end) + : begin(begin), end(end) { + } + constexpr bounds(std::initializer_list components) { + if (components.size() > 3) { + throw std::invalid_argument("Initializer list must have at most 2 elements"); } - constexpr bounds(const size_type& pos) - : begin(pos), end(pos + 1), step(1) { + auto it = components.begin(); + begin = None; + end = None; + if (components.size() >= 1) { + begin = *it; } - constexpr bounds(const size_type& begin, const size_type& end, const size_type& step = 1) - : begin(begin), end(end), step(step) { + if (components.size() >= 2) { + it++; + end = *it; } - constexpr bounds(const None begin, const size_type& end, const size_type& step = 1) - : begin(0), end(end), step(step) { + if (std::holds_alternative(begin)) { + std::cout << "pos " << std::get(begin) << ", "; + } else { + std::cout << "None, "; } - constexpr bounds(std::initializer_list> components) { - if (components.size() > 3) { - throw std::invalid_argument("Initializer list must have at most 2 elements"); - } - auto it = components.begin(); - begin = 0; - end = 1; - step = 1; - if (components.size() >= 1) { - if (std::holds_alternative(*it)) { - begin = std::get(*it); - end = begin + 1; - } else { - std::cout << "None for [0]" << std::endl; - begin = 0; - end = 1; - } - } - if (components.size() >= 2) { - it++; - if (std::holds_alternative(*it)) { - end = std::get(*it); - } - } - if (components.size() >= 3) { - it++; - if (std::holds_alternative(*it)) { - step = std::get(*it); - } else { - step = 1; - } - } + if (std::holds_alternative(end)) { + std::cout << "pos" << std::get(end) << std::endl; + } else { + std::cout << "None" << std::endl; } + } constexpr bool operator==(const bounds& other) const = default; constexpr auto operator<=>(const bounds& other) const = default; - constexpr bounds& operator+=(const size_type& size) { - end = begin + size; + constexpr bounds& operator+=(const size_t& _size) { + const size_type size = static_cast(_size); + if (std::holds_alternative(end)) { + size_type end_pos = std::get(end); + if (end_pos >= 0) { + end = end_pos + size; + } else { + end = ((size_type(-1) - size) < end_pos) ? size_type(-1) : (end_pos + size); + } + } else if (std::holds_alternative(begin)) { + end = std::get(begin) + size; + } return *this; } - constexpr bounds& operator-=(const size_type& size) { - begin = end - size; + + constexpr bounds& operator-=(const size_t& _size) { + const size_type size = static_cast(_size); + if (std::holds_alternative(begin)) { + size_type begin_pos = std::get(begin); + if (!std::holds_alternative(end)) { + end = begin_pos + 1; + begin = begin_pos + 1 - size; + } else { + if (begin_pos < 0) { + begin = begin_pos - size; + } else { + begin = ((size_type(0) + size) > begin_pos) ? size_type(0) : (begin_pos - size); + } + } + } return *this; } - constexpr size_type size() const { - return end - begin; + + constexpr std::pair resolve(size_t _length) const { + const size_type length = static_cast(_length); + // Helper: translate a possibly-negative int into a signed index + auto translate_index = [&](size_type idx) -> size_type { + size_type x = (idx); + if (x < size_type(0)) { + x += length; + } + return x; + }; + + // 1) Compute raw_start + size_type raw_start; + if (std::holds_alternative(begin)) { + raw_start = size_type(0); + } else { + raw_start = translate_index(std::get(begin)); + } + + // 2) Compute raw_end + size_type raw_end; + if (std::holds_alternative(end)) { + raw_end = length; + } else { + raw_end = translate_index(std::get(end)); + } + + // 3) Clamp into [0..L] + return {std::clamp(raw_start, + size_type(0), + length), + std::clamp(raw_end, + size_type(0), + length)}; } }; diff --git a/test/src/test-array.cpp b/test/src/test-array.cpp index 69069038..d6cb2042 100644 --- a/test/src/test-array.cpp +++ b/test/src/test-array.cpp @@ -510,10 +510,12 @@ TEST(BitArrayTest, SliceWithClass) { EXPECT_EQ(span3, span2); auto span4 = arr(bit::bounds{7} -= 4); EXPECT_EQ(span4, span2); - bit::bounds bounds({}, 5); - EXPECT_EQ(bounds.size(), 5); + auto span5 = arr({4, {}}); + auto span6 = arr(4, 32); + EXPECT_EQ(span5, span6); + bit::bounds bounds(0, 5); bit::bounds bounds2{{}, 5}; - EXPECT_EQ(bounds, bounds2); + EXPECT_EQ(bounds.resolve(32), bounds2.resolve(32)); } TEST(BitArrayTest, SliceModify) { From c9a3aeb47ab166a7e10baca0b7c8b260de335fbc Mon Sep 17 00:00:00 2001 From: Peter McLean Date: Sun, 15 Jun 2025 22:23:42 -0400 Subject: [PATCH 5/5] Fix rebase issue --- include/bitlib/bit-containers/bounds.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/bitlib/bit-containers/bounds.hpp b/include/bitlib/bit-containers/bounds.hpp index 2d77d259..5c8dd71a 100644 --- a/include/bitlib/bit-containers/bounds.hpp +++ b/include/bitlib/bit-containers/bounds.hpp @@ -15,12 +15,12 @@ namespace bit { -template +template class bit_array_base; template class bounds { - template + template friend class bit_array_base; public: