diff --git a/CMakeLists.txt b/CMakeLists.txt index 061f11a9..b756d6dc 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,13 +46,17 @@ 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_dynamic_extent.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_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 + ${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..d0586ab9 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,33 @@ 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 { + 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); + } + /** * @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 { + 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 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..5c8dd71a --- /dev/null +++ b/include/bitlib/bit-containers/bounds.hpp @@ -0,0 +1,149 @@ +// ================================= 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 +#include +#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; + + 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"); + } + auto it = components.begin(); + begin = None; + end = None; + if (components.size() >= 1) { + begin = *it; + } + if (components.size() >= 2) { + it++; + end = *it; + } + if (std::holds_alternative(begin)) { + std::cout << "pos " << std::get(begin) << ", "; + } else { + std::cout << "None, "; + } + 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_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_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 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)}; + } +}; + +} // namespace bit + +#endif diff --git a/test/src/test-array.cpp b/test/src/test-array.cpp index 578ce026..d6cb2042 100644 --- a/test/src/test-array.cpp +++ b/test/src/test-array.cpp @@ -496,6 +496,28 @@ 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); + 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.resolve(32), bounds2.resolve(32)); +} + TEST(BitArrayTest, SliceModify) { auto arr = 0x24'DEADBEEF_b; auto span2 = arr(4, 8);