Coverage Report

Created: 2025-12-13 07:57

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
28.9k
Status ValidateTree(const Tree &tree) {
31
28.9k
  int num_properties = 0;
32
121k
  for (auto node : tree) {
33
121k
    if (node.property >= num_properties) {
34
3.09k
      num_properties = node.property + 1;
35
3.09k
    }
36
121k
  }
37
28.9k
  std::vector<int> height(tree.size());
38
28.9k
  std::vector<std::pair<pixel_type, pixel_type>> property_ranges(
39
28.9k
      num_properties * tree.size());
40
46.1k
  for (int i = 0; i < num_properties; i++) {
41
17.1k
    property_ranges[i].first = std::numeric_limits<pixel_type>::min();
42
17.1k
    property_ranges[i].second = std::numeric_limits<pixel_type>::max();
43
17.1k
  }
44
28.9k
  const int kHeightLimit = 2048;
45
150k
  for (size_t i = 0; i < tree.size(); i++) {
46
121k
    if (height[i] > kHeightLimit) {
47
0
      return JXL_FAILURE("Tree too tall: %d", height[i]);
48
0
    }
49
121k
    if (tree[i].property == -1) continue;
50
46.0k
    height[tree[i].lchild] = height[i] + 1;
51
46.0k
    height[tree[i].rchild] = height[i] + 1;
52
763k
    for (size_t p = 0; p < static_cast<size_t>(num_properties); p++) {
53
717k
      if (p == static_cast<size_t>(tree[i].property)) {
54
46.0k
        pixel_type l = property_ranges[i * num_properties + p].first;
55
46.0k
        pixel_type u = property_ranges[i * num_properties + p].second;
56
46.0k
        pixel_type val = tree[i].splitval;
57
46.0k
        if (l > val || u <= val) {
58
4
          return JXL_FAILURE("Invalid tree");
59
4
        }
60
46.0k
        property_ranges[tree[i].lchild * num_properties + p] =
61
46.0k
            std::make_pair(val + 1, u);
62
46.0k
        property_ranges[tree[i].rchild * num_properties + p] =
63
46.0k
            std::make_pair(l, val);
64
671k
      } else {
65
671k
        property_ranges[tree[i].lchild * num_properties + p] =
66
671k
            property_ranges[i * num_properties + p];
67
671k
        property_ranges[tree[i].rchild * num_properties + p] =
68
671k
            property_ranges[i * num_properties + p];
69
671k
      }
70
717k
    }
71
46.0k
  }
72
28.9k
  return true;
73
28.9k
}
74
75
Status DecodeTree(BitReader *br, ANSSymbolReader *reader,
76
                  const std::vector<uint8_t> &context_map, Tree *tree,
77
29.0k
                  size_t tree_size_limit) {
78
29.0k
  size_t leaf_id = 0;
79
29.0k
  size_t to_decode = 1;
80
29.0k
  tree->clear();
81
2.02M
  while (to_decode > 0) {
82
1.99M
    JXL_RETURN_IF_ERROR(br->AllReadsWithinBounds());
83
1.99M
    if (tree->size() > tree_size_limit) {
84
5
      return JXL_FAILURE("Tree is too large: %" PRIuS " nodes vs %" PRIuS
85
5
                         " max nodes",
86
5
                         tree->size(), tree_size_limit);
87
5
    }
88
1.99M
    to_decode--;
89
1.99M
    uint32_t prop1 = reader->ReadHybridUint(kPropertyContext, br, context_map);
90
1.99M
    if (prop1 > 256) return JXL_FAILURE("Invalid tree property value");
91
1.99M
    int property = prop1 - 1;
92
1.99M
    if (property == -1) {
93
99.0k
      size_t predictor =
94
99.0k
          reader->ReadHybridUint(kPredictorContext, br, context_map);
95
99.0k
      if (predictor >= kNumModularPredictors) {
96
8
        return JXL_FAILURE("Invalid predictor");
97
8
      }
98
99.0k
      int64_t predictor_offset =
99
99.0k
          UnpackSigned(reader->ReadHybridUint(kOffsetContext, br, context_map));
100
99.0k
      uint32_t mul_log =
101
99.0k
          reader->ReadHybridUint(kMultiplierLogContext, br, context_map);
102
99.0k
      if (mul_log >= 31) {
103
0
        return JXL_FAILURE("Invalid multiplier logarithm");
104
0
      }
105
99.0k
      uint32_t mul_bits =
106
99.0k
          reader->ReadHybridUint(kMultiplierBitsContext, br, context_map);
107
99.0k
      if (mul_bits >= (1u << (31u - mul_log)) - 1u) {
108
0
        return JXL_FAILURE("Invalid multiplier");
109
0
      }
110
99.0k
      uint32_t multiplier = (mul_bits + 1U) << mul_log;
111
99.0k
      Predictor p = static_cast<Predictor>(static_cast<uint32_t>(predictor));
112
99.0k
      tree->emplace_back(-1, 0, static_cast<int>(leaf_id), 0, p,
113
99.0k
                         predictor_offset, multiplier);
114
99.0k
      leaf_id++;
115
99.0k
      continue;
116
99.0k
    }
117
1.89M
    int splitval =
118
1.89M
        UnpackSigned(reader->ReadHybridUint(kSplitValContext, br, context_map));
119
1.89M
    tree->emplace_back(
120
1.89M
        property, splitval, static_cast<int>(tree->size() + to_decode + 1),
121
1.89M
        static_cast<int>(tree->size() + to_decode + 2), Predictor::Zero, 0, 1);
122
1.89M
    to_decode += 2;
123
1.89M
  }
124
28.9k
  return ValidateTree(*tree);
125
29.0k
}
126
}  // namespace
127
128
Status DecodeTree(JxlMemoryManager *memory_manager, BitReader *br, Tree *tree,
129
29.1k
                  size_t tree_size_limit) {
130
29.1k
  std::vector<uint8_t> tree_context_map;
131
29.1k
  ANSCode tree_code;
132
29.1k
  JXL_RETURN_IF_ERROR(DecodeHistograms(memory_manager, br, kNumTreeContexts,
133
29.1k
                                       &tree_code, &tree_context_map));
134
  // TODO(eustas): investigate more infinite tree cases.
135
29.0k
  if (tree_code.degenerate_symbols[tree_context_map[kPropertyContext]] > 0) {
136
2
    return JXL_FAILURE("Infinite tree");
137
2
  }
138
58.0k
  JXL_ASSIGN_OR_RETURN(ANSSymbolReader reader,
139
58.0k
                       ANSSymbolReader::Create(&tree_code, br));
140
58.0k
  JXL_RETURN_IF_ERROR(DecodeTree(br, &reader, tree_context_map, tree,
141
58.0k
                                 std::min(tree_size_limit, kMaxTreeSize)));
142
28.9k
  if (!reader.CheckANSFinalState()) {
143
0
    return JXL_FAILURE("ANS decode final state failed");
144
0
  }
145
28.9k
  return true;
146
28.9k
}
147
148
}  // namespace jxl