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,204 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "models/performance_result"
|
|
4
|
+
require_relative "models/profile_report"
|
|
5
|
+
require_relative "models/optimization_suggestion"
|
|
6
|
+
|
|
7
|
+
module Omnizip
|
|
8
|
+
# Main profiler interface using Strategy pattern for different profiling approaches
|
|
9
|
+
class Profiler
|
|
10
|
+
attr_reader :report, :enabled
|
|
11
|
+
|
|
12
|
+
def initialize(profile_name: "default", enabled: true)
|
|
13
|
+
@profile_name = profile_name
|
|
14
|
+
@enabled = enabled
|
|
15
|
+
@report = Models::ProfileReport.new(profile_name: profile_name)
|
|
16
|
+
@profilers = {}
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
# Register a profiler strategy
|
|
20
|
+
def register_profiler(name, profiler)
|
|
21
|
+
@profilers[name] = profiler
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# Profile a block of code with the specified profiler strategy
|
|
25
|
+
def profile(operation_name, profiler_type: :method, &block)
|
|
26
|
+
return yield unless enabled
|
|
27
|
+
|
|
28
|
+
profiler = @profilers[profiler_type]
|
|
29
|
+
unless profiler
|
|
30
|
+
raise ArgumentError,
|
|
31
|
+
"Unknown profiler type: #{profiler_type}"
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
result = profiler.profile(operation_name, &block)
|
|
35
|
+
@report.add_result(result)
|
|
36
|
+
result
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# Profile a method call with automatic naming
|
|
40
|
+
def profile_method(object, method_name, *args, profiler_type: :method,
|
|
41
|
+
**kwargs)
|
|
42
|
+
operation_name = "#{object.class}##{method_name}"
|
|
43
|
+
profile(operation_name, profiler_type: profiler_type) do
|
|
44
|
+
object.public_send(method_name, *args, **kwargs)
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# Analyze collected results and identify hot paths
|
|
49
|
+
def analyze_hot_paths(threshold_percentage: 10.0)
|
|
50
|
+
total_time = report.total_execution_time
|
|
51
|
+
return [] if total_time.zero?
|
|
52
|
+
|
|
53
|
+
threshold_time = total_time * (threshold_percentage / 100.0)
|
|
54
|
+
|
|
55
|
+
hot_operations = report.results.select do |result|
|
|
56
|
+
result.total_time && result.total_time >= threshold_time
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
hot_operations.each do |op|
|
|
60
|
+
@report.add_hot_path(
|
|
61
|
+
operation: op.operation_name,
|
|
62
|
+
time: op.total_time,
|
|
63
|
+
percentage: (op.total_time / total_time) * 100,
|
|
64
|
+
)
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
hot_operations
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
# Identify performance bottlenecks
|
|
71
|
+
def identify_bottlenecks
|
|
72
|
+
bottlenecks = []
|
|
73
|
+
|
|
74
|
+
# Memory bottlenecks
|
|
75
|
+
memory_intensive = report.memory_intensive_operations(limit: 3)
|
|
76
|
+
memory_intensive.each do |op|
|
|
77
|
+
bottlenecks << {
|
|
78
|
+
type: :memory,
|
|
79
|
+
operation: op.operation_name,
|
|
80
|
+
allocated: op.memory_allocated,
|
|
81
|
+
severity: calculate_memory_severity(op),
|
|
82
|
+
}
|
|
83
|
+
@report.add_bottleneck(bottlenecks.last)
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
# CPU bottlenecks
|
|
87
|
+
cpu_intensive = report.slowest_operations(limit: 3)
|
|
88
|
+
cpu_intensive.each do |op|
|
|
89
|
+
bottlenecks << {
|
|
90
|
+
type: :cpu,
|
|
91
|
+
operation: op.operation_name,
|
|
92
|
+
time: op.total_time,
|
|
93
|
+
severity: calculate_cpu_severity(op),
|
|
94
|
+
}
|
|
95
|
+
@report.add_bottleneck(bottlenecks.last)
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
# GC pressure bottlenecks
|
|
99
|
+
high_gc = report.results.select do |r|
|
|
100
|
+
r.gc_pressure && r.gc_pressure > 1.0
|
|
101
|
+
end
|
|
102
|
+
high_gc.each do |op|
|
|
103
|
+
bottlenecks << {
|
|
104
|
+
type: :gc,
|
|
105
|
+
operation: op.operation_name,
|
|
106
|
+
gc_pressure: op.gc_pressure,
|
|
107
|
+
severity: :high,
|
|
108
|
+
}
|
|
109
|
+
@report.add_bottleneck(bottlenecks.last)
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
bottlenecks
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
# Generate optimization suggestions based on profiling data
|
|
116
|
+
def generate_suggestions
|
|
117
|
+
# Analyze hot paths for optimization opportunities
|
|
118
|
+
suggestions = analyze_hot_paths.map do |hot_op|
|
|
119
|
+
Models::OptimizationSuggestion.new(
|
|
120
|
+
title: "Optimize hot path: #{hot_op.operation_name}",
|
|
121
|
+
description: "Operation consuming #{hot_op.total_time}s " \
|
|
122
|
+
"(#{((hot_op.total_time / report.total_execution_time) * 100).round(1)}% of total time)",
|
|
123
|
+
severity: :high,
|
|
124
|
+
category: :hotpath,
|
|
125
|
+
impact_estimate: hot_op.total_time / report.total_execution_time,
|
|
126
|
+
related_operations: [hot_op.operation_name],
|
|
127
|
+
metrics: { time: hot_op.total_time },
|
|
128
|
+
)
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
# Analyze memory usage
|
|
132
|
+
report.memory_intensive_operations(limit: 3).each do |mem_op|
|
|
133
|
+
next unless mem_op.memory_allocated > 1_000_000 # > 1MB
|
|
134
|
+
|
|
135
|
+
suggestions << Models::OptimizationSuggestion.new(
|
|
136
|
+
title: "Reduce memory allocation: #{mem_op.operation_name}",
|
|
137
|
+
description: "Operation allocating #{format_bytes(mem_op.memory_allocated)}",
|
|
138
|
+
severity: calculate_memory_severity(mem_op),
|
|
139
|
+
category: :memory,
|
|
140
|
+
impact_estimate: mem_op.memory_allocated / report.total_memory_allocated.to_f,
|
|
141
|
+
related_operations: [mem_op.operation_name],
|
|
142
|
+
metrics: { memory_allocated: mem_op.memory_allocated },
|
|
143
|
+
)
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
suggestions.sort_by(&:priority_score).reverse
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
# Enable profiling
|
|
150
|
+
def enable!
|
|
151
|
+
@enabled = true
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
# Disable profiling
|
|
155
|
+
def disable!
|
|
156
|
+
@enabled = false
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
# Reset profiler state
|
|
160
|
+
def reset!
|
|
161
|
+
@report = Models::ProfileReport.new(profile_name: @profile_name)
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
private
|
|
165
|
+
|
|
166
|
+
def calculate_memory_severity(operation)
|
|
167
|
+
return :low unless operation.memory_allocated
|
|
168
|
+
|
|
169
|
+
mb = operation.memory_allocated / (1024.0 * 1024.0)
|
|
170
|
+
case mb
|
|
171
|
+
when 0...1 then :low
|
|
172
|
+
when 1...10 then :medium
|
|
173
|
+
when 10...50 then :high
|
|
174
|
+
else :critical
|
|
175
|
+
end
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
def calculate_cpu_severity(operation)
|
|
179
|
+
return :low unless operation.total_time
|
|
180
|
+
|
|
181
|
+
case operation.total_time
|
|
182
|
+
when 0...0.1 then :low
|
|
183
|
+
when 0.1...1.0 then :medium
|
|
184
|
+
when 1.0...5.0 then :high
|
|
185
|
+
else :critical
|
|
186
|
+
end
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
def format_bytes(bytes)
|
|
190
|
+
return "0 B" if bytes.zero?
|
|
191
|
+
|
|
192
|
+
units = %w[B KB MB GB]
|
|
193
|
+
size = bytes.to_f
|
|
194
|
+
unit_index = 0
|
|
195
|
+
|
|
196
|
+
while size >= 1024.0 && unit_index < units.size - 1
|
|
197
|
+
size /= 1024.0
|
|
198
|
+
unit_index += 1
|
|
199
|
+
end
|
|
200
|
+
|
|
201
|
+
format("%.2f %s", size, units[unit_index])
|
|
202
|
+
end
|
|
203
|
+
end
|
|
204
|
+
end
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
#
|
|
4
|
+
# Copyright (C) 2025 Ribose Inc.
|
|
5
|
+
#
|
|
6
|
+
|
|
7
|
+
require_relative "progress_reporter"
|
|
8
|
+
|
|
9
|
+
module Omnizip
|
|
10
|
+
module Progress
|
|
11
|
+
# Progress reporter that calls a user-provided Ruby block.
|
|
12
|
+
#
|
|
13
|
+
# This reporter allows users to provide custom callbacks for
|
|
14
|
+
# progress updates, enabling integration with web frameworks,
|
|
15
|
+
# GUI applications, or custom logging systems.
|
|
16
|
+
class CallbackReporter < ProgressReporter
|
|
17
|
+
attr_reader :callback
|
|
18
|
+
|
|
19
|
+
# Initialize a new callback reporter
|
|
20
|
+
#
|
|
21
|
+
# @param callback [Proc] Block to call with progress updates
|
|
22
|
+
# @yield [progress] Yields progress tracker to the block
|
|
23
|
+
def initialize(&callback)
|
|
24
|
+
super()
|
|
25
|
+
@callback = callback || proc { |_| }
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# Report progress by calling the callback
|
|
29
|
+
#
|
|
30
|
+
# @param progress [ProgressTracker] Progress tracker with current state
|
|
31
|
+
def report(progress)
|
|
32
|
+
callback.call(progress)
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
#
|
|
4
|
+
# Copyright (C) 2025 Ribose Inc.
|
|
5
|
+
#
|
|
6
|
+
|
|
7
|
+
require_relative "progress_reporter"
|
|
8
|
+
require_relative "progress_bar"
|
|
9
|
+
|
|
10
|
+
module Omnizip
|
|
11
|
+
module Progress
|
|
12
|
+
# Progress reporter that displays a progress bar in the console.
|
|
13
|
+
#
|
|
14
|
+
# This reporter uses ProgressBar to render a visual progress bar
|
|
15
|
+
# in the terminal, with automatic TTY detection and color support.
|
|
16
|
+
class ConsoleReporter < ProgressReporter
|
|
17
|
+
attr_reader :progress_bar, :output
|
|
18
|
+
|
|
19
|
+
# Initialize a new console reporter
|
|
20
|
+
#
|
|
21
|
+
# @param output [IO] Output stream (default: $stdout)
|
|
22
|
+
# @param width [Integer] Bar width (auto-detect if nil)
|
|
23
|
+
# @param use_color [Boolean] Enable color output
|
|
24
|
+
def initialize(output: $stdout, width: nil, use_color: true)
|
|
25
|
+
super()
|
|
26
|
+
@output = output
|
|
27
|
+
@progress_bar = ProgressBar.new(width: width, use_color: use_color)
|
|
28
|
+
@started = false
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# Report progress to console
|
|
32
|
+
#
|
|
33
|
+
# @param progress [ProgressTracker] Progress tracker with current state
|
|
34
|
+
def report(progress)
|
|
35
|
+
return unless output.tty?
|
|
36
|
+
|
|
37
|
+
@started = true
|
|
38
|
+
output.print progress_bar.render(progress)
|
|
39
|
+
output.flush
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# Called when operation starts
|
|
43
|
+
#
|
|
44
|
+
# @param _progress [ProgressTracker] Progress tracker
|
|
45
|
+
def start(_progress)
|
|
46
|
+
@started = false
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# Called when operation completes
|
|
50
|
+
#
|
|
51
|
+
# @param _progress [ProgressTracker] Progress tracker
|
|
52
|
+
def finish(_progress)
|
|
53
|
+
return unless output.tty? && @started
|
|
54
|
+
|
|
55
|
+
# Clear the progress bar and print newline
|
|
56
|
+
output.print progress_bar.clear
|
|
57
|
+
output.puts
|
|
58
|
+
output.flush
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
#
|
|
4
|
+
# Copyright (C) 2025 Ribose Inc.
|
|
5
|
+
#
|
|
6
|
+
|
|
7
|
+
require_relative "progress_reporter"
|
|
8
|
+
|
|
9
|
+
module Omnizip
|
|
10
|
+
module Progress
|
|
11
|
+
# Progress reporter that writes to a log file.
|
|
12
|
+
#
|
|
13
|
+
# This reporter writes timestamped progress updates to a log file,
|
|
14
|
+
# useful for debugging, auditing, or monitoring long-running operations.
|
|
15
|
+
class LogReporter < ProgressReporter
|
|
16
|
+
attr_reader :log_file, :verbose
|
|
17
|
+
|
|
18
|
+
# Initialize a new log reporter
|
|
19
|
+
#
|
|
20
|
+
# @param log_file [String, IO] Log file path or IO object
|
|
21
|
+
# @param verbose [Boolean] Include detailed information
|
|
22
|
+
def initialize(log_file:, verbose: false)
|
|
23
|
+
super()
|
|
24
|
+
@log_file = log_file.is_a?(String) ? File.open(log_file, "a") : log_file
|
|
25
|
+
@verbose = verbose
|
|
26
|
+
@owns_file = log_file.is_a?(String)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# Report progress to log file
|
|
30
|
+
#
|
|
31
|
+
# @param progress [ProgressTracker] Progress tracker with current state
|
|
32
|
+
def report(progress)
|
|
33
|
+
timestamp = Time.now.strftime("%Y-%m-%d %H:%M:%S")
|
|
34
|
+
|
|
35
|
+
if verbose
|
|
36
|
+
log_file.puts format(
|
|
37
|
+
"[%s] Progress: %.1f%% (%d/%d files, %d/%d bytes) - %s - %s - ETA: %s",
|
|
38
|
+
timestamp,
|
|
39
|
+
progress.percentage,
|
|
40
|
+
progress.files_processed,
|
|
41
|
+
progress.operation_progress.total_files,
|
|
42
|
+
progress.bytes_processed,
|
|
43
|
+
progress.operation_progress.total_bytes,
|
|
44
|
+
progress.current_file || "unknown",
|
|
45
|
+
progress.rate_formatted,
|
|
46
|
+
progress.eta_formatted,
|
|
47
|
+
)
|
|
48
|
+
else
|
|
49
|
+
log_file.puts format(
|
|
50
|
+
"[%s] Progress: %.1f%% - %s",
|
|
51
|
+
timestamp,
|
|
52
|
+
progress.percentage,
|
|
53
|
+
progress.current_file || "processing",
|
|
54
|
+
)
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
log_file.flush
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# Called when operation starts
|
|
61
|
+
#
|
|
62
|
+
# @param progress [ProgressTracker] Progress tracker
|
|
63
|
+
def start(progress)
|
|
64
|
+
timestamp = Time.now.strftime("%Y-%m-%d %H:%M:%S")
|
|
65
|
+
log_file.puts format(
|
|
66
|
+
"[%s] Started: %d files, %d bytes",
|
|
67
|
+
timestamp,
|
|
68
|
+
progress.operation_progress.total_files,
|
|
69
|
+
progress.operation_progress.total_bytes,
|
|
70
|
+
)
|
|
71
|
+
log_file.flush
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
# Called when operation completes
|
|
75
|
+
#
|
|
76
|
+
# @param progress [ProgressTracker] Progress tracker
|
|
77
|
+
def finish(progress)
|
|
78
|
+
timestamp = Time.now.strftime("%Y-%m-%d %H:%M:%S")
|
|
79
|
+
log_file.puts format(
|
|
80
|
+
"[%s] Completed in %.2fs",
|
|
81
|
+
timestamp,
|
|
82
|
+
progress.elapsed_seconds,
|
|
83
|
+
)
|
|
84
|
+
log_file.flush
|
|
85
|
+
|
|
86
|
+
# Close file if we opened it
|
|
87
|
+
log_file.close if @owns_file
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
end
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
#
|
|
4
|
+
# Copyright (C) 2025 Ribose Inc.
|
|
5
|
+
#
|
|
6
|
+
|
|
7
|
+
module Omnizip
|
|
8
|
+
module Progress
|
|
9
|
+
# Represents the current state of an operation's progress.
|
|
10
|
+
#
|
|
11
|
+
# This class stores all data about the current progress of an operation,
|
|
12
|
+
# including file counts, byte counts, current file being processed,
|
|
13
|
+
# and calculated percentages.
|
|
14
|
+
class OperationProgress
|
|
15
|
+
attr_reader :total_files, :total_bytes, :files_done, :bytes_done,
|
|
16
|
+
:current_file, :start_time
|
|
17
|
+
|
|
18
|
+
# Initialize a new operation progress tracker
|
|
19
|
+
#
|
|
20
|
+
# @param total_files [Integer] Total number of files to process
|
|
21
|
+
# @param total_bytes [Integer] Total bytes to process
|
|
22
|
+
def initialize(total_files:, total_bytes:)
|
|
23
|
+
@total_files = total_files
|
|
24
|
+
@total_bytes = total_bytes
|
|
25
|
+
@files_done = 0
|
|
26
|
+
@bytes_done = 0
|
|
27
|
+
@current_file = nil
|
|
28
|
+
@start_time = Time.now
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# Update progress with new values
|
|
32
|
+
#
|
|
33
|
+
# @param files [Integer] Number of files completed
|
|
34
|
+
# @param bytes [Integer] Number of bytes processed
|
|
35
|
+
# @param current_file [String] Name of file currently processing
|
|
36
|
+
def update(files:, bytes:, current_file: nil)
|
|
37
|
+
@files_done = files
|
|
38
|
+
@bytes_done = bytes
|
|
39
|
+
@current_file = current_file
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# Calculate overall percentage complete
|
|
43
|
+
#
|
|
44
|
+
# @return [Float] Percentage complete (0.0-100.0)
|
|
45
|
+
def percentage
|
|
46
|
+
return 0.0 if total_bytes.zero?
|
|
47
|
+
|
|
48
|
+
(bytes_done.to_f / total_bytes * 100.0).round(1)
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# Calculate percentage of files complete
|
|
52
|
+
#
|
|
53
|
+
# @return [Float] Percentage of files complete (0.0-100.0)
|
|
54
|
+
def files_percent
|
|
55
|
+
return 0.0 if total_files.zero?
|
|
56
|
+
|
|
57
|
+
(files_done.to_f / total_files * 100.0).round(1)
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# Calculate percentage of bytes complete
|
|
61
|
+
#
|
|
62
|
+
# @return [Float] Percentage of bytes complete (0.0-100.0)
|
|
63
|
+
def bytes_percent
|
|
64
|
+
return 0.0 if total_bytes.zero?
|
|
65
|
+
|
|
66
|
+
(bytes_done.to_f / total_bytes * 100.0).round(1)
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
# Get elapsed time in seconds
|
|
70
|
+
#
|
|
71
|
+
# @return [Float] Seconds elapsed since start
|
|
72
|
+
def elapsed_seconds
|
|
73
|
+
Time.now - start_time
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
# Get remaining bytes to process
|
|
77
|
+
#
|
|
78
|
+
# @return [Integer] Bytes remaining
|
|
79
|
+
def remaining_bytes
|
|
80
|
+
total_bytes - bytes_done
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
# Get remaining files to process
|
|
84
|
+
#
|
|
85
|
+
# @return [Integer] Files remaining
|
|
86
|
+
def remaining_files
|
|
87
|
+
total_files - files_done
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
# Check if operation is complete
|
|
91
|
+
#
|
|
92
|
+
# @return [Boolean] true if all files and bytes processed
|
|
93
|
+
def complete?
|
|
94
|
+
files_done >= total_files && bytes_done >= total_bytes
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
# Get progress as hash for serialization
|
|
98
|
+
#
|
|
99
|
+
# @return [Hash] Progress data
|
|
100
|
+
def to_h
|
|
101
|
+
{
|
|
102
|
+
total_files: total_files,
|
|
103
|
+
total_bytes: total_bytes,
|
|
104
|
+
files_done: files_done,
|
|
105
|
+
bytes_done: bytes_done,
|
|
106
|
+
current_file: current_file,
|
|
107
|
+
percentage: percentage,
|
|
108
|
+
files_percent: files_percent,
|
|
109
|
+
bytes_percent: bytes_percent,
|
|
110
|
+
elapsed_seconds: elapsed_seconds,
|
|
111
|
+
remaining_bytes: remaining_bytes,
|
|
112
|
+
remaining_files: remaining_files,
|
|
113
|
+
complete: complete?,
|
|
114
|
+
}
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
end
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
#
|
|
4
|
+
# Copyright (C) 2025 Ribose Inc.
|
|
5
|
+
#
|
|
6
|
+
|
|
7
|
+
module Omnizip
|
|
8
|
+
module Progress
|
|
9
|
+
# Visual progress bar for terminal output.
|
|
10
|
+
#
|
|
11
|
+
# This class renders a progress bar with percentage, file counts,
|
|
12
|
+
# rate, and ETA information. It supports color output and automatic
|
|
13
|
+
# width detection.
|
|
14
|
+
class ProgressBar
|
|
15
|
+
attr_reader :width, :use_color
|
|
16
|
+
|
|
17
|
+
# Initialize a new progress bar
|
|
18
|
+
#
|
|
19
|
+
# @param width [Integer] Bar width in characters (auto-detect if nil)
|
|
20
|
+
# @param use_color [Boolean] Enable color output
|
|
21
|
+
def initialize(width: nil, use_color: true)
|
|
22
|
+
@width = width || detect_terminal_width
|
|
23
|
+
@use_color = use_color && color_supported?
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# Render progress bar string
|
|
27
|
+
#
|
|
28
|
+
# @param progress [ProgressTracker] Progress tracker
|
|
29
|
+
# @return [String] Formatted progress bar
|
|
30
|
+
def render(progress)
|
|
31
|
+
bar = build_bar(progress.percentage)
|
|
32
|
+
info = build_info(progress)
|
|
33
|
+
|
|
34
|
+
"\r#{bar} #{info}"
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# Clear the progress bar line
|
|
38
|
+
#
|
|
39
|
+
# @return [String] Clear string
|
|
40
|
+
def clear
|
|
41
|
+
"\r#{' ' * width}\r"
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
private
|
|
45
|
+
|
|
46
|
+
# Build the progress bar component
|
|
47
|
+
#
|
|
48
|
+
# @param percentage [Float] Completion percentage (0.0-100.0)
|
|
49
|
+
# @return [String] Progress bar string
|
|
50
|
+
def build_bar(percentage)
|
|
51
|
+
bar_width = 20
|
|
52
|
+
filled = ((percentage / 100.0) * bar_width).round
|
|
53
|
+
empty = bar_width - filled
|
|
54
|
+
|
|
55
|
+
bar = "[#{'=' * filled}#{'>' if filled.positive?}#{' ' * [empty - 1,
|
|
56
|
+
0].max}]"
|
|
57
|
+
|
|
58
|
+
if use_color
|
|
59
|
+
colorize(bar, :green)
|
|
60
|
+
else
|
|
61
|
+
bar
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
# Build the info component (percentage, files, rate, ETA)
|
|
66
|
+
#
|
|
67
|
+
# @param progress [ProgressTracker] Progress tracker
|
|
68
|
+
# @return [String] Info string
|
|
69
|
+
def build_info(progress)
|
|
70
|
+
parts = []
|
|
71
|
+
|
|
72
|
+
# Percentage
|
|
73
|
+
parts << format("%3.0f%%", progress.percentage)
|
|
74
|
+
|
|
75
|
+
# File count
|
|
76
|
+
parts << format(
|
|
77
|
+
"(%d/%d files)",
|
|
78
|
+
progress.files_processed,
|
|
79
|
+
progress.operation_progress.total_files,
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
# Current file (truncate if too long)
|
|
83
|
+
if progress.current_file
|
|
84
|
+
filename = truncate_filename(progress.current_file, 30)
|
|
85
|
+
parts << filename
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
# Rate
|
|
89
|
+
parts << progress.rate_formatted if progress.rate_bps.positive?
|
|
90
|
+
|
|
91
|
+
# ETA
|
|
92
|
+
eta = progress.eta_formatted
|
|
93
|
+
parts << "ETA: #{eta}" unless eta == "calculating..."
|
|
94
|
+
|
|
95
|
+
parts.join(" - ")
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
# Truncate filename to fit width
|
|
99
|
+
#
|
|
100
|
+
# @param filename [String] Filename to truncate
|
|
101
|
+
# @param max_length [Integer] Maximum length
|
|
102
|
+
# @return [String] Truncated filename
|
|
103
|
+
def truncate_filename(filename, max_length)
|
|
104
|
+
return filename if filename.length <= max_length
|
|
105
|
+
|
|
106
|
+
"...#{filename[(-max_length + 3)..]}"
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
# Detect terminal width
|
|
110
|
+
#
|
|
111
|
+
# @return [Integer] Terminal width in characters
|
|
112
|
+
def detect_terminal_width
|
|
113
|
+
if ENV["COLUMNS"]
|
|
114
|
+
ENV["COLUMNS"].to_i
|
|
115
|
+
elsif $stdout.tty?
|
|
116
|
+
begin
|
|
117
|
+
require "io/console"
|
|
118
|
+
$stdout.winsize[1]
|
|
119
|
+
rescue LoadError, NoMethodError
|
|
120
|
+
80 # Default fallback
|
|
121
|
+
end
|
|
122
|
+
else
|
|
123
|
+
80
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
# Check if terminal supports color
|
|
128
|
+
#
|
|
129
|
+
# @return [Boolean] true if color is supported
|
|
130
|
+
def color_supported?
|
|
131
|
+
return false unless $stdout.tty?
|
|
132
|
+
return false if ENV["TERM"] == "dumb"
|
|
133
|
+
|
|
134
|
+
true
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
# Colorize string with ANSI color codes
|
|
138
|
+
#
|
|
139
|
+
# @param text [String] Text to colorize
|
|
140
|
+
# @param color [Symbol] Color name (:green, :yellow, :red, etc.)
|
|
141
|
+
# @return [String] Colorized string
|
|
142
|
+
def colorize(text, color)
|
|
143
|
+
color_codes = {
|
|
144
|
+
green: 32,
|
|
145
|
+
yellow: 33,
|
|
146
|
+
red: 31,
|
|
147
|
+
blue: 34,
|
|
148
|
+
cyan: 36,
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
code = color_codes[color] || 0
|
|
152
|
+
"\e[#{code}m#{text}\e[0m"
|
|
153
|
+
end
|
|
154
|
+
end
|
|
155
|
+
end
|
|
156
|
+
end
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
#
|
|
4
|
+
# Copyright (C) 2025 Ribose Inc.
|
|
5
|
+
#
|
|
6
|
+
|
|
7
|
+
module Omnizip
|
|
8
|
+
module Progress
|
|
9
|
+
# Abstract base class for progress reporters.
|
|
10
|
+
#
|
|
11
|
+
# This class defines the interface for progress reporting strategies.
|
|
12
|
+
# Subclasses implement specific reporting mechanisms (console, callback,
|
|
13
|
+
# log file, etc.).
|
|
14
|
+
#
|
|
15
|
+
# @abstract Subclass and override {#report} to implement a reporter
|
|
16
|
+
class ProgressReporter
|
|
17
|
+
# Report progress to the output mechanism
|
|
18
|
+
#
|
|
19
|
+
# @param progress [ProgressTracker] Progress tracker with current state
|
|
20
|
+
# @raise [NotImplementedError] if not implemented by subclass
|
|
21
|
+
def report(progress)
|
|
22
|
+
raise NotImplementedError, "#{self.class} must implement #report"
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# Called when operation starts (optional hook)
|
|
26
|
+
#
|
|
27
|
+
# @param progress [ProgressTracker] Progress tracker
|
|
28
|
+
def start(progress)
|
|
29
|
+
# Optional hook for subclasses
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# Called when operation completes (optional hook)
|
|
33
|
+
#
|
|
34
|
+
# @param progress [ProgressTracker] Progress tracker
|
|
35
|
+
def finish(progress)
|
|
36
|
+
# Optional hook for subclasses
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|