Module: Omnizip::Parity

Defined in:
lib/omnizip/parity.rb,
lib/omnizip/parity/models.rb,
lib/omnizip/parity/galois16.rb,
lib/omnizip/parity/par2_creator.rb,
lib/omnizip/parity/models/packet.rb,
lib/omnizip/parity/par2_repairer.rb,
lib/omnizip/parity/par2_verifier.rb,
lib/omnizip/parity/models/ifsc_packet.rb,
lib/omnizip/parity/models/main_packet.rb,
lib/omnizip/parity/reed_solomon_matrix.rb,
lib/omnizip/parity/reed_solomon_decoder.rb,
lib/omnizip/parity/reed_solomon_encoder.rb,
lib/omnizip/parity/models/creator_packet.rb,
lib/omnizip/parity/par2cmdline_algorithm.rb,
lib/omnizip/parity/models/packet_registry.rb,
lib/omnizip/parity/chunked_block_processor.rb,
lib/omnizip/parity/par2cmdline_coefficients.rb,
lib/omnizip/parity/models/recovery_slice_packet.rb,
lib/omnizip/parity/models/file_description_packet.rb

Overview

PAR2 parity archive support

Provides creation, verification, and repair of PAR2 parity files for protecting archives and data files against corruption.

PAR2 uses Reed-Solomon error correction codes to create recovery data that can reconstruct missing or corrupted blocks.

Examples:

Create PAR2 protection

Omnizip::Parity.create('archive.zip', redundancy: 10)

Verify and repair

result = Omnizip::Parity.verify('archive.par2')
Omnizip::Parity.repair('archive.par2') if result.repairable?

Defined Under Namespace

Modules: Models, Par2cmdlineAlgorithm Classes: ChunkedBlockProcessor, Galois16, Par2Creator, Par2Repairer, Par2Verifier, ReedSolomonDecoder, ReedSolomonEncoder, ReedSolomonMatrix

Constant Summary collapse

PAR2CMDLINE_COEFFICIENTS =

Par2cmdline-compatible coefficient table

{
  0 => 0x27c6,
  1 => 0x8eb6,
  2 => 0x1a9f,
  3 => 0x2743,
  4 => 0x24f6,
  5 => 0x60d7,
  6 => 0x2027,
  7 => 0x1cf0,
  8 => 0xd37a,
  9 => 0xa961,
  10 => 0xc6c7,
  11 => 0x653e,
  12 => 0x9c99,
  13 => 0x2e1b,
  14 => 0x8625,
  15 => 0xd81e,
  16 => 0x1fb5,
  17 => 0x2cdd,
  18 => 0x06ce,
  19 => 0x41d5,
  20 => 0xd297,
  21 => 0xeae1,
  22 => 0x9012,
  23 => 0xdc31,
  24 => 0xa33d,
  25 => 0x2480,
  26 => 0x3e2e,
  27 => 0x5dee,
  28 => 0x8f63,
  29 => 0x90c5,
  30 => 0xac21,
  31 => 0x9bf4,
  32 => 0x8b15,
  33 => 0xc489,
  34 => 0x004c,
  35 => 0x6a45,
  36 => 0x56f9,
  37 => 0x6956,
  38 => 0x2548,
  39 => 0x0334,
  40 => 0x3213,
  41 => 0x7c7f,
  42 => 0x1d3c,
  43 => 0x9c1e,
  44 => 0x835c,
  45 => 0x7f30,
  46 => 0x070e,
  47 => 0x5f7d,
  48 => 0x5f97,
  49 => 0xfa32,
  50 => 0x08fd,
  51 => 0x9d43,
  52 => 0x9ec1,
  53 => 0x4643,
  54 => 0x9222,
  55 => 0x1f9c,
  56 => 0xd271,
  57 => 0xbd9f,
  58 => 0xfef3,
  59 => 0x9b83,
}.freeze

Class Method Summary collapse

Class Method Details

.create(file_or_pattern, redundancy: 5, block_size: Par2Creator::DEFAULT_BLOCK_SIZE, output_dir: nil, progress: nil) ⇒ Array<String>

Create PAR2 recovery files for archive or files

Examples:

Create with 10% redundancy

Omnizip::Parity.create('backup.7z', redundancy: 10)

Create for multiple files

Omnizip::Parity.create('data/*.dat', redundancy: 5)

With progress tracking

Omnizip::Parity.create('large.zip',
  redundancy: 10,
  progress: ->(pct, msg) { puts "#{pct}%: #{msg}" }
)

Parameters:

  • file_or_pattern (String)

    File path or glob pattern

  • redundancy (Integer) (defaults to: 5)

    Redundancy percentage (0-100)

  • block_size (Integer) (defaults to: Par2Creator::DEFAULT_BLOCK_SIZE)

    Block size in bytes

  • output_dir (String, nil) (defaults to: nil)

    Output directory for PAR2 files

  • progress (Proc, nil) (defaults to: nil)

    Progress callback

