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