Pixie
Loading...
Searching...
No Matches
packed_value_block.h
1#pragma once
2
3#include <pixie/permutations/detail/packed_bit_block.h>
4
5#include <cassert>
6#include <cstddef>
7#include <cstdint>
8#include <cstring>
9#include <limits>
10#include <span>
11#include <stdexcept>
12#include <type_traits>
13
15namespace pixie::permutations::detail {
16
29template <class T,
30 std::size_t StorageBits = 2048,
31 std::size_t Width = std::numeric_limits<T>::digits,
32 class BitStorage = PackedBitBlock<StorageBits>>
33class PackedValueBlock {
34 static_assert(std::is_integral_v<T> && std::is_unsigned_v<T> &&
35 std::numeric_limits<T>::digits <= 64);
36 static_assert(Width > 0 && Width <= std::numeric_limits<T>::digits);
37 using Bits = BitStorage;
38
39 public:
41 using value_type = T;
44 static constexpr std::size_t capacity = Bits::capacity / Width;
47 static constexpr std::size_t width = Width;
49 static constexpr std::size_t payload_offset_bytes =
50 Bits::payload_offset_bytes;
53 static constexpr std::size_t payload_alignment = Bits::payload_alignment;
54 static_assert(capacity > 0);
55
57 PackedValueBlock() noexcept {}
65 explicit PackedValueBlock(std::span<const T> values) {
66 if (values.size() > capacity) {
67 throw std::invalid_argument("PackedValueBlock: input size");
68 }
69 typename Bits::Payload words;
70 if constexpr (Width != 64) {
71 words.fill(0);
72 }
73 for (std::size_t i = 0; i < values.size(); ++i) {
74 if (static_cast<std::uint64_t>(values[i]) > field_max) {
75 throw std::invalid_argument("PackedValueBlock: value width");
76 }
77 if constexpr (Width == 64) {
78 words[i] = values[i];
79 } else {
80 encode(words, i, values[i]);
81 }
82 }
83 bits_ = Bits(words, values.size() * Width);
84 }
88 std::size_t size() const noexcept { return bits_.size() / Width; }
92 bool empty() const noexcept { return bits_.empty(); }
100 T operator[](std::size_t i) const {
101 if (i >= size()) {
102 throw std::out_of_range("PackedValueBlock: index");
103 }
104 return read_unchecked(i);
105 }
110 T read_unchecked(std::size_t i) const noexcept {
111 assert(i < size());
112 if constexpr (Width == 1) {
113 return static_cast<T>(bits_[i]);
114 } else if constexpr (Width == 64) {
115 return static_cast<T>(bits_.read_normalized_word(i * 64));
116 } else {
117 return static_cast<T>(bits_.read_bits(i * Width, Width));
118 }
119 }
125 void set_at(std::size_t i, T value) noexcept {
126 assert(i < size() && static_cast<std::uint64_t>(value) <= field_max);
127 if constexpr (Width == 64) {
128 bits_.write_aligned_word(i * Width, value);
129 } else {
130 bits_.write_bits(i * Width, Width, value);
131 }
132 }
141 void rotate_left(std::size_t left,
142 std::size_t right,
143 std::size_t distance) noexcept {
144 assert(left <= right && right <= size());
145 if constexpr (Width == 1) {
146 bits_.rotate_left(left, right, distance);
147 } else if (left != right) {
148 bits_.rotate_left(left * Width, right * Width,
149 (distance % (right - left)) * Width);
150 if constexpr (Width == 64) {
151 bits_.normalize();
152 }
153 }
154 }
164 void insert_at(std::size_t offset, T value) noexcept {
165 assert(offset <= size() && size() < capacity);
166 typename Bits::Payload words{};
167 const auto n = size();
168 if constexpr (Width == 64) {
169 for (std::size_t i = 0; i < n; ++i) {
170 words[i] = bits_.read_aligned_word(i * 64);
171 }
172 std::memmove(words.data() + offset + 1, words.data() + offset,
173 (n - offset) * sizeof(std::uint64_t));
174 words[offset] = static_cast<std::uint64_t>(value);
175 } else {
176 for (std::size_t i = 0; i < offset; ++i) {
177 encode(words, i, bits_.read_bits(i * Width, Width));
178 }
179 encode(words, offset, value);
180 for (std::size_t i = offset; i < n; ++i) {
181 encode(words, i + 1, bits_.read_bits(i * Width, Width));
182 }
183 }
184 bits_ = Bits(words, (n + 1) * Width);
185 }
194 void redistribute(PackedValueBlock& rhs, std::size_t left_size) noexcept {
195 assert(this != &rhs && left_size <= capacity &&
196 left_size <= size() + rhs.size() &&
197 size() + rhs.size() - left_size <= capacity);
198 bits_.redistribute(rhs.bits_, left_size * Width);
199 }
207 void add_bias(std::uint64_t bias) noexcept {
208 typename Bits::Payload words{};
209 const auto n = size();
210 for (std::size_t i = 0; i < n; ++i) {
211 const auto value = bits_.read_bits(i * Width, Width);
212 assert(bias <= field_max && value <= field_max - bias);
213 encode(words, i, value + bias);
214 }
215 bits_ = Bits(words, n * Width);
216 }
220 std::size_t payload_capacity_bytes() const noexcept {
221 return bits_.payload_capacity_bytes();
222 }
226 std::size_t metadata_bytes() const noexcept { return bits_.metadata_bytes(); }
227
228 private:
229 static constexpr std::uint64_t field_max =
230 std::numeric_limits<std::uint64_t>::max() >> (64 - Width);
231 static void encode(typename Bits::Payload& words,
232 std::size_t i,
233 std::uint64_t value) noexcept {
234 const auto bit = i * Width;
235 const auto shift = bit % 64;
236 words[bit / 64] |= value << shift;
237 if (Width > 64 - shift) {
238 words[bit / 64 + 1] |= value >> (64 - shift);
239 }
240 }
241 Bits bits_;
242};
243
244} // namespace pixie::permutations::detail