Returns:

  • (Array<String>)

    Created PAR2 file paths



66
67
68
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
102
103
104
# File 'lib/omnizip/parity.rb', line 66

def create(file_or_pattern, redundancy: 5, block_size: Par2Creator::DEFAULT_BLOCK_SIZE,
           output_dir: nil, progress: nil)
  # Try glob expansion first (handles both patterns and single files)
  files = Dir.glob(file_or_pattern)

  # If glob returns nothing, check if it's a single existing file
  if files.empty? && File.exist?(file_or_pattern) && !File.directory?(file_or_pattern)
    files = [file_or_pattern]
  end

  if files.empty?
    raise ArgumentError,
          "No files match pattern: #{file_or_pattern}"
  end

  # Create PAR2 creator
  creator = Par2Creator.new(
    redundancy: redundancy,
    block_size: block_size,
    progress: progress,
  )

  # Add all files
  files.each { |file| creator.add_file(file) }

  # Determine output base name
  dir = output_dir || File.dirname(files.first)
  base_name = if files.size == 1
                # Use file's directory and base name without extension
                File.join(dir, File.basename(files.first, ".*"))
              else
                # Use files' directory name for multiple files
                File.join(dir,
                          File.basename(File.dirname(files.first)))
              end

  # Create PAR2 files
  creator.create(base_name)
end

.info(file_path) ⇒ Hash?

Get PAR2 protection information

Examples:

Get protection info

info = Omnizip::Parity.info('backup.zip')
puts "Redundancy: #{info[:redundancy]}%"
puts "Block size: #{info[:block_size]} bytes"

Parameters:

  • file_path (String)

    Path to protected file

Returns:

  • (Hash, nil)

    Protection information or nil if not protected



170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
# File 'lib/omnizip/parity.rb', line 170

def info(file_path)
  base_name = File.basename(file_path, ".*")
  dir_name = File.dirname(file_path)
  par2_file = File.join(dir_name, "#{base_name}.par2")

  return nil unless File.exist?(par2_file)

  verifier = Par2Verifier.new(par2_file)
  verifier.parse_par2_file

  total_blocks = verifier.calculate_total_blocks
  recovery_blocks = verifier.recovery_blocks.size

  {
    par2_file: par2_file,
    block_size: verifier.[:block_size],
    total_blocks: total_blocks,
    file_count: verifier.[:file_count] || verifier.file_list.size,
    recovery_blocks: recovery_blocks,
    redundancy: calculate_redundancy(total_blocks, recovery_blocks),
  }
end

.protected?(file_path) ⇒ Boolean

Quick check if PAR2 files exist for a file

Examples:

Check for protection

if Omnizip::Parity.protected?('backup.zip')
  puts "File is protected by PAR2"
end

Parameters:

  • file_path (String)

    Path to protected file

Returns:

  • (Boolean)

    true if PAR2 files exist



153
154
155
156
157
158
159
# File 'lib/omnizip/parity.rb', line 153

def protected?(file_path)
  base_name = File.basename(file_path, ".*")
  dir_name = File.dirname(file_path)
  par2_file = File.join(dir_name, "#{base_name}.par2")

  File.exist?(par2_file)
end

.repair(par2_file, output_dir: nil, progress: nil) ⇒ Par2Repairer::RepairResult

Repair damaged files using PAR2 recovery data

Examples:

Repair damaged archive

result = Omnizip::Parity.repair('backup.par2')
if result.success?
  puts "Successfully recovered #{result.recovered_files.join(', ')}"
else
  puts "Repair failed: #{result.error_message}"
end

Parameters:

  • par2_file (String)

    Path to .par2 index file

  • output_dir (String, nil) (defaults to: nil)

    Output directory for repaired files

  • progress (Proc, nil) (defaults to: nil)

    Progress callback

Returns:



139
140
141
142
# File 'lib/omnizip/parity.rb', line 139

def repair(par2_file, output_dir: nil, progress: nil)
  repairer = Par2Repairer.new(par2_file, progress: progress)
  repairer.repair(output_dir: output_dir)
end

.verify(par2_file) ⇒ Par2Verifier::VerificationResult

Verify files using PAR2 recovery data

Examples:

Verify archive integrity

result = Omnizip::Parity.verify('backup.par2')
if result.all_ok?
  puts "All files intact"
elsif result.repairable?
  puts "Damage detected but repairable"
else
  puts "Damage cannot be repaired"
end

Parameters:

  • par2_file (String)

    Path to .par2 index file

Returns:



120
121
122
123
# File 'lib/omnizip/parity.rb', line 120

def verify(par2_file)
  verifier = Par2Verifier.new(par2_file)
  verifier.verify
end