/src/libjxl/lib/jxl/dec_ans.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_ANS_H_ |
7 | | #define LIB_JXL_DEC_ANS_H_ |
8 | | |
9 | | // Library to decode the ANS population counts from the bit-stream and build a |
10 | | // decoding table from them. |
11 | | |
12 | | #include <stddef.h> |
13 | | #include <stdint.h> |
14 | | |
15 | | #include <cstring> |
16 | | #include <vector> |
17 | | |
18 | | #include "lib/jxl/ans_common.h" |
19 | | #include "lib/jxl/ans_params.h" |
20 | | #include "lib/jxl/base/bits.h" |
21 | | #include "lib/jxl/base/byte_order.h" |
22 | | #include "lib/jxl/base/cache_aligned.h" |
23 | | #include "lib/jxl/base/compiler_specific.h" |
24 | | #include "lib/jxl/dec_bit_reader.h" |
25 | | #include "lib/jxl/dec_huffman.h" |
26 | | #include "lib/jxl/field_encodings.h" |
27 | | |
28 | | namespace jxl { |
29 | | |
30 | | class ANSSymbolReader; |
31 | | |
32 | | // Experiments show that best performance is typically achieved for a |
33 | | // split-exponent of 3 or 4. Trend seems to be that '4' is better |
34 | | // for large-ish pictures, and '3' better for rather small-ish pictures. |
35 | | // This is plausible - the more special symbols we have, the better |
36 | | // statistics we need to get a benefit out of them. |
37 | | |
38 | | // Our hybrid-encoding scheme has dedicated tokens for the smallest |
39 | | // (1 << split_exponents) numbers, and for the rest |
40 | | // encodes (number of bits) + (msb_in_token sub-leading binary digits) + |
41 | | // (lsb_in_token lowest binary digits) in the token, with the remaining bits |
42 | | // then being encoded as data. |
43 | | // |
44 | | // Example with split_exponent = 4, msb_in_token = 2, lsb_in_token = 0. |
45 | | // |
46 | | // Numbers N in [0 .. 15]: |
47 | | // These get represented as (token=N, bits=''). |
48 | | // Numbers N >= 16: |
49 | | // If n is such that 2**n <= N < 2**(n+1), |
50 | | // and m = N - 2**n is the 'mantissa', |
51 | | // these get represented as: |
52 | | // (token=split_token + |
53 | | // ((n - split_exponent) * 4) + |
54 | | // (m >> (n - msb_in_token)), |
55 | | // bits=m & (1 << (n - msb_in_token)) - 1) |
56 | | // Specifically, we would get: |
57 | | // N = 0 - 15: (token=N, nbits=0, bits='') |
58 | | // N = 16 (10000): (token=16, nbits=2, bits='00') |
59 | | // N = 17 (10001): (token=16, nbits=2, bits='01') |
60 | | // N = 20 (10100): (token=17, nbits=2, bits='00') |
61 | | // N = 24 (11000): (token=18, nbits=2, bits='00') |
62 | | // N = 28 (11100): (token=19, nbits=2, bits='00') |
63 | | // N = 32 (100000): (token=20, nbits=3, bits='000') |
64 | | // N = 65535: (token=63, nbits=13, bits='1111111111111') |
65 | | struct HybridUintConfig { |
66 | | uint32_t split_exponent; |
67 | | uint32_t split_token; |
68 | | uint32_t msb_in_token; |
69 | | uint32_t lsb_in_token; |
70 | | JXL_INLINE void Encode(uint32_t value, uint32_t* JXL_RESTRICT token, |
71 | | uint32_t* JXL_RESTRICT nbits, |
72 | 192k | uint32_t* JXL_RESTRICT bits) const { |
73 | 192k | if (value < split_token) { |
74 | 91.2k | *token = value; |
75 | 91.2k | *nbits = 0; |
76 | 91.2k | *bits = 0; |
77 | 101k | } else { |
78 | 101k | uint32_t n = FloorLog2Nonzero(value); |
79 | 101k | uint32_t m = value - (1 << n); |
80 | 101k | *token = split_token + |
81 | 101k | ((n - split_exponent) << (msb_in_token + lsb_in_token)) + |
82 | 101k | ((m >> (n - msb_in_token)) << lsb_in_token) + |
83 | 101k | (m & ((1 << lsb_in_token) - 1)); |
84 | 101k | *nbits = n - msb_in_token - lsb_in_token; |
85 | 101k | *bits = (value >> lsb_in_token) & ((1UL << *nbits) - 1); |
86 | 101k | } |
87 | 192k | } |
88 | | |
89 | | explicit HybridUintConfig(uint32_t split_exponent = 4, |
90 | | uint32_t msb_in_token = 2, |
91 | | uint32_t lsb_in_token = 0) |
92 | | : split_exponent(split_exponent), |
93 | | split_token(1 << split_exponent), |
94 | | msb_in_token(msb_in_token), |
95 | 1.18M | lsb_in_token(lsb_in_token) { |
96 | 1.18M | JXL_DASSERT(split_exponent >= msb_in_token + lsb_in_token); |
97 | 1.18M | } |
98 | | }; |
99 | | |
100 | | struct LZ77Params : public Fields { |
101 | | LZ77Params(); |
102 | | JXL_FIELDS_NAME(LZ77Params) |
103 | | Status VisitFields(Visitor* JXL_RESTRICT visitor) override; |
104 | | bool enabled; |
105 | | |
106 | | // Symbols above min_symbol use a special hybrid uint encoding and |
107 | | // represent a length, to be added to min_length. |
108 | | uint32_t min_symbol; |
109 | | uint32_t min_length; |
110 | | |
111 | | // Not serialized by VisitFields. |
112 | | HybridUintConfig length_uint_config{0, 0, 0}; |
113 | | |
114 | | size_t nonserialized_distance_context; |
115 | | }; |
116 | | |
117 | | static constexpr size_t kWindowSize = 1 << 20; |
118 | | static constexpr size_t kNumSpecialDistances = 120; |
119 | | // Table of special distance codes from WebP lossless. |
120 | | static constexpr int8_t kSpecialDistances[kNumSpecialDistances][2] = { |
121 | | {0, 1}, {1, 0}, {1, 1}, {-1, 1}, {0, 2}, {2, 0}, {1, 2}, {-1, 2}, |
122 | | {2, 1}, {-2, 1}, {2, 2}, {-2, 2}, {0, 3}, {3, 0}, {1, 3}, {-1, 3}, |
123 | | {3, 1}, {-3, 1}, {2, 3}, {-2, 3}, {3, 2}, {-3, 2}, {0, 4}, {4, 0}, |
124 | | {1, 4}, {-1, 4}, {4, 1}, {-4, 1}, {3, 3}, {-3, 3}, {2, 4}, {-2, 4}, |
125 | | {4, 2}, {-4, 2}, {0, 5}, {3, 4}, {-3, 4}, {4, 3}, {-4, 3}, {5, 0}, |
126 | | {1, 5}, {-1, 5}, {5, 1}, {-5, 1}, {2, 5}, {-2, 5}, {5, 2}, {-5, 2}, |
127 | | {4, 4}, {-4, 4}, {3, 5}, {-3, 5}, {5, 3}, {-5, 3}, {0, 6}, {6, 0}, |
128 | | {1, 6}, {-1, 6}, {6, 1}, {-6, 1}, {2, 6}, {-2, 6}, {6, 2}, {-6, 2}, |
129 | | {4, 5}, {-4, 5}, {5, 4}, {-5, 4}, {3, 6}, {-3, 6}, {6, 3}, {-6, 3}, |
130 | | {0, 7}, {7, 0}, {1, 7}, {-1, 7}, {5, 5}, {-5, 5}, {7, 1}, {-7, 1}, |
131 | | {4, 6}, {-4, 6}, {6, 4}, {-6, 4}, {2, 7}, {-2, 7}, {7, 2}, {-7, 2}, |
132 | | {3, 7}, {-3, 7}, {7, 3}, {-7, 3}, {5, 6}, {-5, 6}, {6, 5}, {-6, 5}, |
133 | | {8, 0}, {4, 7}, {-4, 7}, {7, 4}, {-7, 4}, {8, 1}, {8, 2}, {6, 6}, |
134 | | {-6, 6}, {8, 3}, {5, 7}, {-5, 7}, {7, 5}, {-7, 5}, {8, 4}, {6, 7}, |
135 | | {-6, 7}, {7, 6}, {-7, 6}, {8, 5}, {7, 7}, {-7, 7}, {8, 6}, {8, 7}}; |
136 | | |
137 | | struct ANSCode { |
138 | | CacheAlignedUniquePtr alias_tables; |
139 | | std::vector<HuffmanDecodingData> huffman_data; |
140 | | std::vector<HybridUintConfig> uint_config; |
141 | | std::vector<int> degenerate_symbols; |
142 | | bool use_prefix_code; |
143 | | uint8_t log_alpha_size; // for ANS. |
144 | | LZ77Params lz77; |
145 | | // Maximum number of bits necessary to represent the result of a |
146 | | // ReadHybridUint call done with this ANSCode. |
147 | | size_t max_num_bits = 0; |
148 | | void UpdateMaxNumBits(size_t ctx, size_t symbol); |
149 | | }; |
150 | | |
151 | | class ANSSymbolReader { |
152 | | public: |
153 | | // Invalid symbol reader, to be overwritten. |
154 | 163k | ANSSymbolReader() = default; |
155 | | ANSSymbolReader(const ANSCode* code, BitReader* JXL_RESTRICT br, |
156 | | size_t distance_multiplier = 0) |
157 | | : alias_tables_( |
158 | | reinterpret_cast<AliasTable::Entry*>(code->alias_tables.get())), |
159 | | huffman_data_(code->huffman_data.data()), |
160 | | use_prefix_code_(code->use_prefix_code), |
161 | 137k | configs(code->uint_config.data()) { |
162 | 137k | if (!use_prefix_code_) { |
163 | 66.4k | state_ = static_cast<uint32_t>(br->ReadFixedBits<32>()); |
164 | 66.4k | log_alpha_size_ = code->log_alpha_size; |
165 | 66.4k | log_entry_size_ = ANS_LOG_TAB_SIZE - code->log_alpha_size; |
166 | 66.4k | entry_size_minus_1_ = (1 << log_entry_size_) - 1; |
167 | 71.0k | } else { |
168 | 71.0k | state_ = (ANS_SIGNATURE << 16u); |
169 | 71.0k | } |
170 | 137k | if (!code->lz77.enabled) return; |
171 | | // a std::vector incurs unacceptable decoding speed loss because of |
172 | | // initialization. |
173 | 24.9k | lz77_window_storage_ = AllocateArray(kWindowSize * sizeof(uint32_t)); |
174 | 24.9k | lz77_window_ = reinterpret_cast<uint32_t*>(lz77_window_storage_.get()); |
175 | 24.9k | lz77_ctx_ = code->lz77.nonserialized_distance_context; |
176 | 24.9k | lz77_length_uint_ = code->lz77.length_uint_config; |
177 | 24.9k | lz77_threshold_ = code->lz77.min_symbol; |
178 | 24.9k | lz77_min_length_ = code->lz77.min_length; |
179 | 24.9k | num_special_distances_ = |
180 | 24.9k | distance_multiplier == 0 ? 0 : kNumSpecialDistances; |
181 | 469k | for (size_t i = 0; i < num_special_distances_; i++) { |
182 | 444k | int dist = kSpecialDistances[i][0]; |
183 | 444k | dist += static_cast<int>(distance_multiplier) * kSpecialDistances[i][1]; |
184 | 444k | if (dist < 1) dist = 1; |
185 | 444k | special_distances_[i] = dist; |
186 | 444k | } |
187 | 24.9k | } |
188 | | |
189 | | JXL_INLINE size_t ReadSymbolANSWithoutRefill(const size_t histo_idx, |
190 | 983M | BitReader* JXL_RESTRICT br) { |
191 | 983M | const uint32_t res = state_ & (ANS_TAB_SIZE - 1u); |
192 | | |
193 | 983M | const AliasTable::Entry* table = |
194 | 983M | &alias_tables_[histo_idx << log_alpha_size_]; |
195 | 983M | const AliasTable::Symbol symbol = |
196 | 983M | AliasTable::Lookup(table, res, log_entry_size_, entry_size_minus_1_); |
197 | 983M | state_ = symbol.freq * (state_ >> ANS_LOG_TAB_SIZE) + symbol.offset; |
198 | | |
199 | 983M | #if 1 |
200 | | // Branchless version is about equally fast on SKX. |
201 | 983M | const uint32_t new_state = |
202 | 983M | (state_ << 16u) | static_cast<uint32_t>(br->PeekFixedBits<16>()); |
203 | 983M | const bool normalize = state_ < (1u << 16u); |
204 | 983M | state_ = normalize ? new_state : state_; |
205 | 983M | br->Consume(normalize ? 16 : 0); |
206 | | #else |
207 | | if (JXL_UNLIKELY(state_ < (1u << 16u))) { |
208 | | state_ = (state_ << 16u) | br->PeekFixedBits<16>(); |
209 | | br->Consume(16); |
210 | | } |
211 | | #endif |
212 | 983M | const uint32_t next_res = state_ & (ANS_TAB_SIZE - 1u); |
213 | 983M | AliasTable::Prefetch(table, next_res, log_entry_size_); |
214 | | |
215 | 983M | return symbol.value; |
216 | 983M | } |
217 | | |
218 | | JXL_INLINE size_t ReadSymbolHuffWithoutRefill(const size_t histo_idx, |
219 | 2.32G | BitReader* JXL_RESTRICT br) { |
220 | 2.32G | return huffman_data_[histo_idx].ReadSymbol(br); |
221 | 2.32G | } |
222 | | |
223 | | JXL_INLINE size_t ReadSymbolWithoutRefill(const size_t histo_idx, |
224 | 3.30G | BitReader* JXL_RESTRICT br) { |
225 | | // TODO(veluca): hoist if in hotter loops. |
226 | 3.30G | if (JXL_UNLIKELY(use_prefix_code_)) { |
227 | 2.32G | return ReadSymbolHuffWithoutRefill(histo_idx, br); |
228 | 2.32G | } |
229 | 982M | return ReadSymbolANSWithoutRefill(histo_idx, br); |
230 | 3.30G | } |
231 | | |
232 | | JXL_INLINE size_t ReadSymbol(const size_t histo_idx, |
233 | 0 | BitReader* JXL_RESTRICT br) { |
234 | 0 | br->Refill(); |
235 | 0 | return ReadSymbolWithoutRefill(histo_idx, br); |
236 | 0 | } |
237 | | |
238 | | #ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION |
239 | 119k | bool CheckANSFinalState() const { return true; } |
240 | | #else |
241 | | bool CheckANSFinalState() const { return state_ == (ANS_SIGNATURE << 16u); } |
242 | | #endif |
243 | | |
244 | | template <typename BitReader> |
245 | | static JXL_INLINE uint32_t ReadHybridUintConfig( |
246 | 3.30G | const HybridUintConfig& config, size_t token, BitReader* br) { |
247 | 3.30G | size_t split_token = config.split_token; |
248 | 3.30G | size_t msb_in_token = config.msb_in_token; |
249 | 3.30G | size_t lsb_in_token = config.lsb_in_token; |
250 | 3.30G | size_t split_exponent = config.split_exponent; |
251 | | // Fast-track version of hybrid integer decoding. |
252 | 3.30G | if (token < split_token) return token; |
253 | 401M | uint32_t nbits = split_exponent - (msb_in_token + lsb_in_token) + |
254 | 401M | ((token - split_token) >> (msb_in_token + lsb_in_token)); |
255 | | // Max amount of bits for ReadBits is 32 and max valid left shift is 29 |
256 | | // bits. However, for speed no error is propagated here, instead limit the |
257 | | // nbits size. If nbits > 29, the code stream is invalid, but no error is |
258 | | // returned. |
259 | | // Note that in most cases we will emit an error if the histogram allows |
260 | | // representing numbers that would cause invalid shifts, but we need to |
261 | | // keep this check as when LZ77 is enabled it might make sense to have an |
262 | | // histogram that could in principle cause invalid shifts. |
263 | 401M | nbits &= 31u; |
264 | 401M | uint32_t low = token & ((1 << lsb_in_token) - 1); |
265 | 401M | token >>= lsb_in_token; |
266 | 401M | const size_t bits = br->PeekBits(nbits); |
267 | 401M | br->Consume(nbits); |
268 | 401M | size_t ret = (((((1 << msb_in_token) | (token & ((1 << msb_in_token) - 1))) |
269 | 401M | << nbits) | |
270 | 401M | bits) |
271 | 401M | << lsb_in_token) | |
272 | 401M | low; |
273 | | // TODO(eustas): mark BitReader as unhealthy if nbits > 29 or ret does not |
274 | | // fit uint32_t |
275 | 401M | return static_cast<uint32_t>(ret); |
276 | 3.30G | } |
277 | | |
278 | | // Takes a *clustered* idx. Can only use if HuffRleOnly() is true. |
279 | | void ReadHybridUintClusteredHuffRleOnly(size_t ctx, |
280 | | BitReader* JXL_RESTRICT br, |
281 | 44.6k | uint32_t* value, uint32_t* run) { |
282 | 44.6k | JXL_DASSERT(HuffRleOnly()); |
283 | 44.6k | br->Refill(); // covers ReadSymbolWithoutRefill + PeekBits |
284 | 44.6k | size_t token = ReadSymbolHuffWithoutRefill(ctx, br); |
285 | 44.6k | if (JXL_UNLIKELY(token >= lz77_threshold_)) { |
286 | 0 | *run = |
287 | 0 | ReadHybridUintConfig(lz77_length_uint_, token - lz77_threshold_, br) + |
288 | 0 | lz77_min_length_ - 1; |
289 | 0 | return; |
290 | 0 | } |
291 | 44.6k | *value = ReadHybridUintConfig(configs[ctx], token, br); |
292 | 44.6k | } |
293 | 51.8k | bool HuffRleOnly() { |
294 | 51.8k | if (lz77_window_ == nullptr) return false; |
295 | 45.0k | if (!use_prefix_code_) return false; |
296 | 402k | for (size_t i = 0; i < kHuffmanTableBits; i++) { |
297 | 357k | if (huffman_data_[lz77_ctx_].table_[i].bits) return false; |
298 | 357k | if (huffman_data_[lz77_ctx_].table_[i].value != 1) return false; |
299 | 357k | } |
300 | 44.6k | if (configs[lz77_ctx_].split_token > 1) return false; |
301 | 44.6k | return true; |
302 | 44.6k | } |
303 | | |
304 | | // Takes a *clustered* idx. |
305 | 3.72G | size_t ReadHybridUintClustered(size_t ctx, BitReader* JXL_RESTRICT br) { |
306 | 3.72G | if (JXL_UNLIKELY(num_to_copy_ > 0)) { |
307 | 434M | size_t ret = lz77_window_[(copy_pos_++) & kWindowMask]; |
308 | 434M | num_to_copy_--; |
309 | 434M | lz77_window_[(num_decoded_++) & kWindowMask] = ret; |
310 | 434M | return ret; |
311 | 434M | } |
312 | 3.29G | br->Refill(); // covers ReadSymbolWithoutRefill + PeekBits |
313 | 3.29G | size_t token = ReadSymbolWithoutRefill(ctx, br); |
314 | 3.29G | if (JXL_UNLIKELY(token >= lz77_threshold_)) { |
315 | 15.0M | num_to_copy_ = |
316 | 15.0M | ReadHybridUintConfig(lz77_length_uint_, token - lz77_threshold_, br) + |
317 | 15.0M | lz77_min_length_; |
318 | 15.0M | br->Refill(); // covers ReadSymbolWithoutRefill + PeekBits |
319 | | // Distance code. |
320 | 15.0M | size_t token = ReadSymbolWithoutRefill(lz77_ctx_, br); |
321 | 15.0M | size_t distance = ReadHybridUintConfig(configs[lz77_ctx_], token, br); |
322 | 15.0M | if (JXL_LIKELY(distance < num_special_distances_)) { |
323 | 8.23k | distance = special_distances_[distance]; |
324 | 15.0M | } else { |
325 | 15.0M | distance = distance + 1 - num_special_distances_; |
326 | 15.0M | } |
327 | 15.0M | if (JXL_UNLIKELY(distance > num_decoded_)) { |
328 | 5.83M | distance = num_decoded_; |
329 | 5.83M | } |
330 | 15.0M | if (JXL_UNLIKELY(distance > kWindowSize)) { |
331 | 4.71M | distance = kWindowSize; |
332 | 4.71M | } |
333 | 15.0M | copy_pos_ = num_decoded_ - distance; |
334 | 15.0M | if (JXL_UNLIKELY(distance == 0)) { |
335 | 1.95k | JXL_DASSERT(lz77_window_ != nullptr); |
336 | | // distance 0 -> num_decoded_ == copy_pos_ == 0 |
337 | 1.95k | size_t to_fill = std::min<size_t>(num_to_copy_, kWindowSize); |
338 | 1.95k | memset(lz77_window_, 0, to_fill * sizeof(lz77_window_[0])); |
339 | 1.95k | } |
340 | | // TODO(eustas): overflow; mark BitReader as unhealthy |
341 | 15.0M | if (num_to_copy_ < lz77_min_length_) return 0; |
342 | 15.0M | return ReadHybridUintClustered(ctx, br); // will trigger a copy. |
343 | 15.0M | } |
344 | 3.27G | size_t ret = ReadHybridUintConfig(configs[ctx], token, br); |
345 | 3.27G | if (lz77_window_) lz77_window_[(num_decoded_++) & kWindowMask] = ret; |
346 | 3.27G | return ret; |
347 | 3.29G | } |
348 | | |
349 | | JXL_INLINE size_t ReadHybridUint(size_t ctx, BitReader* JXL_RESTRICT br, |
350 | 2.99G | const std::vector<uint8_t>& context_map) { |
351 | 2.99G | return ReadHybridUintClustered(context_map[ctx], br); |
352 | 2.99G | } |
353 | | |
354 | | // ctx is a *clustered* context! |
355 | | // This function will modify the ANS state as if `count` symbols have been |
356 | | // decoded. |
357 | 239k | bool IsSingleValueAndAdvance(size_t ctx, uint32_t* value, size_t count) { |
358 | | // TODO(veluca): No optimization for Huffman mode yet. |
359 | 239k | if (use_prefix_code_) return false; |
360 | | // TODO(eustas): propagate "degenerate_symbol" to simplify this method. |
361 | 44.4k | const uint32_t res = state_ & (ANS_TAB_SIZE - 1u); |
362 | 44.4k | const AliasTable::Entry* table = &alias_tables_[ctx << log_alpha_size_]; |
363 | 44.4k | AliasTable::Symbol symbol = |
364 | 44.4k | AliasTable::Lookup(table, res, log_entry_size_, entry_size_minus_1_); |
365 | 44.4k | if (symbol.freq != ANS_TAB_SIZE) return false; |
366 | 13.7k | if (configs[ctx].split_token <= symbol.value) return false; |
367 | 13.5k | if (symbol.value >= lz77_threshold_) return false; |
368 | 13.3k | *value = symbol.value; |
369 | 13.3k | if (lz77_window_) { |
370 | 1.78M | for (size_t i = 0; i < count; i++) { |
371 | 1.78M | lz77_window_[(num_decoded_++) & kWindowMask] = symbol.value; |
372 | 1.78M | } |
373 | 841 | } |
374 | 13.3k | return true; |
375 | 13.5k | } |
376 | | |
377 | | static constexpr size_t kMaxCheckpointInterval = 512; |
378 | | struct Checkpoint { |
379 | | uint32_t state; |
380 | | uint32_t num_to_copy; |
381 | | uint32_t copy_pos; |
382 | | uint32_t num_decoded; |
383 | | uint32_t lz77_window[kMaxCheckpointInterval]; |
384 | | }; |
385 | 11.2k | void Save(Checkpoint* checkpoint) { |
386 | 11.2k | checkpoint->state = state_; |
387 | 11.2k | checkpoint->num_decoded = num_decoded_; |
388 | 11.2k | checkpoint->num_to_copy = num_to_copy_; |
389 | 11.2k | checkpoint->copy_pos = copy_pos_; |
390 | 11.2k | if (lz77_window_) { |
391 | 7.63k | size_t win_start = num_decoded_ & kWindowMask; |
392 | 7.63k | size_t win_end = (num_decoded_ + kMaxCheckpointInterval) & kWindowMask; |
393 | 7.63k | if (win_end > win_start) { |
394 | 7.63k | memcpy(checkpoint->lz77_window, lz77_window_ + win_start, |
395 | 7.63k | (win_end - win_start) * sizeof(*lz77_window_)); |
396 | 7.63k | } else { |
397 | 0 | memcpy(checkpoint->lz77_window, lz77_window_ + win_start, |
398 | 0 | (kWindowSize - win_start) * sizeof(*lz77_window_)); |
399 | 0 | memcpy(checkpoint->lz77_window + (kWindowSize - win_start), |
400 | 0 | lz77_window_, win_end * sizeof(*lz77_window_)); |
401 | 0 | } |
402 | 7.63k | } |
403 | 11.2k | } |
404 | 5.74k | void Restore(const Checkpoint& checkpoint) { |
405 | 5.74k | state_ = checkpoint.state; |
406 | 5.74k | JXL_DASSERT(num_decoded_ <= |
407 | 5.74k | checkpoint.num_decoded + kMaxCheckpointInterval); |
408 | 5.74k | num_decoded_ = checkpoint.num_decoded; |
409 | 5.74k | num_to_copy_ = checkpoint.num_to_copy; |
410 | 5.74k | copy_pos_ = checkpoint.copy_pos; |
411 | 5.74k | if (lz77_window_) { |
412 | 4.67k | size_t win_start = num_decoded_ & kWindowMask; |
413 | 4.67k | size_t win_end = (num_decoded_ + kMaxCheckpointInterval) & kWindowMask; |
414 | 4.67k | if (win_end > win_start) { |
415 | 4.67k | memcpy(lz77_window_ + win_start, checkpoint.lz77_window, |
416 | 4.67k | (win_end - win_start) * sizeof(*lz77_window_)); |
417 | 4.67k | } else { |
418 | 0 | memcpy(lz77_window_ + win_start, checkpoint.lz77_window, |
419 | 0 | (kWindowSize - win_start) * sizeof(*lz77_window_)); |
420 | 0 | memcpy(lz77_window_, checkpoint.lz77_window + (kWindowSize - win_start), |
421 | 0 | win_end * sizeof(*lz77_window_)); |
422 | 0 | } |
423 | 4.67k | } |
424 | 5.74k | } |
425 | | |
426 | | private: |
427 | | const AliasTable::Entry* JXL_RESTRICT alias_tables_; // not owned |
428 | | const HuffmanDecodingData* huffman_data_; |
429 | | bool use_prefix_code_; |
430 | | uint32_t state_ = ANS_SIGNATURE << 16u; |
431 | | const HybridUintConfig* JXL_RESTRICT configs; |
432 | | uint32_t log_alpha_size_{}; |
433 | | uint32_t log_entry_size_{}; |
434 | | uint32_t entry_size_minus_1_{}; |
435 | | |
436 | | // LZ77 structures and constants. |
437 | | static constexpr size_t kWindowMask = kWindowSize - 1; |
438 | | CacheAlignedUniquePtr lz77_window_storage_; |
439 | | uint32_t* lz77_window_ = nullptr; |
440 | | uint32_t num_decoded_ = 0; |
441 | | uint32_t num_to_copy_ = 0; |
442 | | uint32_t copy_pos_ = 0; |
443 | | uint32_t lz77_ctx_ = 0; |
444 | | uint32_t lz77_min_length_ = 0; |
445 | | uint32_t lz77_threshold_ = 1 << 20; // bigger than any symbol. |
446 | | HybridUintConfig lz77_length_uint_; |
447 | | uint32_t special_distances_[kNumSpecialDistances]{}; |
448 | | uint32_t num_special_distances_{}; |
449 | | }; |
450 | | |
451 | | Status DecodeHistograms(BitReader* br, size_t num_contexts, ANSCode* code, |
452 | | std::vector<uint8_t>* context_map, |
453 | | bool disallow_lz77 = false); |
454 | | |
455 | | // Exposed for tests. |
456 | | Status DecodeUintConfigs(size_t log_alpha_size, |
457 | | std::vector<HybridUintConfig>* uint_config, |
458 | | BitReader* br); |
459 | | |
460 | | } // namespace jxl |
461 | | |
462 | | #endif // LIB_JXL_DEC_ANS_H_ |