Pixie
Loading...
Searching...
No Matches
position_samples.h
1#pragma once
2
3#include <pixie/rank_select/detail/compressed/samples.h>
4
5namespace pixie::detail::compressed {
6
16template <StorageImplementation MetadataStorage = AlignedStorage>
17class PositionSamples {
18 static constexpr std::size_t kHeaderWords = 2;
19
20 public:
21 PositionSamples() = default;
22
23 explicit PositionSamples(std::span<const std::uint64_t> values,
24 std::span<const std::uint64_t> wide = {})
26 {
27 if (!std::is_sorted(values.begin(), values.end())) {
28 throw std::invalid_argument("Monotone samples must be non-decreasing");
29 }
30 const std::size_t groups =
31 values.empty() ? 0 : 1 + (values.size() - 1) / 32;
32 for (std::size_t group = 0; group < groups; ++group) {
33 width_ = std::max(width_, group_width(values, group));
34 }
35 const auto wide_groups = make_sample_wide_groups(values.size(), wide);
36 low_offset_ = kHeaderWords + (wide_groups.empty() ? 0 : 2);
37 stride_ = low_offset_ + (32 * width_ + 63) / 64;
38 if (groups > std::numeric_limits<std::size_t>::max() / (stride_ * 64)) {
39 throw std::length_error("Fixed sample allocation too large");
40 }
41 storage_ = MetadataArray<std::uint64_t, MetadataStorage>(groups * stride_);
42 auto* data = storage_.data();
43 for (std::size_t group = 0; group < groups; ++group) {
44 auto* record = data + group * stride_;
45 record[0] = values[group * 32];
46 if (!wide_groups.empty()) {
47 record[kHeaderWords] = wide_groups[group].begin;
48 record[kHeaderWords + 1] = wide_groups[group].mask;
49 }
50 const auto count = std::min<std::size_t>(32, values.size() - group * 32);
51 for (std::size_t i = 0; i < count; ++i) {
52 const auto delta = values[group * 32 + i] - record[0];
53 record[1] |= std::uint64_t{1} << ((delta >> width_) + i);
54 write_monotone_low(record + low_offset_, i, width_, delta);
55 }
56 }
57 }
58
59 std::uint64_t operator[](std::size_t index) const {
60 return decode(group(index / 32), index % 32);
61 }
62
63 std::pair<std::uint64_t, std::uint64_t> bounds(std::size_t index) const {
64 if (index % 32 == 31) {
65 return {(*this)[index], (*this)[index + 1]};
66 }
67 const auto* record = group(index / 32);
68 return {decode(record, index % 32), decode(record, index % 32 + 1)};
69 }
70
71 std::optional<std::size_t> wide_entry(std::size_t index) const {
72 if (low_offset_ == kHeaderWords) {
73 return std::nullopt;
74 }
75 assert(low_offset_ == kHeaderWords + 2);
76 const auto* record = group(index / 32);
77 return sample_wide_entry(record[kHeaderWords], record[kHeaderWords + 1],
78 index % 32);
79 }
80
81 std::size_t allocated_bytes() const { return storage_.allocated_bytes(); }
82
83 private:
85 std::size_t stride_ = 0;
86 std::size_t low_offset_ = kHeaderWords;
87 unsigned width_ = 0;
88
89 static unsigned group_width(std::span<const std::uint64_t> values,
90 std::size_t group) {
91 const auto end = std::min<std::size_t>(32, values.size() - group * 32);
92 return std::bit_width((values[group * 32 + end - 1] - values[group * 32]) >>
93 5);
94 }
95
96 const std::uint64_t* group(std::size_t index) const {
97 return storage_.data() + index * stride_;
98 }
99
100 std::uint64_t decode(const std::uint64_t* record, unsigned index) const {
101 const auto high = ::select_64(record[1], index) - index;
102 return record[0] + (high << width_) +
103 read_monotone_low(record + low_offset_, index, width_);
104 }
105};
106
107} // namespace pixie::detail::compressed
Definition metadata_array.h:27