Pixie
Loading...
Searching...
No Matches
orzo.h
1#pragma once
2
3#ifndef PIXIE_ORZO_SUPPORT
4#error "OrzoRankSelectSupport requires the optional BMI/BMI2-enabled backend"
5#endif
6
7#include <orzo/orzo.h>
8#include <pixie/rank_select.h>
9
10#include <algorithm>
11#include <bit>
12#include <cstddef>
13#include <cstdint>
14#include <limits>
15#include <optional>
16#include <span>
17#include <stdexcept>
18#include <vector>
19
20namespace pixie {
21
35class OrzoRankSelectSupport : public RankSelectBase<OrzoRankSelectSupport> {
36 public:
44 explicit OrzoRankSelectSupport(std::span<const std::uint64_t> source_words,
45 std::size_t num_bits) {
46 num_bits_ = num_bits;
47 if (source_words.size() <= std::numeric_limits<std::size_t>::max() / 64) {
48 num_bits_ = std::min(num_bits_, source_words.size() * 64);
49 }
50 if (num_bits_ == 0) {
51 return;
52 }
53 if (num_bits_ >
54 std::numeric_limits<std::size_t>::max() - (kLowerBits - 1)) {
55 throw std::length_error("Orzo source padding is unrepresentable");
56 }
57 const std::size_t physical_bits =
58 ((num_bits_ + kLowerBits - 1) / kLowerBits) * kLowerBits;
59 words_.resize(physical_bits / 64, 0);
60 const std::size_t logical_words = num_bits_ / 64 + (num_bits_ % 64 != 0);
61 std::copy_n(source_words.begin(), logical_words, words_.begin());
62 if (num_bits_ % 64 != 0) {
63 words_[logical_words - 1] &= (std::uint64_t{1} << (num_bits_ % 64)) - 1;
64 }
65
66 std::size_t bucket_ones = 0;
67 std::size_t sample_entries = 0;
68 for (std::size_t word = 0; word < words_.size(); ++word) {
69 bucket_ones += std::popcount(words_[word]);
70 if ((word + 1) % (kSelectUpperBits / 64) == 0 ||
71 word + 1 == words_.size()) {
72 sample_entries +=
73 std::max<std::size_t>(1, bucket_ones / kSampleFrequency +
74 (bucket_ones % kSampleFrequency != 0));
75 bucket_ones = 0;
76 }
77 }
78 const std::size_t upper_count = ceiling(physical_bits, kUpperBits);
79 const std::size_t lower_count = physical_bits / kLowerBits;
80 const std::size_t select_upper_count =
81 ceiling(physical_bits, kSelectUpperBits);
82 metadata_bytes_ = (upper_count + 2) * sizeof(std::uint64_t) +
83 (lower_count + 1) * sizeof(__uint128_t) +
84 (select_upper_count + 2) * sizeof(std::uint64_t) +
85 select_upper_count * sizeof(std::uint32_t*) +
86 sample_entries * sizeof(std::uint32_t);
87 support_.emplace(words_.data(), physical_bits);
88 one_count_ = support_->get_one_count();
89 }
90
92 OrzoRankSelectSupport& operator=(const OrzoRankSelectSupport&) = delete;
94 OrzoRankSelectSupport& operator=(OrzoRankSelectSupport&&) = delete;
95
97 std::size_t size_impl() const { return num_bits_; }
99 int bit_impl(std::size_t position) const {
100 return (words_[position / 64] >> (position % 64)) & 1;
101 }
102
104 std::uint64_t rank_impl(std::size_t end) const {
105 return end >= num_bits_ ? one_count_ : support_->rank1(words_.data(), end);
106 }
107
110 std::uint64_t select_impl(std::size_t occurrence) const {
111 if (occurrence == 0) {
112 return 0;
113 }
114 return occurrence > one_count_
115 ? num_bits_
116 : support_->select1(words_.data(), occurrence);
117 }
118
120 bool supports_select1_impl() const { return true; }
122 bool supports_select0_impl() const { return false; }
124 std::uint64_t select0_impl(std::size_t occurrence) const {
125 return occurrence == 0 ? 0 : size();
126 }
127
128 std::size_t source_copy_bytes() const {
129 return words_.capacity() * sizeof(std::uint64_t);
130 }
131
132 std::size_t index_logical_bytes() const {
133 return sizeof(*this) + metadata_bytes_;
134 }
135
136 std::size_t memory_usage_bytes_impl() const {
138 }
139
140 private:
141 using Index = Orzo<>;
142 static constexpr std::size_t kLowerBits = 5632;
143 static constexpr std::size_t kUpperBits = 259072;
144 static constexpr std::size_t kSelectUpperBits = 4294895616;
145 static constexpr std::size_t kSampleFrequency = 8192;
146
147 static std::size_t ceiling(std::size_t size, std::size_t block) {
148 return size / block + (size % block != 0);
149 }
150
151 // Upstream's queries lack const signatures, but read source and index only.
152 mutable std::vector<std::uint64_t> words_;
153 mutable std::optional<Index> support_;
154 std::size_t num_bits_ = 0;
155 std::size_t one_count_ = 0;
156 std::size_t metadata_bytes_ = 0;
157};
158
159} // namespace pixie
Optional rank/select implementation over the default Orzo configuration.
Definition orzo.h:35
std::uint64_t select0_impl(std::size_t occurrence) const
Return zero for occurrence zero, otherwise size(): disabled.
Definition orzo.h:124
std::size_t index_logical_bytes() const
Return wrapper, upstream object, and requested directory bytes.
Definition orzo.h:132
std::uint64_t rank_impl(std::size_t end) const
Count ones in [0, end), clamping end to size().
Definition orzo.h:104
std::size_t source_copy_bytes() const
Return allocated source-copy bytes, including zero padding.
Definition orzo.h:128
std::size_t size_impl() const
Return the logical, unpadded source length.
Definition orzo.h:97
bool supports_select0_impl() const
The pinned upstream implementation does not provide zero select.
Definition orzo.h:122
int bit_impl(std::size_t position) const
Return a bit; position must be below size().
Definition orzo.h:99
std::size_t memory_usage_bytes_impl() const
Return object and allocated index/source bytes.
Definition orzo.h:136
bool supports_select1_impl() const
Orzo always builds one-select support in this configuration.
Definition orzo.h:120
std::uint64_t select_impl(std::size_t occurrence) const
Return a one-based one's position, zero for zero, size() if absent.
Definition orzo.h:110
OrzoRankSelectSupport(std::span< const std::uint64_t > source_words, std::size_t num_bits)
Build Orzo from least-significant-bit-first packed words.
Definition orzo.h:44
CRTP facade for immutable rank/select queries and bit access.
Definition rank_select.h:32
std::size_t size() const
Definition rank_select.h:39
Common interface for rank/select support over packed bit sequences.