Pixie
Loading...
Searching...
No Matches
index.h
1#pragma once
2
3#include <pixie/detail/serialization.h>
4#include <pixie/packed_bit_builder.h>
5#include <pixie/rank_select/support.h>
7
8#include <algorithm>
9#include <array>
10#include <cstddef>
11#include <cstdint>
12#include <functional>
13#include <limits>
14#include <numeric>
15#include <queue>
16#include <span>
17#include <stdexcept>
18#include <type_traits>
19#include <vector>
20
21namespace pixie {
22
29template <WaveletTreeSymbol Symbol,
31class WaveletTreeIndex
32 : public WaveletTreeBase<WaveletTreeIndex<Symbol, Storage>, Symbol>,
33 public SerializationBase<WaveletTreeIndex<Symbol, Storage>> {
34 private:
35 using node_index_t = size_t;
36 static constexpr node_index_t npos = std::numeric_limits<node_index_t>::max();
37 static constexpr std::array<std::uint8_t, 8> kSerializationMagic = {
38 'P', 'X', 'W', 'A', 'V', 'E', 'T', '\0'};
39 static constexpr std::uint32_t kSerializationVersion = 5;
40 static constexpr std::size_t kSerializationHeaderBytes = 24;
41
42 struct PreWaveletNode {
43 node_index_t parent = npos;
44 node_index_t left_child = npos;
45 node_index_t right_child = npos;
46 std::size_t middle;
47 PackedBitBuilder stream;
48 explicit PreWaveletNode(std::size_t middle) : middle(middle) {}
49 };
50
59 struct WaveletNode {
60 node_index_t parent, left_child, right_child;
61 std::size_t middle;
62 Storage bit_vector_data;
64
65 WaveletNode() = default;
66
67 WaveletNode(const WaveletNode& node)
68 : parent(node.parent),
69 left_child(node.left_child),
70 right_child(node.right_child),
71 middle(node.middle),
72 bit_vector_data(node.bit_vector_data),
73 data([&] {
74 if constexpr (std::same_as<Storage, AlignedStorage>) {
75 return RankSelectSupport<Storage>(bit_vector_data.as_words64(),
76 node.data.size());
77 } else {
78 return node.data;
79 }
80 }()) {}
81
82 WaveletNode& operator=(const WaveletNode& node) {
83 if (this != &node) {
84 WaveletNode copy(node);
85 *this = std::move(copy);
86 }
87 return *this;
88 }
89
90 WaveletNode(WaveletNode&&) noexcept = default;
91 WaveletNode& operator=(WaveletNode&&) noexcept = default;
92
93 WaveletNode(PreWaveletNode&& node)
94 requires(std::same_as<Storage, AlignedStorage>)
95 : parent(node.parent),
96 left_child(node.left_child),
97 right_child(node.right_child),
98 middle(node.middle) {
99 const std::size_t bit_count = node.stream.size_bits();
100 const std::vector<std::uint64_t> words = node.stream.take_words();
101 bit_vector_data = AlignedStorage(std::span<const std::uint64_t>(words));
102 data =
103 RankSelectSupport<Storage>(bit_vector_data.as_words64(), bit_count);
104 }
105
107 void serialize(BinaryWriter& writer) const {
108 writer.write_size(parent);
109 writer.write_size(left_child);
110 writer.write_size(right_child);
111 writer.write_u64(middle);
112 bit_vector_data.serialize(writer);
113 data.serialize(writer);
114 }
115
117 static WaveletNode deserialize(BinaryReader& reader,
118 DeserializationValidation validation) {
119 WaveletNode result;
120 result.parent = reader.read_size();
121 result.left_child = reader.read_size();
122 result.right_child = reader.read_size();
123 result.middle = reader.read_u64();
124 result.bit_vector_data = Storage::deserialize(reader);
126 reader, result.bit_vector_data.as_words64(), validation);
127 return result;
128 }
129 };
130
131 size_t alphabet_size_ = 0;
132 size_t data_size_ = 0;
133 node_index_t root_ = npos;
134 std::vector<WaveletNode> nodes_;
135 std::vector<node_index_t> leaves_;
136 std::vector<size_t> permutation_, inverse_permutation_;
137
138 void validate_deserialized_topology(
139 DeserializationValidation validation) const {
140 if (root_ == npos) {
141 if (validation == DeserializationValidation::kFull &&
142 std::ranges::any_of(leaves_,
143 [](node_index_t leaf) { return leaf != npos; })) {
144 throw std::invalid_argument(
145 "Invalid serialized empty wavelet-tree leaves");
146 }
147 return;
148 }
149 if (nodes_[root_].parent != npos) {
150 throw std::invalid_argument("Serialized wavelet-tree root has a parent");
151 }
152
153 std::vector<std::uint8_t> incoming_edges(nodes_.size());
154 for (node_index_t parent = 0; parent < nodes_.size(); ++parent) {
155 const WaveletNode& node = nodes_[parent];
156 for (const node_index_t child : {node.left_child, node.right_child}) {
157 if (child == npos) {
158 continue;
159 }
160 if (nodes_[child].parent != parent) {
161 throw std::invalid_argument(
162 "Serialized wavelet-tree parent/child links disagree");
163 }
164 if (incoming_edges[child] != 0) {
165 throw std::invalid_argument(
166 "Serialized wavelet-tree node has multiple parents");
167 }
168 ++incoming_edges[child];
169 }
170 }
171
172 for (node_index_t node = 0; node < nodes_.size(); ++node) {
173 const std::size_t expected_edges = node == root_ ? 0 : 1;
174 if (incoming_edges[node] != expected_edges) {
175 throw std::invalid_argument(
176 "Serialized wavelet-tree node is detached from its parent");
177 }
178 }
179
180 struct PendingNode {
181 node_index_t node;
182 std::size_t symbol_begin;
183 std::size_t symbol_end;
184 };
185 std::vector<bool> reached(nodes_.size());
186 std::vector<PendingNode> pending = {{root_, 0, alphabet_size_}};
187 while (!pending.empty()) {
188 const PendingNode current = pending.back();
189 pending.pop_back();
190 const node_index_t node = current.node;
191 reached[node] = true;
192 const WaveletNode& metadata = nodes_[node];
193 if (metadata.middle <= current.symbol_begin ||
194 metadata.middle >= current.symbol_end) {
195 throw std::invalid_argument(
196 "Serialized wavelet-tree split is outside its symbol range");
197 }
198
199 const std::size_t one_count =
201 ? metadata.data.rank(metadata.data.size())
202 : 0;
203 const std::size_t zero_count =
205 ? metadata.data.size() - one_count
206 : 0;
207 const auto validate_branch = [&](node_index_t child,
208 std::size_t symbol_begin,
209 std::size_t symbol_end,
210 std::size_t expected_size) {
211 if (child != npos) {
212 if (validation == DeserializationValidation::kFull &&
213 nodes_[child].data.size() != expected_size) {
214 throw std::invalid_argument(
215 "Serialized wavelet-tree child has the wrong length");
216 }
217 pending.push_back({child, symbol_begin, symbol_end});
218 return;
219 }
220 if (validation == DeserializationValidation::kFull) {
221 for (std::size_t symbol = symbol_begin; symbol < symbol_end;
222 ++symbol) {
223 if (leaves_[symbol] != node) {
224 throw std::invalid_argument(
225 "Serialized wavelet-tree leaf map disagrees with topology");
226 }
227 }
228 }
229 };
230 validate_branch(metadata.left_child, current.symbol_begin,
231 metadata.middle, zero_count);
232 validate_branch(metadata.right_child, metadata.middle, current.symbol_end,
233 one_count);
234 }
235 if (std::ranges::find(reached, false) != reached.end()) {
236 throw std::invalid_argument(
237 "Serialized wavelet-tree contains unreachable nodes");
238 }
239 }
240
256 template <typename F>
257 node_index_t build_node(size_t begin,
258 size_t end,
259 node_index_t parent,
260 const F& get_middle,
261 std::span<const size_t> prefix_sum,
262 std::vector<PreWaveletNode>& nodes)
263 requires(std::same_as<Storage, AlignedStorage>)
264 {
265 if (end - begin == 1) {
266 leaves_[begin] = parent;
267 return npos;
268 }
269 if (prefix_sum[end] == prefix_sum[begin]) {
270 for (size_t symbol = begin; symbol < end; symbol++) {
271 leaves_[symbol] = parent;
272 }
273 return npos;
274 }
275
276 node_index_t result = nodes.size();
277 size_t middle = get_middle(result);
278 middle = begin + (middle == npos ? (end - begin) / 2 : middle);
279
280 nodes.emplace_back(middle);
281 nodes[result].stream.reserve_bits(prefix_sum[end] - prefix_sum[begin]);
282 nodes[result].parent = parent;
283 nodes[result].left_child =
284 build_node(begin, middle, result, get_middle, prefix_sum, nodes);
285 nodes[result].right_child =
286 build_node(middle, end, result, get_middle, prefix_sum, nodes);
287
288 return result;
289 }
290
306 void copy_segment_content(node_index_t node,
307 size_t begin,
308 size_t end,
309 std::span<Symbol> dst,
310 std::span<Symbol> tmp) const {
311 if (begin == end) {
312 return;
313 }
314 const size_t rank = nodes_[node].data.rank(begin), rank0 = begin - rank;
315 const size_t right = nodes_[node].data.rank(end) - rank,
316 left = (end - begin) - right;
317
318 if (nodes_[node].left_child == npos) {
319 std::fill_n(
320 tmp.begin(), static_cast<long long>(left),
321 static_cast<Symbol>(inverse_permutation_[nodes_[node].middle - 1]));
322 } else {
323 copy_segment_content(nodes_[node].left_child, rank0, rank0 + left,
324 tmp.subspan(0, left), dst.subspan(0, left));
325 }
326 if (nodes_[node].right_child == npos) {
327 std::fill(tmp.begin() + static_cast<long long>(left), tmp.end(),
328 static_cast<Symbol>(inverse_permutation_[nodes_[node].middle]));
329 } else {
330 copy_segment_content(nodes_[node].right_child, rank, rank + right,
331 tmp.subspan(left, right), dst.subspan(left, right));
332 }
333
334 size_t j = 0, k = left;
335 const auto& bit_vector = nodes_[node].bit_vector_data.as_words64();
336 for (size_t i = begin; i < end; i++) {
337 if ((bit_vector[i / 64] >> (i % 64)) & 1) {
338 dst[i - begin] = tmp[k++];
339 } else {
340 dst[i - begin] = tmp[j++];
341 }
342 }
343 }
344
345 static void validate_alphabet_size(std::size_t alphabet_size) {
346 if (alphabet_size != 0 &&
347 alphabet_size - 1 >
348 static_cast<std::size_t>(std::numeric_limits<Symbol>::max())) {
349 throw std::invalid_argument(
350 "Wavelet-tree alphabet does not fit its symbol type");
351 }
352 }
353
354 static std::size_t checked_symbol_index(Symbol symbol,
355 std::size_t alphabet_size) {
356 const std::size_t index = static_cast<std::size_t>(symbol);
357 if (index >= alphabet_size) {
358 throw std::invalid_argument(
359 "Wavelet-tree symbol is outside the alphabet");
360 }
361 return index;
362 }
363
364 template <class ForEachSymbol>
365 void build_from_counts(std::size_t alphabet_size,
366 std::span<const std::size_t> symbol_counts,
367 ForEachSymbol&& for_each_symbol,
368 WaveletTreeBuildType build_type)
369 requires(std::same_as<Storage, AlignedStorage>)
370 {
371 validate_alphabet_size(alphabet_size);
372 if (symbol_counts.size() != alphabet_size) {
373 throw std::invalid_argument(
374 "Wavelet-tree symbol counts must match the alphabet size");
375 }
376 alphabet_size_ = alphabet_size;
377 for (const std::size_t count : symbol_counts) {
378 if (count > std::numeric_limits<std::size_t>::max() - data_size_) {
379 throw std::length_error("Wavelet-tree input is too large");
380 }
381 data_size_ += count;
382 }
383 leaves_.assign(alphabet_size_, npos);
384
385 std::vector<PreWaveletNode> nodes;
386 std::vector<std::size_t> nodes_structure;
387 if (alphabet_size_ != 0) {
388 nodes.reserve(alphabet_size_);
389 nodes_structure.reserve(alphabet_size_);
390
391 if (build_type == WaveletTreeBuildType::Standard) {
392 permutation_.resize(alphabet_size_);
393 inverse_permutation_.resize(alphabet_size_);
394 std::iota(permutation_.begin(), permutation_.end(), 0);
395 std::iota(inverse_permutation_.begin(), inverse_permutation_.end(), 0);
396 nodes_structure.resize(alphabet_size_, npos);
397 } else {
398 struct HuffmanNode {
399 std::size_t size;
400 std::size_t left;
401 std::size_t right;
402 };
403 std::vector<HuffmanNode> huffman_nodes(alphabet_size_, {0, 0, 0});
404 for (std::size_t symbol = 0; symbol < alphabet_size_; ++symbol) {
405 huffman_nodes[symbol].size = symbol_counts[symbol];
406 }
407
408 using QueueElement = std::pair<std::size_t, std::size_t>;
409 std::priority_queue<QueueElement, std::vector<QueueElement>,
410 std::greater<>>
411 queue;
412 for (std::size_t symbol = 0; symbol < alphabet_size_; ++symbol) {
413 queue.emplace(huffman_nodes[symbol].size, symbol);
414 }
415 while (queue.size() >= 2) {
416 const std::size_t right = queue.top().second;
417 queue.pop();
418 const std::size_t left = queue.top().second;
419 queue.pop();
420 huffman_nodes.push_back(
421 {huffman_nodes[left].size + huffman_nodes[right].size, left + 1,
422 right + 1});
423 queue.emplace(huffman_nodes.back().size, huffman_nodes.size() - 1);
424 }
425
426 std::function<std::size_t(std::size_t)> enumerate =
427 [&](std::size_t index) -> std::size_t {
428 const auto& [size, left, right] = huffman_nodes[index];
429 if (left == 0 || right == 0) {
430 permutation_[index] = inverse_permutation_.size();
431 inverse_permutation_.push_back(index);
432 return 1;
433 }
434 const std::size_t node = nodes_structure.size();
435 std::size_t subtree = 0;
436 if (size > 0) {
437 nodes_structure.push_back(0);
438 }
439 subtree += enumerate(left - 1);
440 if (size > 0) {
441 nodes_structure[node] = subtree;
442 }
443 subtree += enumerate(right - 1);
444 return subtree;
445 };
446
447 permutation_.resize(alphabet_size_);
448 inverse_permutation_.reserve(alphabet_size_);
449 enumerate(huffman_nodes.size() - 1);
450 }
451
452 std::vector<std::size_t> prefix_sum(alphabet_size_ + 1);
453 for (std::size_t symbol = 0; symbol < alphabet_size_; ++symbol) {
454 prefix_sum[permutation_[symbol] + 1] = symbol_counts[symbol];
455 }
456 std::partial_sum(prefix_sum.begin(), prefix_sum.end(),
457 prefix_sum.begin());
458 root_ = build_node(
459 0, alphabet_size_, npos,
460 [&](node_index_t node) { return nodes_structure[node]; }, prefix_sum,
461 nodes);
462 }
463
464 std::vector<std::size_t> actual_counts(alphabet_size_);
465 for_each_symbol([&](Symbol symbol) {
466 const std::size_t original = checked_symbol_index(symbol, alphabet_size_);
467 if (actual_counts[original] == std::numeric_limits<std::size_t>::max()) {
468 throw std::length_error("Wavelet-tree symbol count is too large");
469 }
470 ++actual_counts[original];
471 const std::size_t permuted = permutation_[original];
472 for (node_index_t current = root_; current != npos;) {
473 auto& node = nodes[current];
474 const bool go_right = permuted >= node.middle;
475 node.stream.write_bit(go_right);
476 current = go_right ? node.right_child : node.left_child;
477 }
478 });
479 if (!std::ranges::equal(actual_counts, symbol_counts)) {
480 throw std::invalid_argument(
481 "Wavelet-tree emitted symbols do not match their counts");
482 }
483
484 nodes_.reserve(nodes.size());
485 for (auto& node : nodes) {
486 nodes_.emplace_back(std::move(node));
487 }
488 }
489
490 WaveletTreeIndex() = default;
491
492 public:
493 using symbol_type = Symbol;
494
503 std::size_t alphabet_size,
504 std::span<const Symbol> data,
505 const WaveletTreeBuildType build_type = WaveletTreeBuildType::Standard)
506 requires(std::same_as<Storage, AlignedStorage>)
507 {
508 validate_alphabet_size(alphabet_size);
509 std::vector<std::size_t> counts(alphabet_size);
510 for (const Symbol symbol : data) {
511 ++counts[checked_symbol_index(symbol, alphabet_size)];
512 }
513 build_from_counts(
514 alphabet_size, counts,
515 [&](auto&& emit) {
516 for (const Symbol symbol : data) {
517 emit(symbol);
518 }
519 },
520 build_type);
521 }
522
535 template <class ForEachSymbol>
537 std::size_t alphabet_size,
538 std::span<const std::size_t> symbol_counts,
539 ForEachSymbol&& for_each_symbol,
540 const WaveletTreeBuildType build_type = WaveletTreeBuildType::Standard)
541 requires(std::same_as<Storage, AlignedStorage>)
542 {
543 build_from_counts(alphabet_size, symbol_counts,
544 std::forward<ForEachSymbol>(for_each_symbol), build_type);
545 }
546
555 size_t rank_impl(Symbol symbol, size_t pos) const {
556 std::size_t symbol_index = static_cast<std::size_t>(symbol);
557 if (symbol_index >= alphabet_size_) [[unlikely]] {
558 return 0;
559 }
560 symbol_index = permutation_[symbol_index];
561 for (node_index_t current = root_; current != npos;) {
562 const WaveletNode& node = nodes_[current];
563 if (symbol_index < node.middle) {
564 pos = node.data.rank0(pos);
565 current = node.left_child;
566 } else {
567 pos = node.data.rank(pos);
568 current = node.right_child;
569 }
570 }
571 return pos;
572 }
573
582 size_t select_impl(Symbol symbol, size_t rank) const {
583 std::size_t symbol_index = static_cast<std::size_t>(symbol);
584 if (symbol_index >= alphabet_size_ || data_size_ == 0) [[unlikely]] {
585 return data_size_;
586 }
587 symbol_index = permutation_[symbol_index];
588 node_index_t current = leaves_[symbol_index];
589 for (; current != npos; current = nodes_[current].parent) {
590 const WaveletNode& node = nodes_[current];
591 if (symbol_index < node.middle) {
592 rank = node.data.select0(rank) + 1;
593 } else {
594 rank = node.data.select(rank) + 1;
595 }
596 }
597 return rank - 1;
598 }
599
614 std::vector<Symbol> get_segment_impl(size_t begin, size_t end) const {
615 if (alphabet_size_ == 0 || data_size_ == 0 || begin >= end) [[unlikely]] {
616 return {};
617 }
618 const std::size_t length = end - begin;
619 if (root_ == npos) [[unlikely]] {
620 return std::vector<Symbol>(
621 length, static_cast<Symbol>(inverse_permutation_.front()));
622 }
623 if (length > std::vector<Symbol>().max_size() / 2) {
624 throw std::length_error("Wavelet-tree segment is too large");
625 }
626 std::vector<Symbol> result(2 * length);
627 copy_segment_content(root_, begin, end, std::span(result).first(length),
628 std::span(result).subspan(length));
629 result.resize(length);
630 return result;
631 }
632
637 size_t size_impl() const { return data_size_; }
638
645 void serialize_impl(BinaryWriter& writer) const {
646 if (writer.size_bytes() % alignof(std::uint64_t) != 0) {
647 throw std::invalid_argument(
648 "Wavelet-tree serialization requires an aligned writer offset");
649 }
650 const std::size_t artifact_begin = writer.size_bytes();
651 detail::write_magic(writer, kSerializationMagic);
652 writer.write_u32(kSerializationVersion);
653 writer.write_u32(std::numeric_limits<Symbol>::digits);
654 const std::size_t artifact_size_position = writer.write_u64_placeholder();
655
656 writer.write_size(alphabet_size_);
657 writer.write_size(data_size_);
658 writer.write_size(root_);
659 writer.write_size(nodes_.size());
660 for (const WaveletNode& node : nodes_) {
661 node.serialize(writer);
662 }
663 for (const node_index_t leaf : leaves_) {
664 writer.write_size(leaf);
665 }
666 for (const size_t idx : permutation_) {
667 writer.write_size(idx);
668 }
669
670 const std::size_t unpadded_size = writer.size_bytes() - artifact_begin;
671 writer.write_zeros(
672 (sizeof(std::uint64_t) - unpadded_size % sizeof(std::uint64_t)) %
673 sizeof(std::uint64_t));
674 writer.patch_u64(
675 artifact_size_position,
676 static_cast<std::uint64_t>(writer.size_bytes() - artifact_begin));
677 }
678
696 static WaveletTreeIndex deserialize_impl(
697 BinaryReader& reader,
699 requires(std::same_as<Storage, AlignedStorage> ||
700 std::same_as<Storage, ReadOnlyStorageView>)
701 {
702 BinaryReader candidate = reader;
703 if constexpr (std::same_as<Storage, ReadOnlyStorageView>) {
704 if (reinterpret_cast<std::uintptr_t>(candidate.remaining_bytes().data()) %
705 alignof(std::uint64_t) !=
706 0) {
707 throw std::invalid_argument(
708 "Serialized wavelet-tree artifact is not word aligned");
709 }
710 }
711 const std::size_t available_size = candidate.remaining();
712 detail::require_magic(candidate, kSerializationMagic);
713 if (candidate.read_u32() != kSerializationVersion ||
714 candidate.read_u32() != std::numeric_limits<Symbol>::digits) {
715 throw std::invalid_argument(
716 "Incompatible serialized wavelet-tree artifact");
717 }
718 const std::size_t artifact_size = detail::checked_artifact_size(
719 candidate.read_u64(), kSerializationHeaderBytes, available_size);
720 BinaryReader payload =
721 candidate.read_subreader(artifact_size - kSerializationHeaderBytes);
722
723 WaveletTreeIndex result;
724 result.alphabet_size_ = payload.read_size();
725 result.validate_alphabet_size(result.alphabet_size_);
726 result.data_size_ = payload.read_size();
727 result.root_ = payload.read_size();
728 const std::size_t node_count = payload.read_size();
729 const std::vector<WaveletNode> empty_nodes;
730 if (node_count > empty_nodes.max_size()) {
731 throw std::length_error(
732 "Serialized wavelet-tree node count is too large");
733 }
734 constexpr std::size_t kMinimumNodeBytes =
735 4 * sizeof(std::uint64_t) + sizeof(std::uint64_t);
736 if (node_count > payload.remaining() / kMinimumNodeBytes) {
737 throw SerializationError("Truncated serialized wavelet-tree nodes",
738 payload.byte_offset());
739 }
740 result.nodes_.resize(node_count);
741 for (auto& node : result.nodes_) {
742 node = WaveletNode::deserialize(payload, validation);
743 }
744 const std::vector<node_index_t> empty_indices;
745 if (result.alphabet_size_ > empty_indices.max_size() ||
746 result.alphabet_size_ >
747 payload.remaining() / (2 * sizeof(std::uint64_t))) {
748 throw std::length_error("Serialized wavelet-tree alphabet is too large");
749 }
750 result.leaves_.resize(result.alphabet_size_);
751 for (node_index_t& leaf : result.leaves_) {
752 leaf = payload.read_size();
753 }
754 result.permutation_.resize(result.alphabet_size_);
755 for (size_t& index : result.permutation_) {
756 index = payload.read_size();
757 }
758 result.inverse_permutation_.resize(result.alphabet_size_);
759 std::vector<bool> seen(result.alphabet_size_);
760 for (size_t i = 0; i < result.alphabet_size_; i++) {
761 if (result.permutation_[i] >= result.alphabet_size_ ||
762 seen[result.permutation_[i]]) {
763 throw std::invalid_argument(
764 "Invalid serialized wavelet-tree permutation");
765 }
766 seen[result.permutation_[i]] = true;
767 result.inverse_permutation_[result.permutation_[i]] = i;
768 }
769 const auto valid_node_index = [&result](node_index_t index) {
770 return index == npos || index < result.nodes_.size();
771 };
772 if (!valid_node_index(result.root_) ||
773 (result.nodes_.empty() != (result.root_ == npos))) {
774 throw std::invalid_argument("Invalid serialized wavelet-tree root");
775 }
776 for (const node_index_t leaf : result.leaves_) {
777 if (!valid_node_index(leaf)) {
778 throw std::invalid_argument("Invalid serialized wavelet-tree leaf");
779 }
780 }
781 for (const WaveletNode& node : result.nodes_) {
782 if (!valid_node_index(node.parent) ||
783 !valid_node_index(node.left_child) ||
784 !valid_node_index(node.right_child) ||
785 node.data.size() > node.bit_vector_data.size_bits() ||
786 node.middle == 0 || node.middle >= result.alphabet_size_) {
787 throw std::invalid_argument("Invalid serialized wavelet-tree node");
788 }
789 }
790 result.validate_deserialized_topology(validation);
791 if (result.root_ != npos &&
792 result.nodes_[result.root_].data.size() != result.data_size_) {
793 throw std::invalid_argument(
794 "Serialized wavelet-tree root has the wrong length");
795 }
796 payload.require_zero_padding(sizeof(std::uint64_t) - 1);
797 reader = candidate;
798 return result;
799 }
800};
801
802template <WaveletTreeSymbol Symbol>
803using WaveletTree = WaveletTreeIndex<Symbol, AlignedStorage>;
804
805template <WaveletTreeSymbol Symbol>
806using WaveletTreeView = WaveletTreeIndex<Symbol, ReadOnlyStorageView>;
807
808} // namespace pixie
Owning storage with a logical byte size and 64-byte-aligned backing.
Definition aligned.h:41
Bounds-checked reader for canonical little-endian binary data.
Definition serialization.h:526
std::size_t remaining() const noexcept
Return the number of unconsumed bytes.
Definition serialization.h:544
std::span< const std::byte > remaining_bytes() const noexcept
Return all currently unconsumed bytes.
Definition serialization.h:547
BinaryReader read_subreader(std::size_t count)
Read a bounded region as an independent child reader.
Definition serialization.h:615
std::uint64_t read_u64()
Read an unsigned little-endian 64-bit integer.
Definition serialization.h:561
void require_zero_padding(std::size_t maximum)
Consume at most maximum trailing zero-padding bytes.
Definition serialization.h:633
std::uint32_t read_u32()
Read an unsigned little-endian 32-bit integer.
Definition serialization.h:558
std::size_t byte_offset() const noexcept
Return the current byte offset in the outermost input.
Definition serialization.h:541
std::size_t read_size()
Read an unsigned 64-bit size and convert it to size_t.
Definition serialization.h:587
Bounded-buffer writer for canonical little-endian binary data.
Definition serialization.h:198
void write_u32(std::uint32_t value)
Write an unsigned 32-bit integer in little-endian order.
Definition serialization.h:258
std::size_t size_bytes() const noexcept
Return the logical number of bytes written.
Definition serialization.h:244
void write_zeros(std::size_t count)
Append count zero bytes.
Definition serialization.h:328
void patch_u64(std::size_t position, std::uint64_t value)
Replace an existing 64-bit field with value.
Definition serialization.h:373
std::size_t write_u64_placeholder()
Write a zero 64-bit field and return its byte position.
Definition serialization.h:363
void write_u64(std::uint64_t value)
Write an unsigned 64-bit integer in little-endian order.
Definition serialization.h:261
void write_size(std::size_t value)
Write a platform size as an unsigned 64-bit integer.
Definition serialization.h:287
Builder for a packed LSB-first bit sequence.
Definition packed_bit_builder.h:20
std::uint64_t select(std::size_t rank) const
Return the position of the rank-th one bit.
Definition rank_select.h:72
std::uint64_t select0(std::size_t rank) const
Return the position of the rank-th zero bit.
Definition rank_select.h:81
std::uint64_t rank0(std::size_t end_position) const
Count zero bits in the prefix [0, end_position).
Definition rank_select.h:62
std::size_t size() const
Return the number of valid bits.
Definition rank_select.h:31
std::uint64_t rank(std::size_t end_position) const
Count one bits in the prefix [0, end_position).
Definition rank_select.h:53
Rank/select support over an external packed bit sequence.
Definition support.h:58
CRTP facade for optional binary serialization and deserialization.
Definition serialization.h:693
static WaveletTreeIndex< Symbol, AlignedStorage > deserialize(BinaryReader &reader, Context &&... context)
Definition serialization.h:710
void serialize(BinaryWriter &writer) const
Definition serialization.h:696
Error raised while decoding malformed serialized data.
Definition serialization.h:45
CRTP facade for wavelet-tree queries.
Definition wavelet_tree.h:36
std::size_t rank(Symbol symbol, std::size_t end_position) const
Definition wavelet_tree.h:56
void serialize_impl(BinaryWriter &writer) const
Write a versioned canonical little-endian wavelet-tree artifact.
Definition index.h:645
static WaveletTreeIndex deserialize_impl(BinaryReader &reader, DeserializationValidation validation=DeserializationValidation::kQuick)
Restore one checked wavelet-tree artifact.
Definition index.h:696
size_t size_impl() const
Definition index.h:637
WaveletTreeIndex(std::size_t alphabet_size, std::span< const Symbol > data, const WaveletTreeBuildType build_type=WaveletTreeBuildType::Standard)
Construct from a contiguous sequence of typed symbols.
Definition index.h:502
std::vector< Symbol > get_segment_impl(size_t begin, size_t end) const
Accumulates the original data segment.
Definition index.h:614
WaveletTreeIndex(std::size_t alphabet_size, std::span< const std::size_t > symbol_counts, ForEachSymbol &&for_each_symbol, const WaveletTreeBuildType build_type=WaveletTreeBuildType::Standard)
Construct from counts and one streamed pass over the symbols.
Definition index.h:536
size_t select_impl(Symbol symbol, size_t rank) const
Select the position of the rank-th specified symbol (1-indexed)
Definition index.h:582
size_t rank_impl(Symbol symbol, size_t pos) const
Rank of specified symbol up to position pos (exclusive)
Definition index.h:555
A concrete CRTP implementation of StorageBase.
Definition storage.h:290
Unsigned code-unit type indexed by a wavelet tree.
Definition wavelet_tree.h:23
DeserializationValidation
Validation strength used while restoring serialized indexes.
Definition serialization.h:28
@ kQuick
Check framing, dimensions, references, and other conditions needed for memory-safe terminating querie...
Definition serialization.h:33
@ kFull
Additionally authenticate all source-derived metadata against the supplied source contents.
Definition serialization.h:39
Common interface for wavelet-tree indexes.
WaveletTreeBuildType
Construction strategy for a wavelet-tree implementation.
Definition wavelet_tree.h:19