Coverage Report

Created: 2024-05-21 06:24

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