Coverage Report

Created: 2025-06-22 08:04

/src/libjxl/lib/jxl/ans_common.cc
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
#include "lib/jxl/ans_common.h"
7
8
#include <cstddef>
9
#include <cstdint>
10
#include <numeric>
11
#include <vector>
12
13
#include "lib/jxl/ans_params.h"
14
#include "lib/jxl/base/status.h"
15
16
namespace jxl {
17
18
12.1k
std::vector<int32_t> CreateFlatHistogram(int length, int total_count) {
19
12.1k
  JXL_DASSERT(length > 0);
20
12.1k
  JXL_DASSERT(length <= total_count);
21
12.1k
  const int count = total_count / length;
22
12.1k
  std::vector<int32_t> result(length, count);
23
12.1k
  const int rem_counts = total_count % length;
24
101k
  for (int i = 0; i < rem_counts; ++i) {
25
89.3k
    ++result[i];
26
89.3k
  }
27
12.1k
  return result;
28
12.1k
}
29
30
// First, all trailing non-occurring symbols are removed from the distribution;
31
// if this leaves the distribution empty, a placeholder symbol with max weight
32
// is  added. This ensures that the resulting distribution sums to total table
33
// size. Then, `entry_size` is chosen to be the largest power of two so that
34
// `table_size` = ANS_TAB_SIZE/`entry_size` is at least as big as the
35
// distribution size.
36
// Note that each entry will only ever contain two different symbols, and
37
// consecutive ranges of offsets, which allows us to use a compact
38
// representation.
39
// Each entry is initialized with only the (symbol=i, offset) pairs; then
40
// positions for which the entry overflows (i.e. distribution[i] > entry_size)
41
// or is not full are computed, and put into a stack in increasing order.
42
// Missing symbols in the distribution are padded with 0 (because `table_size`
43
// >= number of symbols). The `cutoff` value for each entry is initialized to
44
// the number of occupied slots in that entry (i.e. `distributions[i]`). While
45
// the overflowing-symbol stack is not empty (which implies that the
46
// underflowing-symbol stack also is not), the top overfull and underfull
47
// positions are popped from the stack; the empty slots in the underfull entry
48
// are then filled with as many slots as needed from the overfull entry; such
49
// slots are placed after the slots in the overfull entry, and `offsets[1]` is
50
// computed accordingly. The formerly underfull entry is thus now neither
51
// underfull nor overfull, and represents exactly two symbols. The overfull
52
// entry might be either overfull or underfull, and is pushed into the
53
// corresponding stack.
54
Status InitAliasTable(std::vector<int32_t> distribution, uint32_t log_range,
55
                      size_t log_alpha_size,
56
57.5k
                      AliasTable::Entry* JXL_RESTRICT a) {
57
57.5k
  const uint32_t range = 1 << log_range;
58
57.5k
  const size_t table_size = 1 << log_alpha_size;
59
57.5k
  JXL_ENSURE(table_size <= range);
60
57.5k
  while (!distribution.empty() && distribution.back() == 0) {
61
0
    distribution.pop_back();
62
0
  }
63
  // Ensure that a valid table is always returned, even for an empty
64
  // alphabet. Otherwise, a specially-crafted stream might crash the
65
  // decoder.
66
57.5k
  if (distribution.empty()) {
67
0
    distribution.emplace_back(range);
68
0
  }
69
57.5k
  JXL_ENSURE(distribution.size() <= table_size);
70
57.5k
  const uint32_t entry_size = range >> log_alpha_size;  // this is exact
71
57.5k
  int single_symbol = -1;
72
57.5k
  int sum = 0;
73
  // Special case for single-symbol distributions, that ensures that the state
74
  // does not change when decoding from such a distribution. Note that, since we
75
  // hardcode offset0 == 0, it is not straightforward (if at all possible) to
76
  // fix the general case to produce this result.
77
774k
  for (size_t sym = 0; sym < distribution.size(); sym++) {
78
717k
    int32_t v = distribution[sym];
79
717k
    sum += v;
80
717k
    if (v == ANS_TAB_SIZE) {
81
21.8k
      JXL_ENSURE(single_symbol == -1);
82
21.8k
      single_symbol = sym;
83
21.8k
    }
84
717k
  }
85
57.5k
  JXL_ENSURE(static_cast<uint32_t>(sum) == range);
86
57.5k
  if (single_symbol != -1) {
87
21.8k
    uint8_t sym = single_symbol;
88
21.8k
    JXL_ENSURE(single_symbol == sym);
89
1.34M
    for (size_t i = 0; i < table_size; i++) {
90
1.31M
      a[i].right_value = sym;
91
1.31M
      a[i].cutoff = 0;
92
1.31M
      a[i].offsets1 = entry_size * i;
93
1.31M
      a[i].freq0 = 0;
94
1.31M
      a[i].freq1_xor_freq0 = ANS_TAB_SIZE;
95
1.31M
    }
96
21.8k
    return true;
97
21.8k
  }
98
99
35.6k
  std::vector<uint32_t> underfull_posn;
100
35.6k
  std::vector<uint32_t> overfull_posn;
101
35.6k
  std::vector<uint32_t> cutoffs(1 << log_alpha_size);
102
  // Initialize entries.
103
614k
  for (size_t i = 0; i < distribution.size(); i++) {
104
578k
    cutoffs[i] = distribution[i];
105
578k
    if (cutoffs[i] > entry_size) {
106
332k
      overfull_posn.push_back(i);
107
332k
    } else if (cutoffs[i] < entry_size) {
108
192k
      underfull_posn.push_back(i);
109
192k
    }
110
578k
  }
111
1.87M
  for (uint32_t i = distribution.size(); i < table_size; i++) {
112
1.84M
    cutoffs[i] = 0;
113
1.84M
    underfull_posn.push_back(i);
114
1.84M
  }
115
  // Reassign overflow/underflow values.
116
2.25M
  while (!overfull_posn.empty()) {
117
2.21M
    uint32_t overfull_i = overfull_posn.back();
118
2.21M
    overfull_posn.pop_back();
119
2.21M
    JXL_ENSURE(!underfull_posn.empty());
120
2.21M
    uint32_t underfull_i = underfull_posn.back();
121
2.21M
    underfull_posn.pop_back();
122
2.21M
    uint32_t underfull_by = entry_size - cutoffs[underfull_i];
123
2.21M
    cutoffs[overfull_i] -= underfull_by;
124
    // overfull positions have their original symbols
125
2.21M
    a[underfull_i].right_value = overfull_i;
126
2.21M
    a[underfull_i].offsets1 = cutoffs[overfull_i];
127
    // Slots in the right part of entry underfull_i were taken from the end
128
    // of the symbols in entry overfull_i.
129
2.21M
    if (cutoffs[overfull_i] < entry_size) {
130
182k
      underfull_posn.push_back(overfull_i);
131
2.03M
    } else if (cutoffs[overfull_i] > entry_size) {
132
1.88M
      overfull_posn.push_back(overfull_i);
133
1.88M
    }
134
2.21M
  }
135
2.45M
  for (uint32_t i = 0; i < table_size; i++) {
136
    // cutoffs[i] is properly initialized but the clang-analyzer doesn't infer
137
    // it since it is partially initialized across two for-loops.
138
    // NOLINTNEXTLINE(clang-analyzer-core.UndefinedBinaryOperatorResult)
139
2.41M
    if (cutoffs[i] == entry_size) {
140
204k
      a[i].right_value = i;
141
204k
      a[i].offsets1 = 0;
142
204k
      a[i].cutoff = 0;
143
2.21M
    } else {
144
      // Note that, if cutoff is not equal to entry_size,
145
      // a[i].offsets1 was initialized with (overfull cutoff) -
146
      // (entry_size - a[i].cutoff). Thus, subtracting
147
      // a[i].cutoff cannot make it negative.
148
2.21M
      a[i].offsets1 -= cutoffs[i];
149
2.21M
      a[i].cutoff = cutoffs[i];
150
2.21M
    }
151
2.41M
    const size_t freq0 = i < distribution.size() ? distribution[i] : 0;
152
2.41M
    const size_t i1 = a[i].right_value;
153
18.4E
    const size_t freq1 = i1 < distribution.size() ? distribution[i1] : 0;
154
2.41M
    a[i].freq0 = static_cast<uint16_t>(freq0);
155
2.41M
    a[i].freq1_xor_freq0 = static_cast<uint16_t>(freq1 ^ freq0);
156
2.41M
  }
157
35.6k
  return true;
158
35.6k
}
159
160
}  // namespace jxl