| 67 | } |
| 68 | |
| 69 | UInt32 CompressionCodecMultiple::doDecompressData(const char * source, UInt32 source_size, char * dest, UInt32 decompressed_size) const |
| 70 | { |
| 71 | if (source_size < 1 || !source[0]) |
| 72 | throw Exception(decompression_error_code, "Wrong compression methods list"); |
| 73 | |
| 74 | UInt8 compression_methods_size = source[0]; |
| 75 | /// +1 for the compression_methods_size byte itself |
| 76 | if (static_cast<UInt32>(compression_methods_size) + 1 > source_size) |
| 77 | throw Exception(decompression_error_code, "Wrong compression methods list: header claims {} codecs" |
| 78 | " but compressed data is only {} bytes", |
| 79 | static_cast<UInt32>(compression_methods_size), source_size); |
| 80 | |
| 81 | PODArray<char> compressed_buf(&source[compression_methods_size + 1], &source[source_size]); |
| 82 | PODArray<char> uncompressed_buf; |
| 83 | /// Insert all data into compressed buf |
| 84 | source_size -= (compression_methods_size + 1); |
| 85 | |
| 86 | for (int idx = compression_methods_size - 1; idx >= 0; --idx) |
| 87 | { |
| 88 | UInt8 compression_method = source[idx + 1]; |
| 89 | const auto codec = CompressionCodecFactory::instance().get(compression_method); |
| 90 | auto additional_size_at_the_end_of_buffer = codec->getAdditionalSizeAtTheEndOfBuffer(); |
| 91 | |
| 92 | if (compressed_buf.size() >= 1_GiB) |
| 93 | throw Exception(decompression_error_code, "Too large compressed size: {}", compressed_buf.size()); |
| 94 | |
| 95 | if (source_size < COMPRESSED_BLOCK_HEADER_SIZE) |
| 96 | throw Exception(decompression_error_code, "Compressed data is too short to contain a block header: {} bytes", |
| 97 | source_size); |
| 98 | |
| 99 | { |
| 100 | UInt32 bytes_to_resize = 0; |
| 101 | if (common::addOverflow(static_cast<UInt32>(compressed_buf.size()), additional_size_at_the_end_of_buffer, bytes_to_resize)) |
| 102 | throw Exception(decompression_error_code, "Too large compressed size: {}", compressed_buf.size()); |
| 103 | |
| 104 | compressed_buf.resize(compressed_buf.size() + additional_size_at_the_end_of_buffer); |
| 105 | } |
| 106 | |
| 107 | UInt32 uncompressed_size = readDecompressedBlockSize(compressed_buf.data()); |
| 108 | |
| 109 | if (uncompressed_size >= 1_GiB) |
| 110 | throw Exception(decompression_error_code, "Too large uncompressed size: {}", uncompressed_size); |
| 111 | |
| 112 | if (idx == 0 && uncompressed_size != decompressed_size) |
| 113 | throw Exception(decompression_error_code, "Wrong final decompressed size in codec Multiple, got {}, expected {}", |
| 114 | uncompressed_size, decompressed_size); |
| 115 | |
| 116 | { |
| 117 | UInt32 bytes_to_resize = 0; |
| 118 | if (common::addOverflow(uncompressed_size, additional_size_at_the_end_of_buffer, bytes_to_resize)) |
| 119 | throw Exception(decompression_error_code, "Too large uncompressed size: {}", uncompressed_size); |
| 120 | |
| 121 | uncompressed_buf.resize(bytes_to_resize); |
| 122 | } |
| 123 | |
| 124 | codec->decompress(compressed_buf.data(), source_size, uncompressed_buf.data()); |
| 125 | uncompressed_buf.swap(compressed_buf); |
| 126 | /// The call to decompress will validate uncompressed_size (same readDecompressedBlockSize call as here) |
nothing calls this directly
no test coverage detected