Class: Omnizip::Implementations::SevenZip::LZMA::RangeEncoder

Inherits:
Object
  • Object
show all
Includes:
Algorithms::LZMA::Constants
Defined in:
lib/omnizip/implementations/seven_zip/lzma/range_encoder.rb

Overview

Range encoder for 7-Zip SDK LZMA compression

This is a direct port of 7-Zip SDK's range encoder implementation from LzmaEnc.c for guaranteed compatibility with 7-Zip archives.

KEY DIFFERENCE from XZ Utils:

  • 7-Zip SDK normalizes AFTER encoding each bit
  • XZ Utils normalizes BEFORE encoding each bit

This difference produces different output bytes, so we need separate implementations for 7-Zip and XZ Utils compatibility.

Reference: /Users/mulgogi/src/external/7-Zip/C/LzmaEnc.c lines 730-784

Constant Summary

Constants included from Algorithms::LZMA::Constants

Algorithms::LZMA::Constants::BIT_MODEL_TOTAL, Algorithms::LZMA::Constants::COMPRESSION_LEVEL_DEFAULT, Algorithms::LZMA::Constants::COMPRESSION_LEVEL_MAX, Algorithms::LZMA::Constants::COMPRESSION_LEVEL_MIN, Algorithms::LZMA::Constants::DICT_SIZE_MAX, Algorithms::LZMA::Constants::DICT_SIZE_MIN, Algorithms::LZMA::Constants::DIST_ALIGN_BITS, Algorithms::LZMA::Constants::DIST_ALIGN_SIZE, Algorithms::LZMA::Constants::DIST_SLOT_FAST_LIMIT, Algorithms::LZMA::Constants::END_POS_MODEL_INDEX, Algorithms::LZMA::Constants::EOS_MARKER, Algorithms::LZMA::Constants::INIT_PROBS, Algorithms::LZMA::Constants::LEN_HIGH_SYMBOLS, Algorithms::LZMA::Constants::LEN_LOW_SYMBOLS, Algorithms::LZMA::Constants::LEN_MID_SYMBOLS, Algorithms::LZMA::Constants::LIT_SIZE_MAX, Algorithms::LZMA::Constants::MATCH_LEN_MAX, Algorithms::LZMA::Constants::MATCH_LEN_MIN, Algorithms::LZMA::Constants::MOVE_BITS, Algorithms::LZMA::Constants::NUM_DIRECT_BITS, Algorithms::LZMA::Constants::NUM_DIST_SLOTS, Algorithms::LZMA::Constants::NUM_DIST_SLOT_BITS, Algorithms::LZMA::Constants::NUM_FULL_DISTANCES, Algorithms::LZMA::Constants::NUM_LEN_HIGH_BITS, Algorithms::LZMA::Constants::NUM_LEN_LOW_BITS, Algorithms::LZMA::Constants::NUM_LEN_MID_BITS, Algorithms::LZMA::Constants::NUM_LEN_TO_POS_STATES, Algorithms::LZMA::Constants::NUM_LIT_CONTEXT_BITS_MAX, Algorithms::LZMA::Constants::NUM_LIT_POS_BITS_MAX, Algorithms::LZMA::Constants::NUM_POS_BITS_MAX, Algorithms::LZMA::Constants::NUM_STATES, Algorithms::LZMA::Constants::POS_STATES_MAX, Algorithms::LZMA::Constants::START_POS_MODEL_INDEX, Algorithms::LZMA::Constants::TOP

Instance Method Summary collapse

Constructor Details

#initialize(output_stream) ⇒ RangeEncoder

Initialize the range encoder

Parameters:

  • output_stream (IO)

    The output stream for encoded bytes



50
51
52
53
54
55
56
57
# File 'lib/omnizip/implementations/seven_zip/lzma/range_encoder.rb', line 50

