/src/libjxl/lib/jxl/dec_bit_reader.h
Line | Count | Source (jump to first uncovered line) |
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 | 153k | : buf_(0), |
46 | 153k | bits_in_buf_(0), |
47 | 153k | next_byte_(bytes.data()), |
48 | | // Assumes first_byte_ >= 8. |
49 | 153k | end_minus_8_(bytes.data() - 8 + bytes.size()), |
50 | 153k | first_byte_(bytes.data()) { |
51 | 153k | Refill(); |
52 | 153k | } |
53 | 153k | ~BitReader() { |
54 | | // Close() must be called before destroying an initialized bit reader. |
55 | | // Invalid bit readers will have a nullptr in first_byte_. |
56 | 153k | JXL_DASSERT(close_called_ || !first_byte_); |
57 | 153k | } |
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 | 121M | JXL_INLINE void Refill() { |
85 | 121M | if (JXL_UNLIKELY(next_byte_ > end_minus_8_)) { |
86 | 70.3M | BoundsCheckedRefill(); |
87 | 70.3M | } 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 | 51.3M | buf_ |= LoadLE64(next_byte_) << bits_in_buf_; |
91 | | |
92 | | // Advance by bytes fully absorbed into the buffer. |
93 | 51.3M | 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 | 51.3M | bits_in_buf_ |= 56; |
99 | 51.3M | JXL_DASSERT(56 <= bits_in_buf_ && bits_in_buf_ < 64); |
100 | 51.3M | } |
101 | 121M | } |
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 | 37.0M | JXL_INLINE uint64_t PeekFixedBits() const { |
108 | 37.0M | static_assert(N <= kMaxBitsPerCall, "Reading too many bits in one call."); |
109 | 37.0M | JXL_DASSERT(!close_called_); |
110 | 37.0M | return buf_ & ((1ULL << N) - 1); |
111 | 37.0M | } unsigned long jxl::BitReader::PeekFixedBits<16ul>() const Line | Count | Source | 107 | 33.6M | JXL_INLINE uint64_t PeekFixedBits() const { | 108 | 33.6M | static_assert(N <= kMaxBitsPerCall, "Reading too many bits in one call."); | 109 | 33.6M | JXL_DASSERT(!close_called_); | 110 | 33.6M | return buf_ & ((1ULL << N) - 1); | 111 | 33.6M | } |
unsigned long jxl::BitReader::PeekFixedBits<1ul>() const Line | Count | Source | 107 | 367k | JXL_INLINE uint64_t PeekFixedBits() const { | 108 | 367k | static_assert(N <= kMaxBitsPerCall, "Reading too many bits in one call."); | 109 | 367k | JXL_DASSERT(!close_called_); | 110 | 367k | return buf_ & ((1ULL << N) - 1); | 111 | 367k | } |
unsigned long jxl::BitReader::PeekFixedBits<8ul>() const Line | Count | Source | 107 | 17.4k | JXL_INLINE uint64_t PeekFixedBits() const { | 108 | 17.4k | static_assert(N <= kMaxBitsPerCall, "Reading too many bits in one call."); | 109 | 17.4k | JXL_DASSERT(!close_called_); | 110 | 17.4k | return buf_ & ((1ULL << N) - 1); | 111 | 17.4k | } |
unsigned long jxl::BitReader::PeekFixedBits<4ul>() const Line | Count | Source | 107 | 36.2k | JXL_INLINE uint64_t PeekFixedBits() const { | 108 | 36.2k | static_assert(N <= kMaxBitsPerCall, "Reading too many bits in one call."); | 109 | 36.2k | JXL_DASSERT(!close_called_); | 110 | 36.2k | return buf_ & ((1ULL << N) - 1); | 111 | 36.2k | } |
unsigned long jxl::BitReader::PeekFixedBits<3ul>() const Line | Count | Source | 107 | 20.8k | JXL_INLINE uint64_t PeekFixedBits() const { | 108 | 20.8k | static_assert(N <= kMaxBitsPerCall, "Reading too many bits in one call."); | 109 | 20.8k | JXL_DASSERT(!close_called_); | 110 | 20.8k | return buf_ & ((1ULL << N) - 1); | 111 | 20.8k | } |
unsigned long jxl::BitReader::PeekFixedBits<7ul>() const Line | Count | Source | 107 | 158k | JXL_INLINE uint64_t PeekFixedBits() const { | 108 | 158k | static_assert(N <= kMaxBitsPerCall, "Reading too many bits in one call."); | 109 | 158k | JXL_DASSERT(!close_called_); | 110 | 158k | return buf_ & ((1ULL << N) - 1); | 111 | 158k | } |
unsigned long jxl::BitReader::PeekFixedBits<2ul>() const Line | Count | Source | 107 | 777k | JXL_INLINE uint64_t PeekFixedBits() const { | 108 | 777k | static_assert(N <= kMaxBitsPerCall, "Reading too many bits in one call."); | 109 | 777k | JXL_DASSERT(!close_called_); | 110 | 777k | return buf_ & ((1ULL << N) - 1); | 111 | 777k | } |
unsigned long jxl::BitReader::PeekFixedBits<32ul>() const Line | Count | Source | 107 | 43.1k | JXL_INLINE uint64_t PeekFixedBits() const { | 108 | 43.1k | static_assert(N <= kMaxBitsPerCall, "Reading too many bits in one call."); | 109 | 43.1k | JXL_DASSERT(!close_called_); | 110 | 43.1k | return buf_ & ((1ULL << N) - 1); | 111 | 43.1k | } |
unsigned long jxl::BitReader::PeekFixedBits<5ul>() const Line | Count | Source | 107 | 1.98M | JXL_INLINE uint64_t PeekFixedBits() const { | 108 | 1.98M | static_assert(N <= kMaxBitsPerCall, "Reading too many bits in one call."); | 109 | 1.98M | JXL_DASSERT(!close_called_); | 110 | 1.98M | return buf_ & ((1ULL << N) - 1); | 111 | 1.98M | } |
unsigned long jxl::BitReader::PeekFixedBits<10ul>() const Line | Count | Source | 107 | 15.1k | JXL_INLINE uint64_t PeekFixedBits() const { | 108 | 15.1k | static_assert(N <= kMaxBitsPerCall, "Reading too many bits in one call."); | 109 | 15.1k | JXL_DASSERT(!close_called_); | 110 | 15.1k | return buf_ & ((1ULL << N) - 1); | 111 | 15.1k | } |
unsigned long jxl::BitReader::PeekFixedBits<12ul>() const Line | Count | Source | 107 | 6.01k | JXL_INLINE uint64_t PeekFixedBits() const { | 108 | 6.01k | static_assert(N <= kMaxBitsPerCall, "Reading too many bits in one call."); | 109 | 6.01k | JXL_DASSERT(!close_called_); | 110 | 6.01k | return buf_ & ((1ULL << N) - 1); | 111 | 6.01k | } |
unsigned long jxl::BitReader::PeekFixedBits<0ul>() const Line | Count | Source | 107 | 2.16k | JXL_INLINE uint64_t PeekFixedBits() const { | 108 | 2.16k | static_assert(N <= kMaxBitsPerCall, "Reading too many bits in one call."); | 109 | 2.16k | JXL_DASSERT(!close_called_); | 110 | 2.16k | return buf_ & ((1ULL << N) - 1); | 111 | 2.16k | } |
|
112 | | |
113 | 89.9M | JXL_INLINE uint64_t PeekBits(size_t nbits) const { |
114 | 89.9M | JXL_DASSERT(nbits <= kMaxBitsPerCall); |
115 | 89.9M | 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 | 89.9M | const uint64_t mask = (1ULL << nbits) - 1; |
125 | 89.9M | return buf_ & mask; |
126 | 89.9M | #endif |
127 | 89.9M | } |
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 | 127M | JXL_INLINE void Consume(size_t num_bits) { |
133 | 127M | JXL_DASSERT(!close_called_); |
134 | 127M | JXL_DASSERT(bits_in_buf_ >= num_bits); |
135 | 127M | 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 | 127M | bits_in_buf_ -= num_bits; |
143 | 127M | buf_ >>= num_bits; |
144 | 127M | } |
145 | | |
146 | 2.02M | JXL_INLINE uint64_t ReadBits(size_t nbits) { |
147 | 2.02M | JXL_DASSERT(!close_called_); |
148 | 2.02M | Refill(); |
149 | 2.02M | const uint64_t bits = PeekBits(nbits); |
150 | 2.02M | Consume(nbits); |
151 | 2.02M | return bits; |
152 | 2.02M | } |
153 | | |
154 | | template <size_t N> |
155 | 1.34M | JXL_INLINE uint64_t ReadFixedBits() { |
156 | 1.34M | JXL_DASSERT(!close_called_); |
157 | 1.34M | Refill(); |
158 | 1.34M | const uint64_t bits = PeekFixedBits<N>(); |
159 | 1.34M | Consume(N); |
160 | 1.34M | return bits; |
161 | 1.34M | } unsigned long jxl::BitReader::ReadFixedBits<1ul>() Line | Count | Source | 155 | 367k | JXL_INLINE uint64_t ReadFixedBits() { | 156 | 367k | JXL_DASSERT(!close_called_); | 157 | 367k | Refill(); | 158 | 367k | const uint64_t bits = PeekFixedBits<N>(); | 159 | 367k | Consume(N); | 160 | 367k | return bits; | 161 | 367k | } |
unsigned long jxl::BitReader::ReadFixedBits<8ul>() Line | Count | Source | 155 | 17.4k | JXL_INLINE uint64_t ReadFixedBits() { | 156 | 17.4k | JXL_DASSERT(!close_called_); | 157 | 17.4k | Refill(); | 158 | 17.4k | const uint64_t bits = PeekFixedBits<N>(); | 159 | 17.4k | Consume(N); | 160 | 17.4k | return bits; | 161 | 17.4k | } |
unsigned long jxl::BitReader::ReadFixedBits<4ul>() Line | Count | Source | 155 | 21.4k | JXL_INLINE uint64_t ReadFixedBits() { | 156 | 21.4k | JXL_DASSERT(!close_called_); | 157 | 21.4k | Refill(); | 158 | 21.4k | const uint64_t bits = PeekFixedBits<N>(); | 159 | 21.4k | Consume(N); | 160 | 21.4k | return bits; | 161 | 21.4k | } |
unsigned long jxl::BitReader::ReadFixedBits<3ul>() Line | Count | Source | 155 | 20.8k | JXL_INLINE uint64_t ReadFixedBits() { | 156 | 20.8k | JXL_DASSERT(!close_called_); | 157 | 20.8k | Refill(); | 158 | 20.8k | const uint64_t bits = PeekFixedBits<N>(); | 159 | 20.8k | Consume(N); | 160 | 20.8k | return bits; | 161 | 20.8k | } |
unsigned long jxl::BitReader::ReadFixedBits<2ul>() Line | Count | Source | 155 | 777k | JXL_INLINE uint64_t ReadFixedBits() { | 156 | 777k | JXL_DASSERT(!close_called_); | 157 | 777k | Refill(); | 158 | 777k | const uint64_t bits = PeekFixedBits<N>(); | 159 | 777k | Consume(N); | 160 | 777k | return bits; | 161 | 777k | } |
unsigned long jxl::BitReader::ReadFixedBits<32ul>() Line | Count | Source | 155 | 43.1k | JXL_INLINE uint64_t ReadFixedBits() { | 156 | 43.1k | JXL_DASSERT(!close_called_); | 157 | 43.1k | Refill(); | 158 | 43.1k | const uint64_t bits = PeekFixedBits<N>(); | 159 | 43.1k | Consume(N); | 160 | 43.1k | return bits; | 161 | 43.1k | } |
unsigned long jxl::BitReader::ReadFixedBits<10ul>() Line | Count | Source | 155 | 15.1k | JXL_INLINE uint64_t ReadFixedBits() { | 156 | 15.1k | JXL_DASSERT(!close_called_); | 157 | 15.1k | Refill(); | 158 | 15.1k | const uint64_t bits = PeekFixedBits<N>(); | 159 | 15.1k | Consume(N); | 160 | 15.1k | return bits; | 161 | 15.1k | } |
unsigned long jxl::BitReader::ReadFixedBits<12ul>() Line | Count | Source | 155 | 6.01k | JXL_INLINE uint64_t ReadFixedBits() { | 156 | 6.01k | JXL_DASSERT(!close_called_); | 157 | 6.01k | Refill(); | 158 | 6.01k | const uint64_t bits = PeekFixedBits<N>(); | 159 | 6.01k | Consume(N); | 160 | 6.01k | return bits; | 161 | 6.01k | } |
unsigned long jxl::BitReader::ReadFixedBits<16ul>() Line | Count | Source | 155 | 77.2k | JXL_INLINE uint64_t ReadFixedBits() { | 156 | 77.2k | JXL_DASSERT(!close_called_); | 157 | 77.2k | Refill(); | 158 | 77.2k | const uint64_t bits = PeekFixedBits<N>(); | 159 | 77.2k | Consume(N); | 160 | 77.2k | return bits; | 161 | 77.2k | } |
unsigned long jxl::BitReader::ReadFixedBits<0ul>() Line | Count | Source | 155 | 2.16k | JXL_INLINE uint64_t ReadFixedBits() { | 156 | 2.16k | JXL_DASSERT(!close_called_); | 157 | 2.16k | Refill(); | 158 | 2.16k | const uint64_t bits = PeekFixedBits<N>(); | 159 | 2.16k | Consume(N); | 160 | 2.16k | return bits; | 161 | 2.16k | } |
|
162 | | |
163 | | // Equivalent to calling ReadFixedBits(1) `skip` times, but much faster. |
164 | | // `skip` is typically large. |
165 | 64.9k | void SkipBits(size_t skip) { |
166 | 64.9k | JXL_DASSERT(!close_called_); |
167 | | // Buffer is large enough - don't zero buf_ below. |
168 | 64.9k | if (JXL_UNLIKELY(skip <= bits_in_buf_)) { |
169 | 63.3k | Consume(skip); |
170 | 63.3k | return; |
171 | 63.3k | } |
172 | | |
173 | | // First deduct what we can satisfy from the buffer |
174 | 1.64k | skip -= bits_in_buf_; |
175 | 1.64k | 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 | 1.64k | buf_ = 0; |
179 | | |
180 | | // Skip whole bytes |
181 | 1.64k | const size_t whole_bytes = skip / kBitsPerByte; |
182 | 1.64k | skip %= kBitsPerByte; |
183 | 1.64k | if (JXL_UNLIKELY(whole_bytes > |
184 | 1.64k | 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 | 1.01k | next_byte_ = end_minus_8_ + 8; |
192 | 1.01k | skip += kBitsPerByte; |
193 | 1.01k | } else { |
194 | 633 | next_byte_ += whole_bytes; |
195 | 633 | } |
196 | | |
197 | 1.64k | Refill(); |
198 | 1.64k | Consume(skip); |
199 | 1.64k | } |
200 | | |
201 | 12.9M | size_t TotalBitsConsumed() const { |
202 | 12.9M | const size_t bytes_read = static_cast<size_t>(next_byte_ - first_byte_); |
203 | 12.9M | return (bytes_read + overread_bytes_) * kBitsPerByte - bits_in_buf_; |
204 | 12.9M | } |
205 | | |
206 | 88.5k | Status JumpToByteBoundary() { |
207 | 88.5k | const size_t remainder = TotalBitsConsumed() % kBitsPerByte; |
208 | 88.5k | if (remainder == 0) return true; |
209 | 76.6k | if (JXL_UNLIKELY(ReadBits(kBitsPerByte - remainder) != 0)) { |
210 | 377 | return JXL_FAILURE("Non-zero padding bits"); |
211 | 377 | } |
212 | 76.2k | return true; |
213 | 76.6k | } |
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 | 6.31M | size_t TotalBytes() const { |
219 | 6.31M | return static_cast<size_t>(end_minus_8_ + 8 - first_byte_); |
220 | 6.31M | } |
221 | | |
222 | | // Returns whether all the bits read so far have been within the input bounds. |
223 | | // When reading past the EOF, the Read*() and Consume() functions return zeros |
224 | | // but flag a failure when calling Close() without checking this function. |
225 | 6.17M | Status AllReadsWithinBounds() { |
226 | | // Mark up to which point the user checked the out of bounds condition. If |
227 | | // the user handles the condition at higher level (e.g. fetch more bytes |
228 | | // from network, return a custom JXL_FAILURE, ...), Close() should not |
229 | | // output a debug error (which would break tests with JXL_CRASH_ON_ERROR |
230 | | // even when legitimately handling the situation at higher level). This is |
231 | | // used by Bundle::CanRead. |
232 | 6.17M | checked_out_of_bounds_bits_ = TotalBitsConsumed(); |
233 | 6.17M | if (TotalBitsConsumed() > TotalBytes() * kBitsPerByte) { |
234 | 22.8k | return false; |
235 | 22.8k | } |
236 | 6.15M | return true; |
237 | 6.17M | } |
238 | | |
239 | | // Close the bit reader and return whether all the previous reads were |
240 | | // successful. Close must be called once. |
241 | 153k | Status Close() { |
242 | 153k | JXL_DASSERT(!close_called_); |
243 | 153k | close_called_ = true; |
244 | 153k | if (!first_byte_) return true; |
245 | 153k | if (TotalBitsConsumed() > checked_out_of_bounds_bits_ && |
246 | 153k | TotalBitsConsumed() > TotalBytes() * kBitsPerByte) { |
247 | 0 | return JXL_FAILURE("Read more bits than available in the bit_reader"); |
248 | 0 | } |
249 | 153k | return true; |
250 | 153k | } |
251 | | |
252 | | private: |
253 | | // Separate function avoids inlining this relatively cold code into callers. |
254 | | JXL_NOINLINE void BoundsCheckedRefill(); |
255 | | |
256 | 0 | JXL_NOINLINE uint32_t BoundsCheckedReadByteAlignedWord() { |
257 | 0 | if (next_byte_ + 1 < end_minus_8_ + 8) { |
258 | 0 | uint32_t ret = LoadLE16(next_byte_); |
259 | 0 | next_byte_ += 2; |
260 | 0 | return ret; |
261 | 0 | } |
262 | 0 | overread_bytes_ += 2; |
263 | 0 | return 0; |
264 | 0 | } |
265 | | |
266 | | uint64_t buf_; |
267 | | size_t bits_in_buf_; // [0, 64) |
268 | | const uint8_t* JXL_RESTRICT next_byte_; |
269 | | const uint8_t* end_minus_8_; // for refill bounds check |
270 | | const uint8_t* first_byte_; // for GetSpan |
271 | | |
272 | | // Number of bytes past the end that were loaded into the buf_. These bytes |
273 | | // are not read from memory, but instead assumed 0. It is an error (likely due |
274 | | // to an invalid stream) to Consume() more bits than specified in the range |
275 | | // passed to the constructor. |
276 | | uint64_t overread_bytes_{0}; |
277 | | bool close_called_{false}; |
278 | | |
279 | | uint64_t checked_out_of_bounds_bits_{0}; |
280 | | }; |
281 | | |
282 | | // Closes a BitReader when the BitReaderScopedCloser goes out of scope. When |
283 | | // closing the bit reader, if the status result was failure it sets this failure |
284 | | // to the passed variable pointer. Typical usage. |
285 | | // |
286 | | // Status ret = true; |
287 | | // { |
288 | | // BitReader reader(...); |
289 | | // BitReaderScopedCloser reader_closer(&reader, &ret); |
290 | | // |
291 | | // // ... code that can return errors here ... |
292 | | // } |
293 | | // // ... more code that doesn't use the BitReader. |
294 | | // return ret; |
295 | | |
296 | | class BitReaderScopedCloser { |
297 | | public: |
298 | | BitReaderScopedCloser(BitReader& reader, Status& status) |
299 | 97 | : reader_(&reader), status_(&status) {} |
300 | 97 | ~BitReaderScopedCloser() { |
301 | 97 | if (reader_ != nullptr) { |
302 | 97 | Status close_ret = reader_->Close(); |
303 | 97 | if (!close_ret) *status_ = close_ret; |
304 | 97 | } |
305 | 97 | } |
306 | | BitReaderScopedCloser(const BitReaderScopedCloser&) = delete; |
307 | | |
308 | | private: |
309 | | BitReader* reader_; |
310 | | Status* status_; |
311 | | }; |
312 | | |
313 | | } // namespace jxl |
314 | | |
315 | | #endif // LIB_JXL_DEC_BIT_READER_H_ |