/src/libjxl/lib/jxl/dec_bit_reader.h
Line | Count | Source |
1 | | // Copyright (c) the JPEG XL Project Authors. All rights reserved. |
2 | | // |
3 | | // Use of this source code is governed by a BSD-style |
4 | | // license that can be found in the LICENSE file. |
5 | | |
6 | | #ifndef LIB_JXL_DEC_BIT_READER_H_ |
7 | | #define LIB_JXL_DEC_BIT_READER_H_ |
8 | | |
9 | | // Bounds-checked bit reader; 64-bit buffer with support for deferred refills |
10 | | // and switching to reading byte-aligned words. |
11 | | |
12 | | #include <cstddef> |
13 | | #include <cstdint> |
14 | | #include <cstring> // memcpy |
15 | | |
16 | | #ifdef __BMI2__ |
17 | | #include <immintrin.h> |
18 | | #endif |
19 | | |
20 | | #include "lib/jxl/base/byte_order.h" |
21 | | #include "lib/jxl/base/common.h" |
22 | | #include "lib/jxl/base/compiler_specific.h" |
23 | | #include "lib/jxl/base/status.h" |
24 | | |
25 | | namespace jxl { |
26 | | |
27 | | // Reads bits previously written to memory by BitWriter. Uses unaligned 8-byte |
28 | | // little-endian loads. |
29 | | class BitReader { |
30 | | public: |
31 | | static constexpr size_t kMaxBitsPerCall = 56; |
32 | | |
33 | | // Constructs an invalid BitReader, to be overwritten before usage. |
34 | | BitReader() |
35 | | : buf_(0), |
36 | | bits_in_buf_(0), |
37 | | next_byte_{nullptr}, |
38 | | end_minus_8_{nullptr}, |
39 | 0 | first_byte_(nullptr) {} |
40 | | BitReader(const BitReader&) = delete; |
41 | | |
42 | | // bytes need not be aligned nor padded! |
43 | | template <class ArrayLike> |
44 | | explicit BitReader(const ArrayLike& bytes) |
45 | 3.24M | : buf_(0), |
46 | 3.24M | bits_in_buf_(0), |
47 | 3.24M | next_byte_(bytes.data()), |
48 | | // Assumes first_byte_ >= 8. |
49 | 3.24M | end_minus_8_(bytes.data() - 8 + bytes.size()), |
50 | 3.24M | first_byte_(bytes.data()) { |
51 | 3.24M | Refill(); |
52 | 3.24M | } |
53 | 3.24M | ~BitReader() { |
54 | | // Close() must be called before destroying an initialized bit reader. |
55 | | // Invalid bit readers will have a nullptr in first_byte_. |
56 | 3.24M | JXL_DASSERT(close_called_ || !first_byte_); |
57 | 3.24M | } |
58 | | |
59 | | // Move operator needs to invalidate the other BitReader such that it is |
60 | | // irrelevant if we call Close() on it or not. |
61 | 0 | BitReader& operator=(BitReader&& other) noexcept { |
62 | 0 | // Ensure the current instance was already closed, before we overwrite it |
63 | 0 | // with other. |
64 | 0 | JXL_DASSERT(close_called_ || !first_byte_); |
65 | 0 |
|
66 | 0 | JXL_DASSERT(!other.close_called_); |
67 | 0 | buf_ = other.buf_; |
68 | 0 | bits_in_buf_ = other.bits_in_buf_; |
69 | 0 | next_byte_ = other.next_byte_; |
70 | 0 | end_minus_8_ = other.end_minus_8_; |
71 | 0 | first_byte_ = other.first_byte_; |
72 | 0 | overread_bytes_ = other.overread_bytes_; |
73 | 0 | close_called_ = other.close_called_; |
74 | 0 |
|
75 | 0 | other.first_byte_ = nullptr; |
76 | 0 | other.next_byte_ = nullptr; |
77 | 0 | return *this; |
78 | 0 | } |
79 | | BitReader& operator=(const BitReader& other) = delete; |
80 | | |
81 | | // For time-critical reads, refills can be shared by multiple reads. |
82 | | // Based on variant 4 (plus bounds-checking), see |
83 | | // fgiesen.wordpress.com/2018/02/20/reading-bits-in-far-too-many-ways-part-2/ |
84 | 1.22G | JXL_INLINE void Refill() { |
85 | 1.22G | if (JXL_UNLIKELY(next_byte_ > end_minus_8_)) { |
86 | 593M | BoundsCheckedRefill(); |
87 | 635M | } else { |
88 | | // It's safe to load 64 bits; insert valid (possibly nonzero) bits above |
89 | | // bits_in_buf_. The shift requires bits_in_buf_ < 64. |
90 | 635M | buf_ |= LoadLE64(next_byte_) << bits_in_buf_; |
91 | | |
92 | | // Advance by bytes fully absorbed into the buffer. |
93 | 635M | next_byte_ += (63 - bits_in_buf_) >> 3; |
94 | | |
95 | | // We absorbed a multiple of 8 bits, so the lower 3 bits of bits_in_buf_ |
96 | | // must remain unchanged, otherwise the next refill's shifted bits will |
97 | | // not align with buf_. Set the three upper bits so the result >= 56. |
98 | 635M | bits_in_buf_ |= 56; |
99 | 635M | JXL_DASSERT(56 <= bits_in_buf_ && bits_in_buf_ < 64); |
100 | 635M | } |
101 | 1.22G | } |
102 | | |
103 | | // Returns the bits that would be returned by Read without calling Advance(). |
104 | | // It is legal to PEEK at more bits than present in the bitstream (required |
105 | | // by Huffman), and those bits will be zero. |
106 | | template <size_t N> |
107 | 386M | JXL_INLINE uint64_t PeekFixedBits() const { |
108 | 386M | static_assert(N <= kMaxBitsPerCall, "Reading too many bits in one call."); |
109 | 386M | JXL_DASSERT(!close_called_); |
110 | 386M | return buf_ & ((1ULL << N) - 1); |
111 | 386M | } unsigned long jxl::BitReader::PeekFixedBits<16ul>() const Line | Count | Source | 107 | 358M | JXL_INLINE uint64_t PeekFixedBits() const { | 108 | 358M | static_assert(N <= kMaxBitsPerCall, "Reading too many bits in one call."); | 109 | 358M | JXL_DASSERT(!close_called_); | 110 | 358M | return buf_ & ((1ULL << N) - 1); | 111 | 358M | } |
unsigned long jxl::BitReader::PeekFixedBits<2ul>() const Line | Count | Source | 107 | 13.4M | JXL_INLINE uint64_t PeekFixedBits() const { | 108 | 13.4M | static_assert(N <= kMaxBitsPerCall, "Reading too many bits in one call."); | 109 | 13.4M | JXL_DASSERT(!close_called_); | 110 | 13.4M | return buf_ & ((1ULL << N) - 1); | 111 | 13.4M | } |
unsigned long jxl::BitReader::PeekFixedBits<4ul>() const Line | Count | Source | 107 | 917k | JXL_INLINE uint64_t PeekFixedBits() const { | 108 | 917k | static_assert(N <= kMaxBitsPerCall, "Reading too many bits in one call."); | 109 | 917k | JXL_DASSERT(!close_called_); | 110 | 917k | return buf_ & ((1ULL << N) - 1); | 111 | 917k | } |
unsigned long jxl::BitReader::PeekFixedBits<8ul>() const Line | Count | Source | 107 | 439k | JXL_INLINE uint64_t PeekFixedBits() const { | 108 | 439k | static_assert(N <= kMaxBitsPerCall, "Reading too many bits in one call."); | 109 | 439k | JXL_DASSERT(!close_called_); | 110 | 439k | return buf_ & ((1ULL << N) - 1); | 111 | 439k | } |
unsigned long jxl::BitReader::PeekFixedBits<12ul>() const Line | Count | Source | 107 | 233k | JXL_INLINE uint64_t PeekFixedBits() const { | 108 | 233k | static_assert(N <= kMaxBitsPerCall, "Reading too many bits in one call."); | 109 | 233k | JXL_DASSERT(!close_called_); | 110 | 233k | return buf_ & ((1ULL << N) - 1); | 111 | 233k | } |
unsigned long jxl::BitReader::PeekFixedBits<1ul>() const Line | Count | Source | 107 | 4.04M | JXL_INLINE uint64_t PeekFixedBits() const { | 108 | 4.04M | static_assert(N <= kMaxBitsPerCall, "Reading too many bits in one call."); | 109 | 4.04M | JXL_DASSERT(!close_called_); | 110 | 4.04M | return buf_ & ((1ULL << N) - 1); | 111 | 4.04M | } |
unsigned long jxl::BitReader::PeekFixedBits<3ul>() const Line | Count | Source | 107 | 300k | JXL_INLINE uint64_t PeekFixedBits() const { | 108 | 300k | static_assert(N <= kMaxBitsPerCall, "Reading too many bits in one call."); | 109 | 300k | JXL_DASSERT(!close_called_); | 110 | 300k | return buf_ & ((1ULL << N) - 1); | 111 | 300k | } |
unsigned long jxl::BitReader::PeekFixedBits<0ul>() const Line | Count | Source | 107 | 66.9k | JXL_INLINE uint64_t PeekFixedBits() const { | 108 | 66.9k | static_assert(N <= kMaxBitsPerCall, "Reading too many bits in one call."); | 109 | 66.9k | JXL_DASSERT(!close_called_); | 110 | 66.9k | return buf_ & ((1ULL << N) - 1); | 111 | 66.9k | } |
unsigned long jxl::BitReader::PeekFixedBits<7ul>() const Line | Count | Source | 107 | 3.19M | JXL_INLINE uint64_t PeekFixedBits() const { | 108 | 3.19M | static_assert(N <= kMaxBitsPerCall, "Reading too many bits in one call."); | 109 | 3.19M | JXL_DASSERT(!close_called_); | 110 | 3.19M | return buf_ & ((1ULL << N) - 1); | 111 | 3.19M | } |
unsigned long jxl::BitReader::PeekFixedBits<32ul>() const Line | Count | Source | 107 | 509k | JXL_INLINE uint64_t PeekFixedBits() const { | 108 | 509k | static_assert(N <= kMaxBitsPerCall, "Reading too many bits in one call."); | 109 | 509k | JXL_DASSERT(!close_called_); | 110 | 509k | return buf_ & ((1ULL << N) - 1); | 111 | 509k | } |
unsigned long jxl::BitReader::PeekFixedBits<5ul>() const Line | Count | Source | 107 | 3.46M | JXL_INLINE uint64_t PeekFixedBits() const { | 108 | 3.46M | static_assert(N <= kMaxBitsPerCall, "Reading too many bits in one call."); | 109 | 3.46M | JXL_DASSERT(!close_called_); | 110 | 3.46M | return buf_ & ((1ULL << N) - 1); | 111 | 3.46M | } |
unsigned long jxl::BitReader::PeekFixedBits<10ul>() const Line | Count | Source | 107 | 432k | JXL_INLINE uint64_t PeekFixedBits() const { | 108 | 432k | static_assert(N <= kMaxBitsPerCall, "Reading too many bits in one call."); | 109 | 432k | JXL_DASSERT(!close_called_); | 110 | 432k | return buf_ & ((1ULL << N) - 1); | 111 | 432k | } |
|
112 | | |
113 | 881M | JXL_INLINE uint64_t PeekBits(size_t nbits) const { |
114 | 881M | JXL_DASSERT(nbits <= kMaxBitsPerCall); |
115 | 881M | JXL_DASSERT(!close_called_); |
116 | | |
117 | | // Slightly faster but requires BMI2. It is infeasible to make the many |
118 | | // callers reside between begin/end_target, especially because only the |
119 | | // callers in dec_ans are time-critical. Therefore only enabled if the |
120 | | // entire binary is compiled for (and thus requires) BMI2. |
121 | | #if defined(__BMI2__) && defined(__x86_64__) |
122 | | return _bzhi_u64(buf_, nbits); |
123 | | #else |
124 | 881M | const uint64_t mask = (1ULL << nbits) - 1; |
125 | 881M | return buf_ & mask; |
126 | 881M | #endif |
127 | 881M | } |
128 | | |
129 | | // Removes bits from the buffer. Need not match the previous Peek size, but |
130 | | // the buffer must contain at least num_bits (this prevents consuming more |
131 | | // than the total number of bits). |
132 | 1.26G | JXL_INLINE void Consume(size_t num_bits) { |
133 | 1.26G | JXL_DASSERT(!close_called_); |
134 | 1.26G | JXL_DASSERT(bits_in_buf_ >= num_bits); |
135 | 1.26G | if (JXL_CRASH_ON_ERROR) { |
136 | | // When JXL_CRASH_ON_ERROR is defined, it is a fatal error to read more |
137 | | // bits than available in the stream. A non-zero overread_bytes_ implies |
138 | | // that next_byte_ is already at the end of the stream, so we don't need |
139 | | // to check that. |
140 | 0 | JXL_DASSERT(bits_in_buf_ >= num_bits + overread_bytes_ * kBitsPerByte); |
141 | 0 | } |
142 | 1.26G | bits_in_buf_ -= num_bits; |
143 | 1.26G | buf_ >>= num_bits; |
144 | 1.26G | } |
145 | | |
146 | 39.9M | JXL_INLINE uint64_t ReadBits(size_t nbits) { |
147 | 39.9M | JXL_DASSERT(!close_called_); |
148 | 39.9M | Refill(); |
149 | 39.9M | const uint64_t bits = PeekBits(nbits); |
150 | 39.9M | Consume(nbits); |
151 | 39.9M | return bits; |
152 | 39.9M | } |
153 | | |
154 | | template <size_t N> |
155 | 20.9M | JXL_INLINE uint64_t ReadFixedBits() { |
156 | 20.9M | JXL_DASSERT(!close_called_); |
157 | 20.9M | Refill(); |
158 | 20.9M | const uint64_t bits = PeekFixedBits<N>(); |
159 | 20.9M | Consume(N); |
160 | 20.9M | return bits; |
161 | 20.9M | } unsigned long jxl::BitReader::ReadFixedBits<2ul>() Line | Count | Source | 155 | 13.4M | JXL_INLINE uint64_t ReadFixedBits() { | 156 | 13.4M | JXL_DASSERT(!close_called_); | 157 | 13.4M | Refill(); | 158 | 13.4M | const uint64_t bits = PeekFixedBits<N>(); | 159 | 13.4M | Consume(N); | 160 | 13.4M | return bits; | 161 | 13.4M | } |
unsigned long jxl::BitReader::ReadFixedBits<4ul>() Line | Count | Source | 155 | 679k | JXL_INLINE uint64_t ReadFixedBits() { | 156 | 679k | JXL_DASSERT(!close_called_); | 157 | 679k | Refill(); | 158 | 679k | const uint64_t bits = PeekFixedBits<N>(); | 159 | 679k | Consume(N); | 160 | 679k | return bits; | 161 | 679k | } |
unsigned long jxl::BitReader::ReadFixedBits<8ul>() Line | Count | Source | 155 | 439k | JXL_INLINE uint64_t ReadFixedBits() { | 156 | 439k | JXL_DASSERT(!close_called_); | 157 | 439k | Refill(); | 158 | 439k | const uint64_t bits = PeekFixedBits<N>(); | 159 | 439k | Consume(N); | 160 | 439k | return bits; | 161 | 439k | } |
unsigned long jxl::BitReader::ReadFixedBits<12ul>() Line | Count | Source | 155 | 233k | JXL_INLINE uint64_t ReadFixedBits() { | 156 | 233k | JXL_DASSERT(!close_called_); | 157 | 233k | Refill(); | 158 | 233k | const uint64_t bits = PeekFixedBits<N>(); | 159 | 233k | Consume(N); | 160 | 233k | return bits; | 161 | 233k | } |
unsigned long jxl::BitReader::ReadFixedBits<1ul>() Line | Count | Source | 155 | 4.04M | JXL_INLINE uint64_t ReadFixedBits() { | 156 | 4.04M | JXL_DASSERT(!close_called_); | 157 | 4.04M | Refill(); | 158 | 4.04M | const uint64_t bits = PeekFixedBits<N>(); | 159 | 4.04M | Consume(N); | 160 | 4.04M | return bits; | 161 | 4.04M | } |
unsigned long jxl::BitReader::ReadFixedBits<16ul>() Line | Count | Source | 155 | 807k | JXL_INLINE uint64_t ReadFixedBits() { | 156 | 807k | JXL_DASSERT(!close_called_); | 157 | 807k | Refill(); | 158 | 807k | const uint64_t bits = PeekFixedBits<N>(); | 159 | 807k | Consume(N); | 160 | 807k | return bits; | 161 | 807k | } |
unsigned long jxl::BitReader::ReadFixedBits<3ul>() Line | Count | Source | 155 | 300k | JXL_INLINE uint64_t ReadFixedBits() { | 156 | 300k | JXL_DASSERT(!close_called_); | 157 | 300k | Refill(); | 158 | 300k | const uint64_t bits = PeekFixedBits<N>(); | 159 | 300k | Consume(N); | 160 | 300k | return bits; | 161 | 300k | } |
unsigned long jxl::BitReader::ReadFixedBits<0ul>() Line | Count | Source | 155 | 66.9k | JXL_INLINE uint64_t ReadFixedBits() { | 156 | 66.9k | JXL_DASSERT(!close_called_); | 157 | 66.9k | Refill(); | 158 | 66.9k | const uint64_t bits = PeekFixedBits<N>(); | 159 | 66.9k | Consume(N); | 160 | 66.9k | return bits; | 161 | 66.9k | } |
unsigned long jxl::BitReader::ReadFixedBits<32ul>() Line | Count | Source | 155 | 509k | JXL_INLINE uint64_t ReadFixedBits() { | 156 | 509k | JXL_DASSERT(!close_called_); | 157 | 509k | Refill(); | 158 | 509k | const uint64_t bits = PeekFixedBits<N>(); | 159 | 509k | Consume(N); | 160 | 509k | return bits; | 161 | 509k | } |
unsigned long jxl::BitReader::ReadFixedBits<10ul>() Line | Count | Source | 155 | 432k | JXL_INLINE uint64_t ReadFixedBits() { | 156 | 432k | JXL_DASSERT(!close_called_); | 157 | 432k | Refill(); | 158 | 432k | const uint64_t bits = PeekFixedBits<N>(); | 159 | 432k | Consume(N); | 160 | 432k | return bits; | 161 | 432k | } |
|
162 | | |
163 | | // Equivalent to calling ReadFixedBits(1) `skip` times, but much faster. |
164 | | // `skip` is typically large. |
165 | 2.04M | void SkipBits(size_t skip) { |
166 | 2.04M | JXL_DASSERT(!close_called_); |
167 | | // Buffer is large enough - don't zero buf_ below. |
168 | 2.04M | if (JXL_UNLIKELY(skip <= bits_in_buf_)) { |
169 | 2.00M | Consume(skip); |
170 | 2.00M | return; |
171 | 2.00M | } |
172 | | |
173 | | // First deduct what we can satisfy from the buffer |
174 | 40.2k | skip -= bits_in_buf_; |
175 | 40.2k | bits_in_buf_ = 0; |
176 | | // Not enough to call Advance - that may leave some bits in the buffer |
177 | | // which were previously ABOVE bits_in_buf. |
178 | 40.2k | buf_ = 0; |
179 | | |
180 | | // Skip whole bytes |
181 | 40.2k | const size_t whole_bytes = skip / kBitsPerByte; |
182 | 40.2k | skip %= kBitsPerByte; |
183 | 40.2k | if (JXL_UNLIKELY(whole_bytes > |
184 | 40.2k | static_cast<size_t>(end_minus_8_ + 8 - next_byte_))) { |
185 | | // This is already an overflow condition (skipping past the end of the bit |
186 | | // stream). However if we increase next_byte_ too much we risk overflowing |
187 | | // that value and potentially making it valid again (next_byte_ < end). |
188 | | // This will set next_byte_ to the end of the stream and still consume |
189 | | // some bits in overread_bytes_, however the TotalBitsConsumed() will be |
190 | | // incorrect (still larger than the TotalBytes()). |
191 | 10.5k | next_byte_ = end_minus_8_ + 8; |
192 | 10.5k | skip += kBitsPerByte; |
193 | 29.6k | } else { |
194 | 29.6k | next_byte_ += whole_bytes; |
195 | 29.6k | } |
196 | | |
197 | 40.2k | Refill(); |
198 | 40.2k | Consume(skip); |
199 | 40.2k | } |
200 | | |
201 | 74.1M | size_t TotalBitsConsumed() const { |
202 | 74.1M | const size_t bytes_read = static_cast<size_t>(next_byte_ - first_byte_); |
203 | 74.1M | return (bytes_read + overread_bytes_) * kBitsPerByte - bits_in_buf_; |
204 | 74.1M | } |
205 | | |
206 | 715k | Status JumpToByteBoundary() { |
207 | 715k | const size_t remainder = TotalBitsConsumed() % kBitsPerByte; |
208 | 715k | if (remainder == 0) return true; |
209 | 656k | if (JXL_UNLIKELY(ReadBits(kBitsPerByte - remainder) != 0)) { |
210 | 20.4k | return JXL_FAILURE("Non-zero padding bits"); |
211 | 20.4k | } |
212 | 636k | return true; |
213 | 656k | } |
214 | | |
215 | | // For interoperability with other bitreaders (for resuming at |
216 | | // non-byte-aligned positions). |
217 | 0 | const uint8_t* FirstByte() const { return first_byte_; } |
218 | 33.2M | size_t TotalBytes() const { |
219 | 33.2M | return static_cast<size_t>(end_minus_8_ + 8 - first_byte_); |
220 | 33.2M | } |
221 | | |
222 | | // Forces the BitReader into an out-of-bounds state. Used by higher-level |
223 | | // decoders to short-circuit further reads when they detect a corrupt stream |
224 | | // (e.g. an LZ77 length-code overflow): after this call, subsequent reads |
225 | | // still return zeros, but AllReadsWithinBounds() and Close() will report |
226 | | // failure so the corruption surfaces at the next checkpoint rather than |
227 | | // silently producing phantom-zero symbols. |
228 | 31 | void MarkUnhealthy() { |
229 | 31 | next_byte_ = end_minus_8_ + 8; |
230 | | // Add enough overread to make TotalBitsConsumed() exceed TotalBytes() * |
231 | | // kBitsPerByte regardless of how many bits remain in buf_. |
232 | 31 | overread_bytes_ += 8; |
233 | 31 | } |
234 | | |
235 | | // Returns whether all the bits read so far have been within the input bounds. |
236 | | // When reading past the EOF, the Read*() and Consume() functions return zeros |
237 | | // but flag a failure when calling Close() without checking this function. |
238 | 32.4M | Status AllReadsWithinBounds() { |
239 | | // Mark up to which point the user checked the out of bounds condition. If |
240 | | // the user handles the condition at higher level (e.g. fetch more bytes |
241 | | // from network, return a custom JXL_FAILURE, ...), Close() should not |
242 | | // output a debug error (which would break tests with JXL_CRASH_ON_ERROR |
243 | | // even when legitimately handling the situation at higher level). This is |
244 | | // used by Bundle::CanRead. |
245 | 32.4M | checked_out_of_bounds_bits_ = TotalBitsConsumed(); |
246 | 32.4M | if (TotalBitsConsumed() > TotalBytes() * kBitsPerByte) { |
247 | 331k | return false; |
248 | 331k | } |
249 | 32.1M | return true; |
250 | 32.4M | } |
251 | | |
252 | | // Close the bit reader and return whether all the previous reads were |
253 | | // successful. Close must be called once. |
254 | 3.24M | Status Close() { |
255 | 3.24M | JXL_DASSERT(!close_called_); |
256 | 3.24M | close_called_ = true; |
257 | 3.24M | if (!first_byte_) return true; |
258 | 3.24M | if (TotalBitsConsumed() > checked_out_of_bounds_bits_ && |
259 | 2.19k | TotalBitsConsumed() > TotalBytes() * kBitsPerByte) { |
260 | 0 | return JXL_FAILURE("Read more bits than available in the bit_reader"); |
261 | 0 | } |
262 | 3.24M | return true; |
263 | 3.24M | } |
264 | | |
265 | | private: |
266 | | // Separate function avoids inlining this relatively cold code into callers. |
267 | | JXL_NOINLINE void BoundsCheckedRefill(); |
268 | | |
269 | 0 | JXL_NOINLINE uint32_t BoundsCheckedReadByteAlignedWord() { |
270 | 0 | if (next_byte_ + 1 < end_minus_8_ + 8) { |
271 | 0 | uint32_t ret = LoadLE16(next_byte_); |
272 | 0 | next_byte_ += 2; |
273 | 0 | return ret; |
274 | 0 | } |
275 | 0 | overread_bytes_ += 2; |
276 | 0 | return 0; |
277 | 0 | } |
278 | | |
279 | | uint64_t buf_; |
280 | | size_t bits_in_buf_; // [0, 64) |
281 | | const uint8_t* JXL_RESTRICT next_byte_; |
282 | | const uint8_t* end_minus_8_; // for refill bounds check |
283 | | const uint8_t* first_byte_; // for GetSpan |
284 | | |
285 | | // Number of bytes past the end that were loaded into the buf_. These bytes |
286 | | // are not read from memory, but instead assumed 0. It is an error (likely due |
287 | | // to an invalid stream) to Consume() more bits than specified in the range |
288 | | // passed to the constructor. |
289 | | uint64_t overread_bytes_{0}; |
290 | | bool close_called_{false}; |
291 | | |
292 | | uint64_t checked_out_of_bounds_bits_{0}; |
293 | | }; |
294 | | |
295 | | // Closes a BitReader when the BitReaderScopedCloser goes out of scope. When |
296 | | // closing the bit reader, if the status result was failure it sets this failure |
297 | | // to the passed variable pointer. Typical usage. |
298 | | // |
299 | | // Status ret = true; |
300 | | // { |
301 | | // BitReader reader(...); |
302 | | // BitReaderScopedCloser reader_closer(&reader, &ret); |
303 | | // |
304 | | // // ... code that can return errors here ... |
305 | | // } |
306 | | // // ... more code that doesn't use the BitReader. |
307 | | // return ret; |
308 | | |
309 | | class BitReaderScopedCloser { |
310 | | public: |
311 | | BitReaderScopedCloser(BitReader& reader, Status& status) |
312 | 2.19k | : reader_(&reader), status_(&status) {} |
313 | 2.19k | ~BitReaderScopedCloser() { |
314 | 2.19k | if (reader_ != nullptr) { |
315 | 2.19k | Status close_ret = reader_->Close(); |
316 | 2.19k | if (!close_ret) *status_ = close_ret; |
317 | 2.19k | } |
318 | 2.19k | } |
319 | | BitReaderScopedCloser(const BitReaderScopedCloser&) = delete; |
320 | | |
321 | | private: |
322 | | BitReader* reader_; |
323 | | Status* status_; |
324 | | }; |
325 | | |
326 | | } // namespace jxl |
327 | | |
328 | | #endif // LIB_JXL_DEC_BIT_READER_H_ |