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()
|
BAN::ErrorOr<BAN::Vector<uint8_t>> Compressor::compress()
|
||||||
{
|
{
|
||||||
|
const uint32_t data_size = m_data.size();
|
||||||
|
|
||||||
uint32_t checksum = 0;
|
uint32_t checksum = 0;
|
||||||
switch (m_type)
|
switch (m_type)
|
||||||
{
|
{
|
||||||
|
|
@ -590,6 +592,21 @@ namespace LibDEFLATE
|
||||||
TRY(m_stream.write_bits(0x9C, 8)); // default compression
|
TRY(m_stream.write_bits(0x9C, 8)); // default compression
|
||||||
checksum = calculate_adler32(m_data);
|
checksum = calculate_adler32(m_data);
|
||||||
break;
|
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;
|
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 >> 8, 8));
|
||||||
TRY(m_stream.write_bits(checksum >> 0, 8));
|
TRY(m_stream.write_bits(checksum >> 0, 8));
|
||||||
break;
|
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();
|
return m_stream.take_buffer();
|
||||||
|
|
|
||||||
|
|
@ -127,6 +127,58 @@ namespace LibDEFLATE
|
||||||
TRY(m_stream.take_bits(16));
|
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 {};
|
return {};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -154,6 +206,32 @@ namespace LibDEFLATE
|
||||||
return BAN::Error::from_errno(EINVAL);
|
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 {};
|
return {};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ namespace LibDEFLATE
|
||||||
{
|
{
|
||||||
Raw,
|
Raw,
|
||||||
Zlib,
|
Zlib,
|
||||||
|
GZip,
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,25 @@ namespace LibDEFLATE
|
||||||
return (s2 << 16) | s1;
|
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)
|
inline constexpr uint16_t reverse_bits(uint16_t value, size_t count)
|
||||||
{
|
{
|
||||||
uint16_t reverse = 0;
|
uint16_t reverse = 0;
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue