RPNX::Compress
Self-contained C++20 compression and ZIP library
 
Loading...
Searching...
No Matches
deflate.hpp
Go to the documentation of this file.
1#ifndef RPNX_COMPRESSION_IMPLEMENTATION_DEFLATE_HPP
2#define RPNX_COMPRESSION_IMPLEMENTATION_DEFLATE_HPP
3
4#include <algorithm>
5#include <array>
6#include <cstddef>
7#include <cstdint>
8#include <iterator>
9#include <limits>
10#include <span>
11#include <string>
12#include <type_traits>
13#include <utility>
14#include <vector>
15
17
18/**
19 * @file
20 * @brief Native raw DEFLATE, zlib, and gzip coding implementation.
21 */
22
23/** @brief Internal implementation shared by raw DEFLATE, zlib, gzip, and ZIP. */
25{
26
27 /** Canonical Huffman decoder for the bit-reversed codes used by DEFLATE. */
29 {
30 public:
31 /**
32 * @brief Builds and validates a decoder from per-symbol code lengths.
33 * @param lengths Code length for each symbol in symbol order.
34 * @param stream_format Format used to classify validation errors.
35 * @param allow_empty Whether an all-zero length set is accepted.
36 * @throws compression_error If lengths exceed 15 bits or oversubscribe the tree.
37 */
38 void build(std::span< std::uint8_t const > lengths, format stream_format, bool allow_empty = false)
39 {
40 for (std::vector< std::int16_t >& table : m_symbols_by_length)
41 {
42 table.clear();
43 }
44 std::array< std::uint16_t, 16U > counts{};
45 std::size_t symbol_count = 0U;
46 for (std::uint8_t length : lengths)
47 {
48 if (length > 15U)
49 {
50 throw compression_error(error_code::invalid_data, stream_format, "DEFLATE Huffman code is too long");
51 }
52 if (length != 0U)
53 {
54 ++counts[length];
55 ++symbol_count;
56 }
57 }
58 if (symbol_count == 0U)
59 {
60 if (allow_empty)
61 {
62 return;
63 }
64 throw compression_error(error_code::invalid_data, stream_format, "DEFLATE Huffman tree is empty");
65 }
66
67 std::int32_t remaining_codes = 1;
68 for (std::size_t length = 1U; length <= 15U; ++length)
69 {
70 remaining_codes = remaining_codes * 2 - counts[length];
71 if (remaining_codes < 0)
72 {
73 throw compression_error(error_code::invalid_data, stream_format, "oversubscribed DEFLATE Huffman tree");
74 }
75 }
76
77 std::array< std::uint16_t, 16U > next_code{};
78 std::uint16_t code = 0U;
79 for (std::size_t length = 1U; length <= 15U; ++length)
80 {
81 code = static_cast< std::uint16_t >((code + counts[length - 1U]) << 1U);
82 next_code[length] = code;
83 if (counts[length] != 0U)
84 {
85 m_symbols_by_length[length].assign(static_cast< std::size_t >(1U) << length, -1);
86 }
87 }
88
89 for (std::size_t symbol = 0U; symbol < lengths.size(); ++symbol)
90 {
91 std::uint8_t const length = lengths[symbol];
92 if (length == 0U)
93 {
94 continue;
95 }
96 std::uint16_t canonical_code = next_code[length]++;
97 std::uint16_t reversed_code = 0U;
98 for (std::uint8_t bit = 0U; bit < length; ++bit)
99 {
100 reversed_code = static_cast< std::uint16_t >((static_cast< std::uint32_t >(reversed_code) << 1U) | (static_cast< std::uint32_t >(canonical_code) & 1U));
101 canonical_code = static_cast< std::uint16_t >(canonical_code >> 1U);
102 }
103 m_symbols_by_length[length][reversed_code] = static_cast< std::int16_t >(symbol);
104 }
105 }
106
107 /**
108 * @brief Decodes one symbol from a bit reader.
109 * @tparam reader_type LSB-first reader providing read_bits().
110 * @param reader Source bit reader.
111 * @param stream_format Format used to classify validation errors.
112 * @return Decoded symbol index.
113 * @throws compression_error If no code matches the next input bits.
114 */
115 template < typename reader_type >
116 [[nodiscard]] std::uint16_t decode(reader_type& reader, format stream_format) const
117 {
118 std::uint16_t code = 0U;
119 for (std::uint8_t length = 1U; length <= 15U; ++length)
120 {
121 code = static_cast< std::uint16_t >(code | (reader.read_bits(1U) << (length - 1U)));
122 if (!m_symbols_by_length[length].empty() && m_symbols_by_length[length][code] >= 0)
123 {
124 return static_cast< std::uint16_t >(m_symbols_by_length[length][code]);
125 }
126 }
127 throw compression_error(error_code::invalid_data, stream_format, "invalid DEFLATE Huffman code");
128 }
129
130 private:
131 std::array< std::vector< std::int16_t >, 16U > m_symbols_by_length;
132 };
133
134 /**
135 * @brief Reverses the selected low bits for DEFLATE wire order.
136 * @param value Value containing the bits to reverse.
137 * @param bit_count Number of low bits to reverse.
138 * @return Reversed bits in the low part of the result.
139 */
140 [[nodiscard]] inline std::uint16_t reverse_bits(std::uint16_t value, std::uint8_t bit_count) noexcept
141 {
142 std::uint16_t result = 0U;
143 for (std::uint8_t bit = 0U; bit < bit_count; ++bit)
144 {
145 result = static_cast< std::uint16_t >((static_cast< std::uint32_t >(result) << 1U) | (static_cast< std::uint32_t >(value) & 1U));
146 value = static_cast< std::uint16_t >(value >> 1U);
147 }
148 return result;
149 }
150
151 /**
152 * @brief Emits one fixed-Huffman literal or length symbol.
153 * @tparam writer_type LSB-first writer providing write_bits().
154 * @param writer Destination bit writer.
155 * @param symbol DEFLATE literal, end-of-block, or length symbol.
156 */
157 template < typename writer_type >
158 void write_fixed_symbol(writer_type& writer, std::uint16_t symbol)
159 {
160 if (symbol <= 143U)
161 {
162 writer.write_bits(reverse_bits(static_cast< std::uint16_t >(0x30U + symbol), 8U), 8U);
163 }
164 else if (symbol <= 255U)
165 {
166 writer.write_bits(reverse_bits(static_cast< std::uint16_t >(0x190U + symbol - 144U), 9U), 9U);
167 }
168 else if (symbol <= 279U)
169 {
170 writer.write_bits(reverse_bits(static_cast< std::uint16_t >(symbol - 256U), 7U), 7U);
171 }
172 else
173 {
174 writer.write_bits(reverse_bits(static_cast< std::uint16_t >(0xc0U + symbol - 280U), 8U), 8U);
175 }
176 }
177
178 /**
179 * @brief Computes the reflected IEEE CRC-32 used by gzip and ZIP.
180 * @param input Bytes to checksum.
181 * @return Finalized CRC-32.
182 */
183 [[nodiscard]] inline std::uint32_t crc32(std::span< std::byte const > input) noexcept
184 {
185 std::uint32_t checksum = 0xffffffffU;
186 for (std::byte value : input)
187 {
188 checksum ^= std::to_integer< std::uint8_t >(value);
189 for (std::uint8_t bit = 0U; bit < 8U; ++bit)
190 {
191 std::uint32_t const mask = 0U - (checksum & 1U);
192 checksum = (checksum >> 1U) ^ (0xedb88320U & mask);
193 }
194 }
195 return ~checksum;
196 }
197
198 /**
199 * @brief LSB-first bit writer that emits bytes through an STL output iterator.
200 * @tparam output_iterator Destination accepting byte assignments.
201 */
202 template < typename output_iterator >
204 {
205 public:
206 /**
207 * @brief Constructs a writer owning an output iterator.
208 * @param output Destination iterator.
209 */
210 explicit iterator_bit_writer(output_iterator output) : m_output(std::move(output))
211 {
212 }
213
214 /**
215 * @brief Appends the selected low bits of a value.
216 * @param value Value containing the field.
217 * @param bit_count Number of low bits to emit least significant first.
218 */
219 void write_bits(std::uint32_t value, std::uint8_t bit_count)
220 {
221 m_bits |= static_cast< std::uint64_t >(value) << m_bit_count;
222 m_bit_count = static_cast< std::uint8_t >(m_bit_count + bit_count);
223 while (m_bit_count >= 8U)
224 {
225 implementation::write_byte(m_output, static_cast< std::byte >(m_bits & 0xffU));
226 m_bits >>= 8U;
227 m_bit_count = static_cast< std::uint8_t >(m_bit_count - 8U);
228 }
229 }
230
231 /**
232 * @brief Flushes zero byte padding and releases the output iterator.
233 * @return Destination iterator advanced past all emitted bytes.
234 */
235 [[nodiscard]] output_iterator finish()
236 {
237 if (m_bit_count != 0U)
238 {
239 implementation::write_byte(m_output, static_cast< std::byte >(m_bits & 0xffU));
240 m_bits = 0U;
241 m_bit_count = 0U;
242 }
243 return std::move(m_output);
244 }
245
246 private:
247 output_iterator m_output;
248 std::uint64_t m_bits = 0U;
249 std::uint8_t m_bit_count = 0U;
250 };
251
252 /**
253 * @brief LSB-first bit reader that consumes a single-pass byte reader.
254 * @tparam byte_reader_type Source reader providing read().
255 */
256 template < typename byte_reader_type >
258 {
259 public:
260 /**
261 * @brief Constructs a bit reader for one DEFLATE-family stream.
262 * @param input Borrowed source reader that must outlive this object.
263 * @param stream_format Format used to classify truncated-input errors.
264 */
265 iterator_bit_reader(byte_reader_type& input, format stream_format) noexcept : m_input(input), m_stream_format(stream_format)
266 {
267 }
268
269 /**
270 * @brief Consumes a low-order-first bit field.
271 * @param bit_count Number of bits to consume.
272 * @return Field value with the earliest bit in the least significant position.
273 * @throws compression_error If the source ends before the field is complete.
274 */
275 [[nodiscard]] std::uint32_t read_bits(std::uint8_t bit_count)
276 {
277 while (m_bit_count < bit_count)
278 {
279 std::byte value{};
280 if (!m_input.read(value))
281 {
282 throw compression_error(error_code::invalid_data, m_stream_format, "truncated DEFLATE bit stream");
283 }
284 m_bits |= static_cast< std::uint64_t >(std::to_integer< std::uint8_t >(value)) << m_bit_count;
285 m_bit_count = static_cast< std::uint8_t >(m_bit_count + 8U);
286 }
287 std::uint32_t const mask = bit_count == 32U ? std::numeric_limits< std::uint32_t >::max() : (static_cast< std::uint32_t >(1U) << bit_count) - 1U;
288 std::uint32_t const value = static_cast< std::uint32_t >(m_bits) & mask;
289 m_bits >>= bit_count;
290 m_bit_count = static_cast< std::uint8_t >(m_bit_count - bit_count);
291 return value;
292 }
293
294 /** Discard padding through the next byte boundary. */
295 void align_to_byte() noexcept
296 {
297 m_bits = 0U;
298 m_bit_count = 0U;
299 }
300
301 private:
302 byte_reader_type& m_input;
303 format m_stream_format;
304 std::uint64_t m_bits = 0U;
305 std::uint8_t m_bit_count = 0U;
306 };
307
308 /**
309 * @brief Emits one fixed-Huffman DEFLATE block from a bounded search buffer.
310 * @tparam writer_type LSB-first writer providing write_bits().
311 * @param writer Destination bit writer.
312 * @param input Uncompressed block.
313 * @param final_block Whether to set the DEFLATE final-block bit.
314 * @param level Compression level controlling match-search depth.
315 */
316 template < typename writer_type >
317 void compress_fixed_block(writer_type& writer, std::span< std::byte const > input, bool final_block, std::int32_t level)
318 {
319 constexpr std::array< std::uint16_t, 29U > length_bases{3U, 4U, 5U, 6U, 7U, 8U, 9U, 10U, 11U, 13U, 15U, 17U, 19U, 23U, 27U, 31U, 35U, 43U, 51U, 59U, 67U, 83U, 99U, 115U, 131U, 163U, 195U, 227U, 258U};
320 constexpr std::array< std::uint8_t, 29U > length_extras{0U, 0U, 0U, 0U, 0U, 0U, 0U, 0U, 1U, 1U, 1U, 1U, 2U, 2U, 2U, 2U, 3U, 3U, 3U, 3U, 4U, 4U, 4U, 4U, 5U, 5U, 5U, 5U, 0U};
321 constexpr std::array< std::uint16_t, 30U > distance_bases{1U, 2U, 3U, 4U, 5U, 7U, 9U, 13U, 17U, 25U, 33U, 49U, 65U, 97U, 129U, 193U, 257U, 385U, 513U, 769U, 1025U, 1537U, 2049U, 3073U, 4097U, 6145U, 8193U, 12289U, 16385U, 24577U};
322 constexpr std::array< std::uint8_t, 30U > distance_extras{0U, 0U, 0U, 0U, 1U, 1U, 2U, 2U, 3U, 3U, 4U, 4U, 5U, 5U, 6U, 6U, 7U, 7U, 8U, 8U, 9U, 9U, 10U, 10U, 11U, 11U, 12U, 12U, 13U, 13U};
323
324 writer.write_bits(final_block ? 1U : 0U, 1U);
325 writer.write_bits(1U, 2U);
326 constexpr std::size_t no_position = std::numeric_limits< std::size_t >::max();
327 std::array< std::size_t, 65536U > hash_heads{};
328 hash_heads.fill(no_position);
329 std::vector< std::size_t > previous(input.size(), no_position);
330 std::size_t position = 0U;
331 std::size_t const maximum_search_depth = 8U + static_cast< std::size_t >(level) * 28U;
332 while (position < input.size())
333 {
334 std::size_t best_length = 0U;
335 std::size_t best_distance = 0U;
336 if (position + 2U < input.size())
337 {
338 std::uint32_t const hash = (static_cast< std::uint32_t >(std::to_integer< std::uint8_t >(input[position])) * 251U ^ static_cast< std::uint32_t >(std::to_integer< std::uint8_t >(input[position + 1U])) * 31U ^ std::to_integer< std::uint8_t >(input[position + 2U])) & 0xffffU;
339 std::size_t candidate = hash_heads[hash];
340 previous[position] = candidate;
341 hash_heads[hash] = position;
342 std::size_t depth = 0U;
343 std::size_t const maximum_length = std::min< std::size_t >(258U, input.size() - position);
344 while (candidate != no_position && position - candidate <= 32768U && depth < maximum_search_depth)
345 {
346 std::size_t length = 0U;
347 while (length < maximum_length && input[candidate + length] == input[position + length])
348 {
349 ++length;
350 }
351 if (length > best_length && length >= 3U)
352 {
353 best_length = length;
354 best_distance = position - candidate;
355 }
356 candidate = previous[candidate];
357 ++depth;
358 }
359 }
360 if (best_length < 3U)
361 {
362 write_fixed_symbol(writer, std::to_integer< std::uint8_t >(input[position++]));
363 continue;
364 }
365 std::size_t length_index = 0U;
366 while (length_index + 1U < length_bases.size() && best_length >= length_bases[length_index + 1U])
367 {
368 ++length_index;
369 }
370 write_fixed_symbol(writer, static_cast< std::uint16_t >(257U + length_index));
371 writer.write_bits(static_cast< std::uint32_t >(best_length - length_bases[length_index]), length_extras[length_index]);
372 std::size_t distance_index = 0U;
373 while (distance_index + 1U < distance_bases.size() && best_distance >= distance_bases[distance_index + 1U])
374 {
375 ++distance_index;
376 }
377 writer.write_bits(reverse_bits(static_cast< std::uint16_t >(distance_index), 5U), 5U);
378 writer.write_bits(static_cast< std::uint32_t >(best_distance - distance_bases[distance_index]), distance_extras[distance_index]);
379 for (std::size_t offset = 1U; offset < best_length; ++offset)
380 {
381 std::size_t const inserted = position + offset;
382 if (inserted + 2U >= input.size())
383 {
384 continue;
385 }
386 std::uint32_t const hash = (static_cast< std::uint32_t >(std::to_integer< std::uint8_t >(input[inserted])) * 251U ^ static_cast< std::uint32_t >(std::to_integer< std::uint8_t >(input[inserted + 1U])) * 31U ^ std::to_integer< std::uint8_t >(input[inserted + 2U])) & 0xffffU;
387 previous[inserted] = hash_heads[hash];
388 hash_heads[hash] = inserted;
389 }
390 position += best_length;
391 }
392 write_fixed_symbol(writer, 256U);
393 }
394
395 /**
396 * @brief Compresses an iterator range as raw DEFLATE, zlib, or gzip.
397 * @tparam input_iterator Single-pass byte iterator.
398 * @tparam sentinel Sentinel for @p first.
399 * @tparam output_iterator Destination byte iterator.
400 * @param stream_format One of format::deflate, format::zlib, or format::gzip.
401 * @param first First source byte.
402 * @param last Sentinel past the source.
403 * @param output Destination iterator.
404 * @param options Compression level from 0 through 9.
405 * @return Destination advanced past the stream trailer.
406 */
407 template < std::input_iterator input_iterator, std::sentinel_for< input_iterator > sentinel, typename output_iterator >
408 output_iterator compress(format stream_format, input_iterator first, sentinel last, output_iterator output, compression_options const& options)
409 {
410 std::int32_t const level = options.level.value_or(6);
411 if (level < 0 || level > 9)
412 {
413 throw compression_error(error_code::invalid_option, stream_format, "DEFLATE compression level must be between 0 and 9");
414 }
415 if (stream_format != format::deflate && stream_format != format::zlib && stream_format != format::gzip)
416 {
417 throw compression_error(error_code::invalid_option, stream_format, "native DEFLATE codec received an unrelated format");
418 }
419 if (stream_format == format::zlib)
420 {
421 std::uint8_t const flags = level <= 1 ? 0x01U : level <= 5 ? 0x5eU : level == 6 ? 0x9cU : 0xdaU;
422 implementation::write_byte(output, std::byte{0x78});
423 implementation::write_byte(output, static_cast< std::byte >(flags));
424 }
425 else if (stream_format == format::gzip)
426 {
427 constexpr std::array< std::byte, 10U > header{std::byte{0x1f}, std::byte{0x8b}, std::byte{0x08}, std::byte{0x00}, std::byte{0x00}, std::byte{0x00}, std::byte{0x00}, std::byte{0x00}, std::byte{0x00}, std::byte{0xff}};
428 for (std::byte value : header)
429 {
430 implementation::write_byte(output, value);
431 }
432 }
433
436 std::uint32_t input_size = 0U;
437 std::size_t const block_limit = level == 0 ? 65535U : 64U * 1024U;
438 std::vector< std::byte > block;
439 block.reserve(block_limit);
440 iterator_bit_writer< output_iterator > writer(std::move(output));
441 do
442 {
443 block.clear();
444 while (first != last && block.size() < block_limit)
445 {
446 std::byte const value = implementation::to_byte(*first);
447 ++first;
448 block.push_back(value);
449 crc.update(value);
450 adler.update(value);
451 ++input_size;
452 }
453 bool const final_block = first == last;
454 if (level == 0)
455 {
456 writer.write_bits(final_block ? 1U : 0U, 1U);
457 writer.write_bits(0U, 2U);
458 output_iterator temporary = writer.finish();
459 std::uint16_t const length = static_cast< std::uint16_t >(block.size());
460 std::uint16_t const inverse_length = static_cast< std::uint16_t >(~length);
461 implementation::write_byte(temporary, static_cast< std::byte >(length));
462 implementation::write_byte(temporary, static_cast< std::byte >(length >> 8U));
463 implementation::write_byte(temporary, static_cast< std::byte >(inverse_length));
464 implementation::write_byte(temporary, static_cast< std::byte >(inverse_length >> 8U));
465 for (std::byte value : block)
466 {
467 implementation::write_byte(temporary, value);
468 }
469 writer = iterator_bit_writer< output_iterator >(std::move(temporary));
470 }
471 else
472 {
473 compress_fixed_block(writer, block, final_block, level);
474 }
475 if (final_block)
476 {
477 break;
478 }
479 } while (first != last);
480 output = writer.finish();
481 auto write_u32 = [&](std::uint32_t value, bool little_endian)
482 {
483 for (std::uint8_t index = 0U; index < 4U; ++index)
484 {
485 std::uint8_t const shift = little_endian ? index * 8U : static_cast< std::uint8_t >((3U - index) * 8U);
486 implementation::write_byte(output, static_cast< std::byte >(value >> shift));
487 }
488 };
489 if (stream_format == format::zlib)
490 {
491 write_u32(adler.value(), false);
492 }
493 else if (stream_format == format::gzip)
494 {
495 write_u32(crc.value(), true);
496 write_u32(input_size, true);
497 }
498 return output;
499 }
500
501 /**
502 * @brief Decompresses raw DEFLATE, zlib, or gzip input.
503 * @tparam input_iterator Single-pass byte iterator.
504 * @tparam sentinel Sentinel for @p first.
505 * @tparam output_iterator Destination byte iterator.
506 * @param stream_format One of format::deflate, format::zlib, or format::gzip.
507 * @param first First compressed byte.
508 * @param last Sentinel past the compressed input.
509 * @param output Destination iterator.
510 * @param options Output limit and concatenated-member policy.
511 * @return Destination advanced past the uncompressed data.
512 */
513 template < std::input_iterator input_iterator, std::sentinel_for< input_iterator > sentinel, typename output_iterator >
514 output_iterator decompress(format stream_format, input_iterator first, sentinel last, output_iterator output, decompression_options const& options)
515 {
516 if (stream_format != format::deflate && stream_format != format::zlib && stream_format != format::gzip)
517 {
518 throw compression_error(error_code::invalid_option, stream_format, "native DEFLATE codec received an unrelated format");
519 }
520 constexpr std::array< std::uint16_t, 29U > length_bases{3U, 4U, 5U, 6U, 7U, 8U, 9U, 10U, 11U, 13U, 15U, 17U, 19U, 23U, 27U, 31U, 35U, 43U, 51U, 59U, 67U, 83U, 99U, 115U, 131U, 163U, 195U, 227U, 258U};
521 constexpr std::array< std::uint8_t, 29U > length_extras{0U, 0U, 0U, 0U, 0U, 0U, 0U, 0U, 1U, 1U, 1U, 1U, 2U, 2U, 2U, 2U, 3U, 3U, 3U, 3U, 4U, 4U, 4U, 4U, 5U, 5U, 5U, 5U, 0U};
522 constexpr std::array< std::uint16_t, 30U > distance_bases{1U, 2U, 3U, 4U, 5U, 7U, 9U, 13U, 17U, 25U, 33U, 49U, 65U, 97U, 129U, 193U, 257U, 385U, 513U, 769U, 1025U, 1537U, 2049U, 3073U, 4097U, 6145U, 8193U, 12289U, 16385U, 24577U};
523 constexpr std::array< std::uint8_t, 30U > distance_extras{0U, 0U, 0U, 0U, 1U, 1U, 2U, 2U, 3U, 3U, 4U, 4U, 5U, 5U, 6U, 6U, 7U, 7U, 8U, 8U, 9U, 9U, 10U, 10U, 11U, 11U, 12U, 12U, 13U, 13U};
524
525 implementation::byte_reader< input_iterator, sentinel > source(std::move(first), std::move(last));
526 auto read_byte = [&]() -> std::byte
527 {
528 std::byte value{};
529 if (!source.read(value))
530 {
531 throw compression_error(error_code::invalid_data, stream_format, "truncated DEFLATE-family stream");
532 }
533 return value;
534 };
535 auto read_u32 = [&](bool little_endian) -> std::uint32_t
536 {
537 std::uint32_t value = 0U;
538 for (std::uint8_t index = 0U; index < 4U; ++index)
539 {
540 std::uint8_t const shift = little_endian ? index * 8U : static_cast< std::uint8_t >((3U - index) * 8U);
541 value |= static_cast< std::uint32_t >(std::to_integer< std::uint8_t >(read_byte())) << shift;
542 }
543 return value;
544 };
545
546 std::size_t total_output = 0U;
547 bool decoded_member = false;
548 do
549 {
550 if (decoded_member && !options.allow_concatenated_streams)
551 {
552 throw compression_error(error_code::trailing_data, stream_format, "compressed stream contains trailing data");
553 }
554 if (stream_format == format::zlib)
555 {
556 std::uint8_t const method = std::to_integer< std::uint8_t >(read_byte());
557 std::uint8_t const flags = std::to_integer< std::uint8_t >(read_byte());
558 if ((method & 0x0fU) != 8U || (method >> 4U) > 7U || (static_cast< std::uint16_t >(method) * 256U + flags) % 31U != 0U || (flags & 0x20U) != 0U)
559 {
560 throw compression_error(error_code::invalid_data, stream_format, "invalid or dictionary-based zlib header");
561 }
562 }
563 else if (stream_format == format::gzip)
564 {
566 auto read_header_byte = [&]() -> std::byte
567 {
568 std::byte const value = read_byte();
569 header_crc.update(value);
570 return value;
571 };
572 if (read_header_byte() != std::byte{0x1f} || read_header_byte() != std::byte{0x8b} || read_header_byte() != std::byte{0x08})
573 {
574 throw compression_error(error_code::invalid_data, stream_format, "invalid gzip header");
575 }
576 std::uint8_t const flags = std::to_integer< std::uint8_t >(read_header_byte());
577 if ((flags & 0xe0U) != 0U)
578 {
579 throw compression_error(error_code::invalid_data, stream_format, "gzip header uses reserved flags");
580 }
581 for (std::size_t index = 0U; index < 6U; ++index)
582 {
583 static_cast< void >(read_header_byte());
584 }
585 if ((flags & 0x04U) != 0U)
586 {
587 std::uint16_t const extra_size = static_cast< std::uint16_t >(std::to_integer< std::uint8_t >(read_header_byte()) | (static_cast< std::uint16_t >(std::to_integer< std::uint8_t >(read_header_byte())) << 8U));
588 for (std::size_t index = 0U; index < extra_size; ++index)
589 {
590 static_cast< void >(read_header_byte());
591 }
592 }
593 for (std::uint8_t flag : {std::uint8_t{0x08U}, std::uint8_t{0x10U}})
594 {
595 if ((flags & flag) != 0U)
596 {
597 while (read_header_byte() != std::byte{0x00})
598 {
599 }
600 }
601 }
602 if ((flags & 0x02U) != 0U)
603 {
604 std::uint16_t const expected = static_cast< std::uint16_t >(std::to_integer< std::uint8_t >(read_byte()) | (static_cast< std::uint16_t >(std::to_integer< std::uint8_t >(read_byte())) << 8U));
605 if (static_cast< std::uint16_t >(header_crc.value()) != expected)
606 {
607 throw compression_error(error_code::invalid_data, stream_format, "gzip header checksum mismatch");
608 }
609 }
610 }
611
612 iterator_bit_reader< decltype(source) > reader(source, stream_format);
613 std::array< std::byte, 32768U > history{};
614 std::size_t member_size = 0U;
617 auto emit = [&](std::byte value)
618 {
619 if (total_output == options.maximum_output_size)
620 {
621 throw compression_error(error_code::output_limit_exceeded, stream_format, "decompressed output exceeds configured limit");
622 }
623 history[member_size % history.size()] = value;
624 ++member_size;
625 ++total_output;
626 member_crc.update(value);
627 member_adler.update(value);
628 implementation::write_byte(output, value);
629 };
630
631 bool final_block = false;
632 while (!final_block)
633 {
634 final_block = reader.read_bits(1U) != 0U;
635 std::uint32_t const block_type = reader.read_bits(2U);
636 if (block_type == 0U)
637 {
638 reader.align_to_byte();
639 std::uint16_t const length = static_cast< std::uint16_t >(reader.read_bits(16U));
640 std::uint16_t const inverse = static_cast< std::uint16_t >(reader.read_bits(16U));
641 if (static_cast< std::uint16_t >(length ^ inverse) != 0xffffU)
642 {
643 throw compression_error(error_code::invalid_data, stream_format, "invalid stored DEFLATE block length");
644 }
645 for (std::size_t index = 0U; index < length; ++index)
646 {
647 emit(static_cast< std::byte >(reader.read_bits(8U)));
648 }
649 continue;
650 }
651 if (block_type == 3U)
652 {
653 throw compression_error(error_code::invalid_data, stream_format, "reserved DEFLATE block type");
654 }
655 huffman_decoder literal_decoder;
656 huffman_decoder distance_decoder;
657 if (block_type == 1U)
658 {
659 std::array< std::uint8_t, 288U > literal_lengths{};
660 std::fill(literal_lengths.begin(), literal_lengths.begin() + 144, 8U);
661 std::fill(literal_lengths.begin() + 144, literal_lengths.begin() + 256, 9U);
662 std::fill(literal_lengths.begin() + 256, literal_lengths.begin() + 280, 7U);
663 std::fill(literal_lengths.begin() + 280, literal_lengths.end(), 8U);
664 std::array< std::uint8_t, 32U > distance_lengths{};
665 distance_lengths.fill(5U);
666 literal_decoder.build(literal_lengths, stream_format);
667 distance_decoder.build(distance_lengths, stream_format);
668 }
669 else
670 {
671 std::size_t const literal_count = reader.read_bits(5U) + 257U;
672 std::size_t const distance_count = reader.read_bits(5U) + 1U;
673 std::size_t const code_length_count = reader.read_bits(4U) + 4U;
674 constexpr std::array< std::uint8_t, 19U > order{16U, 17U, 18U, 0U, 8U, 7U, 9U, 6U, 10U, 5U, 11U, 4U, 12U, 3U, 13U, 2U, 14U, 1U, 15U};
675 std::array< std::uint8_t, 19U > code_lengths{};
676 for (std::size_t index = 0U; index < code_length_count; ++index)
677 {
678 code_lengths[order[index]] = static_cast< std::uint8_t >(reader.read_bits(3U));
679 }
680 huffman_decoder code_length_decoder;
681 code_length_decoder.build(code_lengths, stream_format);
682 std::vector< std::uint8_t > lengths;
683 lengths.reserve(literal_count + distance_count);
684 while (lengths.size() < literal_count + distance_count)
685 {
686 std::uint16_t const symbol = code_length_decoder.decode(reader, stream_format);
687 if (symbol <= 15U)
688 {
689 lengths.push_back(static_cast< std::uint8_t >(symbol));
690 }
691 else if (symbol == 16U)
692 {
693 if (lengths.empty())
694 {
695 throw compression_error(error_code::invalid_data, stream_format, "DEFLATE repeat has no previous code length");
696 }
697 std::size_t const repeat = reader.read_bits(2U) + 3U;
698 if (repeat > literal_count + distance_count - lengths.size())
699 {
700 throw compression_error(error_code::invalid_data, stream_format, "DEFLATE code-length repeat exceeds the tree");
701 }
702 lengths.insert(lengths.end(), repeat, lengths.back());
703 }
704 else if (symbol == 17U || symbol == 18U)
705 {
706 std::size_t const repeat = symbol == 17U ? reader.read_bits(3U) + 3U : reader.read_bits(7U) + 11U;
707 if (repeat > literal_count + distance_count - lengths.size())
708 {
709 throw compression_error(error_code::invalid_data, stream_format, "DEFLATE zero repeat exceeds the tree");
710 }
711 lengths.insert(lengths.end(), repeat, 0U);
712 }
713 else
714 {
715 throw compression_error(error_code::invalid_data, stream_format, "invalid DEFLATE code-length symbol");
716 }
717 }
718 if (lengths[256U] == 0U)
719 {
720 throw compression_error(error_code::invalid_data, stream_format, "DEFLATE literal tree has no end marker");
721 }
722 literal_decoder.build(std::span< std::uint8_t const >(lengths.data(), literal_count), stream_format);
723 distance_decoder.build(std::span< std::uint8_t const >(lengths.data() + literal_count, distance_count), stream_format, true);
724 }
725 while (true)
726 {
727 std::uint16_t const symbol = literal_decoder.decode(reader, stream_format);
728 if (symbol < 256U)
729 {
730 emit(static_cast< std::byte >(symbol));
731 continue;
732 }
733 if (symbol == 256U)
734 {
735 break;
736 }
737 if (symbol < 257U || symbol > 285U)
738 {
739 throw compression_error(error_code::invalid_data, stream_format, "invalid DEFLATE length symbol");
740 }
741 std::size_t const length_index = symbol - 257U;
742 std::size_t const length = length_bases[length_index] + reader.read_bits(length_extras[length_index]);
743 std::uint16_t const distance_symbol = distance_decoder.decode(reader, stream_format);
744 if (distance_symbol >= distance_bases.size())
745 {
746 throw compression_error(error_code::invalid_data, stream_format, "invalid DEFLATE distance symbol");
747 }
748 std::size_t const distance = distance_bases[distance_symbol] + reader.read_bits(distance_extras[distance_symbol]);
749 if (distance == 0U || distance > member_size || distance > history.size())
750 {
751 throw compression_error(error_code::invalid_data, stream_format, "DEFLATE match distance exceeds available history");
752 }
753 for (std::size_t index = 0U; index < length; ++index)
754 {
755 emit(history[(member_size - distance) % history.size()]);
756 }
757 }
758 }
759 reader.align_to_byte();
760 if (stream_format == format::zlib && read_u32(false) != member_adler.value())
761 {
762 throw compression_error(error_code::invalid_data, stream_format, "zlib Adler-32 mismatch");
763 }
764 if (stream_format == format::gzip && (read_u32(true) != member_crc.value() || read_u32(true) != static_cast< std::uint32_t >(member_size)))
765 {
766 throw compression_error(error_code::invalid_data, stream_format, "gzip data checksum or size mismatch");
767 }
768 decoded_member = true;
769 } while (!source.empty());
770 return output;
771 }
772
773} // namespace rpnx::compression::deflate_codec
774
775#endif
Exception raised for malformed streams, invalid options, and codec failures.
Canonical Huffman decoder for the bit-reversed codes used by DEFLATE.
Definition deflate.hpp:29
std::uint16_t decode(reader_type &reader, format stream_format) const
Decodes one symbol from a bit reader.
Definition deflate.hpp:116
void build(std::span< std::uint8_t const > lengths, format stream_format, bool allow_empty=false)
Builds and validates a decoder from per-symbol code lengths.
Definition deflate.hpp:38
LSB-first bit reader that consumes a single-pass byte reader.
Definition deflate.hpp:258
void align_to_byte() noexcept
Discard padding through the next byte boundary.
Definition deflate.hpp:295
std::uint32_t read_bits(std::uint8_t bit_count)
Consumes a low-order-first bit field.
Definition deflate.hpp:275
iterator_bit_reader(byte_reader_type &input, format stream_format) noexcept
Constructs a bit reader for one DEFLATE-family stream.
Definition deflate.hpp:265
LSB-first bit writer that emits bytes through an STL output iterator.
Definition deflate.hpp:204
output_iterator finish()
Flushes zero byte padding and releases the output iterator.
Definition deflate.hpp:235
void write_bits(std::uint32_t value, std::uint8_t bit_count)
Appends the selected low bits of a value.
Definition deflate.hpp:219
iterator_bit_writer(output_iterator output)
Constructs a writer owning an output iterator.
Definition deflate.hpp:210
Incremental Adler-32 accumulator.
Definition io.hpp:200
std::uint32_t value() const noexcept
Returns the checksum for all bytes supplied so far.
Definition io.hpp:217
void update(std::byte value) noexcept
Includes one byte in the checksum.
Definition io.hpp:206
Single-pass byte reader over an input iterator and sentinel.
Definition io.hpp:71
bool empty() const
Tests whether no unread byte remains.
Definition io.hpp:86
bool read(std::byte &value)
Reads one byte.
Definition io.hpp:116
Incremental reflected IEEE CRC-32 accumulator.
Definition io.hpp:169
std::uint32_t value() const noexcept
Returns the checksum for all bytes supplied so far.
Definition io.hpp:189
void update(std::byte value) noexcept
Includes one byte in the checksum.
Definition io.hpp:175
Shared iterator, byte-conversion, and checksum primitives.
Internal implementation shared by raw DEFLATE, zlib, gzip, and ZIP.
Definition deflate.hpp:25
output_iterator decompress(format stream_format, input_iterator first, sentinel last, output_iterator output, decompression_options const &options)
Decompresses raw DEFLATE, zlib, or gzip input.
Definition deflate.hpp:514
void compress_fixed_block(writer_type &writer, std::span< std::byte const > input, bool final_block, std::int32_t level)
Emits one fixed-Huffman DEFLATE block from a bounded search buffer.
Definition deflate.hpp:317
void write_fixed_symbol(writer_type &writer, std::uint16_t symbol)
Emits one fixed-Huffman literal or length symbol.
Definition deflate.hpp:158
std::uint16_t reverse_bits(std::uint16_t value, std::uint8_t bit_count) noexcept
Reverses the selected low bits for DEFLATE wire order.
Definition deflate.hpp:140
output_iterator compress(format stream_format, input_iterator first, sentinel last, output_iterator output, compression_options const &options)
Compresses an iterator range as raw DEFLATE, zlib, or gzip.
Definition deflate.hpp:408
std::uint32_t crc32(std::span< std::byte const > input) noexcept
Computes the reflected IEEE CRC-32 used by gzip and ZIP.
Definition deflate.hpp:183
void write_byte(output_iterator &output, std::byte value)
Writes one byte through an output iterator and advances it.
Definition io.hpp:48
constexpr std::byte to_byte(value_type value) noexcept
Converts one supported iterator value to std::byte.
Definition io.hpp:27
format
Wire formats recognized by the library.
@ gzip
RFC 1952 gzip member or concatenated members.
@ zlib
RFC 1950 zlib wrapper around DEFLATE.
@ deflate
Raw RFC 1951 DEFLATE stream.
@ trailing_data
Bytes remain after the permitted stream members.
@ output_limit_exceeded
Decoding would exceed a configured resource limit.
@ invalid_option
An option or format value is outside its accepted range.
@ invalid_data
The input does not conform to the selected format.
Options shared by compression operations.
std::optional< std::int32_t > level
Optional format-specific compression level.
Resource and stream-validation policy for decompression operations.
std::size_t maximum_output_size
Maximum total number of bytes the operation may emit.
bool allow_concatenated_streams
Whether to decode adjacent members for formats that define concatenation.