omnizip 0.3.58 → 0.4.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 +4 -4
- data/CHANGELOG.md +50 -0
- data/docs/rust-backend.md +0 -0
- data/lib/omnizip/algorithms/bzip2.rb +9 -2
- data/lib/omnizip/backends.rb +116 -0
- data/lib/omnizip/implementations/rust/library.rb +151 -0
- data/lib/omnizip/implementations/rust.rb +25 -0
- data/lib/omnizip/version.rb +1 -1
- data/lib/omnizip.rb +2 -0
- data/lib/tasks/rust_backend.rake +48 -0
- metadata +7 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 06cbc9a2cd22e74a84d82211efa5e000d857da117bae9a995b76520839211362
|
|
4
|
+
data.tar.gz: e8ab9ec5638953dda59f4b43aa299cfa55262f77d3c9e9f795bbe8800d443592
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: ecd9426b7a3f0a75c68821ec6c2c4c0f0f4f6f5e5ae7a252dc6e1e4fb9f5eb94a01b84f937c797581b44584e8cc4ba244d35e37c2338b1401c46de42299a1627
|
|
7
|
+
data.tar.gz: 268f70fb015d2e21b8079cae5ea4e09a83e036e967cf83bed03cc37de45b61f1875547b1443b419eec502a3bcc264e323d19bb8c503f72437cfb47bbd9d31853
|
data/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,56 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
### Added
|
|
11
|
+
- `rake rust:build` — one command to build and vendor the Rust
|
|
12
|
+
backend's cdylib (clones omnizip-rs or reuses a checkout via
|
|
13
|
+
REPO=); the loader gains the `vendor/` resolution tier behind
|
|
14
|
+
`OMNIZIP_FFI_DYLIB`. No toolchain, no failure: the gem stays
|
|
15
|
+
pure Ruby. docs/rust-backend.md documents the tier policy,
|
|
16
|
+
resolution order, and CI pinning.
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
### Added
|
|
20
|
+
- Rust-accelerated backend tier: `Omnizip::Backends` is the single
|
|
21
|
+
tier switch between the pure-Ruby cores and the `omnizip-ffi`
|
|
22
|
+
cdylib (stdlib Fiddle — no compiled extension). Under the default
|
|
23
|
+
`auto` policy decode prefers the Rust decoders (output-invariant
|
|
24
|
+
and carrying fixes the Ruby cores lack); encode stays pure-Ruby
|
|
25
|
+
so frame bytes never depend on the tier (content addressing
|
|
26
|
+
stability). `OMNIZIP_BACKEND=rust|ruby` forces a tier; load
|
|
27
|
+
failures fall back to pure Ruby silently. A cross-tier
|
|
28
|
+
differential spec pins decode agreement in both encode
|
|
29
|
+
directions.
|
|
30
|
+
- BZip2 routes its byte core through the backend seam (both paths
|
|
31
|
+
share the algorithm class's IO plumbing).
|
|
32
|
+
|
|
33
|
+
### Fixed
|
|
34
|
+
- Ruby 4.0 compatibility for the tier loader: fiddle moved out of
|
|
35
|
+
the default gems and is now required lazily with a fallback;
|
|
36
|
+
library resolution no longer references Fiddle constants when
|
|
37
|
+
fiddle is absent and considers `.dll` candidates on Windows.
|
|
38
|
+
|
|
39
|
+
## [0.3.58] - 2026-09-03
|
|
40
|
+
|
|
41
|
+
### Fixed
|
|
42
|
+
- `solid: true` on the RAR5 writer silently fell back to
|
|
43
|
+
independent STORE entries (the RAR-compatible LZSS encoder is not
|
|
44
|
+
available); the writer now warns about the fallback. The
|
|
45
|
+
creating-archives guide's solid section stops advertising
|
|
46
|
+
compression ratios the option cannot deliver and the stale
|
|
47
|
+
`(v0.5.0)` version-gate labels are gone from shipped features.
|
|
48
|
+
- The ZIP writer never emits ZIP64 structures; past the classic
|
|
49
|
+
limits `Array#pack` failed with a bare `RangeError` that named no
|
|
50
|
+
limit. Both write paths now raise `UnsupportedFormatError` naming
|
|
51
|
+
the 65,535-entry / 4 GiB size / 4 GiB offset classic-ZIP limits
|
|
52
|
+
(ZIP64 writing is not supported).
|
|
53
|
+
|
|
54
|
+
### Changed
|
|
55
|
+
- The decompress command's archive-vs-stream routing derives from
|
|
56
|
+
the registry's extension routes instead of a private table, so
|
|
57
|
+
formats added to the registry route correctly instead of silently
|
|
58
|
+
falling into the single-file stream path.
|
|
59
|
+
|
|
10
60
|
## [0.3.57] - 2026-09-02
|
|
11
61
|
|
|
12
62
|
### Fixed
|
|
File without changes
|
|
@@ -67,7 +67,10 @@ module Omnizip
|
|
|
67
67
|
# @return [void]
|
|
68
68
|
def compress(input_stream, output_stream, options = nil)
|
|
69
69
|
level = level_from(options)
|
|
70
|
-
|
|
70
|
+
data = input_stream.read
|
|
71
|
+
output_stream.write(
|
|
72
|
+
Backends.compress("bzip2", data, level) { Bz2.compress(data, level) },
|
|
73
|
+
)
|
|
71
74
|
end
|
|
72
75
|
|
|
73
76
|
# Decompress BZip2-compressed data
|
|
@@ -78,7 +81,11 @@ module Omnizip
|
|
|
78
81
|
# @return [void]
|
|
79
82
|
def decompress(input_stream, output_stream, _options = nil)
|
|
80
83
|
output_stream.set_encoding(Encoding::BINARY)
|
|
81
|
-
|
|
84
|
+
compressed = input_stream.read
|
|
85
|
+
plain = Backends.decompress("bzip2", compressed, Backends::UNKNOWN_LENGTH) do
|
|
86
|
+
Bz2.decompress(compressed)
|
|
87
|
+
end
|
|
88
|
+
output_stream.write(plain)
|
|
82
89
|
end
|
|
83
90
|
|
|
84
91
|
private
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Omnizip
|
|
4
|
+
# Byte-level codec backend resolution — THE tier switch
|
|
5
|
+
# (TODO.ref-parity/62). Both paths share the algorithm classes'
|
|
6
|
+
# IO plumbing; only the byte core swaps.
|
|
7
|
+
#
|
|
8
|
+
# Policy:
|
|
9
|
+
# OMNIZIP_BACKEND=ruby force pure Ruby everywhere
|
|
10
|
+
# OMNIZIP_BACKEND=rust require the Rust library (raises when absent)
|
|
11
|
+
# OMNIZIP_BACKEND=auto (default) Rust for directions marked
|
|
12
|
+
# safe, Ruby otherwise, whenever the
|
|
13
|
+
# cdylib is loadable
|
|
14
|
+
#
|
|
15
|
+
# Safety marks (owner guidance 2026-09-19: some Ruby cores are
|
|
16
|
+
# LESS accurate than the Rust ports — be careful which tier is
|
|
17
|
+
# trusted for what):
|
|
18
|
+
# - decompress -> :rust by default. Decode is output-invariant
|
|
19
|
+
# AND the Rust decoders carry fixes the Ruby cores lack;
|
|
20
|
+
# disagreements are treated as Ruby-side accuracy findings
|
|
21
|
+
# (see the cross-tier differential spec), not coin flips.
|
|
22
|
+
# - compress -> :ruby by default. Byte-stability for content
|
|
23
|
+
# addressing (DropId dedup assumes identical frames); the
|
|
24
|
+
# Rust encoders produce DIFFERENT valid streams (probed:
|
|
25
|
+
# bzip2 L6, Ruby 906 B vs Rust 874 B). OMNIZIP_BACKEND=rust
|
|
26
|
+
# opts into Rust encode where correctness-of-ratio is wanted
|
|
27
|
+
# over frame stability.
|
|
28
|
+
module Backends
|
|
29
|
+
# Codecs whose Rust encoder is BYTE-IDENTICAL to the pure-Ruby
|
|
30
|
+
# encoder (empirical, differential-corpus-proven). 2026-09-19
|
|
31
|
+
# probe: bzip2 L6 on a 50 KB corpus => Ruby 906 B vs Rust 874 B —
|
|
32
|
+
# DIFFERENT valid streams. No codec qualifies; encode stays
|
|
33
|
+
# pure-Ruby under :auto so downstream content addressing never
|
|
34
|
+
# sees tier-dependent bytes. Force with OMNIZIP_BACKEND=rust.
|
|
35
|
+
RUST_ENCODE_IDENTICAL = {}.freeze
|
|
36
|
+
|
|
37
|
+
# usize::MAX — the FFI's "caller does not know the plaintext
|
|
38
|
+
# size" sentinel (streaming decode contract).
|
|
39
|
+
UNKNOWN_LENGTH = 0xFFFF_FFFF_FFFF_FFFF
|
|
40
|
+
|
|
41
|
+
class << self
|
|
42
|
+
def for(codec, direction)
|
|
43
|
+
mode = ENV.fetch("OMNIZIP_BACKEND", "auto")
|
|
44
|
+
|
|
45
|
+
return backend_rust if mode == "rust"
|
|
46
|
+
return backend_ruby if mode == "ruby"
|
|
47
|
+
|
|
48
|
+
return backend_ruby unless Implementations::Rust::Library.available?
|
|
49
|
+
return backend_ruby unless Implementations::Rust::Library::CODECS.include?(codec)
|
|
50
|
+
|
|
51
|
+
direction == :encode && !RUST_ENCODE_IDENTICAL.fetch(codec, false) ? backend_ruby : backend_rust
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
# Compress through the tier-selected backend. The block is
|
|
55
|
+
# the pure-Ruby core — evaluated ONLY on the Ruby path, so
|
|
56
|
+
# the accelerated path never pays for it.
|
|
57
|
+
def compress(codec, data, level, &ruby_core)
|
|
58
|
+
backend = Backends.for(codec, :encode)
|
|
59
|
+
return yield if backend == RubyBackend
|
|
60
|
+
|
|
61
|
+
backend.compress(codec, data, level)
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
# Decompress through the tier-selected backend (output-
|
|
65
|
+
# invariant: :auto prefers Rust whenever loadable).
|
|
66
|
+
def decompress(codec, data, expected_len, &ruby_core)
|
|
67
|
+
backend = Backends.for(codec, :decode)
|
|
68
|
+
return yield if backend == RubyBackend
|
|
69
|
+
|
|
70
|
+
backend.decompress(codec, data, expected_len)
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
private
|
|
74
|
+
|
|
75
|
+
def backend_rust
|
|
76
|
+
RustBackend
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def backend_ruby
|
|
80
|
+
RubyBackend
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
# The pure-Ruby path: a no-op backend — callers fall back to
|
|
85
|
+
# their own in-tree cores, which ARE the reference. Kept as an
|
|
86
|
+
# object for symmetry with the Rust side.
|
|
87
|
+
module RubyBackend
|
|
88
|
+
module_function
|
|
89
|
+
|
|
90
|
+
# :nocov:
|
|
91
|
+
def compress(codec, _data, _level)
|
|
92
|
+
raise Error, "#{codec}: ruby core not wired through Backends (call it directly)"
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def decompress(codec, _data, _expected_len)
|
|
96
|
+
raise Error, "#{codec}: ruby core not wired through Backends (call it directly)"
|
|
97
|
+
end
|
|
98
|
+
# :nocov:
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
# The Rust-accelerated path over the Fiddle handle.
|
|
102
|
+
module RustBackend
|
|
103
|
+
module_function
|
|
104
|
+
|
|
105
|
+
def compress(codec, data, level)
|
|
106
|
+
Implementations::Rust::Library.instance.compress(codec, data, level)
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
def decompress(codec, data, expected_len)
|
|
110
|
+
Implementations::Rust::Library.instance.decompress(codec, data, expected_len)
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
class Error < StandardError; end
|
|
115
|
+
end
|
|
116
|
+
end
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
begin
|
|
4
|
+
# Ruby 4.0 moved fiddle out of the default gems.
|
|
5
|
+
require "fiddle"
|
|
6
|
+
require "pathname"
|
|
7
|
+
rescue LoadError
|
|
8
|
+
# :nocov:
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
module Omnizip
|
|
12
|
+
module Implementations
|
|
13
|
+
module Rust
|
|
14
|
+
# The loaded cdylib: one handle, lazily opened, thread-safe
|
|
15
|
+
# after initialization (Fiddle::Function is immutable once
|
|
16
|
+
# built; compress/decompress are pure C calls).
|
|
17
|
+
class Library
|
|
18
|
+
# Codecs the cdylib dispatches by name.
|
|
19
|
+
CODECS = %w[bzip2 zstd lzma xz].freeze
|
|
20
|
+
|
|
21
|
+
class << self
|
|
22
|
+
# The shared handle, or nil when the cdylib cannot load.
|
|
23
|
+
# Load failures are cached: a missing library is a stable
|
|
24
|
+
# condition, not something to re-attempt per call.
|
|
25
|
+
def instance
|
|
26
|
+
return @instance if defined?(@instance)
|
|
27
|
+
|
|
28
|
+
@instance = open
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def available?
|
|
32
|
+
!instance.nil?
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# Drop the cached handle so resolution reruns (test seam:
|
|
36
|
+
# specs exercising fallback/absence paths need a clean
|
|
37
|
+
# slate; production never calls this).
|
|
38
|
+
def forget!
|
|
39
|
+
@instance = nil if defined?(@instance)
|
|
40
|
+
nil
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# Built lazily: the Fiddle::TYPE_* constants only resolve
|
|
44
|
+
# after a successful require; environments without fiddle
|
|
45
|
+
# (Ruby 4.0 without the gem) never touch this table.
|
|
46
|
+
def function_table
|
|
47
|
+
{
|
|
48
|
+
last_error: ["ozip_last_error", [], Fiddle::TYPE_VOIDP],
|
|
49
|
+
free: ["ozip_free", %i[voidp size_t], Fiddle::TYPE_VOID],
|
|
50
|
+
compress: [
|
|
51
|
+
"ozip_compress",
|
|
52
|
+
%i[voidp voidp size_t int voidp],
|
|
53
|
+
Fiddle::TYPE_VOIDP,
|
|
54
|
+
],
|
|
55
|
+
decompress: [
|
|
56
|
+
"ozip_decompress",
|
|
57
|
+
%i[voidp voidp size_t size_t voidp],
|
|
58
|
+
Fiddle::TYPE_VOIDP,
|
|
59
|
+
],
|
|
60
|
+
}
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
private
|
|
64
|
+
|
|
65
|
+
def open
|
|
66
|
+
path = resolve_path
|
|
67
|
+
return nil if path.nil?
|
|
68
|
+
|
|
69
|
+
handle = Fiddle.dlopen(path.to_s)
|
|
70
|
+
funcs = function_table.each_with_object({}) do |(key, (name, args, ret)), h|
|
|
71
|
+
h[key] = Fiddle::Function.new(handle[name], args, ret)
|
|
72
|
+
end
|
|
73
|
+
new(funcs)
|
|
74
|
+
rescue StandardError => e # includes Fiddle::DLError
|
|
75
|
+
warn "omnizip: rust backend unavailable (#{e.message}); using pure Ruby" if ENV["OMNIZIP_BACKEND"] == "rust"
|
|
76
|
+
nil
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def resolve_path
|
|
80
|
+
explicit = ENV.fetch("OMNIZIP_FFI_DYLIB", nil)
|
|
81
|
+
return Pathname.new(explicit) if explicit && File.file?(explicit)
|
|
82
|
+
|
|
83
|
+
candidates = [
|
|
84
|
+
Pathname.new(__dir__).join("../../../../ext/libomnizip_ffi"),
|
|
85
|
+
sibling_checkout,
|
|
86
|
+
].compact.flatten
|
|
87
|
+
|
|
88
|
+
%w[.dylib .so .dll].each do |ext|
|
|
89
|
+
candidates.each do |dir|
|
|
90
|
+
path = dir.join("libomnizip_ffi#{ext}")
|
|
91
|
+
return path if path.file?
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
nil
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
# A sibling omnizip-rs checkout: ../omnizip-rs relative to
|
|
98
|
+
# this gem's repository root.
|
|
99
|
+
def sibling_checkout
|
|
100
|
+
gem_root = Pathname.new(__dir__).join("../../../../..")
|
|
101
|
+
%w[omnizip-rs target/release].map do |rel|
|
|
102
|
+
gem_root.join(rel, "target/release")
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
def initialize(functions)
|
|
108
|
+
@functions = functions.freeze
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
def compress(codec, data, level)
|
|
112
|
+
out_len = [0].pack("Q")
|
|
113
|
+
ptr = call(:compress, codec, data, data.bytesize, level, out_len)
|
|
114
|
+
take(ptr, out_len.unpack1("Q"))
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
def decompress(codec, data, expected_len)
|
|
118
|
+
out_len = [0].pack("Q")
|
|
119
|
+
ptr = call(:decompress, codec, data, data.bytesize, expected_len, out_len)
|
|
120
|
+
take(ptr, out_len.unpack1("Q"))
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
def last_error
|
|
124
|
+
ptr = @functions[:last_error].call
|
|
125
|
+
ptr.null? ? "unknown error" : ptr.to_s
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
private
|
|
129
|
+
|
|
130
|
+
def call(name, codec, input, input_len, a, out_len)
|
|
131
|
+
ptr = @functions[name].call(codec, input, input_len, a, out_len)
|
|
132
|
+
raise Error, last_error if ptr.null?
|
|
133
|
+
|
|
134
|
+
ptr
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
# Copy out + free in one breath: the buffer belongs to the
|
|
138
|
+
# Rust allocator and must never outlive ozip_free.
|
|
139
|
+
def take(ptr, len)
|
|
140
|
+
string = ptr.to_s(len)
|
|
141
|
+
@functions[:free].call(ptr, len)
|
|
142
|
+
string
|
|
143
|
+
end
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
# Raised when a codec call fails; #message carries the Rust
|
|
147
|
+
# side's last_error text.
|
|
148
|
+
class Error < StandardError; end
|
|
149
|
+
end
|
|
150
|
+
end
|
|
151
|
+
end
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Omnizip
|
|
4
|
+
module Implementations
|
|
5
|
+
# Rust-accelerated codec backend (TODO.ref-parity/62).
|
|
6
|
+
#
|
|
7
|
+
# Loads the omnizip-ffi cdylib through stdlib Fiddle — no
|
|
8
|
+
# compiled Ruby extension, no rake-compiler. When the library
|
|
9
|
+
# is unavailable the gem transparently stays on the pure-Ruby
|
|
10
|
+
# path; nothing about the fallback is an error.
|
|
11
|
+
#
|
|
12
|
+
# Library resolution order:
|
|
13
|
+
# 1. ENV['OMNIZIP_FFI_DYLIB'] (explicit path)
|
|
14
|
+
# 2. ../target/release/ inside a sibling omnizip-rs checkout
|
|
15
|
+
#
|
|
16
|
+
# Tier policy (see Omnizip::Backends): decode is always
|
|
17
|
+
# output-invariant, so `auto` uses Rust for decode whenever
|
|
18
|
+
# available; encode uses Rust only when byte-identity with the
|
|
19
|
+
# pure-Ruby encoder has been verified for that codec
|
|
20
|
+
# (RUST_ENCODE_IDENTICAL), keeping content addressing stable.
|
|
21
|
+
module Rust
|
|
22
|
+
autoload :Library, "omnizip/implementations/rust/library"
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
data/lib/omnizip/version.rb
CHANGED
data/lib/omnizip.rb
CHANGED
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
|
|
19
19
|
module Omnizip
|
|
20
20
|
autoload :VERSION, "omnizip/version"
|
|
21
|
+
autoload :Backends, "omnizip/backends"
|
|
21
22
|
|
|
22
23
|
autoload :Error, "omnizip/error"
|
|
23
24
|
autoload :CompressionError, "omnizip/error"
|
|
@@ -120,6 +121,7 @@ module Omnizip
|
|
|
120
121
|
module Implementations
|
|
121
122
|
autoload :SevenZip, "omnizip/implementations/seven_zip"
|
|
122
123
|
autoload :XZUtils, "omnizip/implementations/xz_utils"
|
|
124
|
+
autoload :Rust, "omnizip/implementations/rust"
|
|
123
125
|
|
|
124
126
|
module SevenZip
|
|
125
127
|
module LZMA
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# One command to build the Rust-accelerated backend's cdylib into
|
|
4
|
+
# the gem's vendor dir (TODO.ref-parity/62 — the distribution
|
|
5
|
+
# story). Requires git + a Rust toolchain; the gem stays fully
|
|
6
|
+
# functional (pure Ruby) without ever running this.
|
|
7
|
+
#
|
|
8
|
+
# bundle exec rake rust:build
|
|
9
|
+
# bundle exec rake rust:build REPO=/path/to/omnizip-rs
|
|
10
|
+
desc "Build the omnizip-ffi cdylib into vendor/ for the Rust tier"
|
|
11
|
+
task "rust:build" do
|
|
12
|
+
repo = ENV.fetch("REPO", nil)
|
|
13
|
+
gem_root = File.expand_path("../..", __dir__)
|
|
14
|
+
build = File.join(gem_root, "build", "rust")
|
|
15
|
+
|
|
16
|
+
require "fileutils"
|
|
17
|
+
FileUtils.mkdir_p(build)
|
|
18
|
+
src = if repo
|
|
19
|
+
File.expand_path(repo)
|
|
20
|
+
else
|
|
21
|
+
target = File.join(build, "omnizip-rs")
|
|
22
|
+
if File.directory?(File.join(target, ".git"))
|
|
23
|
+
system("git", "-C", target, "fetch", "--quiet", "origin") or abort "git fetch failed in #{target}"
|
|
24
|
+
system("git", "-C", target, "reset", "--quiet", "--hard", "origin/main") or abort "git reset failed"
|
|
25
|
+
else
|
|
26
|
+
system("git", "clone", "--quiet", "--depth", "1",
|
|
27
|
+
"https://github.com/omnizip/omnizip-rs.git", target) or abort "clone failed"
|
|
28
|
+
end
|
|
29
|
+
target
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
abort "#{src} has no omnizip-ffi crate" unless File.directory?(File.join(src, "omnizip-ffi"))
|
|
33
|
+
|
|
34
|
+
system("cargo", "build", "--release", "-p", "omnizip-ffi", chdir: src) or abort "cargo build failed"
|
|
35
|
+
|
|
36
|
+
vendor = File.join(gem_root, "vendor")
|
|
37
|
+
FileUtils.mkdir_p(vendor)
|
|
38
|
+
%w[dylib so dll].each do |ext|
|
|
39
|
+
candidate = File.join(src, "target", "release", "libomnizip_ffi.#{ext}")
|
|
40
|
+
next unless File.file?(candidate)
|
|
41
|
+
|
|
42
|
+
FileUtils.cp(candidate, vendor)
|
|
43
|
+
puts "vendored: #{File.join(vendor, "libomnizip_ffi.#{ext}")}"
|
|
44
|
+
puts "tier: OMNIZIP_BACKEND=auto now uses the Rust decoders"
|
|
45
|
+
exit 0
|
|
46
|
+
end
|
|
47
|
+
abort "cdylib not found under #{src}/target/release"
|
|
48
|
+
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.
|
|
4
|
+
version: 0.4.1
|
|
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-19 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: base64
|
|
@@ -211,6 +211,7 @@ files:
|
|
|
211
211
|
- docs/reference/index.adoc
|
|
212
212
|
- docs/resources/faq.adoc
|
|
213
213
|
- docs/resources/quick-reference.adoc
|
|
214
|
+
- docs/rust-backend.md
|
|
214
215
|
- docs/troubleshooting/index.adoc
|
|
215
216
|
- docs/xar_format.md
|
|
216
217
|
- examples/api_comparison.rb
|
|
@@ -334,6 +335,7 @@ files:
|
|
|
334
335
|
- lib/omnizip/archive_handlers/tar_handler.rb
|
|
335
336
|
- lib/omnizip/archive_handlers/xar_handler.rb
|
|
336
337
|
- lib/omnizip/archive_handlers/zip_handler.rb
|
|
338
|
+
- lib/omnizip/backends.rb
|
|
337
339
|
- lib/omnizip/buffer.rb
|
|
338
340
|
- lib/omnizip/buffer/memory_archive.rb
|
|
339
341
|
- lib/omnizip/buffer/memory_extractor.rb
|
|
@@ -598,6 +600,8 @@ files:
|
|
|
598
600
|
- lib/omnizip/implementations/base/lzma_decoder_base.rb
|
|
599
601
|
- lib/omnizip/implementations/base/lzma_encoder_base.rb
|
|
600
602
|
- lib/omnizip/implementations/base/state_machine_base.rb
|
|
603
|
+
- lib/omnizip/implementations/rust.rb
|
|
604
|
+
- lib/omnizip/implementations/rust/library.rb
|
|
601
605
|
- lib/omnizip/implementations/seven_zip.rb
|
|
602
606
|
- lib/omnizip/implementations/seven_zip/lzma.rb
|
|
603
607
|
- lib/omnizip/implementations/seven_zip/lzma/decoder.rb
|
|
@@ -719,6 +723,7 @@ files:
|
|
|
719
723
|
- lib/omnizip/zip/input_stream.rb
|
|
720
724
|
- lib/omnizip/zip/output_stream.rb
|
|
721
725
|
- lib/tasks/msi_test_files.rake
|
|
726
|
+
- lib/tasks/rust_backend.rake
|
|
722
727
|
- readme-docs/advanced-features.adoc
|
|
723
728
|
- readme-docs/api-usage.adoc
|
|
724
729
|
- readme-docs/architecture.adoc
|