15#include <pixie/bits.h>
26namespace slot_order_detail {
29constexpr Word mask(std::size_t bits)
noexcept {
30 return bits >=
sizeof(Word) * 8 ? ~Word{0} : (Word{1} << bits) - 1;
34template <
class Word, std::
size_t Words>
35void rotate_bits(std::array<Word, Words>& words,
38 std::size_t distance)
noexcept {
39 constexpr auto width =
sizeof(Word) * 8;
40 if constexpr (Words == 1) {
41 const auto region_mask = mask<Word>(length);
42 const auto region = (words[0] >> left) & region_mask;
43 const auto rotated = (region >> distance) | (region << (length - distance));
45 (words[0] & ~(region_mask << left)) | ((rotated & region_mask) << left);
47#ifdef __SIZEOF_INT128__
48 else if constexpr (
sizeof(Word) == 8 && Words == 2) {
49 using Wide = __uint128_t;
50 const auto original = Wide(words[0]) | (Wide(words[1]) << 64);
51 const auto region_mask = mask<Wide>(length);
52 const auto region = (original >> left) & region_mask;
53 const auto rotated = (region >> distance) | (region << (length - distance));
55 (original & ~(region_mask << left)) | ((rotated & region_mask) << left);
56 words[0] = Word(result);
57 words[1] = Word(result >> 64);
61 const auto original = words;
62 auto copy = [&](std::size_t source, std::size_t target, std::size_t count) {
64 const auto source_bit = source % width;
65 const auto target_bit = target % width;
67 std::min({count, width - source_bit, width - target_bit});
68 const auto bits = mask<Word>(chunk);
69 const auto value = (original[source / width] >> source_bit) & bits;
70 auto& destination = words[target / width];
72 (destination & ~(bits << target_bit)) | (value << target_bit);
78 copy(left + distance, left, length - distance);
79 copy(left, left + length - distance, distance);
83template <std::
size_t N>
84inline constexpr auto identity = [] {
85 std::array<std::uint8_t, N> result{};
86 for (std::size_t i = 0; i < N; ++i) {
94template <std::
size_t N>
95void rotate_bytes(std::array<std::uint8_t, N>& bytes,
98 std::size_t distance)
noexcept {
99#if defined(PIXIE_AVX512_SUPPORT) && defined(__AVX512VBMI__)
100 if constexpr (N == 64) {
101 const auto positions = _mm512_loadu_si512(identity<N>.data());
102 auto source = _mm512_add_epi8(positions, _mm512_set1_epi8(distance));
103 source = _mm512_mask_sub_epi8(
104 source, _mm512_cmpge_epi8_mask(source, _mm512_set1_epi8(right)), source,
105 _mm512_set1_epi8(right - left));
107 _mm512_cmpge_epi8_mask(positions, _mm512_set1_epi8(left)) &
108 _mm512_cmplt_epi8_mask(positions, _mm512_set1_epi8(right));
109 source = _mm512_mask_mov_epi8(positions, active, source);
110 const auto input = _mm512_loadu_si512(bytes.data());
111 _mm512_storeu_si512(bytes.data(), _mm512_permutexvar_epi8(source, input));
115#ifdef PIXIE_AVX2_SUPPORT
117 _mm256_loadu_si256(
reinterpret_cast<const __m256i*
>(bytes.data()));
118 const auto bank0 = _mm256_permute2x128_si256(input0, input0, 0x00);
119 const auto bank1 = _mm256_permute2x128_si256(input0, input0, 0x11);
120 __m256i bank2{}, bank3{};
121 if constexpr (N == 64) {
123 _mm256_loadu_si256(
reinterpret_cast<const __m256i*
>(bytes.data() + 32));
124 bank2 = _mm256_permute2x128_si256(input1, input1, 0x00);
125 bank3 = _mm256_permute2x128_si256(input1, input1, 0x11);
127 for (std::size_t offset = 0; offset < N; offset += 32) {
128 const auto positions = _mm256_loadu_si256(
129 reinterpret_cast<const __m256i*
>(identity<N>.data() + offset));
130 auto source = _mm256_add_epi8(positions, _mm256_set1_epi8(distance));
131 source = _mm256_sub_epi8(
133 _mm256_and_si256(_mm256_cmpgt_epi8(source, _mm256_set1_epi8(right - 1)),
134 _mm256_set1_epi8(right - left)));
135 const auto active = _mm256_and_si256(
136 _mm256_cmpgt_epi8(positions,
137 _mm256_set1_epi8(
static_cast<int>(left) - 1)),
138 _mm256_cmpgt_epi8(_mm256_set1_epi8(right), positions));
139 source = _mm256_blendv_epi8(positions, source, active);
140 const auto upper = _mm256_cmpgt_epi8(
141 _mm256_and_si256(source, _mm256_set1_epi8(16)), _mm256_setzero_si256());
142 auto result = _mm256_blendv_epi8(_mm256_shuffle_epi8(bank0, source),
143 _mm256_shuffle_epi8(bank1, source), upper);
144 if constexpr (N == 64) {
146 _mm256_blendv_epi8(_mm256_shuffle_epi8(bank2, source),
147 _mm256_shuffle_epi8(bank3, source), upper);
148 result = _mm256_blendv_epi8(
149 result, second, _mm256_cmpgt_epi8(source, _mm256_set1_epi8(31)));
151 _mm256_storeu_si256(
reinterpret_cast<__m256i*
>(bytes.data() + offset),
154#elif defined(PIXIE_SSE41_SUPPORT)
155 __m128i banks[N / 16];
156 for (std::size_t i = 0; i < N / 16; ++i) {
157 banks[i] = _mm_loadu_si128(
158 reinterpret_cast<const __m128i*
>(bytes.data() + 16 * i));
160 for (std::size_t offset = 0; offset < N; offset += 16) {
161 const auto positions = _mm_loadu_si128(
162 reinterpret_cast<const __m128i*
>(identity<N>.data() + offset));
163 auto source = _mm_add_epi8(positions, _mm_set1_epi8(distance));
164 source = _mm_sub_epi8(
165 source, _mm_and_si128(_mm_cmpgt_epi8(source, _mm_set1_epi8(right - 1)),
166 _mm_set1_epi8(right - left)));
167 const auto active = _mm_and_si128(
168 _mm_cmpgt_epi8(positions, _mm_set1_epi8(
static_cast<int>(left) - 1)),
169 _mm_cmpgt_epi8(_mm_set1_epi8(right), positions));
170 source = _mm_blendv_epi8(positions, source, active);
171 auto result = _mm_shuffle_epi8(banks[0], source);
172 for (std::size_t i = 1; i < N / 16; ++i) {
174 _mm_blendv_epi8(result, _mm_shuffle_epi8(banks[i], source),
175 _mm_cmpgt_epi8(source, _mm_set1_epi8(16 * i - 1)));
177 _mm_storeu_si128(
reinterpret_cast<__m128i*
>(bytes.data() + offset), result);
180 std::rotate(bytes.begin() + left, bytes.begin() + left + distance,
181 bytes.begin() + right);
194template <std::
size_t N>
196 static_assert(N == 32 || N == 64);
197 static constexpr std::size_t high_bits = N == 32 ? 1 : 2;
198 using HighWord = std::conditional_t<N == 32, std::uint32_t, std::uint64_t>;
199 std::array<std::uint64_t, N / 16> low_{};
200 std::array<HighWord, N * high_bits / (
sizeof(HighWord) * 8)> high_{};
207 static constexpr std::size_t
order_bytes = N * (4 + high_bits) / 8;
211 for (std::size_t i = 0; i < count; ++i) {
212 low_[i / 16] |= std::uint64_t(i & 15) << (4 * (i % 16));
213 constexpr auto width =
sizeof(HighWord) * 8;
214 high_[i * high_bits / width] |= HighWord(i >> 4)
215 << (i * high_bits % width);
219 std::size_t
size() const noexcept {
return count_; }
223 constexpr auto width =
sizeof(HighWord) * 8;
225 (high_[i * high_bits / width] >> (i * high_bits % width)) &
226 ((1u << high_bits) - 1);
227 return unsigned((low_[i / 16] >> (4 * (i % 16))) & 15) |
228 (unsigned(high) << 4);
237 std::uint64_t seen = 0;
238 for (std::size_t i = 0; i <
size(); ++i) {
239 const auto value = (*this)[i];
240 if (value >=
size() || (seen & (std::uint64_t{1} << value))) {
243 seen |= std::uint64_t{1} << value;
252 std::size_t distance)
noexcept {
253 assert(left <= right && right <=
size());
254 const auto length = right - left;
255 if (length == 0 || (distance %= length) == 0) {
258 slot_order_detail::rotate_bits(low_, 4 * left, 4 * length, 4 * distance);
259 slot_order_detail::rotate_bits(high_, high_bits * left, high_bits * length,
260 high_bits * distance);
268 std::size_t distance)
noexcept {
281template <std::
size_t N,
bool Simd = true>
283 static_assert(N == 32 || N == 64);
284 std::array<std::uint8_t, N> order_ = slot_order_detail::identity<N>;
297 std::size_t
size() const noexcept {
return count_; }
310 std::uint64_t seen = 0;
311 for (std::size_t i = 0; i <
size(); ++i) {
312 const auto value = (*this)[i];
313 if (value >=
size() || (seen & (std::uint64_t{1} << value))) {
316 seen |= std::uint64_t{1} << value;
325 std::size_t distance)
noexcept {
326 assert(left <= right && right <=
size());
327 const auto length = right - left;
328 if (length == 0 || (distance %= length) == 0) {
331 if constexpr (Simd) {
332 slot_order_detail::rotate_bytes(order_, left, right, distance);
334 std::rotate(order_.begin() + left, order_.begin() + left + distance,
335 order_.begin() + right);
344 std::size_t distance)
noexcept {
static constexpr std::size_t order_bytes
Order bytes, excluding count and alignment padding.
Definition slot_order.h:291
static constexpr std::size_t capacity
Maximum occupied count.
Definition slot_order.h:289
bool occupied(std::size_t i) const noexcept
Whether physical slot i is occupied; requires i < N.
Definition slot_order.h:304
unsigned operator[](std::size_t i) const noexcept
Physical index; requires i < size().
Definition slot_order.h:299
bool valid() const noexcept
Check mapping uniqueness and occupancy.
Definition slot_order.h:309
ByteSlotOrder(std::size_t count=0) noexcept
Construct identity order; requires count <= N.
Definition slot_order.h:293
void rotate_left(std::size_t left, std::size_t right, std::size_t distance) noexcept
Rotate [left,right); reduce distance modulo length; empty is a no-op.
Definition slot_order.h:323
std::size_t rotate_children(std::array< void *, N > &, std::size_t left, std::size_t right, std::size_t distance) noexcept
Rotate metadata using rotate_left's contract; pointers are untouched.
Definition slot_order.h:341
std::size_t size() const noexcept
Occupied count in [0,N].
Definition slot_order.h:297
void rotate_left(std::size_t left, std::size_t right, std::size_t distance) noexcept
Rotate [left,right) by distance modulo its length; empty is a no-op.
Definition slot_order.h:250
bool valid() const noexcept
Check mapping uniqueness and occupancy.
Definition slot_order.h:236
static constexpr std::size_t order_bytes
Packed order bytes, excluding count and C++ alignment padding.
Definition slot_order.h:207
std::size_t rotate_children(std::array< void *, N > &, std::size_t left, std::size_t right, std::size_t distance) noexcept
Apply rotate_left's contract without touching caller-owned pointers.
Definition slot_order.h:265
std::size_t size() const noexcept
Occupied count, in [0,N].
Definition slot_order.h:219
static constexpr std::size_t capacity
Maximum number of occupied slots.
Definition slot_order.h:205
unsigned operator[](std::size_t i) const noexcept
Physical slot for a logical index strictly below size().
Definition slot_order.h:221
bool occupied(std::size_t i) const noexcept
Whether physical slot i is occupied; requires i < N.
Definition slot_order.h:231
SplitSlotOrder(std::size_t count=0) noexcept
Identity order for physical [0,count); requires count <= N.
Definition slot_order.h:209