56class RankSelectSupport
71 constexpr static size_t kWordSize = 64;
72 constexpr static size_t kSuperBlockRankIntSize = 64;
73 constexpr static size_t kBasicBlockRankIntSize = 16;
74 constexpr static size_t kBasicBlockSize = 512;
75 constexpr static size_t kWordsPerBlock = 8;
76 constexpr static size_t kSuperBlockSize = 65536;
77 constexpr static size_t kBlocksPerSuperBlock = 128;
78 constexpr static size_t kSelectSampleFrequency = 16384;
80 alignas(64)
inline static constexpr std::array<uint64_t, 8> kDeltaSuper = [] {
81 std::array<uint64_t, 8> result{};
82 for (
size_t i = 0; i < result.size(); ++i) {
83 result[i] = i * kSuperBlockSize;
87 alignas(64)
inline static constexpr std::array<uint16_t, 32> kDeltaBasic =
89 std::array<uint16_t, 32> result{};
90 for (
size_t i = 0; i < result.size(); ++i) {
91 result[i] =
static_cast<uint16_t
>(i * kBasicBlockSize);
96 MetadataStorage super_block_rank_;
97 MetadataStorage basic_block_rank_;
98 MetadataStorage select_samples_;
99 ReadOnlyStorageView source_storage_;
100 std::span<const uint64_t> bits_;
102 size_t padded_size_{};
104 size_t select1_sample_begin_{};
105 size_t select1_sample_count_{};
106 size_t select0_sample_begin_{};
107 size_t select0_sample_count_{};
109 bool select0_samples_reversed_ =
false;
112 return (
static_cast<uint8_t
>(support) &
113 static_cast<uint8_t
>(SelectSupport::kSelect1)) != 0;
117 return (
static_cast<uint8_t
>(support) &
118 static_cast<uint8_t
>(SelectSupport::kSelect0)) != 0;
121 static MetadataStorage deserialize_metadata_storage(BinaryReader& reader)
122 requires(std::same_as<MetadataStorage, AlignedStorage> ||
123 std::same_as<MetadataStorage, ReadOnlyStorageView>)
125 const std::size_t
size = reader.read_size();
126 const std::span<const std::byte> bytes = reader.read_bytes(
size);
127 if constexpr (std::same_as<MetadataStorage, ReadOnlyStorageView>) {
128 if (
reinterpret_cast<std::uintptr_t
>(bytes.data()) %
129 alignof(std::uint64_t) !=
131 throw std::invalid_argument(
132 "Serialized rank/select storage is not word aligned");
134 return ReadOnlyStorageView(bytes);
136 if (
size > std::numeric_limits<std::size_t>::max() / 8) {
137 throw std::length_error(
"Serialized rank/select storage is too large");
139 AlignedStorage result(
size * 8);
140 std::ranges::copy(bytes, result.writable_bytes().begin());
145 void validate_deserialized_state(DeserializationValidation validation)
const {
146 const std::size_t required_words =
147 num_bits_ == 0 ? 0 : 1 + (num_bits_ - 1) / kWordSize;
148 if (required_words > bits_.size()) {
149 throw std::invalid_argument(
150 "RankSelectSupport source bit span is too small");
152 if (required_words > std::numeric_limits<std::size_t>::max() / kWordSize) {
153 throw std::length_error(
"RankSelectSupport padded size is too large");
155 if (padded_size_ != required_words * kWordSize || max_rank_ > num_bits_) {
156 throw std::invalid_argument(
157 "Invalid serialized rank/select size metadata");
160 const auto support_value =
static_cast<std::uint8_t
>(select_support_);
161 if (support_value >
static_cast<std::uint8_t
>(SelectSupport::kBoth) ||
162 select0_samples_reversed_) {
163 throw std::invalid_argument(
164 "Invalid serialized rank/select configuration");
167 const std::size_t data_superblocks = data_superblock_count();
168 const std::size_t super_entries = data_superblocks + 1;
169 const std::size_t expected_super_bytes =
170 super_entries *
sizeof(std::uint64_t);
171 const std::size_t expected_basic_bytes =
172 stored_basicblock_count() *
sizeof(std::uint16_t);
173 if (super_block_rank_.size_bytes() != expected_super_bytes ||
174 basic_block_rank_.size_bytes() != expected_basic_bytes ||
175 select_samples_.size_bytes() %
sizeof(std::uint64_t) != 0) {
176 throw std::invalid_argument(
177 "Invalid serialized rank/select storage sizes");
180 const std::size_t sample_count =
181 select_samples_.size_bytes() /
sizeof(std::uint64_t);
182 const auto samples_fit = [sample_count](std::size_t begin,
184 return begin <= sample_count && count <= sample_count - begin;
186 if (!samples_fit(select1_sample_begin_, select1_sample_count_) ||
187 !samples_fit(select0_sample_begin_, select0_sample_count_) ||
188 (builds_select1(select_support_) != (select1_sample_count_ != 0)) ||
189 (builds_select0(select_support_) != (select0_sample_count_ != 0))) {
190 throw std::invalid_argument(
191 "Invalid serialized rank/select sample metadata");
193 if (validation == DeserializationValidation::kFull) {
194 validate_full_source_metadata();
198 const auto samples = select_samples_.as_words64();
199 const auto sample_values_fit = [samples, data_superblocks](
200 std::size_t begin, std::size_t count) {
201 return std::ranges::all_of(samples.subspan(begin, count),
202 [data_superblocks](std::uint64_t sample) {
203 return data_superblocks == 0
205 : sample < data_superblocks;
208 if (!sample_values_fit(select1_sample_begin_, select1_sample_count_) ||
209 !sample_values_fit(select0_sample_begin_, select0_sample_count_)) {
210 throw std::invalid_argument(
211 "Serialized rank/select sample references an invalid super block");
215 void validate_full_source_metadata()
const {
216 const auto super_blocks = super_block_rank_.as_words64();
217 const auto basic_blocks = basic_block_rank_.as_words16();
218 const auto samples = select_samples_.as_words64();
219 std::size_t next_select1 = select1_sample_begin_;
220 std::size_t next_select0 = select0_sample_begin_;
221 const std::size_t select1_end =
222 select1_sample_begin_ + select1_sample_count_;
223 const std::size_t select0_end =
224 select0_sample_begin_ + select0_sample_count_;
226 const auto check_initial_sample = [&](
bool enabled, std::size_t& next,
229 if (next == end || samples[next] != 0) {
230 throw std::invalid_argument(
"Invalid serialized rank/select samples");
235 check_initial_sample(builds_select1(select_support_), next_select1,
237 check_initial_sample(builds_select0(select_support_), next_select0,
240 std::uint64_t rank1 = 0;
241 std::uint64_t
rank0 = 0;
242 std::uint64_t super_rank = 0;
243 std::uint64_t basic_rank = 0;
244 std::uint64_t select1_milestone = kSelectSampleFrequency;
245 std::uint64_t select0_milestone = kSelectSampleFrequency;
246 const std::size_t metadata_word_count =
247 basic_blocks.size() * kWordsPerBlock;
248 for (std::size_t word_index = 0; word_index < metadata_word_count;
250 const std::size_t bit_position = word_index * kWordSize;
251 if (bit_position % kSuperBlockSize == 0) {
252 super_rank += basic_rank;
253 if (super_blocks[bit_position / kSuperBlockSize] != super_rank) {
254 throw std::invalid_argument(
255 "Serialized rank/select super-block ranks disagree with source");
259 if (bit_position % kBasicBlockSize == 0 &&
260 basic_blocks[bit_position / kBasicBlockSize] != basic_rank) {
261 throw std::invalid_argument(
262 "Serialized rank/select basic-block ranks disagree with source");
265 if (word_index >= logical_word_count()) {
268 const std::uint64_t word = logical_word(word_index);
269 const std::size_t word_bits = logical_word_bits(word_index);
270 const std::uint64_t ones = std::popcount(word);
271 const std::uint64_t zeros = word_bits - ones;
272 if (builds_select1(select_support_) &&
273 rank1 + ones >= select1_milestone) {
274 const std::size_t position =
275 word_index * kWordSize +
276 select_64(word, select1_milestone - rank1 - 1);
277 const std::uint64_t expected = position / kSuperBlockSize;
278 if (next_select1 == select1_end || samples[next_select1] != expected) {
279 throw std::invalid_argument(
280 "Serialized rank/select one samples disagree with source");
283 select1_milestone += kSelectSampleFrequency;
285 if (builds_select0(select_support_) &&
286 rank0 + zeros >= select0_milestone) {
287 const std::uint64_t zero_word =
288 ~word & first_bits_mask(
static_cast<std::uint32_t
>(word_bits));
289 const std::size_t position =
290 word_index * kWordSize +
291 select_64(zero_word, select0_milestone -
rank0 - 1);
292 const std::uint64_t expected = position / kSuperBlockSize;
293 if (next_select0 == select0_end || samples[next_select0] != expected) {
294 throw std::invalid_argument(
295 "Serialized rank/select zero samples disagree with source");
298 select0_milestone += kSelectSampleFrequency;
305 if (super_blocks.back() != max_rank_ ||
306 super_rank + basic_rank != max_rank_ || rank1 != max_rank_ ||
307 next_select1 != select1_end || next_select0 != select0_end) {
308 throw std::invalid_argument(
309 "Serialized rank/select totals disagree with source");
313 size_t logical_word_count()
const {
314 return (num_bits_ + kWordSize - 1) / kWordSize;
317 size_t data_superblock_count()
const {
318 return num_bits_ == 0 ? 0 : 1 + (num_bits_ - 1) / kSuperBlockSize;
321 template <StorageImplementation SourceStorage>
322 static ReadOnlyStorageView complete_word_view(
323 const SourceStorage& source_storage) {
324 if constexpr (
requires { source_storage.padded_view(); }) {
325 return source_storage.padded_view();
327 return source_storage.view();
331 size_t stored_basicblock_count()
const {
332 if (num_bits_ == 0) {
335 const size_t data_basicblocks = 1 + (num_bits_ - 1) / kBasicBlockSize;
336 return (data_basicblocks + 31) / 32 * 32;
339 size_t logical_word_bits(
size_t word_index)
const {
340 const size_t begin = word_index * kWordSize;
341 if (begin >= num_bits_) {
344 return std::min(kWordSize, num_bits_ - begin);
347 uint64_t logical_word(
size_t word_index)
const {
348 if (word_index >= bits_.size()) {
351 const size_t bits = logical_word_bits(word_index);
355 if (bits == kWordSize) {
356 return bits_[word_index];
358 return bits_[word_index] & first_bits_mask(bits);
361 uint64_t rank_in_basic_block(
size_t basic_block,
size_t offset)
const {
365 const size_t first_word = basic_block * kWordsPerBlock;
366 if (first_word + kWordsPerBlock <= bits_.size()) {
367 return rank_512(&bits_[first_word], offset);
371 size_t word_index = first_word;
372 while (offset >= kWordSize) {
373 result += std::popcount(logical_word(word_index));
379 std::popcount(logical_word(word_index) & first_bits_mask(offset));
384 uint64_t select_in_words(
size_t first_word,
size_t rank,
bool value)
const {
385 const size_t first_bit = first_word * kWordSize;
386 if (first_bit + kBasicBlockSize <= num_bits_ &&
387 first_word + kWordsPerBlock <= bits_.size()) {
388 return value ? first_bit + select_512(&bits_[first_word],
rank - 1)
389 : first_bit + select0_512(&bits_[first_word],
rank - 1);
392 for (
size_t word_index = first_word; word_index < logical_word_count();
394 const uint64_t word = logical_word(word_index);
395 const uint64_t candidates =
397 : (~word & first_bits_mask(logical_word_bits(word_index)));
398 const size_t count = std::popcount(candidates);
403 return word_index * kWordSize + select_64(candidates,
rank - 1);
408 static size_t select_sample_count_for_rank(
size_t rank_count) {
409 return 1 + rank_count / kSelectSampleFrequency;
412 static size_t select_sample_upper_bound(
size_t bit_count) {
413 return select_sample_count_for_rank(bit_count);
416 struct SelectSampleWriter {
417 std::span<uint64_t> words;
421 bool enabled =
false;
422 bool reversed =
false;
424 SelectSampleWriter() =
default;
426 SelectSampleWriter(std::span<uint64_t> words,
435 reversed(reversed) {}
437 void append(uint64_t sample) {
441 if (count >= capacity) [[unlikely]] {
442 throw std::invalid_argument(
443 "RankSelectSupport one_count hint is inconsistent with input bits");
445 words[next] = sample;
457 struct SelectSampleWriters {
458 SelectSampleWriter ones;
459 SelectSampleWriter zeros;
460 bool shrink_after_build =
false;
463 SelectSampleWriters initialize_select_sample_writers(
466 std::optional<size_t> one_count) {
467 select1_sample_begin_ = 0;
468 select1_sample_count_ = 0;
469 select0_sample_begin_ = 0;
470 select0_sample_count_ = 0;
471 select0_samples_reversed_ =
false;
472 select_samples_.resize(0);
474 SelectSampleWriters writers;
475 if (!need_select1 && !need_select0) {
479 const std::optional<size_t> zero_count =
480 one_count ? std::optional<size_t>(num_bits_ - *one_count)
482 if (need_select1 && need_select0) {
483 const size_t one_sample_capacity =
484 one_count ? select_sample_count_for_rank(*one_count)
485 : 2 + num_bits_ / kSelectSampleFrequency;
486 const size_t zero_sample_capacity =
487 zero_count ? select_sample_count_for_rank(*zero_count)
488 : 2 + num_bits_ / kSelectSampleFrequency;
489 const size_t total_samples =
490 one_count ? one_sample_capacity + zero_sample_capacity
491 : 2 + num_bits_ / kSelectSampleFrequency;
492 select_samples_.resize(total_samples * kWordSize);
493 auto samples = select_samples_.writable_words64();
494 select1_sample_begin_ = 0;
495 select0_samples_reversed_ =
true;
497 SelectSampleWriter(samples, 0, one_sample_capacity,
true,
false);
498 writers.zeros = SelectSampleWriter(samples, total_samples - 1,
499 zero_sample_capacity,
true,
true);
500 writers.ones.append(0);
501 writers.zeros.append(0);
505 const size_t sample_capacity =
506 need_select1 ? (one_count ? select_sample_count_for_rank(*one_count)
507 : select_sample_upper_bound(num_bits_))
508 : (zero_count ? select_sample_count_for_rank(*zero_count)
509 : select_sample_upper_bound(num_bits_));
510 select_samples_.resize(sample_capacity * kWordSize);
511 auto samples = select_samples_.writable_words64();
512 writers.shrink_after_build = !one_count;
514 select1_sample_begin_ = 0;
516 SelectSampleWriter(samples, 0, sample_capacity,
true,
false);
517 writers.ones.append(0);
519 select0_sample_begin_ = 0;
521 SelectSampleWriter(samples, 0, sample_capacity,
true,
false);
522 writers.zeros.append(0);
527 void finalize_select_sample_writers(SelectSampleWriters writers) {
528 select1_sample_count_ = writers.ones.count;
529 select0_sample_count_ = writers.zeros.count;
530 if (writers.zeros.reversed) {
531 select0_sample_begin_ = writers.zeros.next + 1;
532 auto zero_samples = writers.zeros.words.subspan(select0_sample_begin_,
533 select0_sample_count_);
534 std::reverse(zero_samples.begin(), zero_samples.end());
535 select0_samples_reversed_ =
false;
537 if (writers.shrink_after_build) {
538 const size_t sample_count = select1_sample_count_ != 0
539 ? select1_sample_count_
540 : select0_sample_count_;
541 select_samples_.resize(sample_count * kWordSize);
542 select_samples_.shrink_to_fit();
546 uint64_t select1_sample(
size_t sample_index)
const {
547 auto samples = select_samples_.as_words64();
548 return samples[select1_sample_begin_ + sample_index];
551 uint64_t select0_sample(
size_t sample_index)
const {
552 auto samples = select_samples_.as_words64();
553 if (select0_samples_reversed_) {
554 return samples[select0_sample_begin_ + select0_sample_count_ - 1 -
557 return samples[select0_sample_begin_ + sample_index];
564 std::optional<size_t> one_count) {
565 select_support_ = support;
566 const size_t data_superblocks = data_superblock_count();
571 const size_t num_basicblocks = stored_basicblock_count();
572 super_block_rank_.resize((data_superblocks + 1) * 64);
573 basic_block_rank_.resize(num_basicblocks * 16);
575 auto super_block_rank = super_block_rank_.writable_words64();
576 auto basic_block_rank = basic_block_rank_.writable_words16();
578 const bool need_select1 = builds_select1(support);
579 const bool need_select0 = builds_select0(support);
580 if (one_count && *one_count > num_bits_) {
581 throw std::invalid_argument(
582 "RankSelectSupport one_count hint cannot exceed num_bits");
584 auto select_writers =
585 initialize_select_sample_writers(need_select1, need_select0, one_count);
587 uint64_t super_block_sum = 0;
588 uint64_t basic_block_sum = 0;
589 uint64_t milestone = kSelectSampleFrequency;
590 uint64_t milestone0 = kSelectSampleFrequency;
594 for (
size_t i = 0; i / kBasicBlockSize < basic_block_rank.size();
596 if (i % kSuperBlockSize == 0) {
597 super_block_sum += basic_block_sum;
598 super_block_rank[i / kSuperBlockSize] = super_block_sum;
601 if (i % kBasicBlockSize == 0) {
602 basic_block_rank[i / kBasicBlockSize] =
603 static_cast<uint16_t
>(basic_block_sum);
605 if (i / kWordSize < logical_word_count()) {
606 const size_t word_index = i / kWordSize;
607 const uint64_t word = logical_word(word_index);
608 const size_t word_bits = logical_word_bits(word_index);
609 const uint64_t ones = std::popcount(word);
610 const uint64_t zeros = word_bits - ones;
611 if (need_select1 &&
rank + ones >= milestone) {
612 const auto pos = select_64(word, milestone -
rank - 1);
615 select_writers.ones.append((64 * word_index + pos) / kSuperBlockSize);
616 milestone += kSelectSampleFrequency;
618 if (need_select0 &&
rank0 + zeros >= milestone0) {
619 const uint64_t zero_word = ~word & first_bits_mask(word_bits);
620 const auto pos = select_64(zero_word, milestone0 -
rank0 - 1);
621 select_writers.zeros.append((64 * word_index + pos) /
623 milestone0 += kSelectSampleFrequency;
625 basic_block_sum += ones;
630 max_rank_ = super_block_sum + basic_block_sum;
631 super_block_rank[data_superblocks] = max_rank_;
632 finalize_select_sample_writers(select_writers);
640 uint64_t find_superblock(uint64_t
rank)
const {
641 auto super_block_rank = super_block_rank_.as_words64();
643 uint64_t left = select1_sample(
rank / kSelectSampleFrequency);
645 while (left + 7 < super_block_rank.size()) {
646 auto len = lower_bound_8x64(&super_block_rank[left],
rank);
648 return left + len - 1;
652 if (left + 3 < super_block_rank.size()) {
653 auto len = lower_bound_4x64(&super_block_rank[left],
rank);
655 return left + len - 1;
659 while (left < super_block_rank.size() && super_block_rank[left] <
rank) {
670 uint64_t find_superblock_zeros(uint64_t
rank0)
const {
671 auto super_block_rank = super_block_rank_.as_words64();
673 uint64_t left = select0_sample(
rank0 / kSelectSampleFrequency);
675 while (left + 7 < super_block_rank.size()) {
677 lower_bound_delta_8x64(&super_block_rank[left],
rank0,
678 kDeltaSuper.data(), kSuperBlockSize * left);
680 return left + len - 1;
684 if (left + 3 < super_block_rank.size()) {
686 lower_bound_delta_4x64(&super_block_rank[left],
rank0,
687 kDeltaSuper.data(), kSuperBlockSize * left);
689 return left + len - 1;
693 while (left < super_block_rank.size() &&
694 kSuperBlockSize * left - super_block_rank[left] <
rank0) {
711 uint64_t find_basicblock(uint16_t local_rank, uint64_t s_block)
const {
712 auto basic_block_rank = basic_block_rank_.as_words16();
713 const size_t block_begin = kBlocksPerSuperBlock * s_block;
714 const size_t block_count =
715 std::min(kBlocksPerSuperBlock, basic_block_rank.size() - block_begin);
717 for (
size_t pos = 0; pos < block_count; pos += 32) {
719 lower_bound_32x16(&basic_block_rank[block_begin + pos], local_rank);
721 return block_begin + pos + count - 1;
724 return block_begin + block_count - 1;
738 uint64_t find_basicblock_zeros(uint16_t local_rank0, uint64_t s_block)
const {
739 auto basic_block_rank = basic_block_rank_.as_words16();
740 const size_t block_begin = kBlocksPerSuperBlock * s_block;
741 const size_t block_count =
742 std::min(kBlocksPerSuperBlock, basic_block_rank.size() - block_begin);
743 for (
size_t pos = 0; pos < block_count; pos += 32) {
744 auto count = lower_bound_delta_32x16(&basic_block_rank[block_begin + pos],
745 local_rank0, kDeltaBasic.data(),
746 kBasicBlockSize * pos);
748 return block_begin + pos + count - 1;
751 return block_begin + block_count - 1;
771 uint64_t find_basicblock_is(uint16_t local_rank, uint64_t s_block)
const {
772 auto super_block_rank = super_block_rank_.as_words64();
773 auto basic_block_rank = basic_block_rank_.as_words16();
774 const size_t block_begin = kBlocksPerSuperBlock * s_block;
775 const size_t block_count =
776 std::min(kBlocksPerSuperBlock, basic_block_rank.size() - block_begin);
777 const size_t last_group = block_count - 32;
779 auto lower = super_block_rank[s_block];
780 auto upper = super_block_rank[s_block + 1];
782 uint64_t pos = block_count * local_rank / (upper - lower);
783 pos = pos + 16 < 32 ? 0 : (pos - 16);
784 pos = std::min<uint64_t>(pos, last_group);
785 while (pos < last_group) {
787 lower_bound_32x16(&basic_block_rank[block_begin + pos], local_rank);
789 return find_basicblock(local_rank, s_block);
792 return block_begin + pos + count - 1;
798 lower_bound_32x16(&basic_block_rank[block_begin + pos], local_rank);
800 return find_basicblock(local_rank, s_block);
802 return block_begin + pos + count - 1;
814 uint64_t find_basicblock_is_zeros(uint16_t local_rank0,
815 uint64_t s_block)
const {
816 auto super_block_rank = super_block_rank_.as_words64();
817 auto basic_block_rank = basic_block_rank_.as_words16();
818 const size_t block_begin = kBlocksPerSuperBlock * s_block;
819 const size_t block_count =
820 std::min(kBlocksPerSuperBlock, basic_block_rank.size() - block_begin);
821 const size_t last_group = block_count - 32;
823 auto lower = kSuperBlockSize * s_block - super_block_rank[s_block];
825 kSuperBlockSize * (s_block + 1) - super_block_rank[s_block + 1];
827 uint64_t interpolation = block_count * local_rank0 / (upper - lower);
831 const uint64_t block_offset =
832 std::min<uint64_t>(interpolation, block_count - 1);
833 const uint64_t block = block_begin + block_offset;
834 const uint64_t zero_before =
835 kBasicBlockSize * block_offset - basic_block_rank[block];
836 const uint64_t zero_after = block_offset + 1 == block_count
838 : kBasicBlockSize * (block_offset + 1) -
839 basic_block_rank[block + 1];
840 if (zero_before < local_rank0 && local_rank0 <= zero_after) {
844 uint64_t pos = interpolation;
845 pos = pos + 16 < 32 ? 0 : (pos - 16);
846 pos = std::min<uint64_t>(pos, last_group);
847 while (pos < last_group) {
848 auto count = lower_bound_delta_32x16(&basic_block_rank[block_begin + pos],
849 local_rank0, kDeltaBasic.data(),
850 kBasicBlockSize * pos);
852 return find_basicblock_zeros(local_rank0, s_block);
855 return block_begin + pos + count - 1;
860 auto count = lower_bound_delta_32x16(&basic_block_rank[block_begin + pos],
861 local_rank0, kDeltaBasic.data(),
862 kBasicBlockSize * pos);
864 return find_basicblock_zeros(local_rank0, s_block);
866 return block_begin + pos + count - 1;
870 RankSelectSupport() =
default;
871 RankSelectSupport(
const RankSelectSupport&) =
default;
872 RankSelectSupport(RankSelectSupport&&) noexcept = default;
873 RankSelectSupport& operator=(const RankSelectSupport&) = default;
874 RankSelectSupport& operator=(RankSelectSupport&&) noexcept = default;
876#ifdef PIXIE_DIAGNOSTICS
877 struct DiagnosticsBytes {
878 size_t source_bit_sequence_bytes = 0;
879 size_t super_block_rank_bytes = 0;
880 size_t basic_block_rank_bytes = 0;
881 size_t select1_samples_bytes = 0;
882 size_t select0_samples_bytes = 0;
883 size_t total_bytes = 0;
889 DiagnosticsBytes diagnostics_bytes()
const {
890 DiagnosticsBytes result;
891 result.source_bit_sequence_bytes = (num_bits_ + 7) / 8;
892 result.super_block_rank_bytes = super_block_rank_.as_bytes().size();
893 result.basic_block_rank_bytes = basic_block_rank_.as_bytes().size();
894 result.select1_samples_bytes = select1_sample_count_ *
sizeof(uint64_t);
895 result.select0_samples_bytes = select0_sample_count_ *
sizeof(uint64_t);
896 result.total_bytes = result.super_block_rank_bytes +
897 result.basic_block_rank_bytes +
898 select_samples_.as_bytes().size();
905 void memory_report()
const {
906 const auto diagnostics = diagnostics_bytes();
907 const double source_bytes =
908 static_cast<double>(diagnostics.source_bit_sequence_bytes);
909 const auto log_bytes = [&](std::string_view label,
size_t bytes) {
910 const double percentage =
911 source_bytes > 0.0 ? 100.0 *
static_cast<double>(bytes) / source_bytes
913 spdlog::info(
"RankSelectSupport {}: {} bytes ({:.2f}% of source)", label,
916 log_bytes(
"source_bit_sequence", diagnostics.source_bit_sequence_bytes);
917 log_bytes(
"super_block_rank", diagnostics.super_block_rank_bytes);
918 log_bytes(
"basic_block_rank", diagnostics.basic_block_rank_bytes);
919 log_bytes(
"select1_samples", diagnostics.select1_samples_bytes);
920 log_bytes(
"select0_samples", diagnostics.select0_samples_bytes);
921 log_bytes(
"total", diagnostics.total_bytes);
937 std::optional<size_t> one_count = std::nullopt)
938 : source_storage_(source_storage),
939 bits_(source_storage_.as_words64()),
940 num_bits_(std::min(num_bits, bits_.
size() * kWordSize)),
941 padded_size_(((num_bits_ + kWordSize - 1) / kWordSize) * kWordSize) {
942 build_rank_select(select_support, one_count);
953 std::span<const uint64_t> source_words,
956 std::optional<size_t> one_count = std::nullopt)
971 template <StorageImplementation SourceStorage>
973 const SourceStorage& source_storage,
976 std::optional<size_t> one_count = std::nullopt)
977 : RankSelectSupport(complete_word_view(source_storage),
978 std::min(num_bits, source_storage.size_bits()),
1004 requires requires(const MetadataStorage& storage) {
1005 storage.allocated_bytes();
1008 return sizeof(*this) + super_block_rank_.allocated_bytes() +
1009 basic_block_rank_.allocated_bytes() +
1010 select_samples_.allocated_bytes();
1019 size_t word_idx = pos / kWordSize;
1020 size_t bit_off = pos % kWordSize;
1022 return (bits_[word_idx] >> bit_off) & 1;
1032 if (pos >= num_bits_) [[unlikely]] {
1036 auto super_block_rank = super_block_rank_.as_words64();
1037 auto basic_block_rank = basic_block_rank_.as_words16();
1039 uint64_t b_block = pos / kBasicBlockSize;
1040 uint64_t s_block = pos / kSuperBlockSize;
1042 uint64_t result = super_block_rank[s_block] + basic_block_rank[b_block];
1044 result += rank_in_basic_block(b_block, pos - (b_block * kBasicBlockSize));
1055 if (
rank == 0) [[unlikely]] {
1061 if (
rank > max_rank_) [[unlikely]] {
1064 auto super_block_rank = super_block_rank_.as_words64();
1065 auto basic_block_rank = basic_block_rank_.as_words16();
1067 uint64_t s_block = find_superblock(
rank);
1068 rank -= super_block_rank[s_block];
1069 auto pos = find_basicblock_is(
rank, s_block);
1070 rank -= basic_block_rank[pos];
1071 return select_in_words(pos * kWordsPerBlock,
rank,
true);
1082 if (
rank0 == 0) [[unlikely]] {
1088 if (
rank0 > num_bits_ - max_rank_) [[unlikely]] {
1091 auto super_block_rank = super_block_rank_.as_words64();
1092 auto basic_block_rank = basic_block_rank_.as_words16();
1094 uint64_t s_block = find_superblock_zeros(
rank0);
1095 rank0 -= kSuperBlockSize * s_block - super_block_rank[s_block];
1096 auto pos = find_basicblock_is_zeros(
rank0, s_block);
1097 auto pos_in_super_block = pos & (kBlocksPerSuperBlock - 1);
1098 rank0 -= kBasicBlockSize * pos_in_super_block - basic_block_rank[pos];
1099 return select_in_words(pos * kWordsPerBlock,
rank0,
false);
1116 writer.
write_u32(
static_cast<std::uint32_t
>(select_support_));
1117 writer.
write_u32(
static_cast<std::uint32_t
>(select0_samples_reversed_));
1118 super_block_rank_.serialize(writer);
1119 basic_block_rank_.serialize(writer);
1120 select_samples_.serialize(writer);
1142 std::span<const uint64_t> source_bits,
1144 requires(std::same_as<MetadataStorage, AlignedStorage> ||
1145 std::same_as<MetadataStorage, ReadOnlyStorageView>)
1148 RankSelectSupport result;
1150 result.bits_ = result.source_storage_.
as_words64();
1151 result.num_bits_ = candidate.
read_size();
1152 result.padded_size_ = candidate.
read_size();
1153 result.max_rank_ = candidate.
read_size();
1154 result.select1_sample_begin_ = candidate.
read_size();
1155 result.select1_sample_count_ = candidate.
read_size();
1156 result.select0_sample_begin_ = candidate.
read_size();
1157 result.select0_sample_count_ = candidate.
read_size();
1158 const std::uint32_t support = candidate.
read_u32();
1159 if (support >
static_cast<std::uint32_t
>(SelectSupport::kBoth)) {
1160 throw std::invalid_argument(
1161 "Invalid serialized rank/select configuration");
1163 result.select_support_ =
static_cast<SelectSupport>(support);
1164 const std::uint32_t reversed = candidate.
read_u32();
1166 throw std::invalid_argument(
1167 "Invalid serialized rank/select boolean value");
1169 result.select0_samples_reversed_ = reversed != 0;
1170 result.super_block_rank_ = deserialize_metadata_storage(candidate);
1171 result.basic_block_rank_ = deserialize_metadata_storage(candidate);
1172 result.select_samples_ = deserialize_metadata_storage(candidate);
1173 result.validate_deserialized_state(validation);