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,342 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "galois16"
|
|
4
|
+
require_relative "par2cmdline_algorithm"
|
|
5
|
+
|
|
6
|
+
module Omnizip
|
|
7
|
+
module Parity
|
|
8
|
+
# Reed-Solomon matrix for incremental chunk processing
|
|
9
|
+
#
|
|
10
|
+
# This class computes the RS matrix coefficients ONCE using Gaussian
|
|
11
|
+
# elimination, then provides methods to apply those coefficients
|
|
12
|
+
# incrementally to data chunks.
|
|
13
|
+
#
|
|
14
|
+
# Based on par2cmdline's approach (reedsolomon.h, par2repairer.cpp)
|
|
15
|
+
class ReedSolomonMatrix
|
|
16
|
+
# @return [Array<Integer>] Indices of present input blocks
|
|
17
|
+
attr_reader :present_indices
|
|
18
|
+
|
|
19
|
+
# @return [Array<Integer>] Indices of missing input blocks
|
|
20
|
+
attr_reader :missing_indices
|
|
21
|
+
|
|
22
|
+
# @return [Array<Integer>] All available recovery exponents
|
|
23
|
+
attr_reader :recovery_exponents
|
|
24
|
+
|
|
25
|
+
# @return [Array<Integer>] Recovery exponents actually used (first num_missing)
|
|
26
|
+
attr_reader :used_recovery_exponents
|
|
27
|
+
|
|
28
|
+
# @return [Integer] Total number of input blocks
|
|
29
|
+
attr_reader :total_inputs
|
|
30
|
+
|
|
31
|
+
# @return [Integer] Block size in bytes
|
|
32
|
+
attr_reader :block_size
|
|
33
|
+
|
|
34
|
+
# @return [Array<Array<Integer>>] Solved matrix coefficients (num_missing x num_missing)
|
|
35
|
+
attr_reader :matrix
|
|
36
|
+
|
|
37
|
+
# @return [Array<Integer>] Selected bases for Galois field
|
|
38
|
+
attr_reader :bases
|
|
39
|
+
|
|
40
|
+
# Initialize matrix
|
|
41
|
+
#
|
|
42
|
+
# @param present_indices [Array<Integer>] Indices of available data blocks
|
|
43
|
+
# @param missing_indices [Array<Integer>] Indices of blocks to recover
|
|
44
|
+
# @param recovery_exponents [Array<Integer>] Exponents of recovery blocks to use
|
|
45
|
+
# @param total_inputs [Integer] Total number of input blocks (present + missing)
|
|
46
|
+
# @param block_size [Integer] Block size in bytes
|
|
47
|
+
def initialize(present_indices, missing_indices, recovery_exponents,
|
|
48
|
+
total_inputs, block_size)
|
|
49
|
+
@present_indices = present_indices.sort
|
|
50
|
+
@missing_indices = missing_indices.sort
|
|
51
|
+
@recovery_exponents = recovery_exponents.sort
|
|
52
|
+
@total_inputs = total_inputs
|
|
53
|
+
@block_size = block_size
|
|
54
|
+
@matrix = nil # Computed by compute!
|
|
55
|
+
@bases = nil # Computed by compute!
|
|
56
|
+
@used_recovery_exponents = nil # Computed by compute!
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# Compute matrix coefficients using Gaussian elimination
|
|
60
|
+
#
|
|
61
|
+
# CORRECT FORMULATION:
|
|
62
|
+
# We solve: A * x = b
|
|
63
|
+
# where:
|
|
64
|
+
# - A[i,j] = base[missing[j]]^exponent[i] (num_missing x num_missing)
|
|
65
|
+
# - x[j] = missing_block[j] (what we solve for)
|
|
66
|
+
# - b[i] = recovery[i] - sum(present[k] * base[present[k]]^exponent[i])
|
|
67
|
+
#
|
|
68
|
+
# This method computes A^-1, so we can later compute:
|
|
69
|
+
# x = A^-1 * b
|
|
70
|
+
#
|
|
71
|
+
# @return [void]
|
|
72
|
+
def compute!
|
|
73
|
+
@bases = Par2cmdlineAlgorithm.compute_bases(total_inputs)
|
|
74
|
+
|
|
75
|
+
num_missing = missing_indices.size
|
|
76
|
+
|
|
77
|
+
# Select which recovery exponents to use (first num_missing)
|
|
78
|
+
@used_recovery_exponents = recovery_exponents.first(num_missing)
|
|
79
|
+
|
|
80
|
+
# Build A matrix: A[i,j] = base[missing[j]]^exponent[i]
|
|
81
|
+
# This is num_missing x num_missing (SQUARE)
|
|
82
|
+
a_matrix = Array.new(num_missing) { Array.new(num_missing, 0) }
|
|
83
|
+
|
|
84
|
+
@used_recovery_exponents.each_with_index do |exponent, row|
|
|
85
|
+
missing_indices.each_with_index do |idx, col|
|
|
86
|
+
a_matrix[row][col] = Galois16.power(@bases[idx], exponent)
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
# Invert A matrix using Gaussian elimination
|
|
91
|
+
# Create augmented matrix [A | I]
|
|
92
|
+
identity = Array.new(num_missing) do |i|
|
|
93
|
+
Array.new(num_missing) do |j|
|
|
94
|
+
i == j ? 1 : 0
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
gaussian_elimination!(a_matrix, identity)
|
|
99
|
+
|
|
100
|
+
# Store inverted matrix (now in identity position)
|
|
101
|
+
# IMPORTANT: Transpose it so rows=missing_indices, cols=recovery_indices
|
|
102
|
+
# This allows direct indexing: matrix[output_idx][recovery_idx]
|
|
103
|
+
@matrix = identity.transpose
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
# Get matrix coefficient for computing missing blocks
|
|
107
|
+
#
|
|
108
|
+
# This returns the coefficient from A^-1 matrix that tells us how much
|
|
109
|
+
# each recovery block contributes to each missing block.
|
|
110
|
+
#
|
|
111
|
+
# After transposition, matrix structure is:
|
|
112
|
+
# - matrix[recovery_idx][output_idx] (rows=recovery, cols=missing)
|
|
113
|
+
#
|
|
114
|
+
# For x = A^-1 * b:
|
|
115
|
+
# x[output_idx] = sum over recovery_idx of A^-1[output_idx][recovery_idx] * b[recovery_idx]
|
|
116
|
+
#
|
|
117
|
+
# Due to transpose, we access as: matrix[recovery_idx][output_idx]
|
|
118
|
+
#
|
|
119
|
+
# @param output_idx [Integer] Output block index (0..missing_count-1)
|
|
120
|
+
# @param recovery_idx [Integer] Recovery block index (0..recovery_count-1)
|
|
121
|
+
# @return [Integer] Galois field coefficient
|
|
122
|
+
def coefficient(output_idx, recovery_idx)
|
|
123
|
+
raise "Matrix not computed - call compute! first" unless @matrix
|
|
124
|
+
|
|
125
|
+
# After transpose, indices are swapped: @matrix[recovery_idx][output_idx]
|
|
126
|
+
@matrix[recovery_idx][output_idx]
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
# Get coefficient for present block contribution to b vector
|
|
130
|
+
#
|
|
131
|
+
# Returns how much a present block contributes when building b vector.
|
|
132
|
+
# This is: -base[present_idx]^exponent[recovery_idx]
|
|
133
|
+
#
|
|
134
|
+
# @param present_idx [Integer] Index of present data block
|
|
135
|
+
# @param recovery_exponent [Integer] Recovery block exponent
|
|
136
|
+
# @return [Integer] Galois field coefficient
|
|
137
|
+
def present_contribution_coefficient(present_idx, recovery_exponent)
|
|
138
|
+
Galois16.power(@bases[present_idx], recovery_exponent)
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
# Process a chunk of data: output_chunk ^= input_chunk * factor
|
|
142
|
+
#
|
|
143
|
+
# This is called thousands of times during repair to incrementally
|
|
144
|
+
# build up each recovered block chunk by chunk.
|
|
145
|
+
#
|
|
146
|
+
# @param factor [Integer] Galois field multiplier (from matrix)
|
|
147
|
+
# @param input_chunk [String] Input chunk data
|
|
148
|
+
# @param output_block [String] Full output block (modified in place)
|
|
149
|
+
# @param chunk_size [Integer] Chunk size in bytes (must be even)
|
|
150
|
+
# @param output_offset [Integer] Offset within output block where to write
|
|
151
|
+
def process_chunk(factor, input_chunk, output_block, chunk_size,
|
|
152
|
+
output_offset: 0)
|
|
153
|
+
return if factor.zero? # Optimization
|
|
154
|
+
|
|
155
|
+
num_words = chunk_size / 2
|
|
156
|
+
|
|
157
|
+
num_words.times do |i|
|
|
158
|
+
input_offset = i * 2
|
|
159
|
+
block_offset = output_offset + (i * 2)
|
|
160
|
+
|
|
161
|
+
# Read 16-bit words (little-endian)
|
|
162
|
+
input_word = input_chunk.getbyte(input_offset) |
|
|
163
|
+
(input_chunk.getbyte(input_offset + 1) << 8)
|
|
164
|
+
|
|
165
|
+
output_word = output_block.getbyte(block_offset) |
|
|
166
|
+
(output_block.getbyte(block_offset + 1) << 8)
|
|
167
|
+
|
|
168
|
+
# Galois multiplication and addition (XOR)
|
|
169
|
+
result = Galois16.add(output_word,
|
|
170
|
+
Galois16.multiply(input_word, factor))
|
|
171
|
+
|
|
172
|
+
# Write back as bytes (little-endian)
|
|
173
|
+
output_block.setbyte(block_offset, result & 0xFF)
|
|
174
|
+
output_block.setbyte(block_offset + 1, (result >> 8) & 0xFF)
|
|
175
|
+
end
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
# Get number of recovery blocks used
|
|
179
|
+
#
|
|
180
|
+
# @return [Integer] Recovery count
|
|
181
|
+
def recovery_count
|
|
182
|
+
recovery_exponents.size
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
# Get number of output blocks (missing)
|
|
186
|
+
#
|
|
187
|
+
# @return [Integer] Output count
|
|
188
|
+
def output_count
|
|
189
|
+
missing_indices.size
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
private
|
|
193
|
+
|
|
194
|
+
# Perform Gaussian elimination to invert matrix
|
|
195
|
+
#
|
|
196
|
+
# Transforms [A | I] into [I | A^-1]
|
|
197
|
+
#
|
|
198
|
+
# @param left_matrix [Array<Array<Integer>>] Matrix to invert (modified)
|
|
199
|
+
# @param right_matrix [Array<Array<Integer>>] Identity matrix (becomes inverse)
|
|
200
|
+
def gaussian_elimination!(left_matrix, right_matrix)
|
|
201
|
+
num_rows = left_matrix.size
|
|
202
|
+
num_cols = left_matrix[0].size
|
|
203
|
+
|
|
204
|
+
num_rows.times do |pivot_row|
|
|
205
|
+
pivot = left_matrix[pivot_row][pivot_row]
|
|
206
|
+
raise "Singular matrix at row #{pivot_row}" if pivot.zero?
|
|
207
|
+
|
|
208
|
+
# Scale pivot row to make pivot = 1
|
|
209
|
+
unless pivot == 1
|
|
210
|
+
num_cols.times do |col|
|
|
211
|
+
next if left_matrix[pivot_row][col].zero?
|
|
212
|
+
|
|
213
|
+
left_matrix[pivot_row][col] =
|
|
214
|
+
Galois16.divide(left_matrix[pivot_row][col], pivot)
|
|
215
|
+
end
|
|
216
|
+
|
|
217
|
+
num_cols.times do |col|
|
|
218
|
+
next if right_matrix[pivot_row][col].zero?
|
|
219
|
+
|
|
220
|
+
right_matrix[pivot_row][col] =
|
|
221
|
+
Galois16.divide(right_matrix[pivot_row][col], pivot)
|
|
222
|
+
end
|
|
223
|
+
end
|
|
224
|
+
|
|
225
|
+
# Eliminate column in all other rows
|
|
226
|
+
num_rows.times do |row|
|
|
227
|
+
next if row == pivot_row
|
|
228
|
+
|
|
229
|
+
scale = left_matrix[row][pivot_row]
|
|
230
|
+
next if scale.zero?
|
|
231
|
+
|
|
232
|
+
if scale == 1
|
|
233
|
+
num_cols.times do |col|
|
|
234
|
+
next if left_matrix[pivot_row][col].zero?
|
|
235
|
+
|
|
236
|
+
left_matrix[row][col] = Galois16.add(
|
|
237
|
+
left_matrix[row][col],
|
|
238
|
+
left_matrix[pivot_row][col],
|
|
239
|
+
)
|
|
240
|
+
end
|
|
241
|
+
|
|
242
|
+
num_cols.times do |col|
|
|
243
|
+
next if right_matrix[pivot_row][col].zero?
|
|
244
|
+
|
|
245
|
+
right_matrix[row][col] = Galois16.add(
|
|
246
|
+
right_matrix[row][col],
|
|
247
|
+
right_matrix[pivot_row][col],
|
|
248
|
+
)
|
|
249
|
+
end
|
|
250
|
+
else
|
|
251
|
+
num_cols.times do |col|
|
|
252
|
+
next if left_matrix[pivot_row][col].zero?
|
|
253
|
+
|
|
254
|
+
scaled = Galois16.multiply(left_matrix[pivot_row][col], scale)
|
|
255
|
+
left_matrix[row][col] =
|
|
256
|
+
Galois16.add(left_matrix[row][col], scaled)
|
|
257
|
+
end
|
|
258
|
+
|
|
259
|
+
num_cols.times do |col|
|
|
260
|
+
next if right_matrix[pivot_row][col].zero?
|
|
261
|
+
|
|
262
|
+
scaled = Galois16.multiply(right_matrix[pivot_row][col], scale)
|
|
263
|
+
right_matrix[row][col] =
|
|
264
|
+
Galois16.add(right_matrix[row][col], scaled)
|
|
265
|
+
end
|
|
266
|
+
end
|
|
267
|
+
end
|
|
268
|
+
end
|
|
269
|
+
end
|
|
270
|
+
|
|
271
|
+
# Verify that A * A^-1 = Identity
|
|
272
|
+
# @param a_original [Array<Array<Integer>>] Original A matrix
|
|
273
|
+
# @param a_inv [Array<Array<Integer>>] Computed A^-1 matrix
|
|
274
|
+
# @return [Boolean] true if verification passes
|
|
275
|
+
def verify_matrix_inversion(a_original, a_inv)
|
|
276
|
+
n = a_original.size
|
|
277
|
+
|
|
278
|
+
# Compute A * A^-1
|
|
279
|
+
result = Array.new(n) { Array.new(n, 0) }
|
|
280
|
+
n.times do |i|
|
|
281
|
+
n.times do |j|
|
|
282
|
+
sum = 0
|
|
283
|
+
n.times do |k|
|
|
284
|
+
product = Galois16.multiply(a_original[i][k], a_inv[k][j])
|
|
285
|
+
sum = Galois16.add(sum, product)
|
|
286
|
+
end
|
|
287
|
+
result[i][j] = sum
|
|
288
|
+
end
|
|
289
|
+
end
|
|
290
|
+
|
|
291
|
+
# Check if result is identity
|
|
292
|
+
n.times do |i|
|
|
293
|
+
n.times do |j|
|
|
294
|
+
expected = i == j ? 1 : 0
|
|
295
|
+
return false if result[i][j] != expected
|
|
296
|
+
end
|
|
297
|
+
end
|
|
298
|
+
|
|
299
|
+
true
|
|
300
|
+
end
|
|
301
|
+
end
|
|
302
|
+
end
|
|
303
|
+
|
|
304
|
+
# Verify that A * A^-1 = Identity
|
|
305
|
+
#
|
|
306
|
+
# @param a_original [Array<Array<Integer>>] Original A matrix
|
|
307
|
+
# @param a_inv [Array<Array<Integer>>] Computed A^-1 matrix
|
|
308
|
+
# @return [Boolean] true if verification passes
|
|
309
|
+
def self.verify_matrix_inversion(a_original, a_inv)
|
|
310
|
+
n = a_original.size
|
|
311
|
+
|
|
312
|
+
# Compute A * A^-1
|
|
313
|
+
result = Array.new(n) { Array.new(n, 0) }
|
|
314
|
+
n.times do |i|
|
|
315
|
+
n.times do |j|
|
|
316
|
+
sum = 0
|
|
317
|
+
n.times do |k|
|
|
318
|
+
product = Galois16.multiply(a_original[i][k], a_inv[k][j])
|
|
319
|
+
sum = Galois16.add(sum, product)
|
|
320
|
+
end
|
|
321
|
+
result[i][j] = sum
|
|
322
|
+
end
|
|
323
|
+
end
|
|
324
|
+
|
|
325
|
+
# Check if result is identity
|
|
326
|
+
n.times do |i|
|
|
327
|
+
n.times do |j|
|
|
328
|
+
expected = i == j ? 1 : 0
|
|
329
|
+
if result[i][j] != expected
|
|
330
|
+
warn "MATRIX VERIFICATION FAILED!"
|
|
331
|
+
warn " A * A^-1 at [#{i},#{j}] = 0x#{format('%04X',
|
|
332
|
+
result[i][j])} (expected #{expected})"
|
|
333
|
+
warn " This means Gaussian elimination produced wrong inverse!"
|
|
334
|
+
return false
|
|
335
|
+
end
|
|
336
|
+
end
|
|
337
|
+
end
|
|
338
|
+
|
|
339
|
+
warn "Matrix verification: A * A^-1 = I ✓"
|
|
340
|
+
true
|
|
341
|
+
end
|
|
342
|
+
end
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "parity/par2cmdline_coefficients"
|
|
4
|
+
require_relative "parity/par2_creator"
|
|
5
|
+
require_relative "parity/par2_verifier"
|
|
6
|
+
require_relative "parity/par2_repairer"
|
|
7
|
+
|
|
8
|
+
module Omnizip
|
|
9
|
+
# PAR2 parity archive support
|
|
10
|
+
#
|
|
11
|
+
# Provides creation, verification, and repair of PAR2 parity files
|
|
12
|
+
# for protecting archives and data files against corruption.
|
|
13
|
+
#
|
|
14
|
+
# PAR2 uses Reed-Solomon error correction codes to create recovery
|
|
15
|
+
# data that can reconstruct missing or corrupted blocks.
|
|
16
|
+
#
|
|
17
|
+
# @example Create PAR2 protection
|
|
18
|
+
# Omnizip::Parity.create('archive.zip', redundancy: 10)
|
|
19
|
+
#
|
|
20
|
+
# @example Verify and repair
|
|
21
|
+
# result = Omnizip::Parity.verify('archive.par2')
|
|
22
|
+
# Omnizip::Parity.repair('archive.par2') if result.repairable?
|
|
23
|
+
module Parity
|
|
24
|
+
class << self
|
|
25
|
+
# Create PAR2 recovery files for archive or files
|
|
26
|
+
#
|
|
27
|
+
# @param file_or_pattern [String] File path or glob pattern
|
|
28
|
+
# @param redundancy [Integer] Redundancy percentage (0-100)
|
|
29
|
+
# @param block_size [Integer] Block size in bytes
|
|
30
|
+
# @param output_dir [String, nil] Output directory for PAR2 files
|
|
31
|
+
# @param progress [Proc, nil] Progress callback
|
|
32
|
+
# @return [Array<String>] Created PAR2 file paths
|
|
33
|
+
#
|
|
34
|
+
# @example Create with 10% redundancy
|
|
35
|
+
# Omnizip::Parity.create('backup.7z', redundancy: 10)
|
|
36
|
+
#
|
|
37
|
+
# @example Create for multiple files
|
|
38
|
+
# Omnizip::Parity.create('data/*.dat', redundancy: 5)
|
|
39
|
+
#
|
|
40
|
+
# @example With progress tracking
|
|
41
|
+
# Omnizip::Parity.create('large.zip',
|
|
42
|
+
# redundancy: 10,
|
|
43
|
+
# progress: ->(pct, msg) { puts "#{pct}%: #{msg}" }
|
|
44
|
+
# )
|
|
45
|
+
def create(file_or_pattern, redundancy: 5, block_size: Par2Creator::DEFAULT_BLOCK_SIZE,
|
|
46
|
+
output_dir: nil, progress: nil)
|
|
47
|
+
# Try glob expansion first (handles both patterns and single files)
|
|
48
|
+
files = Dir.glob(file_or_pattern)
|
|
49
|
+
|
|
50
|
+
# If glob returns nothing, check if it's a single existing file
|
|
51
|
+
if files.empty? && File.exist?(file_or_pattern) && !File.directory?(file_or_pattern)
|
|
52
|
+
files = [file_or_pattern]
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
if files.empty?
|
|
56
|
+
raise ArgumentError,
|
|
57
|
+
"No files match pattern: #{file_or_pattern}"
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# Create PAR2 creator
|
|
61
|
+
creator = Par2Creator.new(
|
|
62
|
+
redundancy: redundancy,
|
|
63
|
+
block_size: block_size,
|
|
64
|
+
progress: progress,
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
# Add all files
|
|
68
|
+
files.each { |file| creator.add_file(file) }
|
|
69
|
+
|
|
70
|
+
# Determine output base name
|
|
71
|
+
dir = output_dir || File.dirname(files.first)
|
|
72
|
+
base_name = if files.size == 1
|
|
73
|
+
# Use file's directory and base name without extension
|
|
74
|
+
File.join(dir, File.basename(files.first, ".*"))
|
|
75
|
+
else
|
|
76
|
+
# Use files' directory name for multiple files
|
|
77
|
+
File.join(dir,
|
|
78
|
+
File.basename(File.dirname(files.first)))
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
# Create PAR2 files
|
|
82
|
+
creator.create(base_name)
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
# Verify files using PAR2 recovery data
|
|
86
|
+
#
|
|
87
|
+
# @param par2_file [String] Path to .par2 index file
|
|
88
|
+
# @return [Par2Verifier::VerificationResult] Verification results
|
|
89
|
+
#
|
|
90
|
+
# @example Verify archive integrity
|
|
91
|
+
# result = Omnizip::Parity.verify('backup.par2')
|
|
92
|
+
# if result.all_ok?
|
|
93
|
+
# puts "All files intact"
|
|
94
|
+
# elsif result.repairable?
|
|
95
|
+
# puts "Damage detected but repairable"
|
|
96
|
+
# else
|
|
97
|
+
# puts "Damage cannot be repaired"
|
|
98
|
+
# end
|
|
99
|
+
def verify(par2_file)
|
|
100
|
+
verifier = Par2Verifier.new(par2_file)
|
|
101
|
+
verifier.verify
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
# Repair damaged files using PAR2 recovery data
|
|
105
|
+
#
|
|
106
|
+
# @param par2_file [String] Path to .par2 index file
|
|
107
|
+
# @param output_dir [String, nil] Output directory for repaired files
|
|
108
|
+
# @param progress [Proc, nil] Progress callback
|
|
109
|
+
# @return [Par2Repairer::RepairResult] Repair results
|
|
110
|
+
#
|
|
111
|
+
# @example Repair damaged archive
|
|
112
|
+
# result = Omnizip::Parity.repair('backup.par2')
|
|
113
|
+
# if result.success?
|
|
114
|
+
# puts "Successfully recovered #{result.recovered_files.join(', ')}"
|
|
115
|
+
# else
|
|
116
|
+
# puts "Repair failed: #{result.error_message}"
|
|
117
|
+
# end
|
|
118
|
+
def repair(par2_file, output_dir: nil, progress: nil)
|
|
119
|
+
repairer = Par2Repairer.new(par2_file, progress: progress)
|
|
120
|
+
repairer.repair(output_dir: output_dir)
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
# Quick check if PAR2 files exist for a file
|
|
124
|
+
#
|
|
125
|
+
# @param file_path [String] Path to protected file
|
|
126
|
+
# @return [Boolean] true if PAR2 files exist
|
|
127
|
+
#
|
|
128
|
+
# @example Check for protection
|
|
129
|
+
# if Omnizip::Parity.protected?('backup.zip')
|
|
130
|
+
# puts "File is protected by PAR2"
|
|
131
|
+
# end
|
|
132
|
+
def protected?(file_path)
|
|
133
|
+
base_name = File.basename(file_path, ".*")
|
|
134
|
+
dir_name = File.dirname(file_path)
|
|
135
|
+
par2_file = File.join(dir_name, "#{base_name}.par2")
|
|
136
|
+
|
|
137
|
+
File.exist?(par2_file)
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
# Get PAR2 protection information
|
|
141
|
+
#
|
|
142
|
+
# @param file_path [String] Path to protected file
|
|
143
|
+
# @return [Hash, nil] Protection information or nil if not protected
|
|
144
|
+
#
|
|
145
|
+
# @example Get protection info
|
|
146
|
+
# info = Omnizip::Parity.info('backup.zip')
|
|
147
|
+
# puts "Redundancy: #{info[:redundancy]}%"
|
|
148
|
+
# puts "Block size: #{info[:block_size]} bytes"
|
|
149
|
+
def info(file_path)
|
|
150
|
+
base_name = File.basename(file_path, ".*")
|
|
151
|
+
dir_name = File.dirname(file_path)
|
|
152
|
+
par2_file = File.join(dir_name, "#{base_name}.par2")
|
|
153
|
+
|
|
154
|
+
return nil unless File.exist?(par2_file)
|
|
155
|
+
|
|
156
|
+
verifier = Par2Verifier.new(par2_file)
|
|
157
|
+
verifier.send(:parse_par2_file)
|
|
158
|
+
|
|
159
|
+
total_blocks = verifier.send(:calculate_total_blocks)
|
|
160
|
+
recovery_blocks = verifier.instance_variable_get(:@recovery_blocks).size
|
|
161
|
+
|
|
162
|
+
{
|
|
163
|
+
par2_file: par2_file,
|
|
164
|
+
block_size: verifier.metadata[:block_size],
|
|
165
|
+
total_blocks: total_blocks,
|
|
166
|
+
file_count: verifier.metadata[:file_count] || verifier.instance_variable_get(:@file_list).size,
|
|
167
|
+
recovery_blocks: recovery_blocks,
|
|
168
|
+
redundancy: calculate_redundancy(total_blocks, recovery_blocks),
|
|
169
|
+
}
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
private
|
|
173
|
+
|
|
174
|
+
# Calculate redundancy percentage
|
|
175
|
+
#
|
|
176
|
+
# @param total_blocks [Integer] Total data blocks
|
|
177
|
+
# @param recovery_blocks [Integer] Recovery blocks
|
|
178
|
+
# @return [Float] Redundancy percentage
|
|
179
|
+
def calculate_redundancy(total_blocks, recovery_blocks)
|
|
180
|
+
return 0.0 if total_blocks.nil? || total_blocks.zero?
|
|
181
|
+
|
|
182
|
+
(recovery_blocks.to_f / total_blocks * 100).round(2)
|
|
183
|
+
end
|
|
184
|
+
end
|
|
185
|
+
end
|
|
186
|
+
end
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Omnizip
|
|
4
|
+
module Password
|
|
5
|
+
# Registry for encryption strategies
|
|
6
|
+
class EncryptionRegistry
|
|
7
|
+
@strategies = {}
|
|
8
|
+
|
|
9
|
+
class << self
|
|
10
|
+
# Register an encryption strategy
|
|
11
|
+
# @param name [Symbol] Strategy name
|
|
12
|
+
# @param strategy_class [Class] Strategy class
|
|
13
|
+
def register(name, strategy_class)
|
|
14
|
+
@strategies[name] = strategy_class
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
# Get a strategy by name
|
|
18
|
+
# @param name [Symbol] Strategy name
|
|
19
|
+
# @return [Class] Strategy class
|
|
20
|
+
# @raise [ArgumentError] If strategy not found
|
|
21
|
+
def get(name)
|
|
22
|
+
strategy = @strategies[name]
|
|
23
|
+
return strategy if strategy
|
|
24
|
+
|
|
25
|
+
raise ArgumentError, "Unknown encryption strategy: #{name}. " \
|
|
26
|
+
"Available: #{@strategies.keys.join(', ')}"
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# Check if strategy is registered
|
|
30
|
+
# @param name [Symbol] Strategy name
|
|
31
|
+
# @return [Boolean] True if registered
|
|
32
|
+
def registered?(name)
|
|
33
|
+
@strategies.key?(name)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# Get all registered strategy names
|
|
37
|
+
# @return [Array<Symbol>] Strategy names
|
|
38
|
+
def strategies
|
|
39
|
+
@strategies.keys
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# Create a strategy instance
|
|
43
|
+
# @param name [Symbol] Strategy name
|
|
44
|
+
# @param password [String] Password
|
|
45
|
+
# @param options [Hash] Strategy options
|
|
46
|
+
# @return [EncryptionStrategy] Strategy instance
|
|
47
|
+
def create(name, password, **options)
|
|
48
|
+
strategy_class = get(name)
|
|
49
|
+
strategy_class.new(password, **options)
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# Reset registry (for testing)
|
|
53
|
+
def reset
|
|
54
|
+
@strategies = {}
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# Register built-in strategies
|
|
60
|
+
EncryptionRegistry.register(:traditional, ZipCryptoStrategy)
|
|
61
|
+
EncryptionRegistry.register(:zip_crypto, ZipCryptoStrategy)
|
|
62
|
+
EncryptionRegistry.register(:winzip_aes, WinzipAesStrategy)
|
|
63
|
+
EncryptionRegistry.register(:aes256, WinzipAesStrategy)
|
|
64
|
+
end
|
|
65
|
+
end
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Omnizip
|
|
4
|
+
module Password
|
|
5
|
+
# Base class for encryption strategies
|
|
6
|
+
# Defines the interface for encrypting/decrypting archive entries
|
|
7
|
+
class EncryptionStrategy
|
|
8
|
+
attr_reader :password
|
|
9
|
+
|
|
10
|
+
# Initialize encryption strategy
|
|
11
|
+
# @param password [String] Password to use
|
|
12
|
+
def initialize(password)
|
|
13
|
+
@password = password
|
|
14
|
+
validate_password
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
# Encrypt data
|
|
18
|
+
# @param data [String] Data to encrypt
|
|
19
|
+
# @return [String] Encrypted data
|
|
20
|
+
# @raise [NotImplementedError] Subclasses must implement
|
|
21
|
+
def encrypt(data)
|
|
22
|
+
raise NotImplementedError, "#{self.class} must implement #encrypt"
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# Decrypt data
|
|
26
|
+
# @param data [String] Data to decrypt
|
|
27
|
+
# @return [String] Decrypted data
|
|
28
|
+
# @raise [NotImplementedError] Subclasses must implement
|
|
29
|
+
def decrypt(data)
|
|
30
|
+
raise NotImplementedError, "#{self.class} must implement #decrypt"
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# Get encryption method ID for ZIP format
|
|
34
|
+
# @return [Integer] Compression method ID
|
|
35
|
+
# @raise [NotImplementedError] Subclasses must implement
|
|
36
|
+
def compression_method
|
|
37
|
+
raise NotImplementedError,
|
|
38
|
+
"#{self.class} must implement #compression_method"
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# Get extra field data for ZIP header
|
|
42
|
+
# @return [String] Extra field data
|
|
43
|
+
def extra_field_data
|
|
44
|
+
""
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# Get encryption flags for ZIP header
|
|
48
|
+
# @return [Integer] Encryption flags
|
|
49
|
+
def encryption_flags
|
|
50
|
+
0x0001 # Bit 0: encrypted
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# Check if this strategy supports the given data
|
|
54
|
+
# @param data [String] Data to check
|
|
55
|
+
# @return [Boolean] True if supported
|
|
56
|
+
def supports?(_data)
|
|
57
|
+
true
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# Get encryption method name
|
|
61
|
+
# @return [Symbol] Method name
|
|
62
|
+
def method_name
|
|
63
|
+
self.class.name.split("::").last
|
|
64
|
+
.gsub(/Strategy$/, "")
|
|
65
|
+
.gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2')
|
|
66
|
+
.gsub(/([a-z\d])([A-Z])/, '\1_\2')
|
|
67
|
+
.downcase
|
|
68
|
+
.to_sym
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
protected
|
|
72
|
+
|
|
73
|
+
# Validate password
|
|
74
|
+
# @raise [ArgumentError] If password is invalid
|
|
75
|
+
def validate_password
|
|
76
|
+
raise ArgumentError, "Password cannot be nil" if password.nil?
|
|
77
|
+
raise ArgumentError, "Password cannot be empty" if password.empty?
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
# Derive encryption key from password
|
|
81
|
+
# @param salt [String] Salt for key derivation
|
|
82
|
+
# @param iterations [Integer] Number of iterations
|
|
83
|
+
# @return [String] Derived key
|
|
84
|
+
def derive_key(salt, iterations = 1000)
|
|
85
|
+
require "openssl"
|
|
86
|
+
OpenSSL::PKCS5.pbkdf2_hmac(
|
|
87
|
+
password,
|
|
88
|
+
salt,
|
|
89
|
+
iterations,
|
|
90
|
+
32, # 256 bits
|
|
91
|
+
OpenSSL::Digest.new("SHA256"),
|
|
92
|
+
)
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
end
|