/// @file dpf/packed_lane.hpp /// @brief Extract and deposit one sub-byte lane in a leaf node. /// Lane 0 is the low bits of the first byte. Widths are 1 /// (`dpf::bit`, `dpf::gf2`), 2 (`dpf::twobit`, `dpf::gf22`), or 4 /// (`dpf::nyble`, `dpf::gf24`). #ifndef LIBDPF_INCLUDE_DPF_PACKED_LANE_HPP__ #define LIBDPF_INCLUDE_DPF_PACKED_LANE_HPP__ #include #include #include #include #include "hedley/hedley.h" #include "dpf/bit.hpp" #include "dpf/nyble.hpp" #include "dpf/twobit.hpp" namespace dpf { namespace packed { /// @brief Low bits of one lane as an unsigned integer. /// @details Enums convert directly. Class lanes such as `gf2n` expose /// `integral_type` and an explicit conversion to it. template HEDLEY_ALWAYS_INLINE HEDLEY_NO_THROW HEDLEY_PURE constexpr unsigned lane_unsigned(LaneT value) noexcept { if constexpr (std::is_enum_v || std::is_integral_v) return static_cast(value); else return static_cast(static_cast(value)); } template HEDLEY_ALWAYS_INLINE HEDLEY_NO_THROW HEDLEY_PURE LaneT extract_lane(const LeafT & leaf, std::size_t lane) noexcept { constexpr unsigned bits = utils::packed_lane_bits_v; static_assert(bits == 1 || bits == 2 || bits == 4, "packed lane width must be 1, 2, or 4"); constexpr unsigned mask = (1u << bits) - 1u; const auto * bytes = reinterpret_cast( std::addressof(leaf)); const std::size_t bit = lane * bits; assert(bit / 8u < sizeof(LeafT)); const unsigned shift = static_cast(bit % 8u); const unsigned val = (bytes[bit / 8u] >> shift) & mask; return static_cast(val); } /// @brief zero `out` is the caller's job; this writes one lane and leaves /// every other lane untouched. /// @tparam LaneT lane type /// @tparam LeafT leaf type /// @param leaf the leaf value /// @param lane the lane index or lane value /// @param value the value to convert or store template HEDLEY_ALWAYS_INLINE HEDLEY_NO_THROW void deposit_lane(LeafT & leaf, std::size_t lane, LaneT value) noexcept { constexpr unsigned bits = utils::packed_lane_bits_v; static_assert(bits == 1 || bits == 2 || bits == 4, "packed lane width must be 1, 2, or 4"); constexpr unsigned mask = (1u << bits) - 1u; auto * bytes = reinterpret_cast(std::addressof(leaf)); const std::size_t bit = lane * bits; assert(bit / 8u < sizeof(LeafT)); const unsigned shift = static_cast(bit % 8u); const unsigned val = lane_unsigned(value) & mask; unsigned char & cell = bytes[bit / 8u]; cell = static_cast((cell & ~(mask << shift)) | (val << shift)); } template HEDLEY_ALWAYS_INLINE HEDLEY_NO_THROW NodeT make_lane_node(std::size_t lane, LaneT value) noexcept { NodeT node{}; deposit_lane(node, lane, value); return node; } } // namespace packed } // namespace dpf #endif // LIBDPF_INCLUDE_DPF_PACKED_LANE_HPP__