Class: Omnizip::Filters::XzDeltaFilter

Inherits:
Object
  • Object
show all
Defined in:
lib/omnizip/filters/xz_delta.rb

Overview

XZ Utils Delta filter.

This filter computes byte-wise differences using a 256-byte circular history buffer. It is particularly effective for:

  • Stereo audio (distance=4 for 16-bit samples)
  • RGB images (distance=3)
  • RGBA images (distance=4)
  • Multi-channel data with regular patterns

This is DIFFERENT from the 7-Zip Delta filter which uses simple forward differences without a history buffer.

Reference: XZ Utils delta_encoder.c, delta_decoder.c

Constant Summary collapse

FILTER_ID =

Filter ID for XZ format

0x03
DELTA_DIST_MIN =

Minimum distance value (XZ Utils LZMA_DELTA_DIST_MIN)

1
DELTA_DIST_MAX =

Maximum distance value (XZ Utils LZMA_DELTA_DIST_MAX)

256
HISTORY_SIZE =

History buffer size (always 256 bytes in XZ Utils)

256
DELTA_TYPE_BYTE =

Delta type (only BYTE is supported in XZ Utils)

0

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(distance = DELTA_DIST_MIN) ⇒ XzDeltaFilter

Initialize the Delta filter.

Parameters:

  • distance (Integer) (defaults to: DELTA_DIST_MIN)

    Byte distance for delta calculation (1-256)

Raises:

  • (ArgumentError)

    If distance is invalid



82
83
84
85
86
87
88
# File 'lib/omnizip/filters/xz_delta.rb', line 82

def initialize(distance = DELTA_DIST_MIN)
  validate_distance(distance)
  @distance = distance
  # Initialize state (matches XZ Utils lzma_delta_coder_init)
  @pos = 0
  @history = ("\x00" * HISTORY_SIZE).b
end

Instance Attribute Details

#distanceObject (readonly)

Returns the value of attribute distance.



76
77
78
# File 'lib/omnizip/filters/xz_delta.rb', line 76

def distance
  @distance
end

Class Method Details

.decode_properties(properties) ⇒ Integer

Decode properties byte to get distance.

XZ Utils encodes distance as: props = dist - 1 So we decode as: dist = props + 1

Reference: XZ Utils delta_decoder.c:lzma_delta_props_decode

Parameters:

  • properties (String)

    Properties byte (1 byte)

Returns:

  • (Integer)

    Distance value (1-256)

Raises:

  • (ArgumentError)

    If properties size is invalid



177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
# File 'lib/omnizip/filters/xz_delta.rb', line 177

def decode_properties(properties)
  unless properties.is_a?(String) && properties.bytesize == 1
    raise ArgumentError,
          "Delta filter requires exactly 1 property byte, got #{properties&.bytesize}"
  end

  props_byte = properties.getbyte(0)
  # XZ Utils: opt->dist = props[0] + LZMA_DELTA_DIST_MIN
  # where LZMA_DELTA_DIST_MIN = 1
  distance = props_byte + DELTA_DIST_MIN

  # Validate distance is in valid range (inline for class method)
  unless distance.between?(DELTA_DIST_MIN, DELTA_DIST_MAX)
    raise ArgumentError,
          "Invalid distance #{distance}, must be between #{DELTA_DIST_MIN} and #{DELTA_DIST_MAX}"
  end

  distance
end

.encode_properties(distance) ⇒ String

Encode distance to properties byte.

XZ Utils encodes distance as: props = dist - 1

Reference: XZ Utils delta_encoder.c:lzma_delta_props_encode

Parameters:

  • distance (Integer)

    Distance value (1-256)

Returns:

  • (String)

    Properties byte (1 byte)

Raises:

  • (ArgumentError)

    If distance is invalid



206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
# File 'lib/omnizip/filters/xz_delta.rb', line 206

def encode_properties(distance)
  # Validate distance (inline for class method)
  unless distance.is_a?(Integer)
    raise ArgumentError,
          "Distance must be an integer, got #{distance.class}"
  end

  unless distance.between?(DELTA_DIST_MIN, DELTA_DIST_MAX)
    raise ArgumentError,
          "Distance must be between #{DELTA_DIST_MIN} and #{DELTA_DIST_MAX}, got #{distance}"
  end

  # XZ Utils: out[0] = opt->dist - LZMA_DELTA_DIST_MIN
  # where LZMA_DELTA_DIST_MIN = 1
  props_byte = distance - DELTA_DIST_MIN

  [props_byte].pack("C")
end

.metadataHash

Get metadata about this filter.

Returns:

  • (Hash)

    Filter metadata



228
229
230
231
232
233
234
235
236
# File 'lib/omnizip/filters/xz_delta.rb', line 228

def 
  {
    name: "XZ Delta",
    description: "XZ Utils Delta filter with 256-byte circular history buffer",
    filter_id: FILTER_ID,
    typical_usage: "WAV audio (distance=4), BMP images (distance=3), " \
                   "multi-channel data with regular patterns",
  }
end

Instance Method Details

#decode(data) ⇒ String

Decode (postprocess) data by restoring from differences.

For each byte:

buffer[i] += history[(distance + pos) & 0xFF] (mod 256)
history[pos & 0xFF] = buffer[i]
pos--

Reference: XZ Utils delta_decoder.c:decode_buffer

Parameters:

  • data (String)

    Binary data to decode

Returns:

  • (String)

    Decoded binary data



134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
# File 'lib/omnizip/filters/xz_delta.rb', line 134

def decode(data)
  return data.dup.b if data.empty?

  result = data.dup.b
  data.bytes.each_with_index do |byte, i|
    # Get historical value from distance positions back
    tmp = @history.getbyte((@distance + @pos) & 0xFF)

    # Restore original value by adding the difference
    result.setbyte(i, (byte + tmp) & 0xFF)

    # Store restored byte in history
    @history.setbyte(@pos & 0xFF, result.getbyte(i))

    # Move position backward (wraps via & 0xFF)
    @pos = (@pos - 1) & 0xFF
  end

  result
end

#encode(data) ⇒ String

Encode (preprocess) data by computing forward differences.

For each byte:

tmp = history[(distance + pos) & 0xFF]
history[pos & 0xFF] = in[i]
out[i] = in[i] - tmp (mod 256)
pos--

Reference: XZ Utils delta_encoder.c:copy_and_encode

Parameters:

  • data (String)

    Binary data to encode

Returns:

  • (String)

    Encoded binary data



102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
# File 'lib/omnizip/filters/xz_delta.rb', line 102

def encode(data)
  return data.dup.b if data.empty?

  result = data.dup.b
  data.bytes.each_with_index do |byte, i|
    # Get historical value from distance positions back
    tmp = @history.getbyte((@distance + @pos) & 0xFF)

    # Store current byte in history
    @history.setbyte(@pos & 0xFF, byte)

    # Output is the difference
    result.setbyte(i, (byte - tmp) & 0xFF)

    # Move position backward (wraps via & 0xFF)
    @pos = (@pos - 1) & 0xFF
  end

  result
end

#resetvoid

This method returns an undefined value.

Reset the filter state.

This clears the history buffer and resets position to 0. Used when initializing a new filter chain.



161
162
163
164
# File 'lib/omnizip/filters/xz_delta.rb', line 161

def reset
  @pos = 0
  @history = ("\x00" * HISTORY_SIZE).b
end