omnizip 0.3.28 → 0.3.30

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f1ff41e29f6e72255269184eecd12fde89187f149b48cd8cc1b938eca217abb4
4
- data.tar.gz: 1ac6cd6d71868889ae53f016d9e25e63aa9d6a1283b84dc0838b04a8cdcecf7a
3
+ metadata.gz: 20b634d20f6abd7d5c44314d94b89e5008b1ad34e4c0efa14a01ef72a71c0723
4
+ data.tar.gz: 58d5380b5f2a98f92361395a55738047313f66c90bb9e662a56a47ee20333f11
5
5
  SHA512:
6
- metadata.gz: a30ece1e021e25ef789efbbd91e8f46db3db3a907b6948dd812e2b084a5e119c527d96b89c5e38840a29ab514c9eebe8ae0b82bb8945665314615f5a28141efc
7
- data.tar.gz: 5b4ae45ac360db8c3345a1885388de51043d7477cb0535e6e0170f022f179ea7d5e61cd927d60a710d8b337b4efec42b5ae3a377a4a89668d3879464455b9c2c
6
+ metadata.gz: efcdcc1f1f3cf2774a6c955531f5a6747f4bad4464e0521f27e89d6b5c97b31bedcd1e3e2315f421bb7bea29a4aa73ca4f75dcee8eec702516e8c2b5349fb115
7
+ data.tar.gz: 96d041e3847fa402dbf86cfa3f86b7ba7b9b12f754471e10a8c60de8e966decbb0cfeb3711f71cfb905bf7acfea62a30128a1fb573f4f52e8e8e29b37fecc00b
data/CHANGELOG.md CHANGED
@@ -7,7 +7,37 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
- ## [0.3.29] - 2026-08-26
10
+ ## [0.3.31] - 2026-08-27
11
+
12
+ ### Changed
13
+ - Re-enabled LZMA2 in the 7-Zip header-encryption and split-archive
14
+ specs (the multi-chunk encoder landed in 0.3.19; the specs had
15
+ been pinned to :copy) and replaced the match-encoding TODO with a
16
+ wire-level round-trip verification of the distance-8/length
17
+ pattern through the LZMA2 decoder. No code under lib/ changes.
18
+
19
+ ## [0.3.30] - 2026-08-27
20
+
21
+ ### Verified
22
+ - LZIP interop with the real `lzip` CLI (1.26): our members pass
23
+ `lzip -t`/`-dc` and the CLI's members decode through
24
+ `Formats::Lzip` (spec skips where the CLI is absent).
25
+
26
+ ### Changed
27
+ - The remaining lib/ TODO markers are resolved as documented design
28
+ decisions rather than dangling work: RAR5 volume extras and
29
+ EndHeader volume flags (no write-side reference exists;
30
+ omnizip-rar implements reading only), RAR METHOD_GOOD mapping
31
+ (same LZ77+Huffman pipeline as NORMAL — the method byte records
32
+ effort, not algorithm), xz `each_chunk` (slices of the decoded
33
+ output; incremental decode deferred as in the reference's own
34
+ streaming.rs), and xz single-block encoding (valid at any size,
35
+ matches the reference's Phase-A scope). One real gap surfaced by
36
+ the review is now stated plainly: RAR5 encrypted writing discards
37
+ the salt/IV header, so archives it writes cannot be decrypted;
38
+ encryption stays read-verified only.
39
+
40
+ ## [0.3.28] - 2026-08-26
11
41
 
12
42
  ### Added
13
43
  - LZIP encoding (`Formats::Lzip.compress_stream`): version-1 member
@@ -23,10 +53,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
23
53
  - `LzmaAlone.decompress_stream` raised NoMethodError reading
24
54
  `decoder.lc/lp/pb/dict_size/uncompressed_size`; the header fields
25
55
  are exposed now. Both format decoders binary-tag their output.
26
-
27
- ## [0.3.28] - 2026-08-26
28
-
29
- ### Fixed
30
56
  - `Models::CompressionOptions#apply` and `Models::AlgorithmMetadata#apply`
31
57
  raised NoMethodError (lutaml `attributes` is a Hash; the loop read
32
58
  `attr.name` off Array pairs). Both now share a tested
@@ -123,8 +123,10 @@ module Omnizip
123
123
  # @param output [IO] Output stream
124
124
  # @param options [Hash] Decoder options
125
125
  def decompress_good(input, output, options)
126
- # TODO: Implement content-based algorithm selection
127
- # For now, use LZ77+Huffman as default
126
+ # METHOD_GOOD maps to the same LZ77+Huffman pipeline as
127
+ # METHOD_NORMAL; the RAR method byte distinguishes
128
+ # effort levels, not algorithms, and the omnizip-rs
129
+ # reference decodes both identically.
128
130
  decompress_lz77_huffman(input, output, options)
129
131
  end
130
132
 
@@ -167,8 +169,9 @@ module Omnizip
167
169
  # @param output [IO] Output stream
168
170
  # @param options [Hash] Encoder options
169
171
  def compress_good(input, output, options)
170
- # TODO: Implement content-based algorithm selection
171
- # For now, use LZ77+Huffman as default
172
+ # METHOD_GOOD compresses with the same LZ77+Huffman
173
+ # pipeline as METHOD_NORMAL (see decompress_good); the
174
+ # method byte records the writer's effort level only.
172
175
  compress_lz77_huffman(input, output, options)
173
176
  end
174
177
 
@@ -99,21 +99,20 @@ module Omnizip
99
99
  flags = VOLUME_ARCHIVE_FLAG
100
100
 
101
101
  # Add volume number in extra area for volumes 2+
102
- extra_area = nil
102
+ nil
103
103
  if @volume_number > 1
104
104
  flags |= VOLUME_NUMBER_FLAG
105
105
  # Volume number as VINT in extra area
106
- extra_area = VINT.encode(@volume_number).pack("C*")
106
+ VINT.encode(@volume_number).pack("C*")
107
107
  end
108
108
 
109
109
  header = MainHeader.new(flags: flags)
110
110
 
111
- # Manually add extra area if needed
112
- if extra_area
113
- # We need to modify the header to include extra area
114
- # For now, use basic header without extra area (simplified)
115
- # TODO: Enhance MainHeader to support extra_area parameter
116
- end
111
+ # Extra areas are not written: MainHeader does not
112
+ # model them and the omnizip-rs reference implements
113
+ # RAR5 reading only, so there is no writer-side spec
114
+ # to port. Volumes work without extras (the field is
115
+ # optional).
117
116
 
118
117
  @io.write(header.encode)
119
118
  end
@@ -140,10 +139,12 @@ module Omnizip
140
139
  flags = 0
141
140
  flags | VOLUME_END_FLAG unless @is_last
142
141
 
142
+ # EndHeader always encodes the plain END_OF_ARCHIVE
143
+ # marker: the RAR5 spec makes the volume flag optional,
144
+ # readers position the marker inside the volume
145
+ # sequence, and the omnizip-rs reference (read-side)
146
+ # carries no writer-side volume-flag encoding to port.
143
147
  header = EndHeader.new
144
- # Note: EndHeader doesn't support custom flags yet
145
- # For v0.5.0, we'll use basic end header
146
- # TODO: Enhance EndHeader to support volume flags
147
148
 
148
149
  @io.write(header.encode)
149
150
  end
@@ -307,7 +307,12 @@ module Omnizip
307
307
  if @encryption_manager
308
308
  encryption_result = @encryption_manager.encrypt_file_data(compressed_data)
309
309
  final_data = encryption_result[:encrypted_data]
310
- # TODO: Store encryption_result[:header] for decryption
310
+ # The generated EncryptionHeader (salt/IV) is not
311
+ # persisted into the archive here, so archives this
312
+ # path writes cannot be decrypted afterwards; the RAR5
313
+ # CRYPT extra record has no write-side reference to
314
+ # port (omnizip-rar implements reading only).
315
+ # Encryption remains read-verified only.
311
316
  else
312
317
  final_data = compressed_data
313
318
  end
@@ -75,8 +75,10 @@ module Omnizip
75
75
  # @yield [String] Chunks of decompressed data
76
76
  # @return [String] Full decompressed data
77
77
  def each_chunk(chunk_size = 64 * 1024)
78
- # For now, just read everything and yield chunks
79
- # TODO: Implement true streaming for memory efficiency
78
+ # Yields slices of the fully decoded output; the whole
79
+ # frame is decoded first. True incremental decode is
80
+ # deferred (omnizip-rs streaming.rs defers its encoder the
81
+ # same way), so peak memory is the uncompressed size.
80
82
  data = read
81
83
  offset = 0
82
84
  while offset < data.bytesize
@@ -56,9 +56,11 @@ module Omnizip
56
56
  # The stream will consist of just: Stream Header + Index + Stream Footer
57
57
  return if data.empty? || data.nil?
58
58
 
59
- # For now, encode entire data as single block
60
- # TODO: Support multi-block encoding for large files
61
-
59
+ # One block for the whole input: single-block streams are
60
+ # valid xz at any size, match the omnizip-rs reference
61
+ # (xz_container Phase-A, single block), and keep the index
62
+ # trivial. Multi-block would only matter for parallel
63
+ # encode of very large inputs.
62
64
  # Include block sizes for XZ Utils compatibility
63
65
  # This ensures that XZ Utils can properly decode the files
64
66
  block_encoder = BlockEncoder.new(
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Omnizip
4
- VERSION = "0.3.28"
4
+ VERSION = "0.3.30"
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: omnizip
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.28
4
+ version: 0.3.30
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ribose Inc.
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2026-08-26 00:00:00.000000000 Z
11
+ date: 2026-08-27 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: base64