Pixie
Loading...
Searching...
No Matches
grouped_slot_order256.h
Go to the documentation of this file.
1#pragma once
2
13
14#include <array>
15#include <cassert>
16#include <cstddef>
17#include <cstdint>
18
19namespace pixie::experimental {
20
36 public:
38 static constexpr std::size_t capacity = 256;
39
41 explicit GroupedSlotOrder256(std::size_t count = 0) noexcept
42 : groups_(identity & mask(unsigned(4 * ((count + 15) / 16)))),
43 count_(std::uint16_t(count)) {
44 assert(count <= capacity);
45 for (std::size_t group = 0; group < (count + 15) / 16; ++group) {
46 const auto n = count - group * 16 < 16 ? count - group * 16 : 16;
47 slots_[group] = identity & mask(unsigned(n * 4));
48 occupied_[group] = std::uint16_t(mask(unsigned(n)));
49 }
50 }
51
53 std::size_t size() const noexcept { return count_; }
54
56 unsigned operator[](std::size_t index) const noexcept {
57 assert(index < size());
58 const auto group = physical_group(index / 16);
59 return group * 16 + unsigned((slots_[group] >> (4 * (index % 16))) & 15);
60 }
61
63 bool occupied(std::size_t slot) const noexcept {
64 assert(slot < capacity);
65 return (occupied_[slot / 16] & (1u << (slot % 16))) != 0;
66 }
67
69 bool valid() const noexcept {
70 if (size() > capacity) {
71 return false;
72 }
73 std::array<std::uint16_t, 16> seen{};
74 for (std::size_t i = 0; i < size(); ++i) {
75 const auto slot = (*this)[i];
76 const auto bit = std::uint16_t(1u << (slot % 16));
77 if (seen[slot / 16] & bit) {
78 return false;
79 }
80 seen[slot / 16] |= bit;
81 }
82 return seen == occupied_;
83 }
84
92 std::size_t rotate_children(std::array<void*, capacity>& pointers,
93 std::size_t left,
94 std::size_t right,
95 std::size_t distance) noexcept {
96 assert(left <= right && right <= size());
97 const auto length = right - left;
98 if (length == 0 || (distance %= length) == 0) {
99 return 0;
100 }
101 if (left / 16 == (right - 1) / 16) {
102 const auto group = physical_group(left / 16);
103 slots_[group] =
104 rotate_word(slots_[group], left % 16, (right - 1) % 16 + 1, distance);
105 return 0;
106 }
107 if (left % 16 == 0 && right % 16 == 0) {
108 const auto begin = left / 16;
109 const auto end = right / 16;
110 const auto remainder = distance % 16;
111 const bool backwards = remainder > 8;
112 groups_ = rotate_word(groups_, begin, end,
113 distance / 16 + std::size_t(backwards));
114 if (remainder == 0) {
115 return 0;
116 }
117 for (auto i = begin; i < end; ++i) {
118 auto& word = slots_[physical_group(i)];
119 word = rotate_word(word, 0, 16, remainder);
120 }
121 // Exchange the short spill between neighboring groups. The outer
122 // permutation has already handled the whole-group part of the shift.
123 const auto spill = backwards ? 16 - remainder : remainder;
124 for (std::size_t j = 0; j < spill; ++j) {
125 if (backwards) {
126 auto* saved = pointers[(*this)[(end - 1) * 16 + j]];
127 for (auto i = end - 1; i > begin; --i) {
128 pointers[(*this)[i * 16 + j]] = pointers[(*this)[(i - 1) * 16 + j]];
129 }
130 pointers[(*this)[begin * 16 + j]] = saved;
131 } else {
132 const auto offset = 16 - spill + j;
133 auto* saved = pointers[(*this)[begin * 16 + offset]];
134 for (auto i = begin; i + 1 < end; ++i) {
135 pointers[(*this)[i * 16 + offset]] =
136 pointers[(*this)[(i + 1) * 16 + offset]];
137 }
138 pointers[(*this)[(end - 1) * 16 + offset]] = saved;
139 }
140 }
141 return (end - begin) * spill;
142 }
143 std::array<void*, capacity> scratch;
144 for (std::size_t i = 0; i < length; ++i) {
145 scratch[i] = pointers[(*this)[left + i]];
146 }
147 auto source = distance;
148 for (std::size_t i = 0; i < length; ++i) {
149 pointers[(*this)[left + i]] = scratch[source];
150 if (++source == length) {
151 source = 0;
152 }
153 }
154 return length;
155 }
156
157 private:
158 static constexpr std::uint64_t identity = 0xfedcba9876543210ULL;
159 static constexpr std::uint64_t mask(unsigned bits) noexcept {
160 return bits >= 64 ? ~std::uint64_t{0} : (std::uint64_t{1} << bits) - 1;
161 }
162 static std::uint64_t rotate_word(std::uint64_t word,
163 std::size_t left,
164 std::size_t right,
165 std::size_t distance) noexcept {
166 const auto length = right - left;
167 if (length == 0 || (distance %= length) == 0) {
168 return word;
169 }
170 const auto region_mask = mask(unsigned(4 * length));
171 const auto shift = unsigned(4 * left);
172 const auto region = (word >> shift) & region_mask;
173 const auto rotated =
174 (region >> (4 * distance)) | (region << (4 * (length - distance)));
175 return (word & ~(region_mask << shift)) |
176 ((rotated & region_mask) << shift);
177 }
178 unsigned physical_group(std::size_t index) const noexcept {
179 return unsigned((groups_ >> (4 * index)) & 15);
180 }
181 std::uint64_t groups_;
182 std::array<std::uint64_t, 16> slots_{};
183 std::array<std::uint16_t, 16> occupied_{};
184 std::uint16_t count_;
185};
186
187} // namespace pixie::experimental
bool occupied(std::size_t slot) const noexcept
Whether physical slot is live; requires slot < capacity.
Definition grouped_slot_order256.h:63
unsigned operator[](std::size_t index) const noexcept
Physical slot for zero-based index; requires index < size().
Definition grouped_slot_order256.h:56
GroupedSlotOrder256(std::size_t count=0) noexcept
Identity ordering of count live slots; requires count <= 256.
Definition grouped_slot_order256.h:41
bool valid() const noexcept
Check mapping uniqueness and occupancy without allocation.
Definition grouped_slot_order256.h:69
std::size_t size() const noexcept
Number of live logical slots in [0, 256].
Definition grouped_slot_order256.h:53
std::size_t rotate_children(std::array< void *, capacity > &pointers, std::size_t left, std::size_t right, std::size_t distance) noexcept
Rotate logical [left,right) left, updating order and pointer slots.
Definition grouped_slot_order256.h:92
static constexpr std::size_t capacity
Maximum occupied child count.
Definition grouped_slot_order256.h:38
Definition excess.h:90