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:
parent
68984abc43
commit
e6b1e1bb33
2 changed files with 106 additions and 96 deletions
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue