/src/libjxl/lib/jxl/toc.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/toc.h" |
7 | | |
8 | | #include <jxl/memory_manager.h> |
9 | | #include <stdint.h> |
10 | | |
11 | | #include "lib/jxl/base/common.h" |
12 | | #include "lib/jxl/coeff_order.h" |
13 | | #include "lib/jxl/coeff_order_fwd.h" |
14 | | #include "lib/jxl/fields.h" |
15 | | |
16 | | namespace jxl { |
17 | 0 | size_t MaxBits(const size_t num_sizes) { |
18 | 0 | const size_t entry_bits = U32Coder::MaxEncodedBits(kTocDist) * num_sizes; |
19 | | // permutation bit (not its tokens!), padding, entries, padding. |
20 | 0 | return 1 + kBitsPerByte + entry_bits + kBitsPerByte; |
21 | 0 | } |
22 | | |
23 | | Status ReadToc(JxlMemoryManager* memory_manager, size_t toc_entries, |
24 | | BitReader* JXL_RESTRICT reader, |
25 | | std::vector<uint32_t>* JXL_RESTRICT sizes, |
26 | 95.1k | std::vector<coeff_order_t>* JXL_RESTRICT permutation) { |
27 | 95.1k | if (toc_entries > 65536) { |
28 | | // Prevent out of memory if invalid JXL codestream causes a bogus amount |
29 | | // of toc_entries such as 2720436919446 to be computed. |
30 | | // TODO(lode): verify whether 65536 is a reasonable upper bound |
31 | 49 | return JXL_FAILURE("too many toc entries"); |
32 | 49 | } |
33 | | |
34 | 95.1k | sizes->clear(); |
35 | 95.1k | sizes->resize(toc_entries); |
36 | 95.1k | if (reader->TotalBitsConsumed() >= reader->TotalBytes() * kBitsPerByte) { |
37 | 92 | return JXL_STATUS(StatusCode::kNotEnoughBytes, "Not enough bytes for TOC"); |
38 | 92 | } |
39 | 185k | const auto check_bit_budget = [&](size_t num_entries) -> Status { |
40 | | // U32Coder reads 2 bits to recognize variant and kTocDist cheapest variant |
41 | | // is Bits(10), this way at least 12 bits are required per toc-entry. |
42 | 185k | size_t minimal_bit_cost = num_entries * (2 + 10); |
43 | 185k | size_t bit_budget = reader->TotalBytes() * 8; |
44 | 185k | size_t expenses = reader->TotalBitsConsumed(); |
45 | 185k | if ((expenses <= bit_budget) && |
46 | 185k | (minimal_bit_cost <= bit_budget - expenses)) { |
47 | 182k | return true; |
48 | 182k | } |
49 | 3.38k | return JXL_STATUS(StatusCode::kNotEnoughBytes, "Not enough bytes for TOC"); |
50 | 185k | }; |
51 | | |
52 | 95.0k | JXL_ENSURE(toc_entries > 0); |
53 | 95.0k | if (reader->ReadFixedBits<1>() == 1) { |
54 | 5.99k | JXL_RETURN_IF_ERROR(check_bit_budget(toc_entries)); |
55 | 5.04k | permutation->resize(toc_entries); |
56 | 5.04k | JXL_RETURN_IF_ERROR(DecodePermutation( |
57 | 5.04k | memory_manager, /*skip=*/0, toc_entries, permutation->data(), reader)); |
58 | 5.04k | } |
59 | 91.1k | JXL_RETURN_IF_ERROR(reader->JumpToByteBoundary()); |
60 | 90.9k | JXL_RETURN_IF_ERROR(check_bit_budget(toc_entries)); |
61 | 659k | for (size_t i = 0; i < toc_entries; ++i) { |
62 | 570k | (*sizes)[i] = U32Coder::Read(kTocDist, reader); |
63 | 570k | } |
64 | 88.9k | JXL_RETURN_IF_ERROR(reader->JumpToByteBoundary()); |
65 | 88.9k | JXL_RETURN_IF_ERROR(check_bit_budget(0)); |
66 | 88.4k | return true; |
67 | 88.9k | } |
68 | | |
69 | | Status ReadGroupOffsets(JxlMemoryManager* memory_manager, size_t toc_entries, |
70 | | BitReader* JXL_RESTRICT reader, |
71 | | std::vector<uint64_t>* JXL_RESTRICT offsets, |
72 | | std::vector<uint32_t>* JXL_RESTRICT sizes, |
73 | 0 | uint64_t* total_size) { |
74 | 0 | std::vector<coeff_order_t> permutation; |
75 | 0 | JXL_RETURN_IF_ERROR( |
76 | 0 | ReadToc(memory_manager, toc_entries, reader, sizes, &permutation)); |
77 | | |
78 | 0 | offsets->clear(); |
79 | 0 | offsets->resize(toc_entries); |
80 | | |
81 | | // Prefix sum starting with 0 and ending with the offset of the last group |
82 | 0 | uint64_t offset = 0; |
83 | 0 | for (size_t i = 0; i < toc_entries; ++i) { |
84 | 0 | if (offset + (*sizes)[i] < offset) { |
85 | 0 | return JXL_FAILURE("group offset overflow"); |
86 | 0 | } |
87 | 0 | (*offsets)[i] = offset; |
88 | 0 | offset += (*sizes)[i]; |
89 | 0 | } |
90 | 0 | if (total_size) { |
91 | 0 | *total_size = offset; |
92 | 0 | } |
93 | |
|
94 | 0 | if (!permutation.empty()) { |
95 | 0 | std::vector<uint64_t> permuted_offsets; |
96 | 0 | std::vector<uint32_t> permuted_sizes; |
97 | 0 | permuted_offsets.reserve(toc_entries); |
98 | 0 | permuted_sizes.reserve(toc_entries); |
99 | 0 | for (coeff_order_t index : permutation) { |
100 | 0 | permuted_offsets.push_back((*offsets)[index]); |
101 | 0 | permuted_sizes.push_back((*sizes)[index]); |
102 | 0 | } |
103 | 0 | std::swap(*offsets, permuted_offsets); |
104 | 0 | std::swap(*sizes, permuted_sizes); |
105 | 0 | } |
106 | |
|
107 | 0 | return true; |
108 | 0 | } |
109 | | } // namespace jxl |