Class: Cabriolet::Compressors::LZX

Inherits:
Base
  • Object
show all
Defined in:
lib/cabriolet/compressors/lzx.rb

Overview

LZX handles LZX compression Based on libmspack lzxc.c implementation

Phase 1 Implementation:

  • VERBATIM blocks only

  • Basic LZ77 matching

  • Simple tree building

  • No E8 preprocessing

  • 32KB window size

Constant Summary collapse

FRAME_SIZE =

Frame size (32KB per frame)

32_768
BLOCKTYPE_VERBATIM =

Block types

1
BLOCKTYPE_ALIGNED =
2
BLOCKTYPE_UNCOMPRESSED =
3
MIN_MATCH =

Match constants

2
MAX_MATCH =
257
NUM_CHARS =
256
PRETREE_NUM_ELEMENTS =

Tree constants

20
PRETREE_MAXSYMBOLS =
20
ALIGNED_NUM_ELEMENTS =
8
ALIGNED_MAXSYMBOLS =
8
NUM_PRIMARY_LENGTHS =
7
NUM_SECONDARY_LENGTHS =
249
LENGTH_MAXSYMBOLS =
250
POSITION_SLOTS =

Position slots for different window sizes

[30, 32, 34, 36, 38, 42, 50, 66, 98, 162, 290].freeze
EXTRA_BITS =

Extra bits for position slots

[
  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, 14, 14, 15, 15, 16, 16
].freeze
POSITION_BASE =

Position base offsets

[
  0, 1, 2, 3, 4, 6, 8, 12, 16, 24, 32, 48, 64, 96, 128, 192, 256, 384, 512,
  768, 1024, 1536, 2048, 3072, 4096, 6144, 8192, 12_288, 16_384, 24_576, 32_768,
  49_152, 65_536, 98_304, 131_072, 196_608, 262_144, 393_216, 524_288, 655_360,
  786_432, 917_504, 1_048_576, 1_179_648, 1_310_720, 1_441_792, 1_572_864, 1_703_936,
  1_835_008, 1_966_080, 2_097_152
].freeze

Instance Attribute Summary collapse

Attributes inherited from Base

#buffer_size, #input, #io_system, #output

Instance Method Summary collapse

Constructor Details

#initialize(io_system, input, output, buffer_size, window_bits: 15) ⇒ LZX

Initialize LZX compressor

Parameters:



69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
# File 'lib/cabriolet/compressors/lzx.rb', line 69

def initialize(io_system, input, output, buffer_size, window_bits: 15)
  super(io_system, input, output, buffer_size)

  # Validate window_bits
  unless (15..21).cover?(window_bits)
    raise ArgumentError,
          "LZX window_bits must be 15-21, got #{window_bits}"
  end

  @window_bits = window_bits
  @window_size = 1 << window_bits

  # Calculate number of position slots
  @num_offsets = POSITION_SLOTS[window_bits - 15] << 3
  @maintree_maxsymbols = NUM_CHARS + @num_offsets

  # Initialize bitstream writer
  @bitstream = Binary::BitstreamWriter.new(io_system, output, buffer_size)

  # Initialize sliding window for LZ77
  @window = "\0" * @window_size
  @window_pos = 0

  # Initialize R0, R1, R2 (LRU offset registers)
  @r0 = 1
  @r1 = 1
  @r2 = 1

  # Statistics for tree building
  @literal_freq = Array.new(NUM_CHARS, 0)
  @match_freq = Array.new(@num_offsets, 0)
  @length_freq = Array.new(LENGTH_MAXSYMBOLS, 0)
end

Instance Attribute Details

#window_bitsObject (readonly)

Returns the value of attribute window_bits.



60
61
62
# File 'lib/cabriolet/compressors/lzx.rb', line 60

def window_bits
  @window_bits
end

Instance Method Details

#compressInteger

Compress input data using LZX algorithm

Returns:

  • (Integer)

    Number of bytes written



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
# File 'lib/cabriolet/compressors/lzx.rb', line 106

def compress
  input_data = read_all_input
  return 0 if input_data.empty?

  # Write Intel E8 filesize header once at the beginning (1 bit = 0, meaning no E8 processing)
  @bitstream.write_bits(0, 1)

  total_compressed = 0
  pos = 0

  # Process data in FRAME_SIZE chunks
  while pos < input_data.bytesize
    frame_size = [FRAME_SIZE, input_data.bytesize - pos].min
    frame_data = input_data[pos, frame_size]

    # Compress this frame
    compress_frame(frame_data)

    pos += frame_size
    total_compressed += frame_size
  end

  # Flush any remaining bits
  @bitstream.flush

  total_compressed
end