Coverage Report

Created: 2022-08-24 06:33

/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_