73 typename permutations::detail::ElementStorageTraits<T, Storage>::
79 static constexpr bool packed = Traits::packed;
81 std::conditional_t<packed,
82 permutations::detail::PackedValueBlock<T, StorageBits>,
83 permutations::detail::PointerBlock<T, StorageBits>>;
84 static_assert(sizeof(Block) * CHAR_BIT == StorageBits);
85 using Tree = permutations::detail::SequenceTree<Block, Fanout, Layout>;
88 using Slot = typename Chunks::Slot;
89 static constexpr std::size_t chunk_capacity =
90 std::max(std::size_t{1}, ChunkBytes / sizeof(T));
140 if (
this != &other) {
141 tree_ = std::move(other.tree_);
142 chunks_ = std::move(other.chunks_);
160 template <
class Pred>
162 if constexpr (packed) {
163 return tree_.lower_bound(pred);
165 return tree_.lower_bound([&](
const T* p) {
return pred(*p); });
223 const auto tree = tree_.memory_usage();
224 result.blocks = tree.blocks;
225 result.nodes = tree.nodes;
226 result.tree_block_bytes = tree.block_bytes;
227 result.tree_node_bytes = tree.node_bytes;
228 const auto tree_bytes = add(tree.block_bytes, tree.node_bytes);
229 if constexpr (packed) {
230 result.packed_capacity_bits =
231 multiply(multiply(tree.blocks, Block::capacity),
232 std::numeric_limits<T>::digits);
234 result.order_bytes = tree_bytes;
235 for (
auto* chunk = chunks_.head; chunk; chunk = chunk->next) {
237 result.chunk_header_bytes =
238 add(result.chunk_header_bytes,
sizeof(Chunk));
239 result.vector_capacity_bytes =
240 add(result.vector_capacity_bytes,
241 multiply(chunk->values.capacity(),
sizeof(Slot)));
242 result.vector_live_bytes =
243 add(result.vector_live_bytes,
244 multiply(chunk->values.size(),
sizeof(Slot)));
245 result.vector_slack_bytes =
246 add(result.vector_slack_bytes,
247 multiply(chunk->values.capacity() - chunk->values.size(),
252 add(
sizeof(*
this), add(tree_bytes, add(result.chunk_header_bytes,
253 result.vector_capacity_bytes)));
256#ifdef PIXIE_SEQUENCE_TREE_TESTING
261 using test_tree_type = Tree;
268 const Tree& test_tree() const noexcept {
return tree_; }
278 static void test_fail_payload_after(std::ptrdiff_t n)
noexcept {
279 payload_failure_ = n;
284#ifdef PIXIE_SEQUENCE_TREE_TESTING
285 inline static thread_local std::ptrdiff_t payload_failure_ = -1;
287 static void payload_allocation() {
288#ifdef PIXIE_SEQUENCE_TREE_TESTING
289 if (payload_failure_ == 0) {
290 throw std::bad_alloc();
292 if (payload_failure_ > 0) {
297 static std::size_t add(std::size_t a, std::size_t b)
noexcept {
298 const auto max = std::numeric_limits<std::size_t>::max();
299 return b > max - a ? max : a + b;
301 static std::size_t multiply(std::size_t a, std::size_t b)
noexcept {
302 const auto max = std::numeric_limits<std::size_t>::max();
303 return b && a > max / b ? max : a * b;
307 template <std::ranges::input_range Range>
309 constructible_from<T, std::ranges::range_rvalue_reference_t<Range>>
312 auto it = std::ranges::begin(range);
313 const auto end = std::ranges::end(range);
314 Chunk* current =
nullptr;
315 std::size_t offset = 0;
316 auto has_input = [&] {
317 if constexpr (packed) {
320 return (current && offset < current->values.size()) || it != end;
323 auto next_block = [&] {
324 std::array<typename Block::value_type, Block::capacity> scratch;
325 std::size_t count = 0;
326 while (count < Block::capacity && has_input()) {
327 if constexpr (packed) {
328 scratch[count++] = T(std::ranges::iter_move(it));
331 if (!current || offset == current->values.size()) {
332 payload_allocation();
333 auto chunk = std::make_unique<Chunk>();
334 payload_allocation();
335 chunk->values.reserve(chunk_capacity);
336 while (chunk->values.size() < chunk_capacity && it != end) {
337 if constexpr (std::same_as<T, bool>) {
338 chunk->values.emplace_back(
bool(std::ranges::iter_move(it)));
340 chunk->values.emplace_back(std::ranges::iter_move(it));
344 current = chunk.get();
345 result.chunks_.append(chunk.release());
348 if constexpr (std::same_as<T, bool>) {
349 scratch[count++] = std::addressof(current->values[offset++].value);
351 scratch[count++] = std::addressof(current->values[offset++]);
356 std::span<const typename Block::value_type>(scratch.data(), count));
361 decltype(next_block)& next;
364 using value_type [[maybe_unused]] = Block;
365 using difference_type [[maybe_unused]] = std::ptrdiff_t;
366 using iterator_concept [[maybe_unused]] = std::input_iterator_tag;
368 Block& operator*() const noexcept {
return source->current; }
369 Iterator& operator++() {
370 source->current = source->next();
373 void operator++(
int) { ++*
this; }
374 bool operator==(std::default_sentinel_t)
const noexcept {
375 return source->current.size() == 0;
382 std::default_sentinel_t end() const noexcept {
return {}; }
383 } blocks{next_block, {}};
384 result.tree_ = Tree::from_blocks(blocks);
387 std::size_t size_impl() const noexcept {
return tree_.size(); }
389 if constexpr (packed) {
397 void insert_at_impl(std::size_t position, T value) {
398 if (position > tree_.size()) {
399 throw std::out_of_range(
"PermutableSequence: insert position");
401 if constexpr (packed) {
402 tree_.insert_at(position, std::move(value));
404 std::unique_ptr<Chunk> staged;
405 if (!chunks_.tail || chunks_.tail->values.size() >= chunk_capacity) {
406 payload_allocation();
407 staged = std::make_unique<Chunk>();
408 payload_allocation();
409 staged->values.reserve(chunk_capacity);
411 auto& slots = staged ? staged->values : chunks_.tail->values;
412 if constexpr (std::same_as<T, bool>) {
413 slots.emplace_back(
bool(std::move(value)));
415 slots.emplace_back(std::move(value));
418 if constexpr (std::same_as<T, bool>) {
419 ptr = std::addressof(slots.back().value);
421 ptr = std::addressof(slots.back());
424 tree_.insert_at(position, ptr);
430 chunks_.append(staged.release());
434 void rotate_left_impl(std::size_t left,
436 std::size_t distance) {
437 tree_.rotate_left(left, right, distance);
441 if (
this == &donor) {
444 tree_.merge(donor.tree_);
445 if constexpr (!packed) {
446 chunks_.splice(donor.chunks_);
449 std::size_t memory_usage_bytes_impl() const noexcept {
454 [[no_unique_address]] std::conditional_t<packed, NoChunks, Chunks> chunks_;