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.
Files changed (511) hide show
  1. checksums.yaml +7 -0
  2. data/.rspec +3 -0
  3. data/.rubocop.yml +32 -0
  4. data/.rubocop_todo.yml +754 -0
  5. data/COPYING +502 -0
  6. data/Gemfile +17 -0
  7. data/LICENSE +12 -0
  8. data/README.adoc +1045 -0
  9. data/Rakefile +12 -0
  10. data/benchmark/README.md +260 -0
  11. data/benchmark/benchmark_suite.rb +125 -0
  12. data/benchmark/compression_bench.rb +181 -0
  13. data/benchmark/filter_bench.rb +180 -0
  14. data/benchmark/models/benchmark_result.rb +59 -0
  15. data/benchmark/models/comparison_result.rb +69 -0
  16. data/benchmark/profile_suite.rb +167 -0
  17. data/benchmark/reporter.rb +150 -0
  18. data/benchmark/run_benchmarks.rb +66 -0
  19. data/benchmark/test_data.rb +137 -0
  20. data/config/formats/rar3_spec.yml +91 -0
  21. data/config/formats/rar5_spec.yml +102 -0
  22. data/docs/.github/workflows/docs.yml +142 -0
  23. data/docs/.gitignore +21 -0
  24. data/docs/.lychee.toml +67 -0
  25. data/docs/Gemfile +13 -0
  26. data/docs/RAR_WRITE_SUPPORT.md +26 -0
  27. data/docs/README.md +101 -0
  28. data/docs/_config.yml +112 -0
  29. data/docs/assets/logo.svg +1 -0
  30. data/docs/assets/omnizip-logo.pdf +1540 -11
  31. data/docs/comparison/feature-matrix.adoc +694 -0
  32. data/docs/comparison/index.adoc +113 -0
  33. data/docs/comparison/vs-7zip.adoc +309 -0
  34. data/docs/comparison/vs-peazip.adoc +77 -0
  35. data/docs/comparison/vs-rubyzip.adoc +342 -0
  36. data/docs/comparison/vs-winrar.adoc +100 -0
  37. data/docs/compatibility.adoc +579 -0
  38. data/docs/concepts/index.adoc +129 -0
  39. data/docs/developer/architecture.adoc +256 -0
  40. data/docs/developer/contributing.adoc +158 -0
  41. data/docs/developer/index.adoc +25 -0
  42. data/docs/developer/testing.adoc +212 -0
  43. data/docs/getting-started/basic-usage.adoc +271 -0
  44. data/docs/getting-started/index.adoc +42 -0
  45. data/docs/getting-started/installation.adoc +138 -0
  46. data/docs/getting-started/quick-start.adoc +185 -0
  47. data/docs/getting-started/your-first-archive.adoc +218 -0
  48. data/docs/guides/advanced-features/encryption.adoc +300 -0
  49. data/docs/guides/advanced-features/index.adoc +49 -0
  50. data/docs/guides/advanced-features/parallel-processing.adoc +246 -0
  51. data/docs/guides/advanced-features/progress-tracking.adoc +320 -0
  52. data/docs/guides/advanced-features/streaming.adoc +212 -0
  53. data/docs/guides/archive-formats/gzip-format.adoc +107 -0
  54. data/docs/guides/archive-formats/index.adoc +130 -0
  55. data/docs/guides/archive-formats/rar-format.adoc +104 -0
  56. data/docs/guides/archive-formats/rar5.adoc +521 -0
  57. data/docs/guides/archive-formats/seven-zip-format.adoc +35 -0
  58. data/docs/guides/archive-formats/tar-format.adoc +106 -0
  59. data/docs/guides/archive-formats/xz-format.adoc +118 -0
  60. data/docs/guides/archive-formats/zip-format.adoc +35 -0
  61. data/docs/guides/compression-algorithms/bzip2.adoc +113 -0
  62. data/docs/guides/compression-algorithms/deflate.adoc +319 -0
  63. data/docs/guides/compression-algorithms/index.adoc +190 -0
  64. data/docs/guides/compression-algorithms/lzma.adoc +398 -0
  65. data/docs/guides/compression-algorithms/lzma2.adoc +327 -0
  66. data/docs/guides/compression-algorithms/ppmd.adoc +316 -0
  67. data/docs/guides/compression-algorithms/zstandard.adoc +361 -0
  68. data/docs/guides/creating-archives.adoc +354 -0
  69. data/docs/guides/extracting-archives.adoc +53 -0
  70. data/docs/guides/format-conversion.adoc +64 -0
  71. data/docs/guides/index.adoc +49 -0
  72. data/docs/guides/migration-rubyzip.adoc +217 -0
  73. data/docs/guides/parity-archives.adoc +605 -0
  74. data/docs/guides/performance-tuning.adoc +88 -0
  75. data/docs/index.adoc +218 -0
  76. data/docs/lychee.toml +67 -0
  77. data/docs/reference/api/overview.adoc +188 -0
  78. data/docs/reference/cli/compress-command.adoc +114 -0
  79. data/docs/reference/cli/overview.adoc +140 -0
  80. data/docs/reference/index.adoc +26 -0
  81. data/docs/resources/faq.adoc +185 -0
  82. data/docs/resources/quick-reference.adoc +222 -0
  83. data/docs/troubleshooting/index.adoc +208 -0
  84. data/examples/api_comparison.rb +205 -0
  85. data/examples/deflate64_example.rb +96 -0
  86. data/examples/par2_demo.rb +121 -0
  87. data/examples/quick_start_native.rb +150 -0
  88. data/examples/quick_start_rubyzip.rb +115 -0
  89. data/examples/rubyzip_compatibility_demo.rb +194 -0
  90. data/exe/omnizip +27 -0
  91. data/lib/omnizip/algorithm.rb +130 -0
  92. data/lib/omnizip/algorithm_registry.rb +86 -0
  93. data/lib/omnizip/algorithms/.keep +0 -0
  94. data/lib/omnizip/algorithms/bzip2/bwt.rb +225 -0
  95. data/lib/omnizip/algorithms/bzip2/decoder.rb +193 -0
  96. data/lib/omnizip/algorithms/bzip2/encoder.rb +237 -0
  97. data/lib/omnizip/algorithms/bzip2/huffman.rb +206 -0
  98. data/lib/omnizip/algorithms/bzip2/mtf.rb +101 -0
  99. data/lib/omnizip/algorithms/bzip2/rle.rb +151 -0
  100. data/lib/omnizip/algorithms/bzip2.rb +130 -0
  101. data/lib/omnizip/algorithms/deflate/constants.rb +28 -0
  102. data/lib/omnizip/algorithms/deflate/decoder.rb +38 -0
  103. data/lib/omnizip/algorithms/deflate/encoder.rb +46 -0
  104. data/lib/omnizip/algorithms/deflate.rb +128 -0
  105. data/lib/omnizip/algorithms/deflate64/constants.rb +45 -0
  106. data/lib/omnizip/algorithms/deflate64/decoder.rb +153 -0
  107. data/lib/omnizip/algorithms/deflate64/encoder.rb +98 -0
  108. data/lib/omnizip/algorithms/deflate64/huffman_coder.rb +354 -0
  109. data/lib/omnizip/algorithms/deflate64/lz77_encoder.rb +142 -0
  110. data/lib/omnizip/algorithms/deflate64.rb +109 -0
  111. data/lib/omnizip/algorithms/lzma/bit_model.rb +120 -0
  112. data/lib/omnizip/algorithms/lzma/constants.rb +112 -0
  113. data/lib/omnizip/algorithms/lzma/decoder.rb +148 -0
  114. data/lib/omnizip/algorithms/lzma/dictionary.rb +69 -0
  115. data/lib/omnizip/algorithms/lzma/distance_coder.rb +415 -0
  116. data/lib/omnizip/algorithms/lzma/encoder.rb +142 -0
  117. data/lib/omnizip/algorithms/lzma/length_coder.rb +260 -0
  118. data/lib/omnizip/algorithms/lzma/literal_decoder.rb +320 -0
  119. data/lib/omnizip/algorithms/lzma/literal_encoder.rb +210 -0
  120. data/lib/omnizip/algorithms/lzma/lzip_decoder.rb +341 -0
  121. data/lib/omnizip/algorithms/lzma/lzma_alone_decoder.rb +192 -0
  122. data/lib/omnizip/algorithms/lzma/lzma_state.rb +128 -0
  123. data/lib/omnizip/algorithms/lzma/match.rb +32 -0
  124. data/lib/omnizip/algorithms/lzma/match_finder.rb +205 -0
  125. data/lib/omnizip/algorithms/lzma/match_finder_config.rb +142 -0
  126. data/lib/omnizip/algorithms/lzma/match_finder_factory.rb +88 -0
  127. data/lib/omnizip/algorithms/lzma/optimal_encoder.rb +130 -0
  128. data/lib/omnizip/algorithms/lzma/probability_models.rb +72 -0
  129. data/lib/omnizip/algorithms/lzma/range_coder.rb +85 -0
  130. data/lib/omnizip/algorithms/lzma/range_decoder.rb +434 -0
  131. data/lib/omnizip/algorithms/lzma/range_encoder.rb +194 -0
  132. data/lib/omnizip/algorithms/lzma/state.rb +127 -0
  133. data/lib/omnizip/algorithms/lzma/xz_buffered_range_encoder.rb +325 -0
  134. data/lib/omnizip/algorithms/lzma/xz_encoder.rb +426 -0
  135. data/lib/omnizip/algorithms/lzma/xz_encoder_fast.rb +645 -0
  136. data/lib/omnizip/algorithms/lzma/xz_match_finder_adapter.rb +227 -0
  137. data/lib/omnizip/algorithms/lzma/xz_price_calculator.rb +169 -0
  138. data/lib/omnizip/algorithms/lzma/xz_probability_models.rb +261 -0
  139. data/lib/omnizip/algorithms/lzma/xz_range_encoder.rb +223 -0
  140. data/lib/omnizip/algorithms/lzma/xz_range_encoder_exact.rb +331 -0
  141. data/lib/omnizip/algorithms/lzma/xz_state.rb +116 -0
  142. data/lib/omnizip/algorithms/lzma/xz_utils_decoder.rb +2055 -0
  143. data/lib/omnizip/algorithms/lzma.rb +238 -0
  144. data/lib/omnizip/algorithms/lzma2/chunk_manager.rb +182 -0
  145. data/lib/omnizip/algorithms/lzma2/constants.rb +41 -0
  146. data/lib/omnizip/algorithms/lzma2/encoder.rb +147 -0
  147. data/lib/omnizip/algorithms/lzma2/lzma2_chunk.rb +161 -0
  148. data/lib/omnizip/algorithms/lzma2/properties.rb +179 -0
  149. data/lib/omnizip/algorithms/lzma2/simple_lzma2_encoder.rb +127 -0
  150. data/lib/omnizip/algorithms/lzma2/xz_encoder_adapter.rb +85 -0
  151. data/lib/omnizip/algorithms/lzma2.rb +141 -0
  152. data/lib/omnizip/algorithms/ppmd7/constants.rb +74 -0
  153. data/lib/omnizip/algorithms/ppmd7/context.rb +154 -0
  154. data/lib/omnizip/algorithms/ppmd7/decoder.rb +126 -0
  155. data/lib/omnizip/algorithms/ppmd7/encoder.rb +163 -0
  156. data/lib/omnizip/algorithms/ppmd7/model.rb +248 -0
  157. data/lib/omnizip/algorithms/ppmd7/symbol_state.rb +57 -0
  158. data/lib/omnizip/algorithms/ppmd7.rb +116 -0
  159. data/lib/omnizip/algorithms/ppmd8/constants.rb +61 -0
  160. data/lib/omnizip/algorithms/ppmd8/context.rb +34 -0
  161. data/lib/omnizip/algorithms/ppmd8/decoder.rb +107 -0
  162. data/lib/omnizip/algorithms/ppmd8/encoder.rb +138 -0
  163. data/lib/omnizip/algorithms/ppmd8/model.rb +250 -0
  164. data/lib/omnizip/algorithms/ppmd8/restoration_method.rb +78 -0
  165. data/lib/omnizip/algorithms/ppmd8.rb +82 -0
  166. data/lib/omnizip/algorithms/ppmd_base.rb +138 -0
  167. data/lib/omnizip/algorithms/sevenzip_lzma2.rb +123 -0
  168. data/lib/omnizip/algorithms/xz_lzma2.rb +118 -0
  169. data/lib/omnizip/algorithms/zstandard/constants.rb +25 -0
  170. data/lib/omnizip/algorithms/zstandard/decoder.rb +46 -0
  171. data/lib/omnizip/algorithms/zstandard/encoder.rb +51 -0
  172. data/lib/omnizip/algorithms/zstandard.rb +138 -0
  173. data/lib/omnizip/buffer/memory_archive.rb +251 -0
  174. data/lib/omnizip/buffer/memory_extractor.rb +224 -0
  175. data/lib/omnizip/buffer.rb +176 -0
  176. data/lib/omnizip/checksum_registry.rb +114 -0
  177. data/lib/omnizip/checksums/crc32.rb +100 -0
  178. data/lib/omnizip/checksums/crc64.rb +101 -0
  179. data/lib/omnizip/checksums/crc_base.rb +158 -0
  180. data/lib/omnizip/checksums/verifier.rb +131 -0
  181. data/lib/omnizip/chunked/memory_manager.rb +194 -0
  182. data/lib/omnizip/chunked/reader.rb +78 -0
  183. data/lib/omnizip/chunked/writer.rb +120 -0
  184. data/lib/omnizip/chunked.rb +129 -0
  185. data/lib/omnizip/cli/output_formatter.rb +104 -0
  186. data/lib/omnizip/cli.rb +572 -0
  187. data/lib/omnizip/commands/.keep +0 -0
  188. data/lib/omnizip/commands/archive_create_command.rb +427 -0
  189. data/lib/omnizip/commands/archive_extract_command.rb +272 -0
  190. data/lib/omnizip/commands/archive_list_command.rb +218 -0
  191. data/lib/omnizip/commands/archive_repair_command.rb +131 -0
  192. data/lib/omnizip/commands/archive_verify_command.rb +117 -0
  193. data/lib/omnizip/commands/compress_command.rb +117 -0
  194. data/lib/omnizip/commands/decompress_command.rb +120 -0
  195. data/lib/omnizip/commands/list_command.rb +53 -0
  196. data/lib/omnizip/commands/metadata_command.rb +153 -0
  197. data/lib/omnizip/commands/parity_create_command.rb +122 -0
  198. data/lib/omnizip/commands/parity_repair_command.rb +122 -0
  199. data/lib/omnizip/commands/parity_verify_command.rb +124 -0
  200. data/lib/omnizip/commands/profile_list_command.rb +56 -0
  201. data/lib/omnizip/commands/profile_show_command.rb +44 -0
  202. data/lib/omnizip/convenience.rb +359 -0
  203. data/lib/omnizip/converter/conversion_registry.rb +49 -0
  204. data/lib/omnizip/converter/conversion_strategy.rb +121 -0
  205. data/lib/omnizip/converter/seven_zip_to_zip_strategy.rb +97 -0
  206. data/lib/omnizip/converter/zip_to_seven_zip_strategy.rb +112 -0
  207. data/lib/omnizip/converter.rb +105 -0
  208. data/lib/omnizip/crypto/aes256/cipher.rb +100 -0
  209. data/lib/omnizip/crypto/aes256/constants.rb +28 -0
  210. data/lib/omnizip/crypto/aes256/key_derivation.rb +101 -0
  211. data/lib/omnizip/crypto/aes256.rb +102 -0
  212. data/lib/omnizip/error.rb +106 -0
  213. data/lib/omnizip/eta/exponential_smoothing_estimator.rb +98 -0
  214. data/lib/omnizip/eta/moving_average_estimator.rb +99 -0
  215. data/lib/omnizip/eta/rate_calculator.rb +104 -0
  216. data/lib/omnizip/eta/sample_history.rb +143 -0
  217. data/lib/omnizip/eta/time_estimator.rb +106 -0
  218. data/lib/omnizip/eta.rb +63 -0
  219. data/lib/omnizip/extraction/filter_chain.rb +177 -0
  220. data/lib/omnizip/extraction/glob_pattern.rb +140 -0
  221. data/lib/omnizip/extraction/pattern_matcher.rb +70 -0
  222. data/lib/omnizip/extraction/predicate_pattern.rb +52 -0
  223. data/lib/omnizip/extraction/regex_pattern.rb +50 -0
  224. data/lib/omnizip/extraction/selective_extractor.rb +240 -0
  225. data/lib/omnizip/extraction.rb +111 -0
  226. data/lib/omnizip/file_type/mime_classifier.rb +144 -0
  227. data/lib/omnizip/file_type.rb +113 -0
  228. data/lib/omnizip/filter.rb +139 -0
  229. data/lib/omnizip/filter_pipeline.rb +108 -0
  230. data/lib/omnizip/filter_registry.rb +166 -0
  231. data/lib/omnizip/filters/bcj.rb +279 -0
  232. data/lib/omnizip/filters/bcj2/constants.rb +53 -0
  233. data/lib/omnizip/filters/bcj2/decoder.rb +200 -0
  234. data/lib/omnizip/filters/bcj2/encoder.rb +61 -0
  235. data/lib/omnizip/filters/bcj2/stream_data.rb +93 -0
  236. data/lib/omnizip/filters/bcj2.rb +99 -0
  237. data/lib/omnizip/filters/bcj_arm.rb +176 -0
  238. data/lib/omnizip/filters/bcj_arm64.rb +244 -0
  239. data/lib/omnizip/filters/bcj_ia64.rb +196 -0
  240. data/lib/omnizip/filters/bcj_ppc.rb +190 -0
  241. data/lib/omnizip/filters/bcj_sparc.rb +176 -0
  242. data/lib/omnizip/filters/bcj_x86.rb +193 -0
  243. data/lib/omnizip/filters/delta.rb +196 -0
  244. data/lib/omnizip/filters/filter_base.rb +72 -0
  245. data/lib/omnizip/filters/registry.rb +123 -0
  246. data/lib/omnizip/filters/xz_delta.rb +258 -0
  247. data/lib/omnizip/format_detector.rb +162 -0
  248. data/lib/omnizip/format_registry.rb +59 -0
  249. data/lib/omnizip/formats/.keep +0 -0
  250. data/lib/omnizip/formats/bzip2_file.rb +172 -0
  251. data/lib/omnizip/formats/cpio/constants.rb +55 -0
  252. data/lib/omnizip/formats/cpio/entry.rb +385 -0
  253. data/lib/omnizip/formats/cpio/reader.rb +196 -0
  254. data/lib/omnizip/formats/cpio/writer.rb +234 -0
  255. data/lib/omnizip/formats/cpio.rb +140 -0
  256. data/lib/omnizip/formats/format_spec_loader.rb +230 -0
  257. data/lib/omnizip/formats/gzip.rb +238 -0
  258. data/lib/omnizip/formats/iso/directory_builder.rb +297 -0
  259. data/lib/omnizip/formats/iso/directory_record.rb +152 -0
  260. data/lib/omnizip/formats/iso/joliet.rb +204 -0
  261. data/lib/omnizip/formats/iso/path_table.rb +125 -0
  262. data/lib/omnizip/formats/iso/reader.rb +197 -0
  263. data/lib/omnizip/formats/iso/rock_ridge.rb +349 -0
  264. data/lib/omnizip/formats/iso/volume_builder.rb +320 -0
  265. data/lib/omnizip/formats/iso/volume_descriptor.rb +168 -0
  266. data/lib/omnizip/formats/iso/writer.rb +530 -0
  267. data/lib/omnizip/formats/iso.rb +140 -0
  268. data/lib/omnizip/formats/lzip.rb +175 -0
  269. data/lib/omnizip/formats/lzma_alone.rb +171 -0
  270. data/lib/omnizip/formats/rar/archive_repairer.rb +243 -0
  271. data/lib/omnizip/formats/rar/archive_verifier.rb +195 -0
  272. data/lib/omnizip/formats/rar/block_parser.rb +243 -0
  273. data/lib/omnizip/formats/rar/compression/bit_stream.rb +180 -0
  274. data/lib/omnizip/formats/rar/compression/dispatcher.rb +217 -0
  275. data/lib/omnizip/formats/rar/compression/lz77_huffman/decoder.rb +216 -0
  276. data/lib/omnizip/formats/rar/compression/lz77_huffman/encoder.rb +158 -0
  277. data/lib/omnizip/formats/rar/compression/lz77_huffman/huffman_builder.rb +217 -0
  278. data/lib/omnizip/formats/rar/compression/lz77_huffman/huffman_coder.rb +189 -0
  279. data/lib/omnizip/formats/rar/compression/lz77_huffman/match_finder.rb +135 -0
  280. data/lib/omnizip/formats/rar/compression/lz77_huffman/sliding_window.rb +165 -0
  281. data/lib/omnizip/formats/rar/compression/ppmd/context.rb +105 -0
  282. data/lib/omnizip/formats/rar/compression/ppmd/decoder.rb +219 -0
  283. data/lib/omnizip/formats/rar/compression/ppmd/encoder.rb +262 -0
  284. data/lib/omnizip/formats/rar/compression_method_registry.rb +106 -0
  285. data/lib/omnizip/formats/rar/constants.rb +82 -0
  286. data/lib/omnizip/formats/rar/decompressor.rb +238 -0
  287. data/lib/omnizip/formats/rar/external_writer.rb +312 -0
  288. data/lib/omnizip/formats/rar/header.rb +192 -0
  289. data/lib/omnizip/formats/rar/license_validator.rb +109 -0
  290. data/lib/omnizip/formats/rar/models/rar_archive.rb +77 -0
  291. data/lib/omnizip/formats/rar/models/rar_entry.rb +65 -0
  292. data/lib/omnizip/formats/rar/models/rar_volume.rb +56 -0
  293. data/lib/omnizip/formats/rar/parity_handler.rb +292 -0
  294. data/lib/omnizip/formats/rar/rar5/compression/lzma.rb +202 -0
  295. data/lib/omnizip/formats/rar/rar5/compression/lzss.rb +578 -0
  296. data/lib/omnizip/formats/rar/rar5/compression/store.rb +60 -0
  297. data/lib/omnizip/formats/rar/rar5/crc32.rb +39 -0
  298. data/lib/omnizip/formats/rar/rar5/encryption/aes256_cbc.rb +97 -0
  299. data/lib/omnizip/formats/rar/rar5/encryption/encryption_header.rb +114 -0
  300. data/lib/omnizip/formats/rar/rar5/encryption/encryption_manager.rb +166 -0
  301. data/lib/omnizip/formats/rar/rar5/encryption/key_derivation.rb +97 -0
  302. data/lib/omnizip/formats/rar/rar5/header.rb +187 -0
  303. data/lib/omnizip/formats/rar/rar5/models/encryption_options.rb +74 -0
  304. data/lib/omnizip/formats/rar/rar5/models/recovery_options.rb +63 -0
  305. data/lib/omnizip/formats/rar/rar5/models/solid_options.rb +63 -0
  306. data/lib/omnizip/formats/rar/rar5/models/volume_options.rb +74 -0
  307. data/lib/omnizip/formats/rar/rar5/multi_volume/ARCHITECTURE.md +290 -0
  308. data/lib/omnizip/formats/rar/rar5/multi_volume/volume_manager.rb +264 -0
  309. data/lib/omnizip/formats/rar/rar5/multi_volume/volume_splitter.rb +155 -0
  310. data/lib/omnizip/formats/rar/rar5/multi_volume/volume_writer.rb +194 -0
  311. data/lib/omnizip/formats/rar/rar5/solid/solid_encoder.rb +109 -0
  312. data/lib/omnizip/formats/rar/rar5/solid/solid_manager.rb +142 -0
  313. data/lib/omnizip/formats/rar/rar5/solid/solid_stream.rb +121 -0
  314. data/lib/omnizip/formats/rar/rar5/vint.rb +65 -0
  315. data/lib/omnizip/formats/rar/rar5/writer.rb +466 -0
  316. data/lib/omnizip/formats/rar/rar_format_base.rb +241 -0
  317. data/lib/omnizip/formats/rar/reader.rb +366 -0
  318. data/lib/omnizip/formats/rar/recovery_record.rb +245 -0
  319. data/lib/omnizip/formats/rar/volume_manager.rb +168 -0
  320. data/lib/omnizip/formats/rar/writer.rb +431 -0
  321. data/lib/omnizip/formats/rar.rb +205 -0
  322. data/lib/omnizip/formats/rar3/compressor.rb +73 -0
  323. data/lib/omnizip/formats/rar3/decompressor.rb +66 -0
  324. data/lib/omnizip/formats/rar3/reader.rb +386 -0
  325. data/lib/omnizip/formats/rar3/writer.rb +219 -0
  326. data/lib/omnizip/formats/rar5/compressor.rb +73 -0
  327. data/lib/omnizip/formats/rar5/decompressor.rb +66 -0
  328. data/lib/omnizip/formats/rar5/reader.rb +342 -0
  329. data/lib/omnizip/formats/rar5/writer.rb +214 -0
  330. data/lib/omnizip/formats/seven_zip/coder_chain.rb +150 -0
  331. data/lib/omnizip/formats/seven_zip/constants.rb +126 -0
  332. data/lib/omnizip/formats/seven_zip/encoded_header.rb +114 -0
  333. data/lib/omnizip/formats/seven_zip/encrypted_header.rb +142 -0
  334. data/lib/omnizip/formats/seven_zip/file_collector.rb +144 -0
  335. data/lib/omnizip/formats/seven_zip/header.rb +106 -0
  336. data/lib/omnizip/formats/seven_zip/header_encryptor.rb +134 -0
  337. data/lib/omnizip/formats/seven_zip/header_writer.rb +466 -0
  338. data/lib/omnizip/formats/seven_zip/models/coder_info.rb +30 -0
  339. data/lib/omnizip/formats/seven_zip/models/file_entry.rb +58 -0
  340. data/lib/omnizip/formats/seven_zip/models/folder.rb +69 -0
  341. data/lib/omnizip/formats/seven_zip/models/stream_info.rb +42 -0
  342. data/lib/omnizip/formats/seven_zip/parser.rb +660 -0
  343. data/lib/omnizip/formats/seven_zip/reader.rb +458 -0
  344. data/lib/omnizip/formats/seven_zip/split_archive_reader.rb +632 -0
  345. data/lib/omnizip/formats/seven_zip/split_archive_writer.rb +315 -0
  346. data/lib/omnizip/formats/seven_zip/stream_compressor.rb +151 -0
  347. data/lib/omnizip/formats/seven_zip/stream_decompressor.rb +162 -0
  348. data/lib/omnizip/formats/seven_zip/writer.rb +740 -0
  349. data/lib/omnizip/formats/seven_zip.rb +93 -0
  350. data/lib/omnizip/formats/tar/constants.rb +73 -0
  351. data/lib/omnizip/formats/tar/entry.rb +94 -0
  352. data/lib/omnizip/formats/tar/header.rb +168 -0
  353. data/lib/omnizip/formats/tar/reader.rb +121 -0
  354. data/lib/omnizip/formats/tar/writer.rb +216 -0
  355. data/lib/omnizip/formats/tar.rb +84 -0
  356. data/lib/omnizip/formats/xz/reader.rb +116 -0
  357. data/lib/omnizip/formats/xz.rb +237 -0
  358. data/lib/omnizip/formats/xz_impl/block_decoder.rb +754 -0
  359. data/lib/omnizip/formats/xz_impl/block_encoder.rb +306 -0
  360. data/lib/omnizip/formats/xz_impl/block_header.rb +210 -0
  361. data/lib/omnizip/formats/xz_impl/block_header_parser.rb +186 -0
  362. data/lib/omnizip/formats/xz_impl/constants.rb +49 -0
  363. data/lib/omnizip/formats/xz_impl/index_decoder.rb +174 -0
  364. data/lib/omnizip/formats/xz_impl/index_encoder.rb +122 -0
  365. data/lib/omnizip/formats/xz_impl/stream_decoder.rb +468 -0
  366. data/lib/omnizip/formats/xz_impl/stream_encoder.rb +99 -0
  367. data/lib/omnizip/formats/xz_impl/stream_footer.rb +81 -0
  368. data/lib/omnizip/formats/xz_impl/stream_footer_parser.rb +117 -0
  369. data/lib/omnizip/formats/xz_impl/stream_header.rb +55 -0
  370. data/lib/omnizip/formats/xz_impl/stream_header_parser.rb +108 -0
  371. data/lib/omnizip/formats/xz_impl/vli.rb +128 -0
  372. data/lib/omnizip/formats/xz_impl/writer.rb +421 -0
  373. data/lib/omnizip/formats/zip/central_directory_header.rb +195 -0
  374. data/lib/omnizip/formats/zip/constants.rb +69 -0
  375. data/lib/omnizip/formats/zip/end_of_central_directory.rb +133 -0
  376. data/lib/omnizip/formats/zip/local_file_header.rb +138 -0
  377. data/lib/omnizip/formats/zip/reader.rb +250 -0
  378. data/lib/omnizip/formats/zip/unix_extra_field.rb +153 -0
  379. data/lib/omnizip/formats/zip/writer.rb +375 -0
  380. data/lib/omnizip/formats/zip/zip64_end_of_central_directory.rb +104 -0
  381. data/lib/omnizip/formats/zip/zip64_end_of_central_directory_locator.rb +66 -0
  382. data/lib/omnizip/formats/zip/zip64_extra_field.rb +114 -0
  383. data/lib/omnizip/formats/zip.rb +50 -0
  384. data/lib/omnizip/implementations/base/lzma2_decoder_base.rb +75 -0
  385. data/lib/omnizip/implementations/base/lzma2_encoder_base.rb +128 -0
  386. data/lib/omnizip/implementations/base/lzma_decoder_base.rb +83 -0
  387. data/lib/omnizip/implementations/base/lzma_encoder_base.rb +108 -0
  388. data/lib/omnizip/implementations/base/state_machine_base.rb +182 -0
  389. data/lib/omnizip/implementations/seven_zip/lzma/decoder.rb +421 -0
  390. data/lib/omnizip/implementations/seven_zip/lzma/encoder.rb +465 -0
  391. data/lib/omnizip/implementations/seven_zip/lzma/match_finder.rb +288 -0
  392. data/lib/omnizip/implementations/seven_zip/lzma/range_decoder.rb +200 -0
  393. data/lib/omnizip/implementations/seven_zip/lzma/range_encoder.rb +197 -0
  394. data/lib/omnizip/implementations/seven_zip/lzma/state_machine.rb +141 -0
  395. data/lib/omnizip/implementations/seven_zip/lzma2/encoder.rb +519 -0
  396. data/lib/omnizip/implementations/xz_utils/lzma2/decoder.rb +723 -0
  397. data/lib/omnizip/implementations/xz_utils/lzma2/encoder.rb +750 -0
  398. data/lib/omnizip/io/buffered_input.rb +146 -0
  399. data/lib/omnizip/io/buffered_output.rb +105 -0
  400. data/lib/omnizip/io/stream_manager.rb +115 -0
  401. data/lib/omnizip/link_handler/hard_link.rb +79 -0
  402. data/lib/omnizip/link_handler/symbolic_link.rb +74 -0
  403. data/lib/omnizip/link_handler.rb +124 -0
  404. data/lib/omnizip/metadata/archive_metadata.rb +114 -0
  405. data/lib/omnizip/metadata/entry_metadata.rb +146 -0
  406. data/lib/omnizip/metadata/metadata_editor.rb +171 -0
  407. data/lib/omnizip/metadata/metadata_registry.rb +64 -0
  408. data/lib/omnizip/metadata/metadata_validator.rb +99 -0
  409. data/lib/omnizip/metadata.rb +57 -0
  410. data/lib/omnizip/models/.keep +0 -0
  411. data/lib/omnizip/models/algorithm_metadata.rb +73 -0
  412. data/lib/omnizip/models/compression_options.rb +71 -0
  413. data/lib/omnizip/models/conversion_options.rb +87 -0
  414. data/lib/omnizip/models/conversion_result.rb +135 -0
  415. data/lib/omnizip/models/eta_result.rb +46 -0
  416. data/lib/omnizip/models/extraction_rule.rb +115 -0
  417. data/lib/omnizip/models/filter_chain.rb +144 -0
  418. data/lib/omnizip/models/filter_config.rb +183 -0
  419. data/lib/omnizip/models/match_result.rb +124 -0
  420. data/lib/omnizip/models/optimization_suggestion.rb +91 -0
  421. data/lib/omnizip/models/parallel_options.rb +104 -0
  422. data/lib/omnizip/models/performance_result.rb +79 -0
  423. data/lib/omnizip/models/profile_report.rb +82 -0
  424. data/lib/omnizip/models/progress_options.rb +38 -0
  425. data/lib/omnizip/models/split_options.rb +116 -0
  426. data/lib/omnizip/optimization_registry.rb +81 -0
  427. data/lib/omnizip/parallel/job_queue.rb +209 -0
  428. data/lib/omnizip/parallel/job_scheduler.rb +203 -0
  429. data/lib/omnizip/parallel/parallel_compressor.rb +347 -0
  430. data/lib/omnizip/parallel/parallel_extractor.rb +329 -0
  431. data/lib/omnizip/parallel/worker_pool.rb +223 -0
  432. data/lib/omnizip/parallel.rb +149 -0
  433. data/lib/omnizip/parity/chunked_block_processor.rb +196 -0
  434. data/lib/omnizip/parity/galois16.rb +145 -0
  435. data/lib/omnizip/parity/models/creator_packet.rb +73 -0
  436. data/lib/omnizip/parity/models/file_description_packet.rb +133 -0
  437. data/lib/omnizip/parity/models/ifsc_packet.rb +123 -0
  438. data/lib/omnizip/parity/models/main_packet.rb +128 -0
  439. data/lib/omnizip/parity/models/packet.rb +156 -0
  440. data/lib/omnizip/parity/models/packet_registry.rb +109 -0
  441. data/lib/omnizip/parity/models/recovery_slice_packet.rb +78 -0
  442. data/lib/omnizip/parity/par2_creator.rb +531 -0
  443. data/lib/omnizip/parity/par2_repairer.rb +407 -0
  444. data/lib/omnizip/parity/par2_verifier.rb +364 -0
  445. data/lib/omnizip/parity/par2cmdline_algorithm.rb +110 -0
  446. data/lib/omnizip/parity/par2cmdline_coefficients.rb +78 -0
  447. data/lib/omnizip/parity/reed_solomon_decoder.rb +266 -0
  448. data/lib/omnizip/parity/reed_solomon_encoder.rb +111 -0
  449. data/lib/omnizip/parity/reed_solomon_matrix.rb +342 -0
  450. data/lib/omnizip/parity.rb +186 -0
  451. data/lib/omnizip/password/encryption_registry.rb +65 -0
  452. data/lib/omnizip/password/encryption_strategy.rb +96 -0
  453. data/lib/omnizip/password/password_validator.rb +129 -0
  454. data/lib/omnizip/password/winzip_aes_strategy.rb +192 -0
  455. data/lib/omnizip/password/zip_crypto_strategy.rb +141 -0
  456. data/lib/omnizip/password.rb +87 -0
  457. data/lib/omnizip/pipe/stream_compressor.rb +124 -0
  458. data/lib/omnizip/pipe/stream_decompressor.rb +174 -0
  459. data/lib/omnizip/pipe.rb +121 -0
  460. data/lib/omnizip/platform/ntfs_streams.rb +201 -0
  461. data/lib/omnizip/platform.rb +189 -0
  462. data/lib/omnizip/profile/archive_profile.rb +39 -0
  463. data/lib/omnizip/profile/balanced_profile.rb +33 -0
  464. data/lib/omnizip/profile/binary_profile.rb +36 -0
  465. data/lib/omnizip/profile/compression_profile.rb +158 -0
  466. data/lib/omnizip/profile/custom_profile.rb +157 -0
  467. data/lib/omnizip/profile/fast_profile.rb +33 -0
  468. data/lib/omnizip/profile/maximum_profile.rb +33 -0
  469. data/lib/omnizip/profile/profile_detector.rb +110 -0
  470. data/lib/omnizip/profile/profile_registry.rb +161 -0
  471. data/lib/omnizip/profile/text_profile.rb +36 -0
  472. data/lib/omnizip/profile.rb +190 -0
  473. data/lib/omnizip/profiler/memory_profiler.rb +66 -0
  474. data/lib/omnizip/profiler/method_profiler.rb +49 -0
  475. data/lib/omnizip/profiler/report_generator.rb +169 -0
  476. data/lib/omnizip/profiler.rb +204 -0
  477. data/lib/omnizip/progress/callback_reporter.rb +36 -0
  478. data/lib/omnizip/progress/console_reporter.rb +62 -0
  479. data/lib/omnizip/progress/log_reporter.rb +91 -0
  480. data/lib/omnizip/progress/operation_progress.rb +118 -0
  481. data/lib/omnizip/progress/progress_bar.rb +156 -0
  482. data/lib/omnizip/progress/progress_reporter.rb +40 -0
  483. data/lib/omnizip/progress/progress_tracker.rb +190 -0
  484. data/lib/omnizip/progress/silent_reporter.rb +24 -0
  485. data/lib/omnizip/progress.rb +127 -0
  486. data/lib/omnizip/rubyzip_compat.rb +63 -0
  487. data/lib/omnizip/temp/safe_extract.rb +168 -0
  488. data/lib/omnizip/temp/temp_file.rb +124 -0
  489. data/lib/omnizip/temp/temp_file_pool.rb +109 -0
  490. data/lib/omnizip/temp.rb +181 -0
  491. data/lib/omnizip/version.rb +5 -0
  492. data/lib/omnizip/zip/entry.rb +156 -0
  493. data/lib/omnizip/zip/file.rb +485 -0
  494. data/lib/omnizip/zip/input_stream.rb +273 -0
  495. data/lib/omnizip/zip/output_stream.rb +324 -0
  496. data/lib/omnizip.rb +156 -0
  497. data/readme-docs/advanced-features.adoc +515 -0
  498. data/readme-docs/api-usage.adoc +444 -0
  499. data/readme-docs/architecture.adoc +449 -0
  500. data/readme-docs/archive-formats.adoc +479 -0
  501. data/readme-docs/cli-usage.adoc +222 -0
  502. data/readme-docs/compression-algorithms.adoc +442 -0
  503. data/readme-docs/compression-profiles.adoc +247 -0
  504. data/readme-docs/encryption-checksums.adoc +328 -0
  505. data/readme-docs/format-converter.adoc +325 -0
  506. data/readme-docs/installation.adoc +228 -0
  507. data/readme-docs/par2-archives.adoc +608 -0
  508. data/readme-docs/performance-profiler.adoc +389 -0
  509. data/readme-docs/preprocessing-filters.adoc +280 -0
  510. data/xz-file-format-1.2.1.txt +1174 -0
  511. 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
@@ -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