Class: Cabriolet::Decompressors::MSZIP
- Defined in:
- lib/cabriolet/decompressors/mszip.rb
Overview
MSZIP handles MSZIP (deflate) compressed data Based on RFC 1951 and libmspack implementation
Constant Summary collapse
- FRAME_SIZE =
MSZIP frame size (32KB sliding window)
32_768- LITERAL_MAXSYMBOLS =
Huffman tree constants
288- LITERAL_TABLEBITS =
9- DISTANCE_MAXSYMBOLS =
32- DISTANCE_TABLEBITS =
6- SIGNATURE_BYTE_C =
MSZIP signature bytes
0x43- SIGNATURE_BYTE_K =
ASCII 'C'
0x4B- MAX_SIGNATURE_SEARCH =
Maximum bytes to search for CK signature (prevents infinite loops)
10_000- LIT_LENGTHS =
Match lengths for literal codes 257-285
[ 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258 ].freeze
- DIST_OFFSETS =
Match offsets for distance codes 0-29
[ 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, 8193, 12_289, 16_385, 24_577 ].freeze
- LIT_EXTRABITS =
Extra bits for literal codes 257-285
[ 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0 ].freeze
- DIST_EXTRABITS =
Extra bits for distance codes 0-29
[ 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13 ].freeze
- BITLEN_ORDER =
Order of bit length code lengths
[ 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 ].freeze
Instance Attribute Summary
Attributes inherited from Base
#buffer_size, #input, #io_system, #output
Instance Method Summary collapse
-
#decompress(bytes) ⇒ Integer
Decompress MSZIP data.
-
#free ⇒ void
Free resources used by the decompressor.
-
#initialize(io_system, input, output, buffer_size, fix_mszip: false, salvage: false, **_kwargs) ⇒ MSZIP
constructor
Initialize MSZIP decompressor.
Constructor Details
#initialize(io_system, input, output, buffer_size, fix_mszip: false, salvage: false, **_kwargs) ⇒ MSZIP
Initialize MSZIP decompressor
60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 |
# File 'lib/cabriolet/decompressors/mszip.rb', line 60 def initialize(io_system, input, output, buffer_size, fix_mszip: false, salvage: false, **_kwargs) super(io_system, input, output, buffer_size) @fix_mszip = fix_mszip # Initialize sliding window (must be binary to avoid UTF-8 character vs byte mismatch) @window = ("\0" * FRAME_SIZE).b @window_posn = 0 @bytes_output = 0 @window_offset = 0 # Offset into window for unconsumed data (for multi-file CFDATA blocks) # Initialize bitstream @bitstream = Binary::Bitstream.new(io_system, input, buffer_size, salvage: salvage) # Initialize Huffman trees @literal_lengths = Array.new(LITERAL_MAXSYMBOLS, 0) @distance_lengths = Array.new(DISTANCE_MAXSYMBOLS, 0) @literal_tree = nil @distance_tree = nil # Cache ENV lookups once at initialization @debug_mszip = ENV.fetch("DEBUG_MSZIP", nil) @debug_mszip_symbols = ENV.fetch("DEBUG_MSZIP_SYMBOLS", nil) end |
Instance Method Details
#decompress(bytes) ⇒ Integer
Decompress MSZIP data
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 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 |
# File 'lib/cabriolet/decompressors/mszip.rb', line 105 def decompress(bytes) total_written = 0 if @debug_mszip warn "DEBUG MSZIP.decompress(#{bytes}): ENTRY bytes_output=#{@bytes_output} window_offset=#{@window_offset} window_posn=#{@window_posn}" end while bytes.positive? # Check if we have buffered data from previous inflate if @bytes_output.positive? if @debug_mszip warn "DEBUG MSZIP: Using buffered data: bytes_output=#{@bytes_output} window_offset=#{@window_offset}" end # Write from buffer write_amount = [bytes, @bytes_output].min io_system.write(output, @window[@window_offset, write_amount]) total_written += write_amount bytes -= write_amount @bytes_output -= write_amount @window_offset += write_amount if @debug_mszip warn "DEBUG MSZIP: After buffer write: total_written=#{total_written} bytes_remaining=#{bytes} bytes_output=#{@bytes_output}" end # Continue loop to check if we need more data next end # No buffered data - need to inflate a new MSZIP frame # Reset window for new frame @window_offset = 0 @window_posn = 0 # Read 'CK' signature (marks start of MSZIP frame) # Every MSZIP frame starts with a CK signature if @debug_mszip warn "DEBUG MSZIP: Reading CK signature (new MSZIP frame)" end read_signature # Inflate the MSZIP frame (processes deflate blocks until last_block or window full) if @debug_mszip warn "DEBUG MSZIP: Calling inflate_block" end begin inflate_block rescue DecompressionError raise unless @fix_mszip # In repair mode, pad with zeros (@bytes_output...FRAME_SIZE).each do |i| @window.setbyte(i, 0) end @bytes_output = FRAME_SIZE end if @debug_mszip warn "DEBUG MSZIP: After inflate_block: bytes_output=#{@bytes_output} window_posn=#{@window_posn}" end # Now we have data in the window buffer - loop back to write from it end if @debug_mszip warn "DEBUG MSZIP.decompress: EXIT total_written=#{total_written}" end total_written end |
#free ⇒ void
This method returns an undefined value.
Free resources used by the decompressor
Releases memory buffers to prevent memory leaks when the decompressor is no longer needed.
92 93 94 95 96 97 98 99 |
# File 'lib/cabriolet/decompressors/mszip.rb', line 92 def free @window = nil @bitstream = nil @literal_lengths = nil @distance_lengths = nil @literal_tree = nil @distance_tree = nil end |