LibDEFLATE: Add GZip support
This allows compressing and decompressing with data using GZip headers and footers
This commit is contained in:
parent
632787b142
commit
7c1b403e05
|
|
@ -580,6 +580,8 @@ namespace LibDEFLATE
|
|||
|
||||
BAN::ErrorOr<BAN::Vector<uint8_t>> 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();
|
||||
|
|
|
|||
|
|
@ -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<uint32_t>(TRY(m_stream.take_bits(16))) |
|
||||
static_cast<uint32_t>(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<uint32_t>(TRY(m_stream.take_bits(16))) |
|
||||
static_cast<uint32_t>(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 {};
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ namespace LibDEFLATE
|
|||
{
|
||||
Raw,
|
||||
Zlib,
|
||||
GZip,
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
Loading…
Reference in New Issue