瀏覽代碼

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

Tim Schumacher 2 年之前
父節點
當前提交
e6b1e1bb33
共有 2 個文件被更改,包括 106 次插入96 次删除
  1. 105 96
      Userland/Libraries/LibCompress/Xz.cpp
  2. 1 0
      Userland/Libraries/LibCompress/Xz.h

+ 105 - 96
Userland/Libraries/LibCompress/Xz.cpp

@@ -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();

+ 1 - 0
Userland/Libraries/LibCompress/Xz.h

@@ -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;