/src/libjxl/lib/jxl/dec_context_map.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/dec_context_map.h" |
7 | | |
8 | | #include <jxl/memory_manager.h> |
9 | | |
10 | | #include <algorithm> |
11 | | #include <cstddef> |
12 | | #include <cstdint> |
13 | | #include <vector> |
14 | | |
15 | | #include "lib/jxl/base/status.h" |
16 | | #include "lib/jxl/dec_ans.h" |
17 | | #include "lib/jxl/dec_bit_reader.h" |
18 | | #include "lib/jxl/inverse_mtf-inl.h" |
19 | | |
20 | | namespace jxl { |
21 | | |
22 | | namespace { |
23 | | |
24 | | // Context map uses uint8_t. |
25 | | constexpr size_t kMaxClusters = 256; |
26 | | |
27 | | Status VerifyContextMap(const std::vector<uint8_t>& context_map, |
28 | 52.1k | const size_t num_htrees) { |
29 | 52.1k | std::vector<bool> have_htree(num_htrees); |
30 | 52.1k | size_t num_found = 0; |
31 | 6.63M | for (const uint8_t htree : context_map) { |
32 | 6.63M | if (htree >= num_htrees) { |
33 | 0 | return JXL_FAILURE("Invalid histogram index in context map."); |
34 | 0 | } |
35 | 6.63M | if (!have_htree[htree]) { |
36 | 78.3k | have_htree[htree] = true; |
37 | 78.3k | ++num_found; |
38 | 78.3k | } |
39 | 6.63M | } |
40 | 52.1k | if (num_found != num_htrees) { |
41 | 443 | return JXL_FAILURE("Incomplete context map."); |
42 | 443 | } |
43 | 51.7k | return true; |
44 | 52.1k | } |
45 | | |
46 | | } // namespace |
47 | | |
48 | | Status DecodeContextMap(JxlMemoryManager* memory_manager, |
49 | | std::vector<uint8_t>* context_map, size_t* num_htrees, |
50 | 53.0k | BitReader* input) { |
51 | 53.0k | bool is_simple = static_cast<bool>(input->ReadFixedBits<1>()); |
52 | 53.0k | if (is_simple) { |
53 | 35.8k | int bits_per_entry = input->ReadFixedBits<2>(); |
54 | 35.8k | if (bits_per_entry != 0) { |
55 | 308k | for (uint8_t& entry : *context_map) { |
56 | 308k | entry = input->ReadBits(bits_per_entry); |
57 | 308k | } |
58 | 23.9k | } else { |
59 | 23.9k | std::fill(context_map->begin(), context_map->end(), 0); |
60 | 23.9k | } |
61 | 35.8k | } else { |
62 | 17.2k | bool use_mtf = static_cast<bool>(input->ReadFixedBits<1>()); |
63 | 17.2k | ANSCode code; |
64 | 17.2k | std::vector<uint8_t> sink_ctx_map; |
65 | | // Usage of LZ77 is disallowed if decoding only two symbols. This doesn't |
66 | | // make sense in non-malicious bitstreams, and could cause a stack overflow |
67 | | // in malicious bitstreams by making every context map require its own |
68 | | // context map. |
69 | 17.2k | JXL_RETURN_IF_ERROR( |
70 | 17.2k | DecodeHistograms(memory_manager, input, 1, &code, &sink_ctx_map, |
71 | 17.2k | /*disallow_lz77=*/context_map->size() <= 2)); |
72 | 32.9k | JXL_ASSIGN_OR_RETURN(ANSSymbolReader reader, |
73 | 32.9k | ANSSymbolReader::Create(&code, input)); |
74 | 32.9k | size_t i = 0; |
75 | 32.9k | uint32_t maxsym = 0; |
76 | 2.26M | while (i < context_map->size()) { |
77 | 2.24M | uint32_t sym = reader.ReadHybridUintInlined</*uses_lz77=*/true>( |
78 | 2.24M | 0, input, sink_ctx_map); |
79 | 2.24M | maxsym = sym > maxsym ? sym : maxsym; |
80 | 2.24M | (*context_map)[i] = sym; |
81 | 2.24M | i++; |
82 | 2.24M | } |
83 | 32.9k | if (maxsym >= kMaxClusters) { |
84 | 213 | return JXL_FAILURE("Invalid cluster ID"); |
85 | 213 | } |
86 | 16.2k | if (!reader.CheckANSFinalState()) { |
87 | 0 | return JXL_FAILURE("Invalid context map"); |
88 | 0 | } |
89 | 16.2k | if (use_mtf) { |
90 | 3.33k | InverseMoveToFrontTransform(context_map->data(), context_map->size()); |
91 | 3.33k | } |
92 | 16.2k | } |
93 | 52.1k | *num_htrees = *std::max_element(context_map->begin(), context_map->end()) + 1; |
94 | 52.1k | return VerifyContextMap(*context_map, *num_htrees); |
95 | 53.0k | } |
96 | | |
97 | | } // namespace jxl |