vkmEngine 1.0.0
A C++ game engine · vkmengine.com
Loading...
Searching...
No Matches
bit_stream.h
1#pragma once
2
3#include <cstddef>
4#include <cstdint>
5#include <cstring>
6#include <vector>
7
8namespace Vkm::Engine {
9
19class BitWriter {
20 public:
28 BitWriter(std::vector<uint8_t>& bytes, size_t capacity)
29 : m_bytes(bytes)
30 , m_capacity(capacity)
31 {
32 m_bytes.clear();
33 m_bytes.reserve(capacity);
34 }
35
36 ~BitWriter() = default;
37
38 BitWriter(const BitWriter& other) = delete;
39 BitWriter& operator=(const BitWriter& other) = delete;
40
41 BitWriter(BitWriter && other) = delete;
42 BitWriter& operator=(BitWriter && other) = delete;
43
44 public:
51 void bits(uint32_t value, uint32_t width) {
52 if (width == 0 || width > 32) return;
53 if (m_bit + width > m_capacity * 8u) {
54 m_overflowed = true;
55 return;
56 }
57
58 const uint64_t field = static_cast<uint64_t>(value) & ((uint64_t(1) << width) - 1u);
59 m_word |= field << m_wordBits;
60 m_wordBits += width;
61 m_bit += width;
62 while (m_wordBits >= 8) {
63 m_bytes.push_back(static_cast<uint8_t>(m_word));
64 m_word >>= 8;
65 m_wordBits -= 8;
66 }
67 }
68
69 void boolean(bool value) { bits(value ? 1u : 0u, 1); }
70 void u8(uint8_t value) { bits(value, 8); }
71 void u16(uint16_t value) { bits(value, 16); }
72 void u32(uint32_t value) { bits(value, 32); }
73
75 void u64(uint64_t value) {
76 bits(static_cast<uint32_t>(value >> 32), 32);
77 bits(static_cast<uint32_t>(value), 32);
78 }
79
81 void f32(float value) {
82 uint32_t raw = 0;
83 std::memcpy(&raw, &value, sizeof(raw));
84 bits(raw, 32);
85 }
86
96 void append(const uint8_t* bytes, size_t count) {
97 if (m_bit + count > m_capacity * 8u) {
98 m_overflowed = true;
99 return;
100 }
101
102 // A byte at a time, as the source was laid down.
103 size_t done = 0;
104 for (; done + 8 <= count; done += 8) bits(bytes[done >> 3], 8);
105 if (done < count) bits(bytes[done >> 3], static_cast<uint32_t>(count - done));
106 }
107
109 size_t bitCount() const { return m_bit; }
110
112 size_t byteCount() const { return (m_bit + 7u) >> 3; }
113
121 bool overflowed() const { return m_overflowed; }
122
124 void finish() {
125 if (m_wordBits > 0) m_bytes.push_back(static_cast<uint8_t>(m_word));
126 m_word = 0;
127 m_wordBits = 0;
128 }
129
130 private:
131 std::vector<uint8_t>& m_bytes;
132
133 size_t m_capacity = 0;
134 size_t m_bit = 0;
135 uint64_t m_word = 0;
136 uint32_t m_wordBits = 0;
137 bool m_overflowed = false;
138};
139
145class BitReader {
146 public:
147 BitReader(const uint8_t* bytes, size_t size) : m_bytes(bytes), m_size(size) {}
148
149 ~BitReader() = default;
150
151 BitReader(const BitReader& other) = delete;
152 BitReader& operator=(const BitReader& other) = delete;
153
154 BitReader(BitReader && other) = delete;
155 BitReader& operator=(BitReader && other) = delete;
156
157 public:
158 uint32_t bits(uint32_t width) {
159 if (width == 0 || width > 32) return 0;
160 if (m_failed || m_bit + width > m_size * 8u) {
161 m_failed = true;
162 return 0;
163 }
164
165 // The bound above keeps every byte loaded inside the buffer.
166 while (m_wordBits < width) {
167 m_word |= static_cast<uint64_t>(m_bytes[m_next++]) << m_wordBits;
168 m_wordBits += 8;
169 }
170 const uint32_t value = static_cast<uint32_t>(m_word & ((uint64_t(1) << width) - 1u));
171 m_word >>= width;
172 m_wordBits -= width;
173 m_bit += width;
174 return value;
175 }
176
177 bool boolean() { return bits(1) != 0; }
178 uint8_t u8() { return static_cast<uint8_t>(bits(8)); }
179 uint16_t u16() { return static_cast<uint16_t>(bits(16)); }
180 uint32_t u32() { return bits(32); }
181
190 uint64_t u64() {
191 const uint64_t high = bits(32);
192 const uint64_t low = bits(32);
193 return (high << 32) | low;
194 }
195
196 float f32() {
197 const uint32_t raw = bits(32);
198 float value = 0.0f;
199 std::memcpy(&value, &raw, sizeof(value));
200 return value;
201 }
202
204 bool failed() const { return m_failed; }
205
206 private:
207 const uint8_t* m_bytes = nullptr;
208
209 size_t m_size = 0;
210 size_t m_bit = 0;
211 size_t m_next = 0;
212 uint64_t m_word = 0;
213 uint32_t m_wordBits = 0;
214 bool m_failed = false;
215};
216
217} // namespace Vkm::Engine
bool failed() const
True when a read ran past the end; sticky once set.
Definition bit_stream.h:204
uint64_t u64()
Read back the pair u64 wrote, high half first.
Definition bit_stream.h:190
Packs values to the bit into a byte buffer.
Definition bit_stream.h:19
BitWriter(std::vector< uint8_t > &bytes, size_t capacity)
Write into bytes, filling at most capacity of them.
Definition bit_stream.h:28
size_t bitCount() const
Bits written so far.
Definition bit_stream.h:109
size_t byteCount() const
Whole bytes needed to carry what has been written.
Definition bit_stream.h:112
void append(const uint8_t *bytes, size_t count)
Write count bits from bytes, starting at its first bit.
Definition bit_stream.h:96
void bits(uint32_t value, uint32_t width)
Write the low width bits of value.
Definition bit_stream.h:51
void f32(float value)
A float as its exact bits, for the values quantisation would spoil.
Definition bit_stream.h:81
void finish()
Flush the last partial byte into the buffer.
Definition bit_stream.h:124
bool overflowed() const
True when a write did not fit.
Definition bit_stream.h:121
void u64(uint64_t value)
Two words, high first, because the field width is capped at 32.
Definition bit_stream.h:75