Module: Omnizip::Formats::LzmaAlone

Defined in:
lib/omnizip/formats/lzma_alone.rb

Overview

LZMA_Alone (.lzma) compression format

This is the legacy LZMA_Alone format used by LZMA Utils 4.32.x. It is DIFFERENT from both XZ and LZIP formats:

  • XZ (.xz): Container format with stream header/footer/index, LZMA2 compression
  • LZIP (.lz): Standalone format with "LZIP" magic and CRC32 footer, LZMA1 compression
  • LZMA_Alone (.lzma): Legacy standalone format with properties byte, LZMA1 compression

Format structure:

  • Properties (1 byte): encodes lc, lp, pb values
  • Dictionary size (4 bytes, little-endian)
  • Uncompressed size (8 bytes, little-endian, UINT64_MAX = unknown)
  • LZMA1 compressed stream (no footer, no CRC32)

Reference: /Users/mulgogi/src/external/xz/src/liblzma/common/alone_decoder.c

Constant Summary collapse

MAX_UNCOMPRESSED_SIZE =

Maximum valid uncompressed size (256 GiB) From alone_decoder.c:118

(1 << 38)
MAX_PROPERTY_BYTE =

Property byte validation limits From lzma_decoder.c:1218

(((4 * 5) + 4) * 9) + 8
UINT64_MAX =

Value indicating unknown uncompressed size

(1 << 64) - 1

Class Method Summary collapse

Class Method Details

.compress(input_path, output_path, options = {}) ⇒ Object

Compress a file with LZMA_Alone format

Parameters:

  • input_path (String)

    Input file path

  • output_path (String)

    Output LZMA_Alone file path

  • options (Hash) (defaults to: {})

    Compression options

Options Hash (options):

  • :lc (Integer)

    Literal context bits (0-8, default: 3)

  • :lp (Integer)

    Literal position bits (0-4, default: 0)

  • :pb (Integer)

    Position bits (0-4, default: 2)

  • :dict_size (Integer)

    Dictionary size (default: 8MB)

  • :uncompressed_size (Integer)

    Explicit uncompressed size (default: unknown)



47
48
49
50
51
52
53
54
55
56
57
# File 'lib/omnizip/formats/lzma_alone.rb', line 47

def compress(input_path, output_path, options = {})
  input_data = File.binread(input_path)

  File.open(output_path, "wb") do |output|
    compress_stream(
      StringIO.new(input_data),
      output,
      options,
    )
  end
end

.compress_stream(input_io, output_io, options = {}) ⇒ Object

Compress data stream with LZMA_Alone format

Parameters:

  • input_io (IO)

    Input stream

  • output_io (IO)

    Output stream

  • options (Hash) (defaults to: {})

    Compression options

Raises:

  • (NotImplementedError)


96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
# File 'lib/omnizip/formats/lzma_alone.rb', line 96

def compress_stream(input_io, output_io, options = {})
  input_data = input_io.read

  # Get encoding parameters
  lc = options[:lc] || 3
  lp = options[:lp] || 0
  pb = options[:pb] || 2
  dict_size = options[:dict_size] || (8 * 1024 * 1024)
  uncompressed_size = options[:uncompressed_size] || input_data.bytesize

  # Encode properties byte
  prop_byte = (((pb * 5) + lp) * 9) + lc

  # Write header
  output_io.write([prop_byte].pack("C"))
  output_io.write([dict_size].pack("V"))
  output_io.write([uncompressed_size].pack("Q<"))

  # TODO: Implement LZMA1 encoder
  raise NotImplementedError,
        "LZMA_Alone encoding not yet implemented. Use decompression only."
end

.decode(input) ⇒ String

Decode LZMA_Alone data (alias for decompress with no output)

Parameters:

  • input (String, IO)

    Input LZMA_Alone data or file path

Returns:

  • (String)

    Decompressed data



87
88
89
# File 'lib/omnizip/formats/lzma_alone.rb', line 87

def decode(input)
  decompress(input)
end

.decompress(input, output = nil, options = {}) ⇒ String?

Decompress LZMA_Alone data

Parameters:

  • input (String, IO)

    Input data, file path, or IO object

  • output (String, IO, nil) (defaults to: nil)

    Output file path or IO object

  • options (Hash) (defaults to: {})

    Options

Options Hash (options):

  • :picky (Boolean)

    If true, reject files unlikely to be .lzma (default: false)

Returns:

  • (String, nil)

    Decompressed data (if output is nil)



66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
# File 'lib/omnizip/formats/lzma_alone.rb', line 66

def decompress(input, output = nil, options = {})
  data = Omnizip::IO::Source.for(input).read

  # Decode using LzmaAloneDecoder
  decoder = Omnizip::Algorithms::LZMA::LzmaAloneDecoder.new(
    StringIO.new(data),
    options,
  )
  result = decoder.decode_stream

  if output
    Omnizip::IO::Sink.for(output).write(result)
  else
    result
  end
end

.decompress_stream(input_io, output_io, options = {}) ⇒ Hash

Decompress LZMA_Alone stream

Parameters:

  • input_io (IO)

    Input stream

  • output_io (IO)

    Output stream

  • options (Hash) (defaults to: {})

    Options

Returns:

  • (Hash)

    Metadata (lc, lp, pb, dict_size, uncompressed_size)



125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
# File 'lib/omnizip/formats/lzma_alone.rb', line 125

def decompress_stream(input_io, output_io, options = {})
  decoder = Omnizip::Algorithms::LZMA::LzmaAloneDecoder.new(input_io,
                                                            options)
  result = decoder.decode_stream

  output_io.write(result)

  # Return metadata
  {
    lc: decoder.lc,
    lp: decoder.lp,
    pb: decoder.pb,
    dict_size: decoder.dict_size,
    uncompressed_size: decoder.uncompressed_size,
  }
end

.register!Object

Register LZMA_Alone format when loaded



143
144
145
# File 'lib/omnizip/formats/lzma_alone.rb', line 143

def register!
  FormatRegistry.register(".lzma", Omnizip::Formats::LzmaAlone)
end