38 using node_index_t = size_t;
39 static constexpr node_index_t npos = std::numeric_limits<node_index_t>::max();
40 static constexpr std::array<std::uint8_t, 8> kSerializationMagic = {
41 'P',
'X',
'W',
'A',
'V',
'E',
'T',
'\0'};
42 static constexpr std::uint32_t kSerializationVersion = 5;
43 static constexpr std::size_t kSerializationHeaderBytes = 24;
44#if defined(__APPLE__) && defined(__aarch64__)
45 static constexpr std::size_t kByteConstructionBlockSize = 16 * 1024;
47 static constexpr std::size_t kByteConstructionBlockSize = 32 * 1024;
50 struct PreWaveletNode {
51 node_index_t parent = npos;
52 node_index_t left_child = npos;
53 node_index_t right_child = npos;
55 std::size_t left_size = 0;
57 explicit PreWaveletNode(std::size_t middle) : middle(middle) {}
69 node_index_t parent, left_child, right_child;
71 Storage bit_vector_data;
74 WaveletNode() =
default;
76 WaveletNode(
const WaveletNode& node)
77 : parent(node.parent),
78 left_child(node.left_child),
79 right_child(node.right_child),
81 bit_vector_data(node.bit_vector_data),
83 if constexpr (std::same_as<Storage, AlignedStorage>) {
91 WaveletNode& operator=(
const WaveletNode& node) {
93 WaveletNode copy(node);
94 *
this = std::move(copy);
99 WaveletNode(WaveletNode&&)
noexcept =
default;
100 WaveletNode& operator=(WaveletNode&&)
noexcept =
default;
102 WaveletNode(PreWaveletNode&& node)
103 requires(std::same_as<Storage, AlignedStorage>)
104 : parent(node.parent),
105 left_child(node.left_child),
106 right_child(node.right_child),
107 middle(node.middle) {
108 const std::size_t bit_count = node.stream.size_bits();
109 bit_vector_data = node.stream.take_storage();
120 bit_vector_data.serialize(writer);
132 result.bit_vector_data = Storage::deserialize(reader);
134 reader, result.bit_vector_data.as_words64(), validation);
139 size_t alphabet_size_ = 0;
140 size_t data_size_ = 0;
141 node_index_t root_ = npos;
142 std::vector<WaveletNode> nodes_;
143 std::vector<node_index_t> leaves_;
144 std::vector<size_t> permutation_, inverse_permutation_;
146 void validate_deserialized_topology(
150 std::ranges::any_of(leaves_,
151 [](node_index_t leaf) {
return leaf != npos; })) {
152 throw std::invalid_argument(
153 "Invalid serialized empty wavelet-tree leaves");
157 if (nodes_[root_].parent != npos) {
158 throw std::invalid_argument(
"Serialized wavelet-tree root has a parent");
161 std::vector<std::uint8_t> incoming_edges(nodes_.size());
162 for (node_index_t parent = 0; parent < nodes_.size(); ++parent) {
163 const WaveletNode& node = nodes_[parent];
164 for (
const node_index_t child : {node.left_child, node.right_child}) {
168 if (nodes_[child].parent != parent) {
169 throw std::invalid_argument(
170 "Serialized wavelet-tree parent/child links disagree");
172 if (incoming_edges[child] != 0) {
173 throw std::invalid_argument(
174 "Serialized wavelet-tree node has multiple parents");
176 ++incoming_edges[child];
180 for (node_index_t node = 0; node < nodes_.size(); ++node) {
181 const std::size_t expected_edges = node == root_ ? 0 : 1;
182 if (incoming_edges[node] != expected_edges) {
183 throw std::invalid_argument(
184 "Serialized wavelet-tree node is detached from its parent");
190 std::size_t symbol_begin;
191 std::size_t symbol_end;
193 std::vector<bool> reached(nodes_.size());
194 std::vector<PendingNode> pending = {{root_, 0, alphabet_size_}};
195 while (!pending.empty()) {
196 const PendingNode current = pending.back();
198 const node_index_t node = current.node;
199 reached[node] =
true;
200 const WaveletNode& metadata = nodes_[node];
201 if (metadata.middle <= current.symbol_begin ||
202 metadata.middle >= current.symbol_end) {
203 throw std::invalid_argument(
204 "Serialized wavelet-tree split is outside its symbol range");
207 const std::size_t one_count =
209 ? metadata.data.
rank(metadata.data.
size())
211 const std::size_t zero_count =
213 ? metadata.data.
size() - one_count
215 const auto validate_branch = [&](node_index_t child,
216 std::size_t symbol_begin,
217 std::size_t symbol_end,
218 std::size_t expected_size) {
221 nodes_[child].data.size() != expected_size) {
222 throw std::invalid_argument(
223 "Serialized wavelet-tree child has the wrong length");
225 pending.push_back({child, symbol_begin, symbol_end});
229 bool has_mapped_symbol =
false;
230 for (std::size_t symbol = symbol_begin; symbol < symbol_end;
232 if (leaves_[symbol] == node) {
233 has_mapped_symbol =
true;
234 }
else if (leaves_[symbol] != npos) {
235 throw std::invalid_argument(
236 "Serialized wavelet-tree leaf map disagrees with topology");
239 if (expected_size != 0 && !has_mapped_symbol) {
240 throw std::invalid_argument(
241 "Serialized wavelet-tree leaf is missing from its map");
245 validate_branch(metadata.left_child, current.symbol_begin,
246 metadata.middle, zero_count);
247 validate_branch(metadata.right_child, metadata.middle, current.symbol_end,
250 if (std::ranges::find(reached,
false) != reached.end()) {
251 throw std::invalid_argument(
252 "Serialized wavelet-tree contains unreachable nodes");
271 template <
typename F>
272 node_index_t build_node(
size_t begin,
276 std::span<const size_t> prefix_sum,
277 std::vector<PreWaveletNode>& nodes)
278 requires(std::same_as<Storage, AlignedStorage>)
280 if (end - begin == 1) {
281 leaves_[begin] = parent;
284 if (prefix_sum[end] == prefix_sum[begin]) {
285 for (
size_t symbol = begin; symbol < end; symbol++) {
286 leaves_[symbol] = parent;
291 node_index_t result = nodes.size();
292 size_t middle = get_middle(result);
293 middle = begin + (middle == npos ? (end - begin) / 2 : middle);
295 nodes.emplace_back(middle);
296 nodes[result].left_size = prefix_sum[middle] - prefix_sum[begin];
297 nodes[result].stream.reserve_bits(prefix_sum[end] - prefix_sum[begin]);
298 nodes[result].parent = parent;
299 nodes[result].left_child =
300 build_node(begin, middle, result, get_middle, prefix_sum, nodes);
301 nodes[result].right_child =
302 build_node(middle, end, result, get_middle, prefix_sum, nodes);
307 static std::size_t count_huffman_subtree(
309 std::int16_t source_node,
310 std::span<const std::size_t> symbol_counts,
311 std::array<std::size_t, detail::kMaximumHuffmanNodes>& subtree_counts) {
313 if (node.symbol >= 0) {
314 return subtree_counts[source_node] = symbol_counts[node.symbol];
316 const std::size_t left =
317 count_huffman_subtree(table, node.left, symbol_counts, subtree_counts);
318 const std::size_t right =
319 count_huffman_subtree(table, node.right, symbol_counts, subtree_counts);
320 return subtree_counts[source_node] = left + right;
323 node_index_t materialize_huffman_node(
325 std::int16_t source_node,
327 const std::array<std::size_t, detail::kMaximumHuffmanNodes>&
329 std::vector<PreWaveletNode>& nodes)
330 requires(std::same_as<Symbol, std::uint8_t> &&
331 std::same_as<Storage, AlignedStorage>)
334 if (source.symbol >= 0) {
335 leaves_[table.symbol_to_rank[source.symbol]] = parent;
339 const node_index_t result = nodes.size();
340 const std::size_t middle =
341 static_cast<std::size_t
>(table.split_rank[source_node]) + 1;
342 nodes.emplace_back(middle);
343 nodes[result].parent = parent;
344 nodes[result].left_size = subtree_counts[source.left];
345 nodes[result].stream.reserve_bits(subtree_counts[source_node]);
346 nodes[result].left_child = materialize_huffman_node(
347 table, source.left, result, subtree_counts, nodes);
348 nodes[result].right_child = materialize_huffman_node(
349 table, source.right, result, subtree_counts, nodes);
353 void build_huffman_byte_topology(std::span<const std::size_t> symbol_counts,
354 std::vector<PreWaveletNode>& nodes)
355 requires(std::same_as<Symbol, std::uint8_t> &&
356 std::same_as<Storage, AlignedStorage>)
360 permutation_.resize(alphabet_size_);
361 inverse_permutation_.resize(alphabet_size_);
363 std::size_t unused_rank = table.symbol_count;
364 for (std::size_t symbol = 0; symbol < alphabet_size_; ++symbol) {
365 const std::size_t
rank = symbol_counts[symbol] == 0
367 : table.symbol_to_rank[symbol];
368 permutation_[symbol] =
rank;
369 inverse_permutation_[
rank] = symbol;
371 if (table.symbol_count < 2) {
376 std::array<std::size_t, detail::kMaximumHuffmanNodes> subtree_counts{};
377 count_huffman_subtree(table, table.root, symbol_counts, subtree_counts);
378 nodes.reserve(table.symbol_count - 1);
379 root_ = materialize_huffman_node(table, table.root, npos, subtree_counts,
383 void build_node_streams(node_index_t node,
384 std::span<Symbol> input,
385 std::span<Symbol> output,
386 std::vector<PreWaveletNode>& nodes)
387 requires(std::same_as<Storage, AlignedStorage>)
389 PreWaveletNode& current = nodes[node];
391 std::span<const Symbol>(input),
static_cast<Symbol
>(current.middle),
392 output, current.left_size, current.left_child != npos,
393 current.right_child != npos, current.stream);
395 if (current.left_child != npos) {
396 build_node_streams(current.left_child, output.first(current.left_size),
397 input.first(current.left_size), nodes);
399 if (current.right_child != npos) {
400 build_node_streams(current.right_child, output.subspan(current.left_size),
401 input.subspan(current.left_size), nodes);
408 void build_byte_block_streams(node_index_t node,
409 std::span<std::uint8_t> ranks,
410 std::span<std::uint8_t> scratch,
411 std::vector<PreWaveletNode>& nodes)
412 requires(std::same_as<Symbol, std::uint8_t> &&
413 std::same_as<Storage, AlignedStorage>)
415 PreWaveletNode& current = nodes[node];
416 const bool write_left = current.left_child != npos;
417 const bool write_right = current.right_child != npos;
419 ranks,
static_cast<std::uint8_t
>(current.middle), scratch, write_left,
420 write_right, current.stream);
421 const std::size_t right_size = ranks.size() - left_size;
423 if (write_left && left_size != 0) {
424 build_byte_block_streams(current.left_child, ranks.first(left_size),
425 scratch.subspan(right_size, left_size), nodes);
427 if (write_right && right_size != 0) {
428 build_byte_block_streams(current.right_child, scratch.first(right_size),
429 ranks.first(right_size), nodes);
433 template <
bool Val
idateSymbolCounts,
class ForEachSymbol>
434 void build_byte_bit_streams(std::span<const std::size_t> symbol_counts,
435 ForEachSymbol& for_each_symbol,
436 std::vector<PreWaveletNode>& nodes)
437 requires(std::same_as<Symbol, std::uint8_t> &&
438 std::same_as<Storage, AlignedStorage>)
440 std::array<std::uint8_t, detail::kByteAlphabetSize> symbol_to_rank{};
441 std::array<std::uint16_t, detail::kByteAlphabetSize> symbol_to_high_rank{};
442 for (std::size_t symbol = 0; symbol < alphabet_size_; ++symbol) {
443 const auto rank =
static_cast<std::uint8_t
>(permutation_[symbol]);
444 symbol_to_rank[symbol] =
rank;
445 symbol_to_high_rank[symbol] =
static_cast<std::uint16_t
>(
rank) << 8;
448 const std::size_t block_capacity = std::min(
449 kByteConstructionBlockSize, std::max<std::size_t>(data_size_, 1));
450 std::vector<std::uint8_t> block;
451 std::vector<std::uint8_t> scratch(block_capacity);
452 block.reserve(block_capacity);
453 struct NoHistogram {};
454 using ValidationHistogram =
457 ValidationHistogram actual_histogram;
458 const auto encode_block = [&] {
463 build_byte_block_streams(root_, block,
464 std::span(scratch).first(block.size()), nodes);
469 std::size_t emitted_size = 0;
470 const auto append_symbols = [&](std::span<const Symbol> symbols) {
471 if constexpr (ValidateSymbolCounts) {
472 if (emitted_size > data_size_ ||
473 symbols.size() > data_size_ - emitted_size) {
474 throw std::invalid_argument(
475 "Wavelet-tree emitted symbols do not match their counts");
477 emitted_size += symbols.size();
478 actual_histogram.add(std::as_bytes(symbols));
480 while (!symbols.empty()) {
481 const std::size_t copied =
482 std::min(block_capacity - block.size(), symbols.size());
483 const std::size_t destination = block.size();
484 block.resize(destination + copied);
486 symbols.first(copied),
487 std::span(block).subspan(destination, copied), symbol_to_rank,
488 symbol_to_high_rank);
489 symbols = symbols.subspan(copied);
490 if (block.size() == block_capacity) {
495 for_each_symbol([&](
auto&& emitted) {
496 using Emitted = std::remove_cvref_t<
decltype(emitted)>;
497 if constexpr (std::same_as<Emitted, Symbol>) {
498 append_symbols(std::span<const Symbol>(&emitted, 1));
500 append_symbols(std::span<const Symbol>(emitted));
505 if constexpr (ValidateSymbolCounts) {
506 const auto actual_counts = actual_histogram.counts();
507 if (emitted_size != data_size_ ||
508 !std::ranges::equal(actual_counts.begin(),
509 actual_counts.begin() + alphabet_size_,
510 symbol_counts.begin(), symbol_counts.end()) ||
511 std::ranges::any_of(actual_counts.begin() + alphabet_size_,
513 [](std::size_t count) { return count != 0; })) {
514 throw std::invalid_argument(
515 "Wavelet-tree emitted symbols do not match their counts");
520 template <
bool Val
idateSymbolCounts,
class ForEachSymbol>
521 void build_generic_bit_streams(std::span<const std::size_t> symbol_counts,
522 ForEachSymbol& for_each_symbol,
523 std::vector<PreWaveletNode>& nodes)
524 requires(std::same_as<Storage, AlignedStorage>)
526 std::vector<std::size_t> actual_counts;
527 if constexpr (ValidateSymbolCounts) {
528 actual_counts.resize(alphabet_size_);
530 std::vector<Symbol> ranks;
532 ranks.reserve(data_size_);
534 for_each_symbol([&](Symbol symbol) {
535 const std::size_t original =
static_cast<std::size_t
>(symbol);
536 if constexpr (ValidateSymbolCounts) {
537 if (original >= alphabet_size_) {
538 throw std::invalid_argument(
539 "Wavelet-tree symbol is outside the alphabet");
541 if (actual_counts[original] ==
542 std::numeric_limits<std::size_t>::max()) {
543 throw std::length_error(
"Wavelet-tree symbol count is too large");
545 ++actual_counts[original];
548 ranks.push_back(
static_cast<Symbol
>(permutation_[original]));
551 if constexpr (ValidateSymbolCounts) {
552 if (!std::ranges::equal(actual_counts, symbol_counts)) {
553 throw std::invalid_argument(
554 "Wavelet-tree emitted symbols do not match their counts");
561 const PreWaveletNode& root = nodes[root_];
562 const bool root_needs_output =
563 root.left_child != npos || root.right_child != npos;
564 std::vector<Symbol> scratch(root_needs_output ? data_size_ : 0);
565 build_node_streams(root_, ranks, scratch, nodes);
568 template <
bool Val
idateSymbolCounts,
class ForEachSymbol>
569 void build_bit_streams(std::span<const std::size_t> symbol_counts,
570 ForEachSymbol& for_each_symbol,
571 std::vector<PreWaveletNode>& nodes)
572 requires(std::same_as<Storage, AlignedStorage>)
574 if constexpr (std::same_as<Symbol, std::uint8_t>) {
575 build_byte_bit_streams<ValidateSymbolCounts>(symbol_counts,
576 for_each_symbol, nodes);
578 build_generic_bit_streams<ValidateSymbolCounts>(symbol_counts,
579 for_each_symbol, nodes);
598 void copy_segment_content(node_index_t node,
601 std::span<Symbol> dst,
602 std::span<Symbol> tmp)
const {
606 const size_t rank = nodes_[node].data.rank(begin), rank0 = begin -
rank;
607 const size_t right = nodes_[node].data.rank(end) -
rank,
608 left = (end - begin) - right;
610 if (nodes_[node].left_child == npos) {
612 tmp.begin(),
static_cast<long long>(left),
613 static_cast<Symbol
>(inverse_permutation_[nodes_[node].middle - 1]));
615 copy_segment_content(nodes_[node].left_child, rank0, rank0 + left,
616 tmp.subspan(0, left), dst.subspan(0, left));
618 if (nodes_[node].right_child == npos) {
619 std::fill(tmp.begin() +
static_cast<long long>(left), tmp.end(),
620 static_cast<Symbol
>(inverse_permutation_[nodes_[node].middle]));
622 copy_segment_content(nodes_[node].right_child,
rank,
rank + right,
623 tmp.subspan(left, right), dst.subspan(left, right));
626 size_t j = 0, k = left;
627 const auto& bit_vector = nodes_[node].bit_vector_data.as_words64();
628 for (
size_t i = begin; i < end; i++) {
629 if ((bit_vector[i / 64] >> (i % 64)) & 1) {
630 dst[i - begin] = tmp[k++];
632 dst[i - begin] = tmp[j++];
637 static void validate_alphabet_size(std::size_t alphabet_size) {
638 if (alphabet_size != 0 &&
640 static_cast<std::size_t
>(std::numeric_limits<Symbol>::max())) {
641 throw std::invalid_argument(
642 "Wavelet-tree alphabet does not fit its symbol type");
646 static std::size_t checked_symbol_index(Symbol symbol,
647 std::size_t alphabet_size) {
648 const std::size_t index =
static_cast<std::size_t
>(symbol);
649 if (index >= alphabet_size) {
650 throw std::invalid_argument(
651 "Wavelet-tree symbol is outside the alphabet");
656 template <
bool Val
idateSymbolCounts,
class ForEachSymbol>
657 void build_from_counts(std::size_t alphabet_size,
658 std::span<const std::size_t> symbol_counts,
659 ForEachSymbol&& for_each_symbol,
661 requires(std::same_as<Storage, AlignedStorage>)
663 validate_alphabet_size(alphabet_size);
664 if (symbol_counts.size() != alphabet_size) {
665 throw std::invalid_argument(
666 "Wavelet-tree symbol counts must match the alphabet size");
668 alphabet_size_ = alphabet_size;
669 for (
const std::size_t count : symbol_counts) {
670 if (count > std::numeric_limits<std::size_t>::max() - data_size_) {
671 throw std::length_error(
"Wavelet-tree input is too large");
675 leaves_.assign(alphabet_size_, npos);
677 std::vector<PreWaveletNode> nodes;
678 if constexpr (std::same_as<Symbol, std::uint8_t>) {
679 if (build_type == WaveletTreeBuildType::Huffman) {
680 if (alphabet_size_ != 0) {
681 build_huffman_byte_topology(symbol_counts, nodes);
683 build_bit_streams<ValidateSymbolCounts>(symbol_counts, for_each_symbol,
685 nodes_.reserve(nodes.size());
686 for (
auto& node : nodes) {
687 nodes_.emplace_back(std::move(node));
693 std::vector<std::size_t> nodes_structure;
694 if (alphabet_size_ != 0) {
695 nodes.reserve(alphabet_size_);
696 nodes_structure.reserve(alphabet_size_);
698 if (build_type == WaveletTreeBuildType::Standard) {
699 permutation_.resize(alphabet_size_);
700 inverse_permutation_.resize(alphabet_size_);
701 std::iota(permutation_.begin(), permutation_.end(), 0);
702 std::iota(inverse_permutation_.begin(), inverse_permutation_.end(), 0);
703 nodes_structure.resize(alphabet_size_, npos);
710 std::vector<HuffmanNode> huffman_nodes(alphabet_size_, {0, 0, 0});
711 for (std::size_t symbol = 0; symbol < alphabet_size_; ++symbol) {
712 huffman_nodes[symbol].size = symbol_counts[symbol];
715 using QueueElement = std::pair<std::size_t, std::size_t>;
716 std::priority_queue<QueueElement, std::vector<QueueElement>,
719 for (std::size_t symbol = 0; symbol < alphabet_size_; ++symbol) {
720 queue.emplace(huffman_nodes[symbol].
size, symbol);
722 while (queue.size() >= 2) {
723 const std::size_t right = queue.top().second;
725 const std::size_t left = queue.top().second;
727 huffman_nodes.push_back(
728 {huffman_nodes[left].size + huffman_nodes[right].size, left + 1,
730 queue.emplace(huffman_nodes.back().size, huffman_nodes.size() - 1);
733 std::function<std::size_t(std::size_t)> enumerate =
734 [&](std::size_t index) -> std::size_t {
735 const auto& [
size, left, right] = huffman_nodes[index];
736 if (left == 0 || right == 0) {
737 permutation_[index] = inverse_permutation_.size();
738 inverse_permutation_.push_back(index);
741 const std::size_t node = nodes_structure.size();
742 std::size_t subtree = 0;
744 nodes_structure.push_back(0);
746 subtree += enumerate(left - 1);
748 nodes_structure[node] = subtree;
750 subtree += enumerate(right - 1);
754 permutation_.resize(alphabet_size_);
755 inverse_permutation_.reserve(alphabet_size_);
756 enumerate(huffman_nodes.size() - 1);
759 std::vector<std::size_t> prefix_sum(alphabet_size_ + 1);
760 for (std::size_t symbol = 0; symbol < alphabet_size_; ++symbol) {
761 prefix_sum[permutation_[symbol] + 1] = symbol_counts[symbol];
763 std::partial_sum(prefix_sum.begin(), prefix_sum.end(),
766 0, alphabet_size_, npos,
767 [&](node_index_t node) {
return nodes_structure[node]; }, prefix_sum,
771 build_bit_streams<ValidateSymbolCounts>(symbol_counts, for_each_symbol,
774 nodes_.reserve(nodes.size());
775 for (
auto& node : nodes) {
776 nodes_.emplace_back(std::move(node));
780 WaveletTreeIndex() =
default;
783 using symbol_type = Symbol;
796 std::size_t alphabet_size,
797 std::span<const Symbol> data,
799 requires(std::same_as<Storage, AlignedStorage>)
801 validate_alphabet_size(alphabet_size);
802 std::vector<std::size_t> counts;
803 if constexpr (std::same_as<Symbol, std::uint8_t>) {
805 histogram.
add(std::as_bytes(data));
806 const auto byte_counts = histogram.
counts();
807 if (std::ranges::any_of(byte_counts.begin() + alphabet_size,
809 [](std::size_t count) { return count != 0; })) {
810 throw std::invalid_argument(
811 "Wavelet-tree symbol is outside the alphabet");
813 counts.assign(byte_counts.begin(), byte_counts.begin() + alphabet_size);
815 counts.assign(alphabet_size, 0);
816 for (
const Symbol symbol : data) {
817 ++counts[checked_symbol_index(symbol, alphabet_size)];
820 build_from_counts<false>(
821 alphabet_size, counts,
823 if constexpr (
requires { emit(data); }) {
826 for (
const Symbol symbol : data) {
851 template <
class ForEachSymbol>
853 std::size_t alphabet_size,
854 std::span<const std::size_t> symbol_counts,
855 ForEachSymbol&& for_each_symbol,
857 requires(std::same_as<Storage, AlignedStorage>)
859 build_from_counts<true>(alphabet_size, symbol_counts,
860 std::forward<ForEachSymbol>(for_each_symbol),
873 std::size_t symbol_index =
static_cast<std::size_t
>(symbol);
874 if (symbol_index >= alphabet_size_) [[unlikely]] {
877 symbol_index = permutation_[symbol_index];
878 if (root_ == npos) [[unlikely]] {
879 return data_size_ != 0 && symbol_index == 0 ? pos : 0;
881 if (leaves_[symbol_index] == npos) [[unlikely]] {
884 for (node_index_t current = root_; current != npos;) {
885 const WaveletNode& node = nodes_[current];
886 if (symbol_index < node.middle) {
887 pos = node.data.
rank0(pos);
888 current = node.left_child;
890 pos = node.data.
rank(pos);
891 current = node.right_child;
906 std::size_t symbol_index =
static_cast<std::size_t
>(symbol);
907 if (symbol_index >= alphabet_size_ || data_size_ == 0 ||
rank == 0)
911 symbol_index = permutation_[symbol_index];
912 if (root_ == npos) [[unlikely]] {
913 return symbol_index == 0 &&
rank <= data_size_ ?
rank - 1 : data_size_;
915 if (leaves_[symbol_index] == npos) [[unlikely]] {
918 node_index_t current = leaves_[symbol_index];
919 for (; current != npos; current = nodes_[current].parent) {
920 const WaveletNode& node = nodes_[current];
921 if (symbol_index < node.middle) {
945 if (alphabet_size_ == 0 || data_size_ == 0 || begin >= end) [[unlikely]] {
948 const std::size_t length = end - begin;
949 if (root_ == npos) [[unlikely]] {
950 return std::vector<Symbol>(
951 length,
static_cast<Symbol
>(inverse_permutation_.front()));
953 if (length > std::vector<Symbol>().max_size() / 2) {
954 throw std::length_error(
"Wavelet-tree segment is too large");
956 std::vector<Symbol> result(2 * length);
957 copy_segment_content(root_, begin, end, std::span(result).first(length),
958 std::span(result).subspan(length));
959 result.resize(length);
976 if (writer.
size_bytes() %
alignof(std::uint64_t) != 0) {
977 throw std::invalid_argument(
978 "Wavelet-tree serialization requires an aligned writer offset");
980 const std::size_t artifact_begin = writer.
size_bytes();
981 detail::write_magic(writer, kSerializationMagic);
983 writer.
write_u32(std::numeric_limits<Symbol>::digits);
990 for (
const WaveletNode& node : nodes_) {
991 node.serialize(writer);
993 for (
const node_index_t leaf : leaves_) {
996 for (
const size_t idx : permutation_) {
1000 const std::size_t unpadded_size = writer.
size_bytes() - artifact_begin;
1002 (
sizeof(std::uint64_t) - unpadded_size %
sizeof(std::uint64_t)) %
1003 sizeof(std::uint64_t));
1005 artifact_size_position,
1006 static_cast<std::uint64_t
>(writer.
size_bytes() - artifact_begin));
1029 requires(std::same_as<Storage, AlignedStorage> ||
1030 std::same_as<Storage, ReadOnlyStorageView>)
1033 if constexpr (std::same_as<Storage, ReadOnlyStorageView>) {
1034 if (
reinterpret_cast<std::uintptr_t
>(candidate.
remaining_bytes().data()) %
1035 alignof(std::uint64_t) !=
1037 throw std::invalid_argument(
1038 "Serialized wavelet-tree artifact is not word aligned");
1041 const std::size_t available_size = candidate.
remaining();
1042 detail::require_magic(candidate, kSerializationMagic);
1043 if (candidate.
read_u32() != kSerializationVersion ||
1044 candidate.
read_u32() != std::numeric_limits<Symbol>::digits) {
1045 throw std::invalid_argument(
1046 "Incompatible serialized wavelet-tree artifact");
1048 const std::size_t artifact_size = detail::checked_artifact_size(
1049 candidate.
read_u64(), kSerializationHeaderBytes, available_size);
1051 candidate.
read_subreader(artifact_size - kSerializationHeaderBytes);
1053 WaveletTreeIndex result;
1054 result.alphabet_size_ = payload.
read_size();
1055 result.validate_alphabet_size(result.alphabet_size_);
1056 result.data_size_ = payload.
read_size();
1058 const std::size_t node_count = payload.
read_size();
1059 const std::vector<WaveletNode> empty_nodes;
1060 if (node_count > empty_nodes.max_size()) {
1061 throw std::length_error(
1062 "Serialized wavelet-tree node count is too large");
1064 constexpr std::size_t kMinimumNodeBytes =
1065 4 *
sizeof(std::uint64_t) +
sizeof(std::uint64_t);
1066 if (node_count > payload.
remaining() / kMinimumNodeBytes) {
1070 result.nodes_.resize(node_count);
1071 for (
auto& node : result.nodes_) {
1072 node = WaveletNode::deserialize(payload, validation);
1074 const std::vector<node_index_t> empty_indices;
1075 if (result.alphabet_size_ > empty_indices.max_size() ||
1076 result.alphabet_size_ >
1077 payload.
remaining() / (2 *
sizeof(std::uint64_t))) {
1078 throw std::length_error(
"Serialized wavelet-tree alphabet is too large");
1080 result.leaves_.resize(result.alphabet_size_);
1081 for (node_index_t& leaf : result.leaves_) {
1084 result.permutation_.resize(result.alphabet_size_);
1085 for (
size_t& index : result.permutation_) {
1088 result.inverse_permutation_.resize(result.alphabet_size_);
1089 std::vector<bool> seen(result.alphabet_size_);
1090 for (
size_t i = 0; i < result.alphabet_size_; i++) {
1091 if (result.permutation_[i] >= result.alphabet_size_ ||
1092 seen[result.permutation_[i]]) {
1093 throw std::invalid_argument(
1094 "Invalid serialized wavelet-tree permutation");
1096 seen[result.permutation_[i]] =
true;
1097 result.inverse_permutation_[result.permutation_[i]] = i;
1099 const auto valid_node_index = [&result](node_index_t index) {
1100 return index == npos || index < result.nodes_.size();
1102 if (!valid_node_index(result.root_) ||
1103 (result.nodes_.empty() != (result.root_ == npos))) {
1104 throw std::invalid_argument(
"Invalid serialized wavelet-tree root");
1106 for (
const node_index_t leaf : result.leaves_) {
1107 if (!valid_node_index(leaf)) {
1108 throw std::invalid_argument(
"Invalid serialized wavelet-tree leaf");
1111 for (
const WaveletNode& node : result.nodes_) {
1112 if (!valid_node_index(node.parent) ||
1113 !valid_node_index(node.left_child) ||
1114 !valid_node_index(node.right_child) ||
1115 node.data.
size() > node.bit_vector_data.size_bits() ||
1116 node.middle == 0 || node.middle >= result.alphabet_size_) {
1117 throw std::invalid_argument(
"Invalid serialized wavelet-tree node");
1120 result.validate_deserialized_topology(validation);
1121 if (result.root_ != npos &&
1122 result.nodes_[result.root_].data.size() != result.data_size_) {
1123 throw std::invalid_argument(
1124 "Serialized wavelet-tree root has the wrong length");