def initialize(output_stream)
  @stream = output_stream
  @low = 0
  @range = 0xFFFFFFFF # Full 32-bit range
  @cache = 0
  @cache_size = 1 # SDK initializes to 1
  @pre_flush_pos = 0
end

Instance Method Details

#bytes_for_decodeInteger

Return bytes needed for decoding

Returns:

  • (Integer)

    Number of bytes decoder will consume



137
138
139
# File 'lib/omnizip/implementations/seven_zip/lzma/range_encoder.rb', line 137

def bytes_for_decode
  @pre_flush_pos || @stream.pos
end

#encode_bit(model, bit) ⇒ void

This method returns an undefined value.

Encode a single bit using a probability model

Ported from 7-Zip SDK RC_BIT() macro (LzmaEnc.c lines 750-765) The key difference is that normalization happens AFTER encoding.

SDK macro:

#define RC_BIT(p, prob, bit) { \
RC_BIT_PRE(p, prob) \
if (bit == 0) { range = newBound; ttt += (kBitModelTotal - ttt) >> kNumMoveBits; } \
else { (p)->low += newBound; range -= newBound; ttt -= ttt >> kNumMoveBits; } \
*(prob) = (CLzmaProb)ttt; \
RC_NORM(p) \
}

Parameters:

  • model (BitModel)

    The probability model for this bit

  • bit (Integer)

    The bit value (0 or 1)



76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
# File 'lib/omnizip/implementations/seven_zip/lzma/range_encoder.rb', line 76

def encode_bit(model, bit)
  prob = model.probability

  # RC_BIT_PRE: Calculate newBound = (range >> kNumBitModelTotalBits) * prob
  new_bound = (@range >> 11) * prob

  new_prob = if bit.zero?
               # RC_BIT_0: shrink range to lower portion
               @range = new_bound & 0xFFFFFFFF
               # Update probability: ttt += (kBitModelTotal - ttt) >> kNumMoveBits
               prob + ((BIT_MODEL_TOTAL - prob) >> MOVE_BITS)
             else
               # RC_BIT_1: add bound to low, shrink range to upper portion
               @low = (@low + new_bound) & 0xFFFFFFFFFFFFFFFF
               @range = (@range - new_bound) & 0xFFFFFFFF
               # Update probability: ttt -= ttt >> kNumMoveBits
               prob - (prob >> MOVE_BITS)
             end
  model.probability = new_prob

  # RC_NORM: Normalize AFTER encoding (key SDK difference!)
  normalize
end

#encode_direct_bits(value, num_bits) ⇒ void

This method returns an undefined value.

Encode bits directly without using probability model

Used for encoding values with uniform distribution (e.g., distance high bits).

Parameters:

  • value (Integer)

    The value to encode

  • num_bits (Integer)

    Number of bits to encode



108
109
110
111
112
113
114
115
116
# File 'lib/omnizip/implementations/seven_zip/lzma/range_encoder.rb', line 108

def encode_direct_bits(value, num_bits)
  num_bits.times do |i|
    @range >>= 1
    @range &= 0xFFFFFFFF
    bit = (value >> (num_bits - 1 - i)) & 1
    @low = (@low + @range) & 0xFFFFFFFFFFFFFFFF if bit == 1
    normalize
  end
end

#flushvoid

This method returns an undefined value.

Flush remaining bytes to output stream

Writes the final bytes to complete the range coding stream.



123
124
125
126
127
128
129
130
131
132
# File 'lib/omnizip/implementations/seven_zip/lzma/range_encoder.rb', line 123

def flush
  # Store position BEFORE flush for compatibility
  @pre_flush_pos = @stream.pos

  # Prevent further normalizations
  @range = 0xFFFFFFFF

  # Flush 5 bytes (matches SDK behavior)
  5.times { shift_low }
end

#posInteger

Get current output position

Returns:

  • (Integer)

    Current position in output stream



144
145
146
# File 'lib/omnizip/implementations/seven_zip/lzma/range_encoder.rb', line 144

def pos
  @stream.pos
end