Class: Omnizip::Formats::XzImpl::BlockHeader
- Inherits:
-
Object
- Object
- Omnizip::Formats::XzImpl::BlockHeader
- Defined in:
- lib/omnizip/formats/xz_impl/block_header.rb
Overview
XZ block header
Block header contains:
- Header size (1 byte) - size in 4-byte blocks
- Block flags (1 byte)
- Compressed size (variable, optional)
- Uncompressed size (variable, optional)
- Filter flags (variable)
- Padding to 4-byte boundary
- CRC32 (4 bytes)
Constant Summary collapse
- FILTER_LZMA2 =
Filter IDs
0x21
Instance Attribute Summary collapse
-
#compressed_size ⇒ Object
readonly
Returns the value of attribute compressed_size.
-
#filters ⇒ Object
readonly
Returns the value of attribute filters.
-
#uncompressed_size ⇒ Object
readonly
Returns the value of attribute uncompressed_size.
Class Method Summary collapse
-
.decode(io) ⇒ BlockHeader
Decode block header from stream.
-
.decode_filter(data) ⇒ Array<Hash, Integer>
Decode filter.
-
.decode_multibyte_integer(data) ⇒ Array<Integer, Integer>
Decode multibyte integer.
Instance Method Summary collapse
-
#encode ⇒ String
Encode block header to bytes.
-
#initialize(options = {}) ⇒ BlockHeader
constructor
Initialize block header.
Constructor Details
#initialize(options = {}) ⇒ BlockHeader
Initialize block header
25 26 27 28 29 |
# File 'lib/omnizip/formats/xz_impl/block_header.rb', line 25 def initialize( = {}) @compressed_size = [:compressed_size] @uncompressed_size = [:uncompressed_size] @filters = [:filters] || [{ id: FILTER_LZMA2 }] end |
Instance Attribute Details
#compressed_size ⇒ Object (readonly)
Returns the value of attribute compressed_size.
20 21 22 |
# File 'lib/omnizip/formats/xz_impl/block_header.rb', line 20 def compressed_size @compressed_size end |
#filters ⇒ Object (readonly)
Returns the value of attribute filters.
20 21 22 |
# File 'lib/omnizip/formats/xz_impl/block_header.rb', line 20 def filters @filters end |
#uncompressed_size ⇒ Object (readonly)
Returns the value of attribute uncompressed_size.
20 21 22 |
# File 'lib/omnizip/formats/xz_impl/block_header.rb', line 20 def uncompressed_size @uncompressed_size end |
Class Method Details
.decode(io) ⇒ BlockHeader
Decode block header from stream
81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 |
# File 'lib/omnizip/formats/xz_impl/block_header.rb', line 81 def self.decode(io) header_size_blocks = io.read(1).unpack1("C") return nil if header_size_blocks.nil? || header_size_blocks.zero? header_size_bytes = header_size_blocks * 4 # Read rest of header (excluding size byte and CRC32) header_data_size = header_size_bytes - 1 - 4 header_data = io.read(header_data_size) # Read and verify CRC32 crc32_expected = io.read(4).unpack1("V") full_header = [header_size_blocks].pack("C") + header_data crc32_actual = Zlib.crc32(full_header) unless crc32_expected == crc32_actual raise Error, "XZ block header CRC32 mismatch" end # Parse header data flags = header_data.unpack1("C") offset = 1 = {} # Read compressed size if present if flags.anybits?(0x40) [:compressed_size], bytes_read = decode_multibyte_integer(header_data[offset..]) offset += bytes_read end # Read uncompressed size if present if flags.anybits?(0x80) [:uncompressed_size], bytes_read = decode_multibyte_integer(header_data[offset..]) offset += bytes_read end # Parse filters filter_count = (flags & 0x03) + 1 [:filters] = [] filter_count.times do filter, bytes_read = decode_filter(header_data[offset..]) [:filters] << filter offset += bytes_read end new() end |
.decode_filter(data) ⇒ Array<Hash, Integer>
Decode filter
193 194 195 196 197 198 199 200 201 202 203 204 205 206 |
# File 'lib/omnizip/formats/xz_impl/block_header.rb', line 193 def self.decode_filter(data) filter_id, offset = decode_multibyte_integer(data) props_size, bytes_read = decode_multibyte_integer(data[offset..]) offset += bytes_read props = data[offset, props_size] offset += props_size filter = { id: filter_id } filter[:properties] = props unless props.empty? [filter, offset] end |
.decode_multibyte_integer(data) ⇒ Array<Integer, Integer>
Decode multibyte integer
155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 |
# File 'lib/omnizip/formats/xz_impl/block_header.rb', line 155 def self.decode_multibyte_integer(data) value = 0 shift = 0 offset = 0 loop do byte = data[offset].unpack1("C") value |= (byte & 0x7F) << shift offset += 1 break if byte.nobits?(0x80) shift += 7 end [value, offset] end |
Instance Method Details
#encode ⇒ String
Encode block header to bytes
34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 |
# File 'lib/omnizip/formats/xz_impl/block_header.rb', line 34 def encode # Block flags byte flags = 0 flags |= 0x40 if @compressed_size flags |= 0x80 if @uncompressed_size # Number of filters (0 = 1 filter, 3 = 4 filters) filter_count = [@filters.size - 1, 3].min flags |= filter_count data = [flags].pack("C") # Add sizes if present (encoded as multibyte integers) if @compressed_size data << encode_multibyte_integer(@compressed_size) end if @uncompressed_size data << encode_multibyte_integer(@uncompressed_size) end # Add filter properties @filters.each do |filter| data << encode_filter(filter) end # Calculate header size (including size byte and CRC32) # Round up to 4-byte blocks header_size_bytes = 1 + data.bytesize + 4 header_size_blocks = (header_size_bytes + 3) / 4 # Add padding padding_size = (header_size_blocks * 4) - header_size_bytes data << ("\0" * padding_size) if padding_size.positive? # Prepend header size full_header = [header_size_blocks].pack("C") + data # Append CRC32 crc32 = Zlib.crc32(full_header) full_header + [crc32].pack("V") end |