3#include <pixie/storage/aligned.h>
16namespace pixie::permutations::detail {
29template <
class T, std::
size_t StorageBits = 2048>
31 static_assert(StorageBits % (
sizeof(CacheLine) * CHAR_BIT) == 0);
32 static_assert(StorageBits / CHAR_BIT >=
33 2 *
sizeof(std::size_t) +
sizeof(
const T*));
40 using value_type =
const T*;
46 static constexpr std::size_t capacity =
47 (StorageBits / CHAR_BIT - 2 *
sizeof(std::size_t)) /
sizeof(value_type);
48 static_assert(capacity <= std::numeric_limits<std::size_t>::max() / 2);
53 PointerBlock() noexcept = default;
62 explicit PointerBlock(std::span<const value_type> input) {
63 if (input.size() > capacity) {
64 throw std::length_error(
"PointerBlock: capacity");
66 std::copy(input.begin(), input.end(), storage_.values.begin());
67 storage_.length = input.size();
74 std::size_t size() const noexcept {
return storage_.length; }
83 value_type operator[](std::size_t i)
const noexcept {
85 return storage_.values[(storage_.origin + i) % size()];
92 void set_at(std::size_t i, value_type value)
noexcept {
106 void rotate_left(std::size_t left,
108 std::size_t distance)
noexcept {
109 assert(left <= right && right <= size());
110 const auto n = right - left;
111 if (n == 0 || (distance %= n) == 0) {
114 if (left == 0 && right == size()) {
115 storage_.origin = (storage_.origin + distance) % size();
118 auto reverse = [&](std::size_t begin, std::size_t end) {
119 while (begin < end && begin < --end) {
120 std::swap(slot(begin++), slot(end));
123 reverse(left, left + distance);
124 reverse(left + distance, right);
125 reverse(left, right);
136 void insert_at(std::size_t offset, value_type value)
noexcept {
137 assert(offset <= size() && size() < capacity);
138 std::array<value_type, capacity> scratch;
139 for (std::size_t i = 0; i < offset; ++i) {
140 scratch[i] = (*this)[i];
142 scratch[offset] = value;
143 const auto n = size();
144 for (std::size_t i = offset; i < n; ++i) {
145 scratch[i + 1] = (*this)[i];
147 std::copy_n(scratch.begin(), n + 1, storage_.values.begin());
148 storage_.length = n + 1;
162 void redistribute(PointerBlock& right, std::size_t left_count)
noexcept {
163 const auto total = size() + right.size();
164 assert(
this != &right && left_count <= capacity && left_count <= total &&
165 total - left_count <= capacity);
166 std::array<value_type, 2 * capacity> scratch;
167 for (std::size_t i = 0; i < size(); ++i) {
168 scratch[i] = (*this)[i];
170 for (std::size_t i = 0; i < right.size(); ++i) {
171 scratch[size() + i] = right[i];
173 std::copy_n(scratch.begin(), left_count, storage_.values.begin());
174 std::copy_n(scratch.begin() + left_count, total - left_count,
175 right.storage_.values.begin());
176 storage_.length = left_count;
177 right.storage_.length = total - left_count;
178 storage_.origin = right.storage_.origin = 0;
182 struct alignas(CacheLine) Storage {
183 std::size_t length = 0;
184 std::size_t origin = 0;
185 std::array<value_type, capacity> values{};
187 static_assert(
sizeof(Storage) == StorageBits / CHAR_BIT);
190 value_type& slot(std::size_t i)
noexcept {
191 return storage_.values[(storage_.origin + i) % storage_.length];