toc.cc (3921B)
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 size_t MaxBits(const size_t num_sizes) { 18 const size_t entry_bits = U32Coder::MaxEncodedBits(kTocDist) * num_sizes; 19 // permutation bit (not its tokens!), padding, entries, padding. 20 return 1 + kBitsPerByte + entry_bits + kBitsPerByte; 21 } 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 std::vector<coeff_order_t>* JXL_RESTRICT permutation) { 27 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 return JXL_FAILURE("too many toc entries"); 32 } 33 34 sizes->clear(); 35 sizes->resize(toc_entries); 36 if (reader->TotalBitsConsumed() >= reader->TotalBytes() * kBitsPerByte) { 37 return JXL_STATUS(StatusCode::kNotEnoughBytes, "Not enough bytes for TOC"); 38 } 39 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 size_t minimal_bit_cost = num_entries * (2 + 10); 43 size_t bit_budget = reader->TotalBytes() * 8; 44 size_t expenses = reader->TotalBitsConsumed(); 45 if ((expenses <= bit_budget) && 46 (minimal_bit_cost <= bit_budget - expenses)) { 47 return true; 48 } 49 return JXL_STATUS(StatusCode::kNotEnoughBytes, "Not enough bytes for TOC"); 50 }; 51 52 JXL_ENSURE(toc_entries > 0); 53 if (reader->ReadFixedBits<1>() == 1) { 54 JXL_RETURN_IF_ERROR(check_bit_budget(toc_entries)); 55 permutation->resize(toc_entries); 56 JXL_RETURN_IF_ERROR(DecodePermutation( 57 memory_manager, /*skip=*/0, toc_entries, permutation->data(), reader)); 58 } 59 JXL_RETURN_IF_ERROR(reader->JumpToByteBoundary()); 60 JXL_RETURN_IF_ERROR(check_bit_budget(toc_entries)); 61 for (size_t i = 0; i < toc_entries; ++i) { 62 (*sizes)[i] = U32Coder::Read(kTocDist, reader); 63 } 64 JXL_RETURN_IF_ERROR(reader->JumpToByteBoundary()); 65 JXL_RETURN_IF_ERROR(check_bit_budget(0)); 66 return true; 67 } 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 uint64_t* total_size) { 74 std::vector<coeff_order_t> permutation; 75 JXL_RETURN_IF_ERROR( 76 ReadToc(memory_manager, toc_entries, reader, sizes, &permutation)); 77 78 offsets->clear(); 79 offsets->resize(toc_entries); 80 81 // Prefix sum starting with 0 and ending with the offset of the last group 82 uint64_t offset = 0; 83 for (size_t i = 0; i < toc_entries; ++i) { 84 if (offset + (*sizes)[i] < offset) { 85 return JXL_FAILURE("group offset overflow"); 86 } 87 (*offsets)[i] = offset; 88 offset += (*sizes)[i]; 89 } 90 if (total_size) { 91 *total_size = offset; 92 } 93 94 if (!permutation.empty()) { 95 std::vector<uint64_t> permuted_offsets; 96 std::vector<uint32_t> permuted_sizes; 97 permuted_offsets.reserve(toc_entries); 98 permuted_sizes.reserve(toc_entries); 99 for (coeff_order_t index : permutation) { 100 permuted_offsets.push_back((*offsets)[index]); 101 permuted_sizes.push_back((*sizes)[index]); 102 } 103 std::swap(*offsets, permuted_offsets); 104 std::swap(*sizes, permuted_sizes); 105 } 106 107 return true; 108 } 109 } // namespace jxl