diff --git a/LibDEFLATE/Compressor.cpp b/LibDEFLATE/Compressor.cpp index 4664d04..9d45864 100644 --- a/LibDEFLATE/Compressor.cpp +++ b/LibDEFLATE/Compressor.cpp @@ -580,6 +580,8 @@ namespace LibDEFLATE BAN::ErrorOr> Compressor::compress() { + const uint32_t data_size = m_data.size(); + uint32_t checksum = 0; switch (m_type) { @@ -590,6 +592,21 @@ namespace LibDEFLATE TRY(m_stream.write_bits(0x9C, 8)); // default compression checksum = calculate_adler32(m_data); break; + case StreamType::GZip: + { + const time_t current_time = time(nullptr); + TRY(m_stream.write_bits(0x1F, 8)); // ID1 + TRY(m_stream.write_bits(0x8B, 8)); // ID2 + TRY(m_stream.write_bits(8, 8)); // CM (deflate) + TRY(m_stream.write_bits(0, 8)); // FLG + TRY(m_stream.write_bits(current_time >> 0, 8)); // MTIME + TRY(m_stream.write_bits(current_time >> 8, 8)); + TRY(m_stream.write_bits(current_time >> 16, 8)); + TRY(m_stream.write_bits(current_time >> 24, 8)); + TRY(m_stream.write_bits(0, 8)); // XFL + TRY(m_stream.write_bits(3, 8)); // OS (Unix) + checksum = calculate_crc32(m_data); + } } constexpr size_t max_block_size = 16 * 1024; @@ -612,6 +629,16 @@ namespace LibDEFLATE TRY(m_stream.write_bits(checksum >> 8, 8)); TRY(m_stream.write_bits(checksum >> 0, 8)); break; + case StreamType::GZip: + TRY(m_stream.write_bits(checksum >> 0, 8)); + TRY(m_stream.write_bits(checksum >> 8, 8)); + TRY(m_stream.write_bits(checksum >> 16, 8)); + TRY(m_stream.write_bits(checksum >> 24, 8)); + TRY(m_stream.write_bits(data_size >> 0, 8)); + TRY(m_stream.write_bits(data_size >> 8, 8)); + TRY(m_stream.write_bits(data_size >> 16, 8)); + TRY(m_stream.write_bits(data_size >> 24, 8)); + break; } return m_stream.take_buffer(); diff --git a/LibDEFLATE/Decompressor.cpp b/LibDEFLATE/Decompressor.cpp index c5d757b..f2179a5 100644 --- a/LibDEFLATE/Decompressor.cpp +++ b/LibDEFLATE/Decompressor.cpp @@ -127,6 +127,58 @@ namespace LibDEFLATE TRY(m_stream.take_bits(16)); } + return {}; + } + case StreamType::GZip: + { + const uint8_t id1 = TRY(m_stream.take_bits(8)); + const uint8_t id2 = TRY(m_stream.take_bits(8)); + if (id1 != 0x1F || id2 != 0x8B) + { + dwarnln("gzip header invalid identification"); + return BAN::Error::from_errno(EINVAL); + + } + + const uint8_t cm = TRY(m_stream.take_bits(8)); + if (cm != 8) + { + dwarnln("gzip does not use DEFLATE"); + return BAN::Error::from_errno(EINVAL); + } + + const uint8_t flg = TRY(m_stream.take_bits(8)); + + TRY(m_stream.take_bits(16)); // mtime + TRY(m_stream.take_bits(16)); + + TRY(m_stream.take_bits(8)); // xfl + + TRY(m_stream.take_bits(8)); // os + + // extra fields + if (flg & (1 << 2)) + { + const uint16_t xlen = TRY(m_stream.take_bits(16)); + for (size_t i = 0; i < xlen; i++) + TRY(m_stream.take_bits(8)); + } + + // file name + if (flg & (1 << 3)) + while (TRY(m_stream.take_bits(8)) != '\0') + continue; + + // file comment + if (flg & (1 << 4)) + while (TRY(m_stream.take_bits(8)) != '\0') + continue; + + // crc16 + // TODO: validate + if (flg & (1 << 1)) + TRY(m_stream.take_bits(16)); + return {}; } } @@ -154,6 +206,32 @@ namespace LibDEFLATE return BAN::Error::from_errno(EINVAL); } + return {}; + } + case StreamType::GZip: + { + m_stream.skip_to_byte_boundary(); + + const uint32_t crc32 = + static_cast(TRY(m_stream.take_bits(16))) | + static_cast(TRY(m_stream.take_bits(16))) << 16; + + if (crc32 != calculate_crc32(m_output.span())) + { + dwarnln("gzip final crc32 checksum failed"); + return BAN::Error::from_errno(EINVAL); + } + + const uint32_t isize = + static_cast(TRY(m_stream.take_bits(16))) | + static_cast(TRY(m_stream.take_bits(16))) << 16; + + if (isize != m_output.size() % UINT32_MAX) + { + dwarnln("gzip final isize does not match {} vs {}", isize, m_output.size()); + return BAN::Error::from_errno(EINVAL); + } + return {}; } } diff --git a/LibDEFLATE/include/LibDEFLATE/StreamType.h b/LibDEFLATE/include/LibDEFLATE/StreamType.h index 8ec35f2..894bd58 100644 --- a/LibDEFLATE/include/LibDEFLATE/StreamType.h +++ b/LibDEFLATE/include/LibDEFLATE/StreamType.h @@ -7,6 +7,7 @@ namespace LibDEFLATE { Raw, Zlib, + GZip, }; } diff --git a/LibDEFLATE/include/LibDEFLATE/Utils.h b/LibDEFLATE/include/LibDEFLATE/Utils.h index 699550b..ed4bef4 100644 --- a/LibDEFLATE/include/LibDEFLATE/Utils.h +++ b/LibDEFLATE/include/LibDEFLATE/Utils.h @@ -19,6 +19,25 @@ namespace LibDEFLATE return (s2 << 16) | s1; } + inline uint32_t calculate_crc32(BAN::ConstByteSpan data) + { + uint32_t crc32 = 0xFFFFFFFF; + uint32_t polynomial = 0xEDB88320; + + for (size_t i = 0; i < data.size(); i++) { + crc32 ^= data[i]; + + for (size_t j = 0; j < 8; j++) { + if (crc32 & 1) + crc32 = (crc32 >> 1) ^ polynomial; + else + crc32 >>= 1; + } + } + + return ~crc32; + } + inline constexpr uint16_t reverse_bits(uint16_t value, size_t count) { uint16_t reverse = 0;