Pixie
Loading...
Searching...
No Matches
packed_bit_builder.h
1#pragma once
2
3#include <pixie/storage/aligned.h>
4
5#include <algorithm>
6#include <cstddef>
7#include <cstdint>
8#include <limits>
9#include <stdexcept>
10#include <utility>
11#include <vector>
12
13namespace pixie {
14
24 private:
25 std::size_t size_ = 0;
26 AlignedStorage data_;
27
28 static std::size_t bits_for_words(std::size_t words) {
29 if (words > std::numeric_limits<std::size_t>::max() / 64) {
30 throw std::length_error("Packed bit sequence is too large");
31 }
32 return words * 64;
33 }
34
35 void ensure_word_capacity(std::size_t required_words) {
36 const std::size_t current_words =
37 data_.size_bytes() / sizeof(std::uint64_t);
38 if (required_words <= current_words) {
39 return;
40 }
41 if (current_words == 0) {
42 data_ = AlignedStorage(
43 bits_for_words(std::max(required_words, kAlignedStorageLineWords64)));
44 return;
45 }
46 const std::size_t doubled_words =
47 current_words > std::numeric_limits<std::size_t>::max() / 2
48 ? required_words
49 : current_words * 2;
50 const std::size_t grown_words = std::max(required_words, doubled_words);
51 data_.resize(bits_for_words(grown_words));
52 }
53
54 public:
58 void write_bit(bool bit) {
59 if (size_ % 64 == 0) {
60 const std::size_t word = size_ / 64;
61 ensure_word_capacity(word + 1);
62 data_.writable_words64()[word] = static_cast<std::uint64_t>(bit);
63 } else if (bit) {
64 data_.writable_words64()[size_ / 64] |= 1ull << (size_ % 64);
65 }
66 ++size_;
67 }
68
75 void write_bits(std::uint64_t bits, std::size_t width) {
76 if (width > 64) {
77 throw std::invalid_argument("Packed bit width is greater than 64");
78 }
79 if (width == 0) {
80 return;
81 }
82 if (size_ > std::numeric_limits<std::size_t>::max() - width) {
83 throw std::length_error("Packed bit sequence is too large");
84 }
85
86 const std::size_t offset = size_ % 64;
87 if (offset == 0) {
88 const std::size_t word = size_ / 64;
89 ensure_word_capacity(word + 1);
90 data_.writable_words64()[word] =
91 width == 64 ? bits : bits & ((1ull << width) - 1);
92 } else {
93 const std::size_t prefix = std::min(width, 64 - offset);
94 const std::uint64_t prefix_mask =
95 prefix == 64 ? ~std::uint64_t{0} : (1ull << prefix) - 1;
96 auto words = data_.writable_words64();
97 words[size_ / 64] |= (bits & prefix_mask) << offset;
98 if (prefix < width) {
99 const std::size_t word = size_ / 64 + 1;
100 ensure_word_capacity(word + 1);
101 data_.writable_words64()[word] = bits >> prefix;
102 }
103 }
104 size_ += width;
105 }
106
108 std::size_t size_bits() const noexcept { return size_; }
109
113 void reserve_bits(std::size_t size_bits) {
114 const std::size_t words =
115 size_bits / 64 + static_cast<std::size_t>(size_bits % 64 != 0);
116 ensure_word_capacity(words);
117 }
118
122 std::vector<std::uint64_t> take_words() {
123 const std::size_t words =
124 size_ / 64 + static_cast<std::size_t>(size_ % 64 != 0);
125 const auto source = data_.as_words64().first(words);
126 std::vector<std::uint64_t> result(source.begin(), source.end());
127 size_ = 0;
128 data_ = {};
129 return result;
130 }
131
136 const std::size_t words =
137 size_ / 64 + static_cast<std::size_t>(size_ % 64 != 0);
138 data_.resize(bits_for_words(words));
139 size_ = 0;
140 return std::exchange(data_, {});
141 }
142};
143
144} // namespace pixie
Owning storage with a logical byte size and 64-byte-aligned backing.
Definition aligned.h:41
Builder for a packed LSB-first bit sequence.
Definition packed_bit_builder.h:23
AlignedStorage take_storage()
Transfer the packed words in aligned storage and reset this builder.
Definition packed_bit_builder.h:135
void write_bit(bool bit)
Append one bit.
Definition packed_bit_builder.h:58
std::vector< std::uint64_t > take_words()
Materialize the packed words and reset this builder.
Definition packed_bit_builder.h:122
std::size_t size_bits() const noexcept
Return the number of appended bits.
Definition packed_bit_builder.h:108
void reserve_bits(std::size_t size_bits)
Reserve storage for at least size_bits bits.
Definition packed_bit_builder.h:113
void write_bits(std::uint64_t bits, std::size_t width)
Append the low width bits of bits, least-significant bit first.
Definition packed_bit_builder.h:75
std::size_t size_bytes() const
Return the logical exposed storage size in bytes.
Definition storage.h:75
void resize(std::size_t size_bits)
Resize mutable storage to hold at least size_bits bits.
Definition storage.h:221