Coverage Report

Created: 2026-11-12 07:35

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libjxl/lib/jxl/modular/encoding/dec_ma.cc
Line
Count
Source
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/modular/encoding/dec_ma.h"
7
8
#include <jxl/memory_manager.h>
9
10
#include <algorithm>
11
#include <cstddef>
12
#include <cstdint>
13
#include <limits>
14
#include <utility>
15
#include <vector>
16
17
#include "lib/jxl/base/printf_macros.h"
18
#include "lib/jxl/base/status.h"
19
#include "lib/jxl/dec_ans.h"
20
#include "lib/jxl/dec_bit_reader.h"
21
#include "lib/jxl/modular/encoding/ma_common.h"
22
#include "lib/jxl/modular/modular_image.h"
23
#include "lib/jxl/modular/options.h"
24
#include "lib/jxl/pack_signed.h"
25
26
namespace jxl {
27
28
namespace {
29
30
enum class NextAction { CHECK_AND_GO_LEFT, GO_RIGHT, POP };
31
32
struct WorkItem {
33
  size_t node_index;
34
  pixel_type orig_l;
35
  pixel_type orig_u;
36
  NextAction action;
37
};
38
39
41.2k
Status ValidateTree(const Tree& tree) {
40
41.2k
  if (tree.empty()) return true;
41
  // TODO(eustas): or invalid?
42
43
41.2k
  int num_properties = 0;
44
148k
  for (auto node : tree) {
45
148k
    if (node.property >= num_properties) {
46
5.86k
      num_properties = node.property + 1;
47
5.86k
    }
48
148k
  }
49
50
41.2k
  std::vector<std::pair<pixel_type, pixel_type>> property_ranges(
51
41.2k
      num_properties);
52
87.1k
  for (int i = 0; i < num_properties; i++) {
53
45.8k
    property_ranges[i].first = std::numeric_limits<pixel_type>::min();
54
45.8k
    property_ranges[i].second = std::numeric_limits<pixel_type>::max();
55
45.8k
  }
56
57
41.2k
  constexpr size_t kHeightLimit = 2048;
58
59
41.2k
  std::vector<WorkItem> stack;
60
41.2k
  stack.push_back({/*node_index=*/0, /*orig_l=*/0, /*orig_u=*/0,
61
41.2k
                   NextAction::CHECK_AND_GO_LEFT});
62
63
295k
  while (!stack.empty()) {
64
254k
    if (stack.size() >= kHeightLimit) return JXL_FAILURE("Tree too tall");
65
254k
    WorkItem& item = stack.back();
66
254k
    const auto& node = tree[item.node_index];
67
254k
    switch (item.action) {
68
148k
      case NextAction::CHECK_AND_GO_LEFT: {
69
148k
        int16_t p = node.property;
70
148k
        if (p == -1) {
71
94.6k
          stack.pop_back();
72
94.6k
          continue;
73
94.6k
        }
74
53.4k
        PropertyVal v = node.splitval;
75
53.4k
        pixel_type l = property_ranges[p].first;
76
53.4k
        pixel_type u = property_ranges[p].second;
77
53.4k
        if (l > v || u <= v) {
78
28
          return JXL_FAILURE("Invalid tree");
79
28
        }
80
53.4k
        item.orig_l = l;
81
53.4k
        item.orig_u = u;
82
53.4k
        item.action = NextAction::GO_RIGHT;
83
53.4k
        property_ranges[node.property].first = node.splitval + 1;
84
53.4k
        stack.push_back({/*node_index=*/node.lchild,
85
53.4k
                         /*orig_l=*/0, /*orig_u=*/0,
86
53.4k
                         NextAction::CHECK_AND_GO_LEFT});
87
53.4k
        continue;
88
53.4k
      }
89
90
53.3k
      case NextAction::GO_RIGHT:
91
53.3k
        item.action = NextAction::POP;
92
53.3k
        property_ranges[node.property].first = item.orig_l;
93
53.3k
        property_ranges[node.property].second = node.splitval;
94
53.3k
        stack.push_back({/*node_index=*/node.rchild,
95
53.3k
                         /*orig_l=*/0, /*orig_u=*/0,
96
53.3k
                         NextAction::CHECK_AND_GO_LEFT});
97
53.3k
        continue;
98
99
53.3k
      case NextAction::POP:
100
53.3k
        property_ranges[node.property].second = item.orig_u;
101
53.3k
        stack.pop_back();
102
53.3k
        continue;
103
254k
    }
104
254k
  }
105
106
41.2k
  return true;
107
41.2k
}
108
109
Status DecodeTree(BitReader* br, ANSSymbolReader* reader,
110
                  const std::vector<uint8_t>& context_map, Tree* tree,
111
41.4k
                  size_t tree_size_limit) {
112
41.4k
  size_t leaf_id = 0;
113
41.4k
  size_t to_decode = 1;
114
41.4k
  tree->clear();
115
644k
  while (to_decode > 0) {
116
602k
    JXL_RETURN_IF_ERROR(br->AllReadsWithinBounds());
117
602k
    if (tree->size() > tree_size_limit) {
118
16
      return JXL_FAILURE("Tree is too large: %" PRIuS " nodes vs %" PRIuS
119
16
                         " max nodes",
120
16
                         tree->size(), tree_size_limit);
121
16
    }
122
602k
    to_decode--;
123
602k
    uint32_t prop1 = reader->ReadHybridUint(kPropertyContext, br, context_map);
124
602k
    if (prop1 > 256) return JXL_FAILURE("Invalid tree property value");
125
602k
    int property = prop1 - 1;
126
602k
    if (property == -1) {
127
144k
      size_t predictor =
128
144k
          reader->ReadHybridUint(kPredictorContext, br, context_map);
129
144k
      if (predictor >= kNumModularPredictors) {
130
5
        return JXL_FAILURE("Invalid predictor");
131
5
      }
132
144k
      int64_t predictor_offset =
133
144k
          UnpackSigned(reader->ReadHybridUint(kOffsetContext, br, context_map));
134
144k
      uint32_t mul_log =
135
144k
          reader->ReadHybridUint(kMultiplierLogContext, br, context_map);
136
144k
      if (mul_log >= 31) {
137
3
        return JXL_FAILURE("Invalid multiplier logarithm");
138
3
      }
139
144k
      uint32_t mul_bits =
140
144k
          reader->ReadHybridUint(kMultiplierBitsContext, br, context_map);
141
144k
      if (mul_bits >= (1u << (31u - mul_log)) - 1u) {
142
3
        return JXL_FAILURE("Invalid multiplier");
143
3
      }
144
144k
      uint32_t multiplier = (mul_bits + 1U) << mul_log;
145
144k
      Predictor p = static_cast<Predictor>(static_cast<uint32_t>(predictor));
146
144k
      tree->emplace_back(-1, 0, static_cast<int>(leaf_id), 0, p,
147
144k
                         predictor_offset, multiplier);
148
144k
      leaf_id++;
149
144k
      continue;
150
144k
    }
151
458k
    int splitval =
152
458k
        UnpackSigned(reader->ReadHybridUint(kSplitValContext, br, context_map));
153
458k
    tree->emplace_back(
154
458k
        property, splitval, static_cast<int>(tree->size() + to_decode + 1),
155
458k
        static_cast<int>(tree->size() + to_decode + 2), Predictor::Zero, 0, 1);
156
458k
    to_decode += 2;
157
458k
  }
158
41.2k
  return ValidateTree(*tree);
159
41.4k
}
160
}  // namespace
161
162
Status DecodeTree(JxlMemoryManager* memory_manager, BitReader* br, Tree* tree,
163
41.9k
                  size_t tree_size_limit) {
164
41.9k
  std::vector<uint8_t> tree_context_map;
165
41.9k
  ANSCode tree_code;
166
41.9k
  JXL_RETURN_IF_ERROR(DecodeHistograms(memory_manager, br, kNumTreeContexts,
167
41.9k
                                       &tree_code, &tree_context_map));
168
  // TODO(eustas): investigate more infinite tree cases.
169
41.5k
  if (tree_code.degenerate_symbols[tree_context_map[kPropertyContext]] > 0) {
170
10
    return JXL_FAILURE("Infinite tree");
171
10
  }
172
82.9k
  JXL_ASSIGN_OR_RETURN(ANSSymbolReader reader,
173
82.9k
                       ANSSymbolReader::Create(&tree_code, br));
174
82.9k
  JXL_RETURN_IF_ERROR(DecodeTree(br, &reader, tree_context_map, tree,
175
82.9k
                                 std::min(tree_size_limit, kMaxTreeSize)));
176
41.2k
  if (!reader.CheckANSFinalState()) {
177
0
    return JXL_FAILURE("ANS decode final state failed");
178
0
  }
179
41.2k
  return true;
180
41.2k
}
181
182
}  // namespace jxl