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,98 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
#
|
|
4
|
+
# Copyright (C) 2025 Ribose Inc.
|
|
5
|
+
#
|
|
6
|
+
|
|
7
|
+
require_relative "time_estimator"
|
|
8
|
+
require_relative "../models/eta_result"
|
|
9
|
+
|
|
10
|
+
module Omnizip
|
|
11
|
+
module ETA
|
|
12
|
+
# ETA estimator using exponential smoothing.
|
|
13
|
+
#
|
|
14
|
+
# This estimator uses exponential smoothing to give more weight to
|
|
15
|
+
# recent samples while still considering historical data. This provides
|
|
16
|
+
# a good balance between responsiveness and stability.
|
|
17
|
+
#
|
|
18
|
+
# The smoothing factor (alpha) determines how much weight to give to
|
|
19
|
+
# new samples: 0.0 = ignore new data, 1.0 = only use new data.
|
|
20
|
+
class ExponentialSmoothingEstimator < TimeEstimator
|
|
21
|
+
attr_reader :smoothing_factor, :smoothed_rate
|
|
22
|
+
|
|
23
|
+
# Initialize a new exponential smoothing estimator
|
|
24
|
+
#
|
|
25
|
+
# @param smoothing_factor [Float] Alpha value (0.0-1.0), default 0.3
|
|
26
|
+
# @param sample_history [SampleHistory] History of samples
|
|
27
|
+
# @param rate_calculator [RateCalculator] Rate calculator
|
|
28
|
+
def initialize(smoothing_factor: 0.3, **options)
|
|
29
|
+
super(**options)
|
|
30
|
+
@smoothing_factor = smoothing_factor.clamp(0.0, 1.0)
|
|
31
|
+
@smoothed_rate = nil
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# Estimate time remaining using exponential smoothing
|
|
35
|
+
#
|
|
36
|
+
# @param remaining_bytes [Integer] Bytes remaining to process
|
|
37
|
+
# @return [Models::ETAResult] ETA result with confidence interval
|
|
38
|
+
def estimate(remaining_bytes)
|
|
39
|
+
return zero_result if remaining_bytes <= 0
|
|
40
|
+
|
|
41
|
+
unless sufficient_samples?
|
|
42
|
+
return Models::ETAResult.new.tap do |result|
|
|
43
|
+
result.seconds_remaining = 0.0
|
|
44
|
+
result.formatted = "calculating..."
|
|
45
|
+
result.confidence_lower = 0.0
|
|
46
|
+
result.confidence_upper = 0.0
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# Update smoothed rate
|
|
51
|
+
current_rate = rate_calculator.bytes_per_second
|
|
52
|
+
|
|
53
|
+
@smoothed_rate = if @smoothed_rate.nil?
|
|
54
|
+
current_rate
|
|
55
|
+
else
|
|
56
|
+
(smoothing_factor * current_rate) +
|
|
57
|
+
((1.0 - smoothing_factor) * @smoothed_rate)
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# Calculate ETA
|
|
61
|
+
seconds_remaining = if @smoothed_rate.positive?
|
|
62
|
+
remaining_bytes / @smoothed_rate
|
|
63
|
+
else
|
|
64
|
+
Float::INFINITY
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
# Calculate confidence interval
|
|
68
|
+
lower, upper = confidence_interval(seconds_remaining)
|
|
69
|
+
|
|
70
|
+
Models::ETAResult.new.tap do |result|
|
|
71
|
+
result.seconds_remaining = seconds_remaining
|
|
72
|
+
result.formatted = format_time(seconds_remaining)
|
|
73
|
+
result.confidence_lower = lower
|
|
74
|
+
result.confidence_upper = upper
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
# Reset smoothed rate (e.g., when operation changes significantly)
|
|
79
|
+
def reset
|
|
80
|
+
@smoothed_rate = nil
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
private
|
|
84
|
+
|
|
85
|
+
# Return zero result for completed operation
|
|
86
|
+
#
|
|
87
|
+
# @return [Models::ETAResult] Zero result
|
|
88
|
+
def zero_result
|
|
89
|
+
Models::ETAResult.new.tap do |result|
|
|
90
|
+
result.seconds_remaining = 0.0
|
|
91
|
+
result.formatted = "0s"
|
|
92
|
+
result.confidence_lower = 0.0
|
|
93
|
+
result.confidence_upper = 0.0
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
end
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
#
|
|
4
|
+
# Copyright (C) 2025 Ribose Inc.
|
|
5
|
+
#
|
|
6
|
+
|
|
7
|
+
require_relative "time_estimator"
|
|
8
|
+
require_relative "../models/eta_result"
|
|
9
|
+
|
|
10
|
+
module Omnizip
|
|
11
|
+
module ETA
|
|
12
|
+
# ETA estimator using simple moving average.
|
|
13
|
+
#
|
|
14
|
+
# This estimator calculates the average rate over recent samples and
|
|
15
|
+
# uses that to estimate time remaining. Simpler than exponential
|
|
16
|
+
# smoothing but may be less responsive to changes.
|
|
17
|
+
class MovingAverageEstimator < TimeEstimator
|
|
18
|
+
attr_reader :window_size
|
|
19
|
+
|
|
20
|
+
# Initialize a new moving average estimator
|
|
21
|
+
#
|
|
22
|
+
# @param window_size [Integer] Number of recent samples to average
|
|
23
|
+
# @param sample_history [SampleHistory] History of samples
|
|
24
|
+
# @param rate_calculator [RateCalculator] Rate calculator
|
|
25
|
+
def initialize(window_size: 10, **options)
|
|
26
|
+
super(**options)
|
|
27
|
+
@window_size = window_size
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# Estimate time remaining using moving average
|
|
31
|
+
#
|
|
32
|
+
# @param remaining_bytes [Integer] Bytes remaining to process
|
|
33
|
+
# @return [Models::ETAResult] ETA result with confidence interval
|
|
34
|
+
def estimate(remaining_bytes)
|
|
35
|
+
return zero_result if remaining_bytes <= 0
|
|
36
|
+
|
|
37
|
+
unless sufficient_samples?
|
|
38
|
+
return Models::ETAResult.new.tap do |result|
|
|
39
|
+
result.seconds_remaining = 0.0
|
|
40
|
+
result.formatted = "calculating..."
|
|
41
|
+
result.confidence_lower = 0.0
|
|
42
|
+
result.confidence_upper = 0.0
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# Get average rate over recent samples
|
|
47
|
+
avg_rate = calculate_average_rate
|
|
48
|
+
|
|
49
|
+
# Calculate ETA
|
|
50
|
+
seconds_remaining = if avg_rate.positive?
|
|
51
|
+
remaining_bytes / avg_rate
|
|
52
|
+
else
|
|
53
|
+
Float::INFINITY
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
# Calculate confidence interval
|
|
57
|
+
lower, upper = confidence_interval(seconds_remaining)
|
|
58
|
+
|
|
59
|
+
Models::ETAResult.new.tap do |result|
|
|
60
|
+
result.seconds_remaining = seconds_remaining
|
|
61
|
+
result.formatted = format_time(seconds_remaining)
|
|
62
|
+
result.confidence_lower = lower
|
|
63
|
+
result.confidence_upper = upper
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
private
|
|
68
|
+
|
|
69
|
+
# Calculate average rate over recent window
|
|
70
|
+
#
|
|
71
|
+
# @return [Float] Average bytes per second
|
|
72
|
+
def calculate_average_rate
|
|
73
|
+
samples = sample_history.samples
|
|
74
|
+
return 0.0 if samples.size < 2
|
|
75
|
+
|
|
76
|
+
# Get last N samples (or all if less than N)
|
|
77
|
+
recent = samples.last([window_size, samples.size].min)
|
|
78
|
+
return 0.0 if recent.size < 2
|
|
79
|
+
|
|
80
|
+
# Calculate rate between first and last of window
|
|
81
|
+
first = recent.first
|
|
82
|
+
last = recent.last
|
|
83
|
+
last.rate_since(first)
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
# Return zero result for completed operation
|
|
87
|
+
#
|
|
88
|
+
# @return [Models::ETAResult] Zero result
|
|
89
|
+
def zero_result
|
|
90
|
+
Models::ETAResult.new.tap do |result|
|
|
91
|
+
result.seconds_remaining = 0.0
|
|
92
|
+
result.formatted = "0s"
|
|
93
|
+
result.confidence_lower = 0.0
|
|
94
|
+
result.confidence_upper = 0.0
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
end
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
#
|
|
4
|
+
# Copyright (C) 2025 Ribose Inc.
|
|
5
|
+
#
|
|
6
|
+
|
|
7
|
+
module Omnizip
|
|
8
|
+
module ETA
|
|
9
|
+
# Calculates processing rates from sample history.
|
|
10
|
+
#
|
|
11
|
+
# This class computes various rates (bytes/sec, files/sec) with
|
|
12
|
+
# smoothing over a time window to reduce noise from fluctuations.
|
|
13
|
+
class RateCalculator
|
|
14
|
+
attr_reader :sample_history, :window_seconds
|
|
15
|
+
|
|
16
|
+
# Initialize a new rate calculator
|
|
17
|
+
#
|
|
18
|
+
# @param sample_history [SampleHistory] History of samples
|
|
19
|
+
# @param window_seconds [Float] Time window for rate calculation
|
|
20
|
+
def initialize(sample_history:, window_seconds: 10.0)
|
|
21
|
+
@sample_history = sample_history
|
|
22
|
+
@window_seconds = window_seconds
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# Calculate current bytes per second rate
|
|
26
|
+
#
|
|
27
|
+
# @return [Float] Bytes per second over recent window
|
|
28
|
+
def bytes_per_second
|
|
29
|
+
sample_history.recent_rate(window_seconds)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# Calculate current megabytes per second rate
|
|
33
|
+
#
|
|
34
|
+
# @return [Float] Megabytes per second
|
|
35
|
+
def megabytes_per_second
|
|
36
|
+
bytes_per_second / (1024.0 * 1024.0)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# Calculate current files per second rate
|
|
40
|
+
#
|
|
41
|
+
# @return [Float] Files per second over recent window
|
|
42
|
+
def files_per_second
|
|
43
|
+
recent = sample_history.recent_samples(window_seconds)
|
|
44
|
+
return 0.0 if recent.size < 2
|
|
45
|
+
|
|
46
|
+
first = recent.first
|
|
47
|
+
last = recent.last
|
|
48
|
+
|
|
49
|
+
time_diff = last.timestamp - first.timestamp
|
|
50
|
+
return 0.0 if time_diff <= 0
|
|
51
|
+
|
|
52
|
+
files_diff = last.files_processed - first.files_processed
|
|
53
|
+
files_diff / time_diff
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
# Calculate instantaneous rate (last two samples)
|
|
57
|
+
#
|
|
58
|
+
# @return [Float] Bytes per second between last two samples
|
|
59
|
+
def instantaneous_rate
|
|
60
|
+
return 0.0 if sample_history.size < 2
|
|
61
|
+
|
|
62
|
+
samples = sample_history.samples
|
|
63
|
+
last = samples[-1]
|
|
64
|
+
previous = samples[-2]
|
|
65
|
+
|
|
66
|
+
last.rate_since(previous)
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
# Format bytes per second as human-readable string
|
|
70
|
+
#
|
|
71
|
+
# @param rate [Float] Rate in bytes/second
|
|
72
|
+
# @return [String] Formatted rate (e.g., "2.5 MB/s")
|
|
73
|
+
def format_rate(rate = bytes_per_second)
|
|
74
|
+
return "0 B/s" if rate.zero?
|
|
75
|
+
|
|
76
|
+
if rate < 1024
|
|
77
|
+
"#{rate.round(1)} B/s"
|
|
78
|
+
elsif rate < 1024 * 1024
|
|
79
|
+
"#{(rate / 1024.0).round(1)} KB/s"
|
|
80
|
+
elsif rate < 1024 * 1024 * 1024
|
|
81
|
+
"#{(rate / (1024.0 * 1024.0)).round(1)} MB/s"
|
|
82
|
+
else
|
|
83
|
+
"#{(rate / (1024.0 * 1024.0 * 1024.0)).round(1)} GB/s"
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
# Check if rate is stable (low variance)
|
|
88
|
+
#
|
|
89
|
+
# @param threshold [Float] Max coefficient of variation for stability
|
|
90
|
+
# @return [Boolean] true if rate is stable
|
|
91
|
+
def stable?(threshold: 0.2)
|
|
92
|
+
return false if sample_history.size < 5
|
|
93
|
+
|
|
94
|
+
mean_rate = bytes_per_second
|
|
95
|
+
return true if mean_rate.zero? # No data = stable
|
|
96
|
+
|
|
97
|
+
std_dev = sample_history.rate_std_dev
|
|
98
|
+
coefficient_of_variation = std_dev / mean_rate
|
|
99
|
+
|
|
100
|
+
coefficient_of_variation < threshold
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
end
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
#
|
|
4
|
+
# Copyright (C) 2025 Ribose Inc.
|
|
5
|
+
#
|
|
6
|
+
|
|
7
|
+
module Omnizip
|
|
8
|
+
module ETA
|
|
9
|
+
# Stores historical samples for ETA calculation.
|
|
10
|
+
#
|
|
11
|
+
# This class maintains a time-series of progress samples with a limited
|
|
12
|
+
# size to avoid unbounded memory growth. It provides statistics on the
|
|
13
|
+
# samples for rate calculation and trend analysis.
|
|
14
|
+
class SampleHistory
|
|
15
|
+
# Single sample data point
|
|
16
|
+
Sample = Struct.new(:timestamp, :bytes_processed, :files_processed) do
|
|
17
|
+
# Calculate bytes/second rate between two samples
|
|
18
|
+
#
|
|
19
|
+
# @param other [Sample] Earlier sample
|
|
20
|
+
# @return [Float] Bytes per second
|
|
21
|
+
def rate_since(other)
|
|
22
|
+
time_diff = timestamp - other.timestamp
|
|
23
|
+
return 0.0 if time_diff <= 0
|
|
24
|
+
|
|
25
|
+
bytes_diff = bytes_processed - other.bytes_processed
|
|
26
|
+
bytes_diff / time_diff
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
attr_reader :max_size, :samples
|
|
31
|
+
|
|
32
|
+
# Initialize a new sample history
|
|
33
|
+
#
|
|
34
|
+
# @param max_size [Integer] Maximum number of samples to retain
|
|
35
|
+
def initialize(max_size: 100)
|
|
36
|
+
@max_size = max_size
|
|
37
|
+
@samples = []
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# Add a new sample to the history
|
|
41
|
+
#
|
|
42
|
+
# @param bytes_processed [Integer] Total bytes processed so far
|
|
43
|
+
# @param files_processed [Integer] Total files processed so far
|
|
44
|
+
# @param timestamp [Time] Sample timestamp (defaults to now)
|
|
45
|
+
def add_sample(bytes_processed:, files_processed:, timestamp: Time.now)
|
|
46
|
+
sample = Sample.new(timestamp, bytes_processed, files_processed)
|
|
47
|
+
@samples << sample
|
|
48
|
+
|
|
49
|
+
# Trim oldest samples if we exceed max size
|
|
50
|
+
@samples.shift if @samples.size > max_size
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# Get the most recent sample
|
|
54
|
+
#
|
|
55
|
+
# @return [Sample, nil] Most recent sample or nil if empty
|
|
56
|
+
def latest
|
|
57
|
+
@samples.last
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# Get the oldest sample
|
|
61
|
+
#
|
|
62
|
+
# @return [Sample, nil] Oldest sample or nil if empty
|
|
63
|
+
def oldest
|
|
64
|
+
@samples.first
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
# Get samples from a specific time window
|
|
68
|
+
#
|
|
69
|
+
# @param seconds [Float] Number of seconds to look back
|
|
70
|
+
# @return [Array<Sample>] Samples within the time window
|
|
71
|
+
def recent_samples(seconds)
|
|
72
|
+
return [] if @samples.empty?
|
|
73
|
+
|
|
74
|
+
cutoff_time = Time.now - seconds
|
|
75
|
+
@samples.select { |s| s.timestamp >= cutoff_time }
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
# Calculate average rate over all samples
|
|
79
|
+
#
|
|
80
|
+
# @return [Float] Average bytes per second
|
|
81
|
+
def average_rate
|
|
82
|
+
return 0.0 if @samples.size < 2
|
|
83
|
+
|
|
84
|
+
first = @samples.first
|
|
85
|
+
last = @samples.last
|
|
86
|
+
last.rate_since(first)
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
# Calculate average rate over recent time window
|
|
90
|
+
#
|
|
91
|
+
# @param seconds [Float] Time window in seconds
|
|
92
|
+
# @return [Float] Average bytes per second over window
|
|
93
|
+
def recent_rate(seconds = 10.0)
|
|
94
|
+
recent = recent_samples(seconds)
|
|
95
|
+
return 0.0 if recent.size < 2
|
|
96
|
+
|
|
97
|
+
first = recent.first
|
|
98
|
+
last = recent.last
|
|
99
|
+
last.rate_since(first)
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
# Calculate standard deviation of recent rates
|
|
103
|
+
#
|
|
104
|
+
# @param window_size [Integer] Number of samples to use
|
|
105
|
+
# @return [Float] Standard deviation of rates
|
|
106
|
+
def rate_std_dev(window_size = 10)
|
|
107
|
+
return 0.0 if @samples.size < 3
|
|
108
|
+
|
|
109
|
+
recent = @samples.last([window_size, @samples.size].min)
|
|
110
|
+
rates = []
|
|
111
|
+
|
|
112
|
+
1.upto(recent.size - 1) do |i|
|
|
113
|
+
rates << recent[i].rate_since(recent[i - 1])
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
return 0.0 if rates.empty?
|
|
117
|
+
|
|
118
|
+
mean = rates.sum / rates.size
|
|
119
|
+
variance = rates.sum { |r| (r - mean)**2 } / rates.size
|
|
120
|
+
Math.sqrt(variance)
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
# Clear all samples
|
|
124
|
+
def clear
|
|
125
|
+
@samples.clear
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
# Get number of samples
|
|
129
|
+
#
|
|
130
|
+
# @return [Integer] Number of samples stored
|
|
131
|
+
def size
|
|
132
|
+
@samples.size
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
# Check if history is empty
|
|
136
|
+
#
|
|
137
|
+
# @return [Boolean] true if no samples
|
|
138
|
+
def empty?
|
|
139
|
+
@samples.empty?
|
|
140
|
+
end
|
|
141
|
+
end
|
|
142
|
+
end
|
|
143
|
+
end
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
#
|
|
4
|
+
# Copyright (C) 2025 Ribose Inc.
|
|
5
|
+
#
|
|
6
|
+
|
|
7
|
+
module Omnizip
|
|
8
|
+
module ETA
|
|
9
|
+
# Abstract base class for time estimation strategies.
|
|
10
|
+
#
|
|
11
|
+
# This class defines the interface for ETA estimators and provides
|
|
12
|
+
# common functionality. Subclasses implement specific estimation
|
|
13
|
+
# algorithms (exponential smoothing, moving average, etc.).
|
|
14
|
+
class TimeEstimator
|
|
15
|
+
attr_reader :sample_history, :rate_calculator
|
|
16
|
+
|
|
17
|
+
# Initialize a new time estimator
|
|
18
|
+
#
|
|
19
|
+
# @param sample_history [SampleHistory] History of samples
|
|
20
|
+
# @param rate_calculator [RateCalculator] Rate calculator
|
|
21
|
+
def initialize(sample_history: nil, rate_calculator: nil)
|
|
22
|
+
@sample_history = sample_history || SampleHistory.new
|
|
23
|
+
@rate_calculator = rate_calculator ||
|
|
24
|
+
RateCalculator.new(sample_history: @sample_history)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# Add a sample to the history
|
|
28
|
+
#
|
|
29
|
+
# @param bytes_processed [Integer] Total bytes processed
|
|
30
|
+
# @param files_processed [Integer] Total files processed
|
|
31
|
+
# @param timestamp [Time] Sample timestamp
|
|
32
|
+
def add_sample(bytes_processed:, files_processed:, timestamp: Time.now)
|
|
33
|
+
sample_history.add_sample(
|
|
34
|
+
bytes_processed: bytes_processed,
|
|
35
|
+
files_processed: files_processed,
|
|
36
|
+
timestamp: timestamp,
|
|
37
|
+
)
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# Estimate time remaining (to be implemented by subclasses)
|
|
41
|
+
#
|
|
42
|
+
# @param remaining_bytes [Integer] Bytes remaining to process
|
|
43
|
+
# @return [Models::ETAResult] ETA result
|
|
44
|
+
# @raise [NotImplementedError] if not implemented by subclass
|
|
45
|
+
def estimate(remaining_bytes)
|
|
46
|
+
raise NotImplementedError, "#{self.class} must implement #estimate"
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# Format seconds as human-readable string
|
|
50
|
+
#
|
|
51
|
+
# @param seconds [Float] Seconds to format
|
|
52
|
+
# @return [String] Formatted time (e.g., "2m 30s", "1h 15m")
|
|
53
|
+
def format_time(seconds)
|
|
54
|
+
return "0s" if seconds <= 0
|
|
55
|
+
return "∞" if seconds.infinite?
|
|
56
|
+
|
|
57
|
+
hours = (seconds / 3600).floor
|
|
58
|
+
minutes = ((seconds % 3600) / 60).floor
|
|
59
|
+
secs = (seconds % 60).round
|
|
60
|
+
|
|
61
|
+
parts = []
|
|
62
|
+
parts << "#{hours}h" if hours.positive?
|
|
63
|
+
parts << "#{minutes}m" if minutes.positive? || hours.positive?
|
|
64
|
+
parts << "#{secs}s"
|
|
65
|
+
|
|
66
|
+
parts.join(" ")
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
# Calculate confidence interval based on rate variance
|
|
70
|
+
#
|
|
71
|
+
# @param estimated_seconds [Float] Estimated time in seconds
|
|
72
|
+
# @param confidence_level [Float] Confidence level (0.95 = 95%)
|
|
73
|
+
# @return [Array<Float>] [lower_bound, upper_bound] in seconds
|
|
74
|
+
def confidence_interval(estimated_seconds, confidence_level: 0.95)
|
|
75
|
+
return [0.0, 0.0] if sample_history.size < 3
|
|
76
|
+
|
|
77
|
+
# Use standard deviation of rates to calculate confidence interval
|
|
78
|
+
std_dev = sample_history.rate_std_dev
|
|
79
|
+
current_rate = rate_calculator.bytes_per_second
|
|
80
|
+
|
|
81
|
+
return [estimated_seconds, estimated_seconds] if current_rate.zero?
|
|
82
|
+
|
|
83
|
+
# Calculate coefficient of variation
|
|
84
|
+
cv = std_dev / current_rate
|
|
85
|
+
|
|
86
|
+
# Z-score for confidence level (approximation)
|
|
87
|
+
z_score = confidence_level >= 0.99 ? 2.576 : 1.96
|
|
88
|
+
|
|
89
|
+
# Calculate interval as percentage of estimate
|
|
90
|
+
margin = estimated_seconds * cv * z_score
|
|
91
|
+
|
|
92
|
+
lower = [estimated_seconds - margin, 0.0].max
|
|
93
|
+
upper = estimated_seconds + margin
|
|
94
|
+
|
|
95
|
+
[lower, upper]
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
# Check if we have enough samples for reliable estimation
|
|
99
|
+
#
|
|
100
|
+
# @return [Boolean] true if enough samples
|
|
101
|
+
def sufficient_samples?
|
|
102
|
+
sample_history.size >= 3
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
end
|
data/lib/omnizip/eta.rb
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
#
|
|
4
|
+
# Copyright (C) 2025 Ribose Inc.
|
|
5
|
+
#
|
|
6
|
+
|
|
7
|
+
require_relative "eta/sample_history"
|
|
8
|
+
require_relative "eta/rate_calculator"
|
|
9
|
+
require_relative "eta/time_estimator"
|
|
10
|
+
require_relative "eta/exponential_smoothing_estimator"
|
|
11
|
+
require_relative "eta/moving_average_estimator"
|
|
12
|
+
|
|
13
|
+
module Omnizip
|
|
14
|
+
# ETA (Estimated Time to Arrival) calculation module.
|
|
15
|
+
#
|
|
16
|
+
# This module provides time estimation capabilities for long-running
|
|
17
|
+
# operations. It tracks historical progress samples and uses various
|
|
18
|
+
# estimation strategies to predict completion time.
|
|
19
|
+
#
|
|
20
|
+
# @example Basic usage
|
|
21
|
+
# estimator = Omnizip::ETA.create_estimator(:exponential_smoothing)
|
|
22
|
+
# estimator.add_sample(bytes_processed: 1000, files_processed: 10)
|
|
23
|
+
# # ... more samples ...
|
|
24
|
+
# eta = estimator.estimate(remaining_bytes: 5000)
|
|
25
|
+
# puts "ETA: #{eta.formatted}"
|
|
26
|
+
module ETA
|
|
27
|
+
# Create a new time estimator
|
|
28
|
+
#
|
|
29
|
+
# @param strategy [Symbol] Estimation strategy (:exponential_smoothing, :moving_average)
|
|
30
|
+
# @param options [Hash] Strategy-specific options
|
|
31
|
+
# @return [TimeEstimator] Configured estimator
|
|
32
|
+
def self.create_estimator(strategy = :exponential_smoothing, **options)
|
|
33
|
+
case strategy
|
|
34
|
+
when :exponential_smoothing
|
|
35
|
+
ExponentialSmoothingEstimator.new(**options)
|
|
36
|
+
when :moving_average
|
|
37
|
+
MovingAverageEstimator.new(**options)
|
|
38
|
+
else
|
|
39
|
+
raise ArgumentError, "Unknown estimation strategy: #{strategy}"
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# Format seconds as human-readable time string
|
|
44
|
+
#
|
|
45
|
+
# @param seconds [Float] Seconds to format
|
|
46
|
+
# @return [String] Formatted time (e.g., "2m 30s")
|
|
47
|
+
def self.format_time(seconds)
|
|
48
|
+
return "0s" if seconds <= 0
|
|
49
|
+
return "∞" if seconds.infinite?
|
|
50
|
+
|
|
51
|
+
hours = (seconds / 3600).floor
|
|
52
|
+
minutes = ((seconds % 3600) / 60).floor
|
|
53
|
+
secs = (seconds % 60).round
|
|
54
|
+
|
|
55
|
+
parts = []
|
|
56
|
+
parts << "#{hours}h" if hours.positive?
|
|
57
|
+
parts << "#{minutes}m" if minutes.positive? || hours.positive?
|
|
58
|
+
parts << "#{secs}s"
|
|
59
|
+
|
|
60
|
+
parts.join(" ")
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|