1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-25 22:47:47 +00:00

LibCompress: Move finishing the current XZ stream into its own function

This commit is contained in:
Tim Schumacher 2023-04-05 16:41:05 +02:00 committed by Brian Gianforcaro
parent 68984abc43
commit e6b1e1bb33
2 changed files with 106 additions and 96 deletions

View file

@ -301,6 +301,109 @@ ErrorOr<void> XzDecompressor::finish_current_block()
return {};
}
ErrorOr<void> XzDecompressor::finish_current_stream()
{
// We already read the Index Indicator (one byte) to determine that this is an Index.
auto start_of_current_block = m_stream->read_bytes() - 1;
// 4.2. Number of Records:
// "This field indicates how many Records there are in the List
// of Records field, and thus how many Blocks there are in the
// Stream. The value is stored using the encoding described in
// Section 1.2."
u64 number_of_records = TRY(m_stream->read_value<XzMultibyteInteger>());
if (m_processed_blocks.size() != number_of_records)
return Error::from_string_literal("Number of Records in XZ Index does not match the number of processed Blocks");
// 4.3. List of Records:
// "List of Records consists of as many Records as indicated by the
// Number of Records field:"
for (u64 i = 0; i < number_of_records; i++) {
// "Each Record contains information about one Block:
//
// +===============+===================+
// | Unpadded Size | Uncompressed Size |
// +===============+===================+"
// 4.3.1. Unpadded Size:
// "This field indicates the size of the Block excluding the Block
// Padding field. That is, Unpadded Size is the size of the Block
// Header, Compressed Data, and Check fields. Unpadded Size is
// stored using the encoding described in Section 1.2."
u64 unpadded_size = TRY(m_stream->read_value<XzMultibyteInteger>());
// "The value MUST never be zero; with the current structure of Blocks, the
// actual minimum value for Unpadded Size is five."
if (unpadded_size < 5)
return Error::from_string_literal("XZ index contains a record with an unpadded size of less than five");
// 4.3.2. Uncompressed Size:
// "This field indicates the Uncompressed Size of the respective
// Block as bytes. The value is stored using the encoding
// described in Section 1.2."
u64 uncompressed_size = TRY(m_stream->read_value<XzMultibyteInteger>());
// 4.3. List of Records:
// "If the decoder has decoded all the Blocks of the Stream, it
// MUST verify that the contents of the Records match the real
// Unpadded Size and Uncompressed Size of the respective Blocks."
if (m_processed_blocks[i].uncompressed_size != uncompressed_size)
return Error::from_string_literal("Uncompressed size of XZ Block does not match the Index");
if (m_processed_blocks[i].unpadded_size != unpadded_size)
return Error::from_string_literal("Unpadded size of XZ Block does not match the Index");
}
// 4.4. Index Padding:
// "This field MUST contain 0-3 null bytes to pad the Index to
// a multiple of four bytes. If any of the bytes are not null
// bytes, the decoder MUST indicate an error."
while ((m_stream->read_bytes() - start_of_current_block) % 4 != 0) {
auto padding_byte = TRY(m_stream->read_value<u8>());
if (padding_byte != 0)
return Error::from_string_literal("XZ index contains a non-null padding byte");
}
// 4.5. CRC32:
// "The CRC32 is calculated over everything in the Index field
// except the CRC32 field itself. The CRC32 is stored as an
// unsigned 32-bit little endian integer."
u32 index_crc32 = TRY(m_stream->read_value<LittleEndian<u32>>());
// "If the calculated value does not match the stored one, the decoder MUST indicate
// an error."
// TODO: Validation of the index CRC32 is currently unimplemented.
(void)index_crc32;
auto size_of_index = m_stream->read_bytes() - start_of_current_block;
// According to the specification of a stream (2.1. Stream), the index is the last element in a stream,
// followed by the stream footer (2.1.2. Stream Footer).
auto stream_footer = TRY(m_stream->read_value<XzStreamFooter>());
// This handles verifying the CRC32 (2.1.2.1. CRC32) and the magic bytes (2.1.2.4. Footer Magic Bytes).
TRY(stream_footer.validate());
// 2.1.2.2. Backward Size:
// "If the stored value does not match the real size of the Index
// field, the decoder MUST indicate an error."
if (stream_footer.backward_size() != size_of_index)
return Error::from_string_literal("XZ index size does not match the stored size in the stream footer");
// 2.1.2.3. Stream Flags:
// "This is a copy of the Stream Flags field from the Stream
// Header. The information stored to Stream Flags is needed
// when parsing the Stream backwards. The decoder MUST compare
// the Stream Flags fields in both Stream Header and Stream
// Footer, and indicate an error if they are not identical."
if (Bytes { &*m_stream_flags, sizeof(XzStreamFlags) } != Bytes { &stream_footer.flags, sizeof(stream_footer.flags) })
return Error::from_string_literal("XZ stream header flags don't match the stream footer");
return {};
}
ErrorOr<Bytes> XzDecompressor::read_some(Bytes bytes)
{
if (!m_stream_flags.has_value()) {
@ -324,102 +427,8 @@ ErrorOr<Bytes> XzDecompressor::read_some(Bytes bytes)
auto encoded_block_header_size_or_index_indicator = TRY(m_stream->read_value<u8>());
if (encoded_block_header_size_or_index_indicator == 0x00) {
// This is an Index.
// 4.2. Number of Records:
// "This field indicates how many Records there are in the List
// of Records field, and thus how many Blocks there are in the
// Stream. The value is stored using the encoding described in
// Section 1.2."
u64 number_of_records = TRY(m_stream->read_value<XzMultibyteInteger>());
if (m_processed_blocks.size() != number_of_records)
return Error::from_string_literal("Number of Records in XZ Index does not match the number of processed Blocks");
// 4.3. List of Records:
// "List of Records consists of as many Records as indicated by the
// Number of Records field:"
for (u64 i = 0; i < number_of_records; i++) {
// "Each Record contains information about one Block:
//
// +===============+===================+
// | Unpadded Size | Uncompressed Size |
// +===============+===================+"
// 4.3.1. Unpadded Size:
// "This field indicates the size of the Block excluding the Block
// Padding field. That is, Unpadded Size is the size of the Block
// Header, Compressed Data, and Check fields. Unpadded Size is
// stored using the encoding described in Section 1.2."
u64 unpadded_size = TRY(m_stream->read_value<XzMultibyteInteger>());
// "The value MUST never be zero; with the current structure of Blocks, the
// actual minimum value for Unpadded Size is five."
if (unpadded_size < 5)
return Error::from_string_literal("XZ index contains a record with an unpadded size of less than five");
// 4.3.2. Uncompressed Size:
// "This field indicates the Uncompressed Size of the respective
// Block as bytes. The value is stored using the encoding
// described in Section 1.2."
u64 uncompressed_size = TRY(m_stream->read_value<XzMultibyteInteger>());
// 4.3. List of Records:
// "If the decoder has decoded all the Blocks of the Stream, it
// MUST verify that the contents of the Records match the real
// Unpadded Size and Uncompressed Size of the respective Blocks."
if (m_processed_blocks[i].uncompressed_size != uncompressed_size)
return Error::from_string_literal("Uncompressed size of XZ Block does not match the Index");
if (m_processed_blocks[i].unpadded_size != unpadded_size)
return Error::from_string_literal("Unpadded size of XZ Block does not match the Index");
}
// 4.4. Index Padding:
// "This field MUST contain 0-3 null bytes to pad the Index to
// a multiple of four bytes. If any of the bytes are not null
// bytes, the decoder MUST indicate an error."
while ((m_stream->read_bytes() - start_of_current_block) % 4 != 0) {
auto padding_byte = TRY(m_stream->read_value<u8>());
if (padding_byte != 0)
return Error::from_string_literal("XZ index contains a non-null padding byte");
}
// 4.5. CRC32:
// "The CRC32 is calculated over everything in the Index field
// except the CRC32 field itself. The CRC32 is stored as an
// unsigned 32-bit little endian integer."
u32 index_crc32 = TRY(m_stream->read_value<LittleEndian<u32>>());
// "If the calculated value does not match the stored one, the decoder MUST indicate
// an error."
// TODO: Validation of the index CRC32 is currently unimplemented.
(void)index_crc32;
auto size_of_index = m_stream->read_bytes() - start_of_current_block;
// According to the specification of a stream (2.1. Stream), the index is the last element in a stream,
// followed by the stream footer (2.1.2. Stream Footer).
auto stream_footer = TRY(m_stream->read_value<XzStreamFooter>());
// This handles verifying the CRC32 (2.1.2.1. CRC32) and the magic bytes (2.1.2.4. Footer Magic Bytes).
TRY(stream_footer.validate());
// 2.1.2.2. Backward Size:
// "If the stored value does not match the real size of the Index
// field, the decoder MUST indicate an error."
if (stream_footer.backward_size() != size_of_index)
return Error::from_string_literal("XZ index size does not match the stored size in the stream footer");
// 2.1.2.3. Stream Flags:
// "This is a copy of the Stream Flags field from the Stream
// Header. The information stored to Stream Flags is needed
// when parsing the Stream backwards. The decoder MUST compare
// the Stream Flags fields in both Stream Header and Stream
// Footer, and indicate an error if they are not identical."
if (Bytes { &*m_stream_flags, sizeof(XzStreamFlags) } != Bytes { &stream_footer.flags, sizeof(stream_footer.flags) })
return Error::from_string_literal("XZ stream header flags don't match the stream footer");
// This is an Index, which is the last element before the stream footer.
TRY(finish_current_stream());
// Another XZ Stream might follow, so we just unset the current information and continue on the next read.
m_stream_flags.clear();

View file

@ -113,6 +113,7 @@ private:
ErrorOr<bool> load_next_stream();
ErrorOr<void> finish_current_block();
ErrorOr<void> finish_current_stream();
NonnullOwnPtr<CountingStream> m_stream;
Optional<XzStreamFlags> m_stream_flags;