Pixie
Loading...
Searching...
No Matches
pointer_block.h
1#pragma once
2
3#include <pixie/storage/aligned.h>
4
5#include <algorithm>
6#include <array>
7#include <cassert>
8#include <climits>
9#include <cstddef>
10#include <limits>
11#include <span>
12#include <stdexcept>
13#include <utility>
14
16namespace pixie::permutations::detail {
17
29template <class T, std::size_t StorageBits = 2048>
30class PointerBlock {
31 static_assert(StorageBits % (sizeof(CacheLine) * CHAR_BIT) == 0);
32 static_assert(StorageBits / CHAR_BIT >=
33 2 * sizeof(std::size_t) + sizeof(const T*));
34
35 public:
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");
65 }
66 std::copy(input.begin(), input.end(), storage_.values.begin());
67 storage_.length = input.size();
68 }
74 std::size_t size() const noexcept { return storage_.length; }
83 value_type operator[](std::size_t i) const noexcept {
84 assert(i < size());
85 return storage_.values[(storage_.origin + i) % size()];
86 }
92 void set_at(std::size_t i, value_type value) noexcept {
93 assert(i < size());
94 slot(i) = value;
95 }
106 void rotate_left(std::size_t left,
107 std::size_t right,
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) {
112 return;
113 }
114 if (left == 0 && right == size()) {
115 storage_.origin = (storage_.origin + distance) % size();
116 return;
117 }
118 auto reverse = [&](std::size_t begin, std::size_t end) {
119 while (begin < end && begin < --end) {
120 std::swap(slot(begin++), slot(end));
121 }
122 };
123 reverse(left, left + distance);
124 reverse(left + distance, right);
125 reverse(left, right);
126 }
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];
141 }
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];
146 }
147 std::copy_n(scratch.begin(), n + 1, storage_.values.begin());
148 storage_.length = n + 1;
149 storage_.origin = 0;
150 }
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];
169 }
170 for (std::size_t i = 0; i < right.size(); ++i) {
171 scratch[size() + i] = right[i];
172 }
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;
179 }
180
181 private:
182 struct alignas(CacheLine) Storage {
183 std::size_t length = 0;
184 std::size_t origin = 0;
185 std::array<value_type, capacity> values{};
186 };
187 static_assert(sizeof(Storage) == StorageBits / CHAR_BIT);
188 Storage storage_;
189
190 value_type& slot(std::size_t i) noexcept {
191 return storage_.values[(storage_.origin + i) % storage_.length];
192 }
193};
194
195} // namespace pixie::permutations::detail