omnizip 0.4.5 → 0.4.6
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 +4 -4
- data/CHANGELOG.md +11 -0
- data/lib/omnizip/archive_handlers/zip_handler.rb +11 -0
- data/lib/omnizip/backends.rb +20 -0
- data/lib/omnizip/implementations/rust/archive.rb +93 -0
- data/lib/omnizip/implementations/rust/library.rb +23 -2
- data/lib/omnizip/implementations/rust.rb +1 -0
- data/lib/omnizip/version.rb +1 -1
- metadata +3 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 7dc6e02021b3915af5f65944e4d734bd3f95a5624b2a01c30100b6bbc36d5856
|
|
4
|
+
data.tar.gz: 998838c2bd3df94ced6617b9d199a0f2eab552e332d285bdc6819860fef50324
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: f02b277ef180e076b5e0deb3847ed4cdf3f2a83abf829095779cdbfbdbc811891c8d9db9e000fb071c1d3bc164bd3cf4f031b8e19207b7d6e48232a21bf573dc
|
|
7
|
+
data.tar.gz: 4d20378f2973be45cc1cb691894b9c54916c1f5546d45a64a5143feaf8cabf0e053d8d604b8f30b6534d123ad12084021d9815b82945f78f5bfacd64f4daef96
|
data/CHANGELOG.md
CHANGED
|
@@ -25,6 +25,17 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
25
25
|
route decode through the Rust backend when it loads, each pinned by
|
|
26
26
|
a cross-implementation differential. A Rust failure falls back to
|
|
27
27
|
the Ruby core — auto mode is never worse than pure Ruby.
|
|
28
|
+
- Archive-level acceleration: `Implementations::Rust::Archive` binds
|
|
29
|
+
the cdylib's whole-archive surface (open, list entries, read entry)
|
|
30
|
+
over every multi-entry format — zip, tar, cpio, 7z, rar3/4/5, iso,
|
|
31
|
+
rpm, xar, ole, encrypted archives included. `ZipHandler#list` and
|
|
32
|
+
`#read_entry` route through it first (auto mode; any Rust error or
|
|
33
|
+
a missing cdylib falls back to the pure-Ruby path). The tier's
|
|
34
|
+
600-mutant fuzz gate also caught a Rust decompression bomb
|
|
35
|
+
(truncated deflate streams decoded an endless phantom bit-stream;
|
|
36
|
+
fixed upstream in omnizip 0.21.108).
|
|
37
|
+
- `Library#bind` / `Library#take_buffer` seams the archive surface
|
|
38
|
+
rides (Fiddle symbol binding + copy-out-and-free).
|
|
28
39
|
|
|
29
40
|
### Fixed
|
|
30
41
|
- RAR recovery-record repair fails loudly instead of pretending:
|
|
@@ -48,6 +48,14 @@ module Omnizip
|
|
|
48
48
|
end
|
|
49
49
|
|
|
50
50
|
def list(path, details: false, **_)
|
|
51
|
+
# Whole-archive tier (names-only path): the Rust archive
|
|
52
|
+
# surface lists any supported format; the Ruby reader stays
|
|
53
|
+
# the fallback and the details path's source.
|
|
54
|
+
unless details
|
|
55
|
+
tier_names = Omnizip::Backends.archive_entry_names(path)
|
|
56
|
+
return tier_names if tier_names
|
|
57
|
+
end
|
|
58
|
+
|
|
51
59
|
reader = Formats::Zip::Reader.new(path)
|
|
52
60
|
return reader.entries.map(&:filename) unless details
|
|
53
61
|
|
|
@@ -65,6 +73,9 @@ module Omnizip
|
|
|
65
73
|
end
|
|
66
74
|
|
|
67
75
|
def read_entry(path, entry_name)
|
|
76
|
+
tier = Omnizip::Backends.archive_read_entry(path, entry_name)
|
|
77
|
+
return tier unless tier.nil?
|
|
78
|
+
|
|
68
79
|
Formats::Zip::Reader.new(path).read_entry(entry_name)
|
|
69
80
|
end
|
|
70
81
|
|
data/lib/omnizip/backends.rb
CHANGED
|
@@ -94,6 +94,26 @@ module Omnizip
|
|
|
94
94
|
end
|
|
95
95
|
end
|
|
96
96
|
|
|
97
|
+
# Whole-archive tier helpers: nil means "Rust unavailable or
|
|
98
|
+
# entry not found — caller falls back to the Ruby reader".
|
|
99
|
+
def archive_entry_names(path)
|
|
100
|
+
Omnizip::Implementations::Rust::Archive.open(File.binread(path)) { |a| return a.entry_names }
|
|
101
|
+
rescue Omnizip::Implementations::Rust::Archive::LibraryMissing, Omnizip::Implementations::Rust::Error, StandardError
|
|
102
|
+
nil
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
# rubocop:disable-next Lint/ReturnInVoidContext
|
|
106
|
+
def archive_read_entry(path, entry_name)
|
|
107
|
+
Omnizip::Implementations::Rust::Archive.open(File.binread(path)) do |a|
|
|
108
|
+
idx = a.entry_names.index(entry_name)
|
|
109
|
+
return nil if idx.nil?
|
|
110
|
+
|
|
111
|
+
return a.read_entry(idx)
|
|
112
|
+
end
|
|
113
|
+
rescue Omnizip::Implementations::Rust::Archive::LibraryMissing, Omnizip::Implementations::Rust::Error, StandardError
|
|
114
|
+
nil
|
|
115
|
+
end
|
|
116
|
+
|
|
97
117
|
private
|
|
98
118
|
|
|
99
119
|
def backend_rust
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Omnizip
|
|
4
|
+
module Implementations
|
|
5
|
+
module Rust
|
|
6
|
+
# Whole-archive acceleration over the cdylib: open any supported
|
|
7
|
+
# format (auto-detected), list entries, read entry bytes. This is
|
|
8
|
+
# the archive-level tier surface — the Ruby core remains the
|
|
9
|
+
# fallback (the cdylib unavailable ⇒ these raise LibraryMissing
|
|
10
|
+
# and callers fall back).
|
|
11
|
+
class Archive
|
|
12
|
+
class LibraryMissing < StandardError; end
|
|
13
|
+
|
|
14
|
+
# Built lazily inside #initialize: the Fiddle::TYPE_* constants
|
|
15
|
+
# only resolve after a successful require, and Ruby 4.0 moved
|
|
16
|
+
# fiddle out of the default gems (same reason
|
|
17
|
+
# Library.function_table is a method, not a constant).
|
|
18
|
+
def initialize(data, password: nil)
|
|
19
|
+
lib = Library.instance
|
|
20
|
+
raise LibraryMissing, "omnizip-ffi cdylib not loaded" unless lib
|
|
21
|
+
|
|
22
|
+
@lib = lib
|
|
23
|
+
funcs = function_table
|
|
24
|
+
@funcs = funcs.each_with_object({}) do |(key, (name, args, ret)), h|
|
|
25
|
+
h[key] = lib.bind(name, args, ret)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
pw = password&.to_s
|
|
29
|
+
pw_ptr = pw && !pw.empty? ? Fiddle::Pointer[pw] : nil
|
|
30
|
+
@handle = @funcs[:arch_open].call(Fiddle::Pointer[data], data.bytesize, pw_ptr)
|
|
31
|
+
return unless @handle.null?
|
|
32
|
+
|
|
33
|
+
raise Error, @lib.last_error
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def entry_count
|
|
37
|
+
@funcs[:arch_count].call(@handle)
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def entry_names
|
|
41
|
+
(0...entry_count).map do |i|
|
|
42
|
+
ptr = @funcs[:arch_entry_name].call(@handle, i)
|
|
43
|
+
next nil if ptr.null?
|
|
44
|
+
|
|
45
|
+
ptr.to_s
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def entry_size(index)
|
|
50
|
+
@funcs[:arch_entry_size].call(@handle, index)
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# Read one entry's uncompressed bytes.
|
|
54
|
+
#
|
|
55
|
+
# @return [String] binary
|
|
56
|
+
def read_entry(index)
|
|
57
|
+
out_len = [0].pack("Q")
|
|
58
|
+
ptr = @funcs[:arch_read_entry].call(@handle, index, out_len)
|
|
59
|
+
raise Error, @lib.last_error if ptr.null?
|
|
60
|
+
|
|
61
|
+
@lib.take_buffer(ptr, out_len.unpack1("Q"))
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def close
|
|
65
|
+
return unless @handle
|
|
66
|
+
|
|
67
|
+
@funcs[:arch_close].call(@handle)
|
|
68
|
+
@handle = nil
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def self.open(data, password: nil)
|
|
72
|
+
archive = new(data, password: password)
|
|
73
|
+
yield archive
|
|
74
|
+
ensure
|
|
75
|
+
archive&.close
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
private
|
|
79
|
+
|
|
80
|
+
def function_table
|
|
81
|
+
{
|
|
82
|
+
arch_open: ["ozip_arch_open", %i[voidp size_t voidp], Fiddle::TYPE_VOIDP],
|
|
83
|
+
arch_count: ["ozip_arch_count", [:voidp], Fiddle::TYPE_SIZE_T],
|
|
84
|
+
arch_entry_name: ["ozip_arch_entry_name", %i[voidp size_t], Fiddle::TYPE_VOIDP],
|
|
85
|
+
arch_entry_size: ["ozip_arch_entry_size", %i[voidp size_t], Fiddle::TYPE_VOIDP],
|
|
86
|
+
arch_read_entry: ["ozip_arch_read_entry", %i[voidp size_t voidp], Fiddle::TYPE_VOIDP],
|
|
87
|
+
arch_close: ["ozip_arch_close", [:voidp], Fiddle::TYPE_VOID],
|
|
88
|
+
}
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
end
|
|
@@ -86,7 +86,7 @@ module Omnizip
|
|
|
86
86
|
funcs = function_table.each_with_object({}) do |(key, (name, args, ret)), h|
|
|
87
87
|
h[key] = Fiddle::Function.new(handle[name], args, ret)
|
|
88
88
|
end
|
|
89
|
-
new(funcs)
|
|
89
|
+
new(funcs, handle)
|
|
90
90
|
rescue StandardError => e # includes Fiddle::DLError
|
|
91
91
|
warn "omnizip: rust backend unavailable (#{e.message}); using pure Ruby" if ENV["OMNIZIP_BACKEND"] == "rust"
|
|
92
92
|
nil
|
|
@@ -120,8 +120,9 @@ module Omnizip
|
|
|
120
120
|
end
|
|
121
121
|
end
|
|
122
122
|
|
|
123
|
-
def initialize(functions)
|
|
123
|
+
def initialize(functions, handle = nil)
|
|
124
124
|
@functions = functions.freeze
|
|
125
|
+
@handle = handle
|
|
125
126
|
end
|
|
126
127
|
|
|
127
128
|
def compress(codec, data, level)
|
|
@@ -136,11 +137,31 @@ module Omnizip
|
|
|
136
137
|
take(ptr, out_len.unpack1("Q"))
|
|
137
138
|
end
|
|
138
139
|
|
|
140
|
+
# Bind one extra C symbol from the loaded cdylib (the archive
|
|
141
|
+
# surface uses this; Fiddle::Function is immutable once built).
|
|
142
|
+
def bind(symbol, args, ret)
|
|
143
|
+
handle = instance_handle
|
|
144
|
+
raise Error, "cdylib not loaded" if handle.nil?
|
|
145
|
+
|
|
146
|
+
Fiddle::Function.new(handle[symbol.to_s], args, ret)
|
|
147
|
+
end
|
|
148
|
+
|
|
139
149
|
def last_error
|
|
140
150
|
ptr = @functions[:last_error].call
|
|
141
151
|
ptr.null? ? "unknown error" : ptr.to_s
|
|
142
152
|
end
|
|
143
153
|
|
|
154
|
+
# The raw Fiddle::Handle (for extra symbol binds).
|
|
155
|
+
def instance_handle
|
|
156
|
+
@handle
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
# Copy out + free one returned buffer (public seam for the
|
|
160
|
+
# archive surface).
|
|
161
|
+
def take_buffer(ptr, len)
|
|
162
|
+
take(ptr, len)
|
|
163
|
+
end
|
|
164
|
+
|
|
144
165
|
private
|
|
145
166
|
|
|
146
167
|
def call(name, codec, input, input_len, a, out_len)
|
data/lib/omnizip/version.rb
CHANGED
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.4.
|
|
4
|
+
version: 0.4.6
|
|
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-09-
|
|
11
|
+
date: 2026-09-21 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: base64
|
|
@@ -601,6 +601,7 @@ files:
|
|
|
601
601
|
- lib/omnizip/implementations/base/lzma_encoder_base.rb
|
|
602
602
|
- lib/omnizip/implementations/base/state_machine_base.rb
|
|
603
603
|
- lib/omnizip/implementations/rust.rb
|
|
604
|
+
- lib/omnizip/implementations/rust/archive.rb
|
|
604
605
|
- lib/omnizip/implementations/rust/library.rb
|
|
605
606
|
- lib/omnizip/implementations/seven_zip.rb
|
|
606
607
|
- lib/omnizip/implementations/seven_zip/lzma.rb
|