Coverage Report

Created: 2024-09-08 07:14

/src/libjxl/lib/jxl/modular/encoding/dec_ma.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/modular/encoding/dec_ma.h"
7
8
#include <jxl/memory_manager.h>
9
10
#include <limits>
11
12
#include "lib/jxl/base/printf_macros.h"
13
#include "lib/jxl/base/status.h"
14
#include "lib/jxl/dec_ans.h"
15
#include "lib/jxl/modular/encoding/ma_common.h"
16
#include "lib/jxl/modular/modular_image.h"
17
#include "lib/jxl/pack_signed.h"
18
19
namespace jxl {
20
21
namespace {
22
23
Status ValidateTree(
24
    const Tree &tree,
25
    const std::vector<std::pair<pixel_type, pixel_type>> &prop_bounds,
26
154k
    size_t root) {
27
154k
  if (tree[root].property == -1) return true;
28
46.9k
  size_t p = tree[root].property;
29
46.9k
  int val = tree[root].splitval;
30
46.9k
  if (prop_bounds[p].first > val) return JXL_FAILURE("Invalid tree");
31
  // Splitting at max value makes no sense: left range will be exactly same
32
  // as parent, right range will be invalid (min > max).
33
46.9k
  if (prop_bounds[p].second <= val) return JXL_FAILURE("Invalid tree");
34
46.9k
  auto new_bounds = prop_bounds;
35
46.9k
  new_bounds[p].first = val + 1;
36
46.9k
  JXL_RETURN_IF_ERROR(ValidateTree(tree, new_bounds, tree[root].lchild));
37
46.9k
  new_bounds[p] = prop_bounds[p];
38
46.9k
  new_bounds[p].second = val;
39
46.9k
  return ValidateTree(tree, new_bounds, tree[root].rchild);
40
46.9k
}
41
42
Status DecodeTree(BitReader *br, ANSSymbolReader *reader,
43
                  const std::vector<uint8_t> &context_map, Tree *tree,
44
60.5k
                  size_t tree_size_limit) {
45
60.5k
  size_t leaf_id = 0;
46
60.5k
  size_t to_decode = 1;
47
60.5k
  tree->clear();
48
502k
  while (to_decode > 0) {
49
442k
    JXL_RETURN_IF_ERROR(br->AllReadsWithinBounds());
50
442k
    if (tree->size() > tree_size_limit) {
51
9
      return JXL_FAILURE("Tree is too large: %" PRIuS " nodes vs %" PRIuS
52
9
                         " max nodes",
53
9
                         tree->size(), tree_size_limit);
54
9
    }
55
442k
    to_decode--;
56
442k
    uint32_t prop1 = reader->ReadHybridUint(kPropertyContext, br, context_map);
57
442k
    if (prop1 > 256) return JXL_FAILURE("Invalid tree property value");
58
442k
    int property = prop1 - 1;
59
442k
    if (property == -1) {
60
108k
      size_t predictor =
61
108k
          reader->ReadHybridUint(kPredictorContext, br, context_map);
62
108k
      if (predictor >= kNumModularPredictors) {
63
11
        return JXL_FAILURE("Invalid predictor");
64
11
      }
65
108k
      int64_t predictor_offset =
66
108k
          UnpackSigned(reader->ReadHybridUint(kOffsetContext, br, context_map));
67
108k
      uint32_t mul_log =
68
108k
          reader->ReadHybridUint(kMultiplierLogContext, br, context_map);
69
108k
      if (mul_log >= 31) {
70
4
        return JXL_FAILURE("Invalid multiplier logarithm");
71
4
      }
72
108k
      uint32_t mul_bits =
73
108k
          reader->ReadHybridUint(kMultiplierBitsContext, br, context_map);
74
108k
      if (mul_bits >= (1u << (31u - mul_log)) - 1u) {
75
2
        return JXL_FAILURE("Invalid multiplier");
76
2
      }
77
108k
      uint32_t multiplier = (mul_bits + 1U) << mul_log;
78
108k
      tree->emplace_back(-1, 0, leaf_id++, 0, static_cast<Predictor>(predictor),
79
108k
                         predictor_offset, multiplier);
80
108k
      continue;
81
108k
    }
82
333k
    int splitval =
83
333k
        UnpackSigned(reader->ReadHybridUint(kSplitValContext, br, context_map));
84
333k
    tree->emplace_back(property, splitval, tree->size() + to_decode + 1,
85
333k
                       tree->size() + to_decode + 2, Predictor::Zero, 0, 1);
86
333k
    to_decode += 2;
87
333k
  }
88
60.3k
  std::vector<std::pair<pixel_type, pixel_type>> prop_bounds;
89
60.3k
  prop_bounds.resize(256, {std::numeric_limits<pixel_type>::min(),
90
60.3k
                           std::numeric_limits<pixel_type>::max()});
91
60.3k
  return ValidateTree(*tree, prop_bounds, 0);
92
60.5k
}
93
}  // namespace
94
95
Status DecodeTree(JxlMemoryManager *memory_manager, BitReader *br, Tree *tree,
96
61.1k
                  size_t tree_size_limit) {
97
61.1k
  std::vector<uint8_t> tree_context_map;
98
61.1k
  ANSCode tree_code;
99
61.1k
  JXL_RETURN_IF_ERROR(DecodeHistograms(memory_manager, br, kNumTreeContexts,
100
61.1k
                                       &tree_code, &tree_context_map));
101
  // TODO(eustas): investigate more infinite tree cases.
102
60.5k
  if (tree_code.degenerate_symbols[tree_context_map[kPropertyContext]] > 0) {
103
9
    return JXL_FAILURE("Infinite tree");
104
9
  }
105
121k
  JXL_ASSIGN_OR_RETURN(ANSSymbolReader reader,
106
121k
                       ANSSymbolReader::Create(&tree_code, br));
107
121k
  JXL_RETURN_IF_ERROR(DecodeTree(br, &reader, tree_context_map, tree,
108
121k
                                 std::min(tree_size_limit, kMaxTreeSize)));
109
60.3k
  if (!reader.CheckANSFinalState()) {
110
0
    return JXL_FAILURE("ANS decode final state failed");
111
0
  }
112
60.3k
  return true;
113
60.3k
}
114
115
}  // namespace jxl