Class: Omnizip::Formats::Xz::Writer

Inherits:
Object
  • Object
show all
Includes:
Omnizip::Formats::XzConst
Defined in:
lib/omnizip/formats/xz/writer.rb

Overview

XZ format writer

Creates .xz files compatible with XZ Utils. Structure: Stream Header + Block(s) + Index + Stream Footer

Based on: xz/src/liblzma/common/stream_encoder.c

Constant Summary collapse

HEADER_MAGIC =

XZ format magic bytes

[0xFD, 0x37, 0x7A, 0x58, 0x5A, 0x00].freeze
[0x59, 0x5A].freeze

Constants included from Omnizip::Formats::XzConst

Omnizip::Formats::XzConst::BACKWARD_SIZE_MAX, Omnizip::Formats::XzConst::BACKWARD_SIZE_MIN, Omnizip::Formats::XzConst::BLOCK_HEADER_SIZE_MAX, Omnizip::Formats::XzConst::BLOCK_HEADER_SIZE_MIN, Omnizip::Formats::XzConst::CHECK_CRC32, Omnizip::Formats::XzConst::CHECK_CRC64, Omnizip::Formats::XzConst::CHECK_NONE, Omnizip::Formats::XzConst::CHECK_SHA256, Omnizip::Formats::XzConst::FILTERS_MAX, Omnizip::Formats::XzConst::FILTER_LZMA2, Omnizip::Formats::XzConst::INDEX_INDICATOR, Omnizip::Formats::XzConst::MAGIC, Omnizip::Formats::XzConst::STREAM_FLAGS_SIZE, Omnizip::Formats::XzConst::STREAM_FOOTER_SIZE, Omnizip::Formats::XzConst::STREAM_HEADER_SIZE, Omnizip::Formats::XzConst::VLI_BYTES_MAX, Omnizip::Formats::XzConst::VLI_UNKNOWN

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(output, options = {}) ⇒ Writer

Initialize XZ writer

Parameters:

  • output (IO)

    Output stream

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

    Encoding options



59
60
61
62
63
64
65
# File 'lib/omnizip/formats/xz/writer.rb', line 59

def initialize(output, options = {})
  @output = output
  @options = options
  @blocks = []

  write_stream_header
end

Class Method Details

.create(filename, options = {}) {|writer| ... } ⇒ Object

Create XZ file with block given

Parameters:

  • filename (String)

    Output filename

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

    Encoding options

Yields:

  • (writer)

    Block receives writer instance



47
48
49
50
51
52
53
# File 'lib/omnizip/formats/xz/writer.rb', line 47

def self.create(filename, options = {})
  File.open(filename, "wb") do |f|
    writer = new(f, options)
    yield writer if block_given?
    writer.finalize
  end
end

.encode_vli(value) ⇒ String

Encode VLI (variable-length integer)

Parameters:

  • value (Integer)

    Value to encode

Returns:

  • (String)

    Encoded bytes (low bits first)



130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
# File 'lib/omnizip/formats/xz/writer.rb', line 130

def self.encode_vli(value)
  value
  bytes = []
  loop do
    # Get low 7 bits
    byte = value & 0x7F
    value >>= 7
    # Set continuation bit if there's more data
    byte |= 0x80 unless value.zero?
    bytes << byte
    break if value.zero?
  end

  bytes.pack("C*")
end

Instance Method Details

#add_data(data) ⇒ Object

Add data block to stream

Parameters:

  • data (String)

    Data to compress and add



70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
# File 'lib/omnizip/formats/xz/writer.rb', line 70

def add_data(data)
  # Use BlockEncoder for XZ Utils compatibility
  # This produces compressed blocks compatible with XZ Utils
  block_encoder = Omnizip::Formats::XzImpl::BlockEncoder.new(
    check_type: @options[:check_type] || CHECK_CRC64,
    dict_size: @options[:dict_size] || (64 * 1024 * 1024), # Use 64MB to match XZ Utils default
    include_block_sizes: true, # Include size fields for XZ Utils compatibility
  )

  block = block_encoder.encode_block(data)

  # Store block info for index
  @blocks << {
    compressed: block[:data],
    actual_compressed_size: block[:compressed_size],
    uncompressed_data: data,
    uncompressed_size: block[:uncompressed_size],
    unpadded_size: block_encoder.unpadded_size,
  }

  # Write block using the encoded data from BlockEncoder
  write_block_from_encoder(block)
end

#finalizeObject

Finalize XZ stream



119
120
121
122
# File 'lib/omnizip/formats/xz/writer.rb', line 119

def finalize
  write_index
  write_stream_footer
end

#write_block_from_encoder(block) ⇒ Object

Write block from BlockEncoder output

Parameters:

  • block (Hash)

    Block info from BlockEncoder



97
98
99
100
101
102
103
104
105
106
107
108
109
# File 'lib/omnizip/formats/xz/writer.rb', line 97

def write_block_from_encoder(block)
  # Write block header (from BlockEncoder)
  @output.write(block[:header])

  # Write compressed data
  @output.write(block[:data])

  # Write padding (from BlockEncoder)
  @output.write(block[:padding])

  # Write check (CRC64 of uncompressed data)
  write_check_from_block(block)
end

#write_check_from_block(block) ⇒ Object

Write check value from block

Parameters:

  • block (Hash)

    Block info from BlockEncoder



114
115
116
# File 'lib/omnizip/formats/xz/writer.rb', line 114

def write_check_from_block(block)
  @output.write(block[:check])
end