omnizip 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.rspec +3 -0
- data/.rubocop.yml +32 -0
- data/.rubocop_todo.yml +754 -0
- data/COPYING +502 -0
- data/Gemfile +17 -0
- data/LICENSE +12 -0
- data/README.adoc +1045 -0
- data/Rakefile +12 -0
- data/benchmark/README.md +260 -0
- data/benchmark/benchmark_suite.rb +125 -0
- data/benchmark/compression_bench.rb +181 -0
- data/benchmark/filter_bench.rb +180 -0
- data/benchmark/models/benchmark_result.rb +59 -0
- data/benchmark/models/comparison_result.rb +69 -0
- data/benchmark/profile_suite.rb +167 -0
- data/benchmark/reporter.rb +150 -0
- data/benchmark/run_benchmarks.rb +66 -0
- data/benchmark/test_data.rb +137 -0
- data/config/formats/rar3_spec.yml +91 -0
- data/config/formats/rar5_spec.yml +102 -0
- data/docs/.github/workflows/docs.yml +142 -0
- data/docs/.gitignore +21 -0
- data/docs/.lychee.toml +67 -0
- data/docs/Gemfile +13 -0
- data/docs/RAR_WRITE_SUPPORT.md +26 -0
- data/docs/README.md +101 -0
- data/docs/_config.yml +112 -0
- data/docs/assets/logo.svg +1 -0
- data/docs/assets/omnizip-logo.pdf +1540 -11
- data/docs/comparison/feature-matrix.adoc +694 -0
- data/docs/comparison/index.adoc +113 -0
- data/docs/comparison/vs-7zip.adoc +309 -0
- data/docs/comparison/vs-peazip.adoc +77 -0
- data/docs/comparison/vs-rubyzip.adoc +342 -0
- data/docs/comparison/vs-winrar.adoc +100 -0
- data/docs/compatibility.adoc +579 -0
- data/docs/concepts/index.adoc +129 -0
- data/docs/developer/architecture.adoc +256 -0
- data/docs/developer/contributing.adoc +158 -0
- data/docs/developer/index.adoc +25 -0
- data/docs/developer/testing.adoc +212 -0
- data/docs/getting-started/basic-usage.adoc +271 -0
- data/docs/getting-started/index.adoc +42 -0
- data/docs/getting-started/installation.adoc +138 -0
- data/docs/getting-started/quick-start.adoc +185 -0
- data/docs/getting-started/your-first-archive.adoc +218 -0
- data/docs/guides/advanced-features/encryption.adoc +300 -0
- data/docs/guides/advanced-features/index.adoc +49 -0
- data/docs/guides/advanced-features/parallel-processing.adoc +246 -0
- data/docs/guides/advanced-features/progress-tracking.adoc +320 -0
- data/docs/guides/advanced-features/streaming.adoc +212 -0
- data/docs/guides/archive-formats/gzip-format.adoc +107 -0
- data/docs/guides/archive-formats/index.adoc +130 -0
- data/docs/guides/archive-formats/rar-format.adoc +104 -0
- data/docs/guides/archive-formats/rar5.adoc +521 -0
- data/docs/guides/archive-formats/seven-zip-format.adoc +35 -0
- data/docs/guides/archive-formats/tar-format.adoc +106 -0
- data/docs/guides/archive-formats/xz-format.adoc +118 -0
- data/docs/guides/archive-formats/zip-format.adoc +35 -0
- data/docs/guides/compression-algorithms/bzip2.adoc +113 -0
- data/docs/guides/compression-algorithms/deflate.adoc +319 -0
- data/docs/guides/compression-algorithms/index.adoc +190 -0
- data/docs/guides/compression-algorithms/lzma.adoc +398 -0
- data/docs/guides/compression-algorithms/lzma2.adoc +327 -0
- data/docs/guides/compression-algorithms/ppmd.adoc +316 -0
- data/docs/guides/compression-algorithms/zstandard.adoc +361 -0
- data/docs/guides/creating-archives.adoc +354 -0
- data/docs/guides/extracting-archives.adoc +53 -0
- data/docs/guides/format-conversion.adoc +64 -0
- data/docs/guides/index.adoc +49 -0
- data/docs/guides/migration-rubyzip.adoc +217 -0
- data/docs/guides/parity-archives.adoc +605 -0
- data/docs/guides/performance-tuning.adoc +88 -0
- data/docs/index.adoc +218 -0
- data/docs/lychee.toml +67 -0
- data/docs/reference/api/overview.adoc +188 -0
- data/docs/reference/cli/compress-command.adoc +114 -0
- data/docs/reference/cli/overview.adoc +140 -0
- data/docs/reference/index.adoc +26 -0
- data/docs/resources/faq.adoc +185 -0
- data/docs/resources/quick-reference.adoc +222 -0
- data/docs/troubleshooting/index.adoc +208 -0
- data/examples/api_comparison.rb +205 -0
- data/examples/deflate64_example.rb +96 -0
- data/examples/par2_demo.rb +121 -0
- data/examples/quick_start_native.rb +150 -0
- data/examples/quick_start_rubyzip.rb +115 -0
- data/examples/rubyzip_compatibility_demo.rb +194 -0
- data/exe/omnizip +27 -0
- data/lib/omnizip/algorithm.rb +130 -0
- data/lib/omnizip/algorithm_registry.rb +86 -0
- data/lib/omnizip/algorithms/.keep +0 -0
- data/lib/omnizip/algorithms/bzip2/bwt.rb +225 -0
- data/lib/omnizip/algorithms/bzip2/decoder.rb +193 -0
- data/lib/omnizip/algorithms/bzip2/encoder.rb +237 -0
- data/lib/omnizip/algorithms/bzip2/huffman.rb +206 -0
- data/lib/omnizip/algorithms/bzip2/mtf.rb +101 -0
- data/lib/omnizip/algorithms/bzip2/rle.rb +151 -0
- data/lib/omnizip/algorithms/bzip2.rb +130 -0
- data/lib/omnizip/algorithms/deflate/constants.rb +28 -0
- data/lib/omnizip/algorithms/deflate/decoder.rb +38 -0
- data/lib/omnizip/algorithms/deflate/encoder.rb +46 -0
- data/lib/omnizip/algorithms/deflate.rb +128 -0
- data/lib/omnizip/algorithms/deflate64/constants.rb +45 -0
- data/lib/omnizip/algorithms/deflate64/decoder.rb +153 -0
- data/lib/omnizip/algorithms/deflate64/encoder.rb +98 -0
- data/lib/omnizip/algorithms/deflate64/huffman_coder.rb +354 -0
- data/lib/omnizip/algorithms/deflate64/lz77_encoder.rb +142 -0
- data/lib/omnizip/algorithms/deflate64.rb +109 -0
- data/lib/omnizip/algorithms/lzma/bit_model.rb +120 -0
- data/lib/omnizip/algorithms/lzma/constants.rb +112 -0
- data/lib/omnizip/algorithms/lzma/decoder.rb +148 -0
- data/lib/omnizip/algorithms/lzma/dictionary.rb +69 -0
- data/lib/omnizip/algorithms/lzma/distance_coder.rb +415 -0
- data/lib/omnizip/algorithms/lzma/encoder.rb +142 -0
- data/lib/omnizip/algorithms/lzma/length_coder.rb +260 -0
- data/lib/omnizip/algorithms/lzma/literal_decoder.rb +320 -0
- data/lib/omnizip/algorithms/lzma/literal_encoder.rb +210 -0
- data/lib/omnizip/algorithms/lzma/lzip_decoder.rb +341 -0
- data/lib/omnizip/algorithms/lzma/lzma_alone_decoder.rb +192 -0
- data/lib/omnizip/algorithms/lzma/lzma_state.rb +128 -0
- data/lib/omnizip/algorithms/lzma/match.rb +32 -0
- data/lib/omnizip/algorithms/lzma/match_finder.rb +205 -0
- data/lib/omnizip/algorithms/lzma/match_finder_config.rb +142 -0
- data/lib/omnizip/algorithms/lzma/match_finder_factory.rb +88 -0
- data/lib/omnizip/algorithms/lzma/optimal_encoder.rb +130 -0
- data/lib/omnizip/algorithms/lzma/probability_models.rb +72 -0
- data/lib/omnizip/algorithms/lzma/range_coder.rb +85 -0
- data/lib/omnizip/algorithms/lzma/range_decoder.rb +434 -0
- data/lib/omnizip/algorithms/lzma/range_encoder.rb +194 -0
- data/lib/omnizip/algorithms/lzma/state.rb +127 -0
- data/lib/omnizip/algorithms/lzma/xz_buffered_range_encoder.rb +325 -0
- data/lib/omnizip/algorithms/lzma/xz_encoder.rb +426 -0
- data/lib/omnizip/algorithms/lzma/xz_encoder_fast.rb +645 -0
- data/lib/omnizip/algorithms/lzma/xz_match_finder_adapter.rb +227 -0
- data/lib/omnizip/algorithms/lzma/xz_price_calculator.rb +169 -0
- data/lib/omnizip/algorithms/lzma/xz_probability_models.rb +261 -0
- data/lib/omnizip/algorithms/lzma/xz_range_encoder.rb +223 -0
- data/lib/omnizip/algorithms/lzma/xz_range_encoder_exact.rb +331 -0
- data/lib/omnizip/algorithms/lzma/xz_state.rb +116 -0
- data/lib/omnizip/algorithms/lzma/xz_utils_decoder.rb +2055 -0
- data/lib/omnizip/algorithms/lzma.rb +238 -0
- data/lib/omnizip/algorithms/lzma2/chunk_manager.rb +182 -0
- data/lib/omnizip/algorithms/lzma2/constants.rb +41 -0
- data/lib/omnizip/algorithms/lzma2/encoder.rb +147 -0
- data/lib/omnizip/algorithms/lzma2/lzma2_chunk.rb +161 -0
- data/lib/omnizip/algorithms/lzma2/properties.rb +179 -0
- data/lib/omnizip/algorithms/lzma2/simple_lzma2_encoder.rb +127 -0
- data/lib/omnizip/algorithms/lzma2/xz_encoder_adapter.rb +85 -0
- data/lib/omnizip/algorithms/lzma2.rb +141 -0
- data/lib/omnizip/algorithms/ppmd7/constants.rb +74 -0
- data/lib/omnizip/algorithms/ppmd7/context.rb +154 -0
- data/lib/omnizip/algorithms/ppmd7/decoder.rb +126 -0
- data/lib/omnizip/algorithms/ppmd7/encoder.rb +163 -0
- data/lib/omnizip/algorithms/ppmd7/model.rb +248 -0
- data/lib/omnizip/algorithms/ppmd7/symbol_state.rb +57 -0
- data/lib/omnizip/algorithms/ppmd7.rb +116 -0
- data/lib/omnizip/algorithms/ppmd8/constants.rb +61 -0
- data/lib/omnizip/algorithms/ppmd8/context.rb +34 -0
- data/lib/omnizip/algorithms/ppmd8/decoder.rb +107 -0
- data/lib/omnizip/algorithms/ppmd8/encoder.rb +138 -0
- data/lib/omnizip/algorithms/ppmd8/model.rb +250 -0
- data/lib/omnizip/algorithms/ppmd8/restoration_method.rb +78 -0
- data/lib/omnizip/algorithms/ppmd8.rb +82 -0
- data/lib/omnizip/algorithms/ppmd_base.rb +138 -0
- data/lib/omnizip/algorithms/sevenzip_lzma2.rb +123 -0
- data/lib/omnizip/algorithms/xz_lzma2.rb +118 -0
- data/lib/omnizip/algorithms/zstandard/constants.rb +25 -0
- data/lib/omnizip/algorithms/zstandard/decoder.rb +46 -0
- data/lib/omnizip/algorithms/zstandard/encoder.rb +51 -0
- data/lib/omnizip/algorithms/zstandard.rb +138 -0
- data/lib/omnizip/buffer/memory_archive.rb +251 -0
- data/lib/omnizip/buffer/memory_extractor.rb +224 -0
- data/lib/omnizip/buffer.rb +176 -0
- data/lib/omnizip/checksum_registry.rb +114 -0
- data/lib/omnizip/checksums/crc32.rb +100 -0
- data/lib/omnizip/checksums/crc64.rb +101 -0
- data/lib/omnizip/checksums/crc_base.rb +158 -0
- data/lib/omnizip/checksums/verifier.rb +131 -0
- data/lib/omnizip/chunked/memory_manager.rb +194 -0
- data/lib/omnizip/chunked/reader.rb +78 -0
- data/lib/omnizip/chunked/writer.rb +120 -0
- data/lib/omnizip/chunked.rb +129 -0
- data/lib/omnizip/cli/output_formatter.rb +104 -0
- data/lib/omnizip/cli.rb +572 -0
- data/lib/omnizip/commands/.keep +0 -0
- data/lib/omnizip/commands/archive_create_command.rb +427 -0
- data/lib/omnizip/commands/archive_extract_command.rb +272 -0
- data/lib/omnizip/commands/archive_list_command.rb +218 -0
- data/lib/omnizip/commands/archive_repair_command.rb +131 -0
- data/lib/omnizip/commands/archive_verify_command.rb +117 -0
- data/lib/omnizip/commands/compress_command.rb +117 -0
- data/lib/omnizip/commands/decompress_command.rb +120 -0
- data/lib/omnizip/commands/list_command.rb +53 -0
- data/lib/omnizip/commands/metadata_command.rb +153 -0
- data/lib/omnizip/commands/parity_create_command.rb +122 -0
- data/lib/omnizip/commands/parity_repair_command.rb +122 -0
- data/lib/omnizip/commands/parity_verify_command.rb +124 -0
- data/lib/omnizip/commands/profile_list_command.rb +56 -0
- data/lib/omnizip/commands/profile_show_command.rb +44 -0
- data/lib/omnizip/convenience.rb +359 -0
- data/lib/omnizip/converter/conversion_registry.rb +49 -0
- data/lib/omnizip/converter/conversion_strategy.rb +121 -0
- data/lib/omnizip/converter/seven_zip_to_zip_strategy.rb +97 -0
- data/lib/omnizip/converter/zip_to_seven_zip_strategy.rb +112 -0
- data/lib/omnizip/converter.rb +105 -0
- data/lib/omnizip/crypto/aes256/cipher.rb +100 -0
- data/lib/omnizip/crypto/aes256/constants.rb +28 -0
- data/lib/omnizip/crypto/aes256/key_derivation.rb +101 -0
- data/lib/omnizip/crypto/aes256.rb +102 -0
- data/lib/omnizip/error.rb +106 -0
- data/lib/omnizip/eta/exponential_smoothing_estimator.rb +98 -0
- data/lib/omnizip/eta/moving_average_estimator.rb +99 -0
- data/lib/omnizip/eta/rate_calculator.rb +104 -0
- data/lib/omnizip/eta/sample_history.rb +143 -0
- data/lib/omnizip/eta/time_estimator.rb +106 -0
- data/lib/omnizip/eta.rb +63 -0
- data/lib/omnizip/extraction/filter_chain.rb +177 -0
- data/lib/omnizip/extraction/glob_pattern.rb +140 -0
- data/lib/omnizip/extraction/pattern_matcher.rb +70 -0
- data/lib/omnizip/extraction/predicate_pattern.rb +52 -0
- data/lib/omnizip/extraction/regex_pattern.rb +50 -0
- data/lib/omnizip/extraction/selective_extractor.rb +240 -0
- data/lib/omnizip/extraction.rb +111 -0
- data/lib/omnizip/file_type/mime_classifier.rb +144 -0
- data/lib/omnizip/file_type.rb +113 -0
- data/lib/omnizip/filter.rb +139 -0
- data/lib/omnizip/filter_pipeline.rb +108 -0
- data/lib/omnizip/filter_registry.rb +166 -0
- data/lib/omnizip/filters/bcj.rb +279 -0
- data/lib/omnizip/filters/bcj2/constants.rb +53 -0
- data/lib/omnizip/filters/bcj2/decoder.rb +200 -0
- data/lib/omnizip/filters/bcj2/encoder.rb +61 -0
- data/lib/omnizip/filters/bcj2/stream_data.rb +93 -0
- data/lib/omnizip/filters/bcj2.rb +99 -0
- data/lib/omnizip/filters/bcj_arm.rb +176 -0
- data/lib/omnizip/filters/bcj_arm64.rb +244 -0
- data/lib/omnizip/filters/bcj_ia64.rb +196 -0
- data/lib/omnizip/filters/bcj_ppc.rb +190 -0
- data/lib/omnizip/filters/bcj_sparc.rb +176 -0
- data/lib/omnizip/filters/bcj_x86.rb +193 -0
- data/lib/omnizip/filters/delta.rb +196 -0
- data/lib/omnizip/filters/filter_base.rb +72 -0
- data/lib/omnizip/filters/registry.rb +123 -0
- data/lib/omnizip/filters/xz_delta.rb +258 -0
- data/lib/omnizip/format_detector.rb +162 -0
- data/lib/omnizip/format_registry.rb +59 -0
- data/lib/omnizip/formats/.keep +0 -0
- data/lib/omnizip/formats/bzip2_file.rb +172 -0
- data/lib/omnizip/formats/cpio/constants.rb +55 -0
- data/lib/omnizip/formats/cpio/entry.rb +385 -0
- data/lib/omnizip/formats/cpio/reader.rb +196 -0
- data/lib/omnizip/formats/cpio/writer.rb +234 -0
- data/lib/omnizip/formats/cpio.rb +140 -0
- data/lib/omnizip/formats/format_spec_loader.rb +230 -0
- data/lib/omnizip/formats/gzip.rb +238 -0
- data/lib/omnizip/formats/iso/directory_builder.rb +297 -0
- data/lib/omnizip/formats/iso/directory_record.rb +152 -0
- data/lib/omnizip/formats/iso/joliet.rb +204 -0
- data/lib/omnizip/formats/iso/path_table.rb +125 -0
- data/lib/omnizip/formats/iso/reader.rb +197 -0
- data/lib/omnizip/formats/iso/rock_ridge.rb +349 -0
- data/lib/omnizip/formats/iso/volume_builder.rb +320 -0
- data/lib/omnizip/formats/iso/volume_descriptor.rb +168 -0
- data/lib/omnizip/formats/iso/writer.rb +530 -0
- data/lib/omnizip/formats/iso.rb +140 -0
- data/lib/omnizip/formats/lzip.rb +175 -0
- data/lib/omnizip/formats/lzma_alone.rb +171 -0
- data/lib/omnizip/formats/rar/archive_repairer.rb +243 -0
- data/lib/omnizip/formats/rar/archive_verifier.rb +195 -0
- data/lib/omnizip/formats/rar/block_parser.rb +243 -0
- data/lib/omnizip/formats/rar/compression/bit_stream.rb +180 -0
- data/lib/omnizip/formats/rar/compression/dispatcher.rb +217 -0
- data/lib/omnizip/formats/rar/compression/lz77_huffman/decoder.rb +216 -0
- data/lib/omnizip/formats/rar/compression/lz77_huffman/encoder.rb +158 -0
- data/lib/omnizip/formats/rar/compression/lz77_huffman/huffman_builder.rb +217 -0
- data/lib/omnizip/formats/rar/compression/lz77_huffman/huffman_coder.rb +189 -0
- data/lib/omnizip/formats/rar/compression/lz77_huffman/match_finder.rb +135 -0
- data/lib/omnizip/formats/rar/compression/lz77_huffman/sliding_window.rb +165 -0
- data/lib/omnizip/formats/rar/compression/ppmd/context.rb +105 -0
- data/lib/omnizip/formats/rar/compression/ppmd/decoder.rb +219 -0
- data/lib/omnizip/formats/rar/compression/ppmd/encoder.rb +262 -0
- data/lib/omnizip/formats/rar/compression_method_registry.rb +106 -0
- data/lib/omnizip/formats/rar/constants.rb +82 -0
- data/lib/omnizip/formats/rar/decompressor.rb +238 -0
- data/lib/omnizip/formats/rar/external_writer.rb +312 -0
- data/lib/omnizip/formats/rar/header.rb +192 -0
- data/lib/omnizip/formats/rar/license_validator.rb +109 -0
- data/lib/omnizip/formats/rar/models/rar_archive.rb +77 -0
- data/lib/omnizip/formats/rar/models/rar_entry.rb +65 -0
- data/lib/omnizip/formats/rar/models/rar_volume.rb +56 -0
- data/lib/omnizip/formats/rar/parity_handler.rb +292 -0
- data/lib/omnizip/formats/rar/rar5/compression/lzma.rb +202 -0
- data/lib/omnizip/formats/rar/rar5/compression/lzss.rb +578 -0
- data/lib/omnizip/formats/rar/rar5/compression/store.rb +60 -0
- data/lib/omnizip/formats/rar/rar5/crc32.rb +39 -0
- data/lib/omnizip/formats/rar/rar5/encryption/aes256_cbc.rb +97 -0
- data/lib/omnizip/formats/rar/rar5/encryption/encryption_header.rb +114 -0
- data/lib/omnizip/formats/rar/rar5/encryption/encryption_manager.rb +166 -0
- data/lib/omnizip/formats/rar/rar5/encryption/key_derivation.rb +97 -0
- data/lib/omnizip/formats/rar/rar5/header.rb +187 -0
- data/lib/omnizip/formats/rar/rar5/models/encryption_options.rb +74 -0
- data/lib/omnizip/formats/rar/rar5/models/recovery_options.rb +63 -0
- data/lib/omnizip/formats/rar/rar5/models/solid_options.rb +63 -0
- data/lib/omnizip/formats/rar/rar5/models/volume_options.rb +74 -0
- data/lib/omnizip/formats/rar/rar5/multi_volume/ARCHITECTURE.md +290 -0
- data/lib/omnizip/formats/rar/rar5/multi_volume/volume_manager.rb +264 -0
- data/lib/omnizip/formats/rar/rar5/multi_volume/volume_splitter.rb +155 -0
- data/lib/omnizip/formats/rar/rar5/multi_volume/volume_writer.rb +194 -0
- data/lib/omnizip/formats/rar/rar5/solid/solid_encoder.rb +109 -0
- data/lib/omnizip/formats/rar/rar5/solid/solid_manager.rb +142 -0
- data/lib/omnizip/formats/rar/rar5/solid/solid_stream.rb +121 -0
- data/lib/omnizip/formats/rar/rar5/vint.rb +65 -0
- data/lib/omnizip/formats/rar/rar5/writer.rb +466 -0
- data/lib/omnizip/formats/rar/rar_format_base.rb +241 -0
- data/lib/omnizip/formats/rar/reader.rb +366 -0
- data/lib/omnizip/formats/rar/recovery_record.rb +245 -0
- data/lib/omnizip/formats/rar/volume_manager.rb +168 -0
- data/lib/omnizip/formats/rar/writer.rb +431 -0
- data/lib/omnizip/formats/rar.rb +205 -0
- data/lib/omnizip/formats/rar3/compressor.rb +73 -0
- data/lib/omnizip/formats/rar3/decompressor.rb +66 -0
- data/lib/omnizip/formats/rar3/reader.rb +386 -0
- data/lib/omnizip/formats/rar3/writer.rb +219 -0
- data/lib/omnizip/formats/rar5/compressor.rb +73 -0
- data/lib/omnizip/formats/rar5/decompressor.rb +66 -0
- data/lib/omnizip/formats/rar5/reader.rb +342 -0
- data/lib/omnizip/formats/rar5/writer.rb +214 -0
- data/lib/omnizip/formats/seven_zip/coder_chain.rb +150 -0
- data/lib/omnizip/formats/seven_zip/constants.rb +126 -0
- data/lib/omnizip/formats/seven_zip/encoded_header.rb +114 -0
- data/lib/omnizip/formats/seven_zip/encrypted_header.rb +142 -0
- data/lib/omnizip/formats/seven_zip/file_collector.rb +144 -0
- data/lib/omnizip/formats/seven_zip/header.rb +106 -0
- data/lib/omnizip/formats/seven_zip/header_encryptor.rb +134 -0
- data/lib/omnizip/formats/seven_zip/header_writer.rb +466 -0
- data/lib/omnizip/formats/seven_zip/models/coder_info.rb +30 -0
- data/lib/omnizip/formats/seven_zip/models/file_entry.rb +58 -0
- data/lib/omnizip/formats/seven_zip/models/folder.rb +69 -0
- data/lib/omnizip/formats/seven_zip/models/stream_info.rb +42 -0
- data/lib/omnizip/formats/seven_zip/parser.rb +660 -0
- data/lib/omnizip/formats/seven_zip/reader.rb +458 -0
- data/lib/omnizip/formats/seven_zip/split_archive_reader.rb +632 -0
- data/lib/omnizip/formats/seven_zip/split_archive_writer.rb +315 -0
- data/lib/omnizip/formats/seven_zip/stream_compressor.rb +151 -0
- data/lib/omnizip/formats/seven_zip/stream_decompressor.rb +162 -0
- data/lib/omnizip/formats/seven_zip/writer.rb +740 -0
- data/lib/omnizip/formats/seven_zip.rb +93 -0
- data/lib/omnizip/formats/tar/constants.rb +73 -0
- data/lib/omnizip/formats/tar/entry.rb +94 -0
- data/lib/omnizip/formats/tar/header.rb +168 -0
- data/lib/omnizip/formats/tar/reader.rb +121 -0
- data/lib/omnizip/formats/tar/writer.rb +216 -0
- data/lib/omnizip/formats/tar.rb +84 -0
- data/lib/omnizip/formats/xz/reader.rb +116 -0
- data/lib/omnizip/formats/xz.rb +237 -0
- data/lib/omnizip/formats/xz_impl/block_decoder.rb +754 -0
- data/lib/omnizip/formats/xz_impl/block_encoder.rb +306 -0
- data/lib/omnizip/formats/xz_impl/block_header.rb +210 -0
- data/lib/omnizip/formats/xz_impl/block_header_parser.rb +186 -0
- data/lib/omnizip/formats/xz_impl/constants.rb +49 -0
- data/lib/omnizip/formats/xz_impl/index_decoder.rb +174 -0
- data/lib/omnizip/formats/xz_impl/index_encoder.rb +122 -0
- data/lib/omnizip/formats/xz_impl/stream_decoder.rb +468 -0
- data/lib/omnizip/formats/xz_impl/stream_encoder.rb +99 -0
- data/lib/omnizip/formats/xz_impl/stream_footer.rb +81 -0
- data/lib/omnizip/formats/xz_impl/stream_footer_parser.rb +117 -0
- data/lib/omnizip/formats/xz_impl/stream_header.rb +55 -0
- data/lib/omnizip/formats/xz_impl/stream_header_parser.rb +108 -0
- data/lib/omnizip/formats/xz_impl/vli.rb +128 -0
- data/lib/omnizip/formats/xz_impl/writer.rb +421 -0
- data/lib/omnizip/formats/zip/central_directory_header.rb +195 -0
- data/lib/omnizip/formats/zip/constants.rb +69 -0
- data/lib/omnizip/formats/zip/end_of_central_directory.rb +133 -0
- data/lib/omnizip/formats/zip/local_file_header.rb +138 -0
- data/lib/omnizip/formats/zip/reader.rb +250 -0
- data/lib/omnizip/formats/zip/unix_extra_field.rb +153 -0
- data/lib/omnizip/formats/zip/writer.rb +375 -0
- data/lib/omnizip/formats/zip/zip64_end_of_central_directory.rb +104 -0
- data/lib/omnizip/formats/zip/zip64_end_of_central_directory_locator.rb +66 -0
- data/lib/omnizip/formats/zip/zip64_extra_field.rb +114 -0
- data/lib/omnizip/formats/zip.rb +50 -0
- data/lib/omnizip/implementations/base/lzma2_decoder_base.rb +75 -0
- data/lib/omnizip/implementations/base/lzma2_encoder_base.rb +128 -0
- data/lib/omnizip/implementations/base/lzma_decoder_base.rb +83 -0
- data/lib/omnizip/implementations/base/lzma_encoder_base.rb +108 -0
- data/lib/omnizip/implementations/base/state_machine_base.rb +182 -0
- data/lib/omnizip/implementations/seven_zip/lzma/decoder.rb +421 -0
- data/lib/omnizip/implementations/seven_zip/lzma/encoder.rb +465 -0
- data/lib/omnizip/implementations/seven_zip/lzma/match_finder.rb +288 -0
- data/lib/omnizip/implementations/seven_zip/lzma/range_decoder.rb +200 -0
- data/lib/omnizip/implementations/seven_zip/lzma/range_encoder.rb +197 -0
- data/lib/omnizip/implementations/seven_zip/lzma/state_machine.rb +141 -0
- data/lib/omnizip/implementations/seven_zip/lzma2/encoder.rb +519 -0
- data/lib/omnizip/implementations/xz_utils/lzma2/decoder.rb +723 -0
- data/lib/omnizip/implementations/xz_utils/lzma2/encoder.rb +750 -0
- data/lib/omnizip/io/buffered_input.rb +146 -0
- data/lib/omnizip/io/buffered_output.rb +105 -0
- data/lib/omnizip/io/stream_manager.rb +115 -0
- data/lib/omnizip/link_handler/hard_link.rb +79 -0
- data/lib/omnizip/link_handler/symbolic_link.rb +74 -0
- data/lib/omnizip/link_handler.rb +124 -0
- data/lib/omnizip/metadata/archive_metadata.rb +114 -0
- data/lib/omnizip/metadata/entry_metadata.rb +146 -0
- data/lib/omnizip/metadata/metadata_editor.rb +171 -0
- data/lib/omnizip/metadata/metadata_registry.rb +64 -0
- data/lib/omnizip/metadata/metadata_validator.rb +99 -0
- data/lib/omnizip/metadata.rb +57 -0
- data/lib/omnizip/models/.keep +0 -0
- data/lib/omnizip/models/algorithm_metadata.rb +73 -0
- data/lib/omnizip/models/compression_options.rb +71 -0
- data/lib/omnizip/models/conversion_options.rb +87 -0
- data/lib/omnizip/models/conversion_result.rb +135 -0
- data/lib/omnizip/models/eta_result.rb +46 -0
- data/lib/omnizip/models/extraction_rule.rb +115 -0
- data/lib/omnizip/models/filter_chain.rb +144 -0
- data/lib/omnizip/models/filter_config.rb +183 -0
- data/lib/omnizip/models/match_result.rb +124 -0
- data/lib/omnizip/models/optimization_suggestion.rb +91 -0
- data/lib/omnizip/models/parallel_options.rb +104 -0
- data/lib/omnizip/models/performance_result.rb +79 -0
- data/lib/omnizip/models/profile_report.rb +82 -0
- data/lib/omnizip/models/progress_options.rb +38 -0
- data/lib/omnizip/models/split_options.rb +116 -0
- data/lib/omnizip/optimization_registry.rb +81 -0
- data/lib/omnizip/parallel/job_queue.rb +209 -0
- data/lib/omnizip/parallel/job_scheduler.rb +203 -0
- data/lib/omnizip/parallel/parallel_compressor.rb +347 -0
- data/lib/omnizip/parallel/parallel_extractor.rb +329 -0
- data/lib/omnizip/parallel/worker_pool.rb +223 -0
- data/lib/omnizip/parallel.rb +149 -0
- data/lib/omnizip/parity/chunked_block_processor.rb +196 -0
- data/lib/omnizip/parity/galois16.rb +145 -0
- data/lib/omnizip/parity/models/creator_packet.rb +73 -0
- data/lib/omnizip/parity/models/file_description_packet.rb +133 -0
- data/lib/omnizip/parity/models/ifsc_packet.rb +123 -0
- data/lib/omnizip/parity/models/main_packet.rb +128 -0
- data/lib/omnizip/parity/models/packet.rb +156 -0
- data/lib/omnizip/parity/models/packet_registry.rb +109 -0
- data/lib/omnizip/parity/models/recovery_slice_packet.rb +78 -0
- data/lib/omnizip/parity/par2_creator.rb +531 -0
- data/lib/omnizip/parity/par2_repairer.rb +407 -0
- data/lib/omnizip/parity/par2_verifier.rb +364 -0
- data/lib/omnizip/parity/par2cmdline_algorithm.rb +110 -0
- data/lib/omnizip/parity/par2cmdline_coefficients.rb +78 -0
- data/lib/omnizip/parity/reed_solomon_decoder.rb +266 -0
- data/lib/omnizip/parity/reed_solomon_encoder.rb +111 -0
- data/lib/omnizip/parity/reed_solomon_matrix.rb +342 -0
- data/lib/omnizip/parity.rb +186 -0
- data/lib/omnizip/password/encryption_registry.rb +65 -0
- data/lib/omnizip/password/encryption_strategy.rb +96 -0
- data/lib/omnizip/password/password_validator.rb +129 -0
- data/lib/omnizip/password/winzip_aes_strategy.rb +192 -0
- data/lib/omnizip/password/zip_crypto_strategy.rb +141 -0
- data/lib/omnizip/password.rb +87 -0
- data/lib/omnizip/pipe/stream_compressor.rb +124 -0
- data/lib/omnizip/pipe/stream_decompressor.rb +174 -0
- data/lib/omnizip/pipe.rb +121 -0
- data/lib/omnizip/platform/ntfs_streams.rb +201 -0
- data/lib/omnizip/platform.rb +189 -0
- data/lib/omnizip/profile/archive_profile.rb +39 -0
- data/lib/omnizip/profile/balanced_profile.rb +33 -0
- data/lib/omnizip/profile/binary_profile.rb +36 -0
- data/lib/omnizip/profile/compression_profile.rb +158 -0
- data/lib/omnizip/profile/custom_profile.rb +157 -0
- data/lib/omnizip/profile/fast_profile.rb +33 -0
- data/lib/omnizip/profile/maximum_profile.rb +33 -0
- data/lib/omnizip/profile/profile_detector.rb +110 -0
- data/lib/omnizip/profile/profile_registry.rb +161 -0
- data/lib/omnizip/profile/text_profile.rb +36 -0
- data/lib/omnizip/profile.rb +190 -0
- data/lib/omnizip/profiler/memory_profiler.rb +66 -0
- data/lib/omnizip/profiler/method_profiler.rb +49 -0
- data/lib/omnizip/profiler/report_generator.rb +169 -0
- data/lib/omnizip/profiler.rb +204 -0
- data/lib/omnizip/progress/callback_reporter.rb +36 -0
- data/lib/omnizip/progress/console_reporter.rb +62 -0
- data/lib/omnizip/progress/log_reporter.rb +91 -0
- data/lib/omnizip/progress/operation_progress.rb +118 -0
- data/lib/omnizip/progress/progress_bar.rb +156 -0
- data/lib/omnizip/progress/progress_reporter.rb +40 -0
- data/lib/omnizip/progress/progress_tracker.rb +190 -0
- data/lib/omnizip/progress/silent_reporter.rb +24 -0
- data/lib/omnizip/progress.rb +127 -0
- data/lib/omnizip/rubyzip_compat.rb +63 -0
- data/lib/omnizip/temp/safe_extract.rb +168 -0
- data/lib/omnizip/temp/temp_file.rb +124 -0
- data/lib/omnizip/temp/temp_file_pool.rb +109 -0
- data/lib/omnizip/temp.rb +181 -0
- data/lib/omnizip/version.rb +5 -0
- data/lib/omnizip/zip/entry.rb +156 -0
- data/lib/omnizip/zip/file.rb +485 -0
- data/lib/omnizip/zip/input_stream.rb +273 -0
- data/lib/omnizip/zip/output_stream.rb +324 -0
- data/lib/omnizip.rb +156 -0
- data/readme-docs/advanced-features.adoc +515 -0
- data/readme-docs/api-usage.adoc +444 -0
- data/readme-docs/architecture.adoc +449 -0
- data/readme-docs/archive-formats.adoc +479 -0
- data/readme-docs/cli-usage.adoc +222 -0
- data/readme-docs/compression-algorithms.adoc +442 -0
- data/readme-docs/compression-profiles.adoc +247 -0
- data/readme-docs/encryption-checksums.adoc +328 -0
- data/readme-docs/format-converter.adoc +325 -0
- data/readme-docs/installation.adoc +228 -0
- data/readme-docs/par2-archives.adoc +608 -0
- data/readme-docs/performance-profiler.adoc +389 -0
- data/readme-docs/preprocessing-filters.adoc +280 -0
- data/xz-file-format-1.2.1.txt +1174 -0
- metadata +617 -0
|
@@ -0,0 +1,364 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "digest"
|
|
4
|
+
require_relative "models/packet_registry"
|
|
5
|
+
|
|
6
|
+
module Omnizip
|
|
7
|
+
module Parity
|
|
8
|
+
# PAR2 archive verifier
|
|
9
|
+
#
|
|
10
|
+
# Verifies file integrity using PAR2 recovery files and checks
|
|
11
|
+
# if damaged files can be repaired.
|
|
12
|
+
#
|
|
13
|
+
# Uses PacketRegistry and packet models for clean object-oriented
|
|
14
|
+
# architecture.
|
|
15
|
+
#
|
|
16
|
+
# @example Verify files
|
|
17
|
+
# verifier = Par2Verifier.new('backup.par2')
|
|
18
|
+
# result = verifier.verify
|
|
19
|
+
# if result.repairable?
|
|
20
|
+
# puts "#{result.damaged_blocks.size} blocks damaged, can repair"
|
|
21
|
+
# end
|
|
22
|
+
class Par2Verifier
|
|
23
|
+
# Verification result
|
|
24
|
+
VerificationResult = Struct.new(
|
|
25
|
+
:all_ok, # All files intact?
|
|
26
|
+
:damaged_files, # Array of damaged file names
|
|
27
|
+
:damaged_blocks, # Array of damaged block indices
|
|
28
|
+
:missing_files, # Array of missing file names
|
|
29
|
+
:repairable, # Can be repaired with available parity?
|
|
30
|
+
:total_blocks, # Total number of blocks
|
|
31
|
+
:recovery_blocks, # Number of recovery blocks available
|
|
32
|
+
keyword_init: true,
|
|
33
|
+
) do
|
|
34
|
+
# Check if all files are OK
|
|
35
|
+
#
|
|
36
|
+
# @return [Boolean] true if no damage detected
|
|
37
|
+
def all_ok?
|
|
38
|
+
damaged_files.empty? && missing_files.empty?
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# Check if damage can be repaired
|
|
42
|
+
#
|
|
43
|
+
# @return [Boolean] true if repairable
|
|
44
|
+
def repairable?
|
|
45
|
+
repairable
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# Get total damage count
|
|
49
|
+
#
|
|
50
|
+
# @return [Integer] Number of damaged/missing blocks
|
|
51
|
+
def damage_count
|
|
52
|
+
damaged_blocks.size + missing_files.size
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
# @return [String] Path to PAR2 index file
|
|
57
|
+
attr_reader :par2_file
|
|
58
|
+
|
|
59
|
+
# @return [Hash] Parsed PAR2 metadata
|
|
60
|
+
attr_reader :metadata
|
|
61
|
+
|
|
62
|
+
# Initialize verifier
|
|
63
|
+
#
|
|
64
|
+
# @param par2_file [String] Path to .par2 index file
|
|
65
|
+
# @raise [ArgumentError] if file doesn't exist
|
|
66
|
+
def initialize(par2_file)
|
|
67
|
+
raise ArgumentError, "PAR2 file not found: #{par2_file}" unless
|
|
68
|
+
File.exist?(par2_file)
|
|
69
|
+
|
|
70
|
+
@par2_file = par2_file
|
|
71
|
+
@metadata = {}
|
|
72
|
+
@file_list = []
|
|
73
|
+
@recovery_blocks = []
|
|
74
|
+
@block_hashes = {} # Store IFSC block hashes for verification
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
# Verify files against PAR2 data
|
|
78
|
+
#
|
|
79
|
+
# @return [VerificationResult] Verification results
|
|
80
|
+
def verify
|
|
81
|
+
parse_par2_file
|
|
82
|
+
|
|
83
|
+
damaged_files = []
|
|
84
|
+
damaged_blocks = []
|
|
85
|
+
missing_files = []
|
|
86
|
+
|
|
87
|
+
# Track global block position as we iterate through files
|
|
88
|
+
global_block_idx = 0
|
|
89
|
+
|
|
90
|
+
# Check each file
|
|
91
|
+
@file_list.each do |file_info|
|
|
92
|
+
file_path = find_file_path(file_info[:filename])
|
|
93
|
+
num_blocks = (file_info[:size].to_f / @metadata[:block_size]).ceil
|
|
94
|
+
|
|
95
|
+
if file_path.nil?
|
|
96
|
+
missing_files << file_info[:filename]
|
|
97
|
+
global_block_idx += num_blocks
|
|
98
|
+
next
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
# Verify file integrity
|
|
102
|
+
damage = verify_file(file_path, file_info)
|
|
103
|
+
if damage[:damaged]
|
|
104
|
+
damaged_files << file_info[:filename]
|
|
105
|
+
# Convert file-relative indices to global indices
|
|
106
|
+
damage[:blocks].each do |file_relative_idx|
|
|
107
|
+
damaged_blocks << (global_block_idx + file_relative_idx)
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
global_block_idx += num_blocks
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
# Check if repairable
|
|
115
|
+
total_damage = damaged_blocks.size + (missing_files.size * avg_blocks_per_file)
|
|
116
|
+
repairable = total_damage <= @recovery_blocks.size
|
|
117
|
+
|
|
118
|
+
VerificationResult.new(
|
|
119
|
+
all_ok: damaged_files.empty? && missing_files.empty?,
|
|
120
|
+
damaged_files: damaged_files,
|
|
121
|
+
damaged_blocks: damaged_blocks,
|
|
122
|
+
missing_files: missing_files,
|
|
123
|
+
repairable: repairable,
|
|
124
|
+
total_blocks: calculate_total_blocks,
|
|
125
|
+
recovery_blocks: @recovery_blocks.size,
|
|
126
|
+
)
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
private
|
|
130
|
+
|
|
131
|
+
# Parse PAR2 index file using packet models
|
|
132
|
+
def parse_par2_file
|
|
133
|
+
# Reset state to prevent accumulation if called multiple times
|
|
134
|
+
@metadata = {}
|
|
135
|
+
@file_list = []
|
|
136
|
+
@recovery_blocks = []
|
|
137
|
+
@block_hashes = {}
|
|
138
|
+
|
|
139
|
+
File.open(@par2_file, "rb") do |io|
|
|
140
|
+
while !io.eof?
|
|
141
|
+
packet = Models::PacketRegistry.read_packet(io)
|
|
142
|
+
break unless packet
|
|
143
|
+
|
|
144
|
+
process_packet_model(packet)
|
|
145
|
+
end
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
# CRITICAL: Sort file_list by position in Main packet file_ids
|
|
149
|
+
# The Main packet defines canonical file order for Reed-Solomon matrix
|
|
150
|
+
# Do NOT sort by file_id string - that breaks recovery!
|
|
151
|
+
if @metadata[:file_ids]
|
|
152
|
+
file_id_order = {}
|
|
153
|
+
@metadata[:file_ids].each_with_index do |fid, idx|
|
|
154
|
+
file_id_order[fid] = idx
|
|
155
|
+
end
|
|
156
|
+
@file_list.sort_by! { |f| file_id_order[f[:file_id]] || 999 }
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
# Load recovery blocks from volume files
|
|
160
|
+
load_recovery_volumes
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
# Process packet using model-based approach
|
|
164
|
+
#
|
|
165
|
+
# @param packet [Models::Packet] Parsed packet model
|
|
166
|
+
def process_packet_model(packet)
|
|
167
|
+
case packet
|
|
168
|
+
when Models::MainPacket
|
|
169
|
+
process_main_packet_model(packet)
|
|
170
|
+
when Models::FileDescriptionPacket
|
|
171
|
+
process_file_description_packet_model(packet)
|
|
172
|
+
when Models::IfscPacket
|
|
173
|
+
process_ifsc_packet_model(packet)
|
|
174
|
+
when Models::RecoverySlicePacket
|
|
175
|
+
process_recovery_packet_model(packet)
|
|
176
|
+
when Models::CreatorPacket
|
|
177
|
+
# Creator packets are informational only
|
|
178
|
+
end
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
# Process main packet model
|
|
182
|
+
#
|
|
183
|
+
# @param packet [Models::MainPacket] Main packet
|
|
184
|
+
def process_main_packet_model(packet)
|
|
185
|
+
@metadata[:block_size] = packet.block_size
|
|
186
|
+
@metadata[:file_ids] = packet.file_ids.dup
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
# Process file description packet model
|
|
190
|
+
#
|
|
191
|
+
# @param packet [Models::FileDescriptionPacket] File description packet
|
|
192
|
+
def process_file_description_packet_model(packet)
|
|
193
|
+
# Skip packets with incomplete data
|
|
194
|
+
return if packet.file_id.nil? || packet.filename.nil? || packet.filename.empty?
|
|
195
|
+
return if packet.file_hash.nil? || packet.length.nil?
|
|
196
|
+
|
|
197
|
+
@file_list << {
|
|
198
|
+
file_id: packet.file_id,
|
|
199
|
+
hash_full: packet.file_hash,
|
|
200
|
+
hash_16k: packet.file_hash_16k,
|
|
201
|
+
size: packet.length,
|
|
202
|
+
filename: packet.filename,
|
|
203
|
+
}
|
|
204
|
+
end
|
|
205
|
+
|
|
206
|
+
# Process IFSC packet model
|
|
207
|
+
#
|
|
208
|
+
# Each IFSC packet contains checksums for all blocks of a file
|
|
209
|
+
#
|
|
210
|
+
# @param packet [Models::IfscPacket] IFSC packet
|
|
211
|
+
def process_ifsc_packet_model(packet)
|
|
212
|
+
# Store block hashes keyed by file_id
|
|
213
|
+
# Each IFSC packet contains all hashes for one file
|
|
214
|
+
@block_hashes[packet.file_id] = packet.block_hashes
|
|
215
|
+
end
|
|
216
|
+
|
|
217
|
+
# Process recovery packet model
|
|
218
|
+
#
|
|
219
|
+
# @param packet [Models::RecoverySlicePacket] Recovery packet
|
|
220
|
+
def process_recovery_packet_model(packet)
|
|
221
|
+
@recovery_blocks << {
|
|
222
|
+
exponent: packet.exponent,
|
|
223
|
+
data: packet.recovery_data,
|
|
224
|
+
}
|
|
225
|
+
end
|
|
226
|
+
|
|
227
|
+
# Load recovery blocks from volume files
|
|
228
|
+
def load_recovery_volumes
|
|
229
|
+
base_name = File.basename(@par2_file, ".par2")
|
|
230
|
+
dir_name = File.dirname(@par2_file)
|
|
231
|
+
|
|
232
|
+
# Find all volume files
|
|
233
|
+
pattern = File.join(dir_name, "#{base_name}.vol*.par2")
|
|
234
|
+
volume_files = Dir.glob(pattern)
|
|
235
|
+
|
|
236
|
+
volume_files.each do |volume_file|
|
|
237
|
+
load_recovery_volume(volume_file)
|
|
238
|
+
end
|
|
239
|
+
end
|
|
240
|
+
|
|
241
|
+
# Load recovery blocks from single volume file
|
|
242
|
+
#
|
|
243
|
+
# @param volume_file [String] Path to volume file
|
|
244
|
+
def load_recovery_volume(volume_file)
|
|
245
|
+
File.open(volume_file, "rb") do |io|
|
|
246
|
+
while !io.eof?
|
|
247
|
+
packet = Models::PacketRegistry.read_packet(io)
|
|
248
|
+
break unless packet
|
|
249
|
+
|
|
250
|
+
# Only process recovery packets from volume files
|
|
251
|
+
process_packet_model(packet) if packet.is_a?(Models::RecoverySlicePacket)
|
|
252
|
+
end
|
|
253
|
+
end
|
|
254
|
+
end
|
|
255
|
+
|
|
256
|
+
# Find file path for filename
|
|
257
|
+
#
|
|
258
|
+
# @param filename [String] Filename from PAR2
|
|
259
|
+
# @return [String, nil] Full path or nil if not found
|
|
260
|
+
def find_file_path(filename)
|
|
261
|
+
# Look in same directory as PAR2 file
|
|
262
|
+
dir = File.dirname(@par2_file)
|
|
263
|
+
candidate = File.join(dir, filename)
|
|
264
|
+
|
|
265
|
+
return candidate if File.exist?(candidate)
|
|
266
|
+
|
|
267
|
+
# Look in current directory
|
|
268
|
+
return filename if File.exist?(filename)
|
|
269
|
+
|
|
270
|
+
nil
|
|
271
|
+
end
|
|
272
|
+
|
|
273
|
+
# Verify single file
|
|
274
|
+
#
|
|
275
|
+
# @param file_path [String] Path to file
|
|
276
|
+
# @param file_info [Hash] Expected file information
|
|
277
|
+
# @return [Hash] Damage information
|
|
278
|
+
def verify_file(file_path, file_info)
|
|
279
|
+
damaged_blocks = []
|
|
280
|
+
damaged = false
|
|
281
|
+
|
|
282
|
+
File.open(file_path, "rb") do |io|
|
|
283
|
+
# Quick check: file size
|
|
284
|
+
if io.size != file_info[:size]
|
|
285
|
+
damaged = true
|
|
286
|
+
# Still identify damaged blocks even with size mismatch
|
|
287
|
+
damaged_blocks = identify_damaged_blocks(io, file_info)
|
|
288
|
+
return { damaged: damaged, blocks: damaged_blocks,
|
|
289
|
+
size_mismatch: true }
|
|
290
|
+
end
|
|
291
|
+
|
|
292
|
+
# Quick check: first 16KB hash
|
|
293
|
+
first_16k = io.read(16384) || ""
|
|
294
|
+
hash_16k = Digest::MD5.digest(first_16k)
|
|
295
|
+
if hash_16k == file_info[:hash_16k]
|
|
296
|
+
# Full check: complete file hash
|
|
297
|
+
io.rewind
|
|
298
|
+
hash_full = Digest::MD5.file(file_path).digest
|
|
299
|
+
if hash_full != file_info[:hash_full]
|
|
300
|
+
damaged = true
|
|
301
|
+
# Identify damaged blocks
|
|
302
|
+
damaged_blocks = identify_damaged_blocks(io, file_info)
|
|
303
|
+
end
|
|
304
|
+
else
|
|
305
|
+
damaged = true
|
|
306
|
+
# Identify damaged blocks when quick check fails
|
|
307
|
+
damaged_blocks = identify_damaged_blocks(io, file_info)
|
|
308
|
+
end
|
|
309
|
+
end
|
|
310
|
+
|
|
311
|
+
{ damaged: damaged, blocks: damaged_blocks }
|
|
312
|
+
end
|
|
313
|
+
|
|
314
|
+
# Identify which blocks are damaged
|
|
315
|
+
#
|
|
316
|
+
# @param io [IO] File IO
|
|
317
|
+
# @param file_info [Hash] File information
|
|
318
|
+
# @return [Array<Integer>] Damaged block indices
|
|
319
|
+
def identify_damaged_blocks(io, file_info)
|
|
320
|
+
damaged = []
|
|
321
|
+
block_idx = 0
|
|
322
|
+
expected_hashes = @block_hashes[file_info[:file_id]] || []
|
|
323
|
+
|
|
324
|
+
io.rewind
|
|
325
|
+
while (data = io.read(@metadata[:block_size]))
|
|
326
|
+
# Pad last block
|
|
327
|
+
if data.bytesize < @metadata[:block_size]
|
|
328
|
+
data += "\x00" * (@metadata[:block_size] - data.bytesize)
|
|
329
|
+
end
|
|
330
|
+
|
|
331
|
+
# Check block hash against stored IFSC hash
|
|
332
|
+
block_hash = Digest::MD5.digest(data)
|
|
333
|
+
if block_idx < expected_hashes.size && block_hash != expected_hashes[block_idx]
|
|
334
|
+
damaged << block_idx
|
|
335
|
+
end
|
|
336
|
+
|
|
337
|
+
block_idx += 1
|
|
338
|
+
end
|
|
339
|
+
|
|
340
|
+
damaged
|
|
341
|
+
end
|
|
342
|
+
|
|
343
|
+
# Calculate average blocks per file
|
|
344
|
+
#
|
|
345
|
+
# @return [Integer] Average blocks per file
|
|
346
|
+
def avg_blocks_per_file
|
|
347
|
+
return 0 if @file_list.empty?
|
|
348
|
+
|
|
349
|
+
total_size = @file_list.sum { |f| f[:size] }
|
|
350
|
+
total_blocks = (total_size.to_f / @metadata[:block_size]).ceil
|
|
351
|
+
(total_blocks.to_f / @file_list.size).ceil
|
|
352
|
+
end
|
|
353
|
+
|
|
354
|
+
# Calculate total blocks
|
|
355
|
+
#
|
|
356
|
+
# @return [Integer] Total number of blocks
|
|
357
|
+
def calculate_total_blocks
|
|
358
|
+
@file_list.sum do |file_info|
|
|
359
|
+
(file_info[:size].to_f / @metadata[:block_size]).ceil
|
|
360
|
+
end
|
|
361
|
+
end
|
|
362
|
+
end
|
|
363
|
+
end
|
|
364
|
+
end
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "galois16"
|
|
4
|
+
|
|
5
|
+
module Omnizip
|
|
6
|
+
module Parity
|
|
7
|
+
# Par2cmdline-compatible Reed-Solomon algorithm
|
|
8
|
+
#
|
|
9
|
+
# This implements the EXACT algorithm from par2cmdline reedsolomon.cpp
|
|
10
|
+
# Lines 230-262 show the base value computation for encoding.
|
|
11
|
+
#
|
|
12
|
+
# Key algorithm from par2cmdline:
|
|
13
|
+
# unsigned int logbase = 0;
|
|
14
|
+
# for (unsigned int index=0; index<count; index++)
|
|
15
|
+
# {
|
|
16
|
+
# while (gcd(G::Limit, logbase) != 1)
|
|
17
|
+
# {
|
|
18
|
+
# logbase++;
|
|
19
|
+
# }
|
|
20
|
+
# G::ValueType base = G(logbase++).ALog();
|
|
21
|
+
# database[index] = base;
|
|
22
|
+
# }
|
|
23
|
+
#
|
|
24
|
+
# Then for matrix: coefficient = base[col] ^ exponent
|
|
25
|
+
#
|
|
26
|
+
module Par2cmdlineAlgorithm
|
|
27
|
+
# Compute base values exactly as par2cmdline does
|
|
28
|
+
#
|
|
29
|
+
# @param data_count [Integer] Number of data blocks
|
|
30
|
+
# @return [Array<Integer>] Base values for each data block
|
|
31
|
+
def self.compute_bases(data_count)
|
|
32
|
+
bases = []
|
|
33
|
+
logbase = 0
|
|
34
|
+
limit = 65535 # GF(2^16) limit
|
|
35
|
+
|
|
36
|
+
data_count.times do
|
|
37
|
+
# Find next logbase where gcd(65535, logbase) == 1
|
|
38
|
+
logbase += 1 while gcd(limit, logbase) != 1
|
|
39
|
+
|
|
40
|
+
raise "Too many input blocks" if logbase >= limit
|
|
41
|
+
|
|
42
|
+
# Use antilog to convert logbase to base value
|
|
43
|
+
# This is the key: base = antilog[logbase]
|
|
44
|
+
base = Galois16.antilog(logbase)
|
|
45
|
+
bases << base
|
|
46
|
+
logbase += 1
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
bases
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# Build encoding matrix using par2cmdline algorithm
|
|
53
|
+
#
|
|
54
|
+
# @param data_count [Integer] Number of data blocks
|
|
55
|
+
# @param recovery_count [Integer] Number of recovery blocks
|
|
56
|
+
# @return [Array<Array<Integer>>] Encoding matrix
|
|
57
|
+
def self.build_encoding_matrix(data_count, recovery_count)
|
|
58
|
+
bases = compute_bases(data_count)
|
|
59
|
+
|
|
60
|
+
matrix = Array.new(recovery_count) { Array.new(data_count) }
|
|
61
|
+
|
|
62
|
+
recovery_count.times do |exponent|
|
|
63
|
+
data_count.times do |col|
|
|
64
|
+
# Matrix coefficient = base[col] ^ exponent
|
|
65
|
+
matrix[exponent][col] = Galois16.power(bases[col], exponent)
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
matrix
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
# Compute greatest common divisor
|
|
73
|
+
#
|
|
74
|
+
# @param a [Integer]
|
|
75
|
+
# @param b [Integer]
|
|
76
|
+
# @return [Integer] GCD of a and b
|
|
77
|
+
def self.gcd(a, b)
|
|
78
|
+
return 0 if a.zero? && b.zero?
|
|
79
|
+
return a + b if a.zero? || b.zero?
|
|
80
|
+
|
|
81
|
+
while a.positive? && b.positive?
|
|
82
|
+
if a > b
|
|
83
|
+
a %= b
|
|
84
|
+
else
|
|
85
|
+
b %= a
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
a + b
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
# Verify algorithm by generating expected coefficients
|
|
93
|
+
#
|
|
94
|
+
# @param max_exponent [Integer] Maximum exponent to test
|
|
95
|
+
# @return [Hash<Integer, Integer>] Coefficient for each exponent
|
|
96
|
+
def self.generate_coefficient_table(max_exponent = 100)
|
|
97
|
+
bases = compute_bases(10) # Test with 10 data blocks
|
|
98
|
+
coefficients = {}
|
|
99
|
+
|
|
100
|
+
(0..max_exponent).each do |exponent|
|
|
101
|
+
# For par2cmdline, ALL data blocks use the same coefficient
|
|
102
|
+
# which is base[0] ^ exponent
|
|
103
|
+
coefficients[exponent] = Galois16.power(bases[0], exponent)
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
coefficients
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
end
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Par2cmdline coefficient lookup table
|
|
4
|
+
#
|
|
5
|
+
# Discovered by reverse-engineering par2cmdline recovery blocks.
|
|
6
|
+
# Par2cmdline uses the SAME coefficient for ALL data blocks for a given exponent.
|
|
7
|
+
# This is different from standard Vandermonde matrix approach.
|
|
8
|
+
#
|
|
9
|
+
# Formula: coefficient[exponent, data_index] = PAR2CMDLINE_COEFFICIENTS[exponent]
|
|
10
|
+
# (same coefficient for all data_index values)
|
|
11
|
+
|
|
12
|
+
module Omnizip
|
|
13
|
+
module Parity
|
|
14
|
+
# Par2cmdline-compatible coefficient table
|
|
15
|
+
PAR2CMDLINE_COEFFICIENTS = {
|
|
16
|
+
0 => 0x27c6,
|
|
17
|
+
1 => 0x8eb6,
|
|
18
|
+
2 => 0x1a9f,
|
|
19
|
+
3 => 0x2743,
|
|
20
|
+
4 => 0x24f6,
|
|
21
|
+
5 => 0x60d7,
|
|
22
|
+
6 => 0x2027,
|
|
23
|
+
7 => 0x1cf0,
|
|
24
|
+
8 => 0xd37a,
|
|
25
|
+
9 => 0xa961,
|
|
26
|
+
10 => 0xc6c7,
|
|
27
|
+
11 => 0x653e,
|
|
28
|
+
12 => 0x9c99,
|
|
29
|
+
13 => 0x2e1b,
|
|
30
|
+
14 => 0x8625,
|
|
31
|
+
15 => 0xd81e,
|
|
32
|
+
16 => 0x1fb5,
|
|
33
|
+
17 => 0x2cdd,
|
|
34
|
+
18 => 0x06ce,
|
|
35
|
+
19 => 0x41d5,
|
|
36
|
+
20 => 0xd297,
|
|
37
|
+
21 => 0xeae1,
|
|
38
|
+
22 => 0x9012,
|
|
39
|
+
23 => 0xdc31,
|
|
40
|
+
24 => 0xa33d,
|
|
41
|
+
25 => 0x2480,
|
|
42
|
+
26 => 0x3e2e,
|
|
43
|
+
27 => 0x5dee,
|
|
44
|
+
28 => 0x8f63,
|
|
45
|
+
29 => 0x90c5,
|
|
46
|
+
30 => 0xac21,
|
|
47
|
+
31 => 0x9bf4,
|
|
48
|
+
32 => 0x8b15,
|
|
49
|
+
33 => 0xc489,
|
|
50
|
+
34 => 0x004c,
|
|
51
|
+
35 => 0x6a45,
|
|
52
|
+
36 => 0x56f9,
|
|
53
|
+
37 => 0x6956,
|
|
54
|
+
38 => 0x2548,
|
|
55
|
+
39 => 0x0334,
|
|
56
|
+
40 => 0x3213,
|
|
57
|
+
41 => 0x7c7f,
|
|
58
|
+
42 => 0x1d3c,
|
|
59
|
+
43 => 0x9c1e,
|
|
60
|
+
44 => 0x835c,
|
|
61
|
+
45 => 0x7f30,
|
|
62
|
+
46 => 0x070e,
|
|
63
|
+
47 => 0x5f7d,
|
|
64
|
+
48 => 0x5f97,
|
|
65
|
+
49 => 0xfa32,
|
|
66
|
+
50 => 0x08fd,
|
|
67
|
+
51 => 0x9d43,
|
|
68
|
+
52 => 0x9ec1,
|
|
69
|
+
53 => 0x4643,
|
|
70
|
+
54 => 0x9222,
|
|
71
|
+
55 => 0x1f9c,
|
|
72
|
+
56 => 0xd271,
|
|
73
|
+
57 => 0xbd9f,
|
|
74
|
+
58 => 0xfef3,
|
|
75
|
+
59 => 0x9b83,
|
|
76
|
+
}.freeze
|
|
77
|
+
end
|
|
78
|
+
end
|