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