omnizip 0.4.9-arm-linux → 0.4.11-arm-linux
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/.rubocop-oss.yml +814 -0
- data/.rubocop.yml +15 -1
- data/.rubocop_todo.yml +7 -1
- data/CHANGELOG.md +17 -0
- data/Gemfile +23 -1
- data/README.adoc +101 -1282
- data/docs/compatibility.adoc +1 -2
- data/docs/lychee.toml +1 -7
- data/lib/omnizip/algorithms/deflate/constants.rb +5 -3
- data/lib/omnizip/implementations/rust/archive.rb +9 -41
- data/lib/omnizip/implementations/rust/ffi_library.rb +127 -0
- data/lib/omnizip/implementations/rust/fiddle_library.rb +144 -0
- data/lib/omnizip/implementations/rust/library.rb +39 -104
- data/lib/omnizip/implementations/rust.rb +2 -0
- data/lib/omnizip/version.rb +1 -1
- data/lib/tasks/docs.rake +146 -0
- data/readme-docs/rar-archives.adoc +505 -0
- data/scripts/generate_rar_oracle_fixtures.rb +177 -0
- data/vendor/libomnizip_ffi.so +0 -0
- metadata +21 -74
- data/TODO.refactor/00-overview.md +0 -67
- data/TODO.refactor/01-registry-base-class.md +0 -81
- data/TODO.refactor/02-error-hierarchy.md +0 -57
- data/TODO.refactor/03-autoload-entry-point.md +0 -69
- data/TODO.refactor/04-filter-base-consolidation.md +0 -49
- data/TODO.refactor/05-send-private-methods.md +0 -64
- data/TODO.refactor/06-instance-variable-access.md +0 -69
- data/TODO.refactor/07-respond-to-replacement.md +0 -237
- data/TODO.refactor/08-spec-doubles.md +0 -27
- data/TODO.refactor/09-cli-shared-module.md +0 -35
- data/TODO.refactor/10-format-detector-ocp.md +0 -34
- data/TODO.refactor/11-convenience-decoupling.md +0 -38
- data/TODO.refactor/12-thread-safety.md +0 -8
- data/TODO.refactor/13-lutaml-model-migration.md +0 -158
- data/TODO.refactor/14-add-missing-specs.md +0 -25
- data/docs/.github/workflows/docs.yml +0 -142
- data/docs/.gitignore +0 -21
- data/docs/Gemfile +0 -13
- data/docs/README.md +0 -101
- data/docs/_config.yml +0 -112
- data/docs/comparison/feature-matrix.adoc +0 -694
- data/docs/comparison/index.adoc +0 -113
- data/docs/comparison/vs-7zip.adoc +0 -309
- data/docs/comparison/vs-peazip.adoc +0 -77
- data/docs/comparison/vs-rubyzip.adoc +0 -342
- data/docs/comparison/vs-winrar.adoc +0 -100
- data/docs/concepts/index.adoc +0 -129
- data/docs/developer/architecture.adoc +0 -256
- data/docs/developer/contributing.adoc +0 -158
- data/docs/developer/index.adoc +0 -25
- data/docs/developer/testing.adoc +0 -212
- data/docs/getting-started/basic-usage.adoc +0 -263
- data/docs/getting-started/index.adoc +0 -42
- data/docs/getting-started/installation.adoc +0 -138
- data/docs/getting-started/quick-start.adoc +0 -183
- data/docs/getting-started/your-first-archive.adoc +0 -219
- data/docs/guides/advanced-features/encryption.adoc +0 -277
- data/docs/guides/advanced-features/index.adoc +0 -59
- data/docs/guides/advanced-features/parallel-processing.adoc +0 -144
- data/docs/guides/advanced-features/progress-tracking.adoc +0 -124
- data/docs/guides/advanced-features/streaming.adoc +0 -142
- data/docs/guides/archive-formats/gzip-format.adoc +0 -105
- data/docs/guides/archive-formats/index.adoc +0 -160
- data/docs/guides/archive-formats/ole-format.adoc +0 -151
- data/docs/guides/archive-formats/rar-format.adoc +0 -104
- data/docs/guides/archive-formats/rar5.adoc +0 -521
- data/docs/guides/archive-formats/rpm-format.adoc +0 -108
- data/docs/guides/archive-formats/seven-zip-format.adoc +0 -35
- data/docs/guides/archive-formats/tar-format.adoc +0 -106
- data/docs/guides/archive-formats/xz-format.adoc +0 -118
- data/docs/guides/archive-formats/zip-format.adoc +0 -35
- data/docs/guides/compression-algorithms/bzip2.adoc +0 -114
- data/docs/guides/compression-algorithms/deflate.adoc +0 -313
- data/docs/guides/compression-algorithms/index.adoc +0 -190
- data/docs/guides/compression-algorithms/lzma.adoc +0 -394
- data/docs/guides/compression-algorithms/lzma2.adoc +0 -244
- data/docs/guides/compression-algorithms/ppmd.adoc +0 -313
- data/docs/guides/compression-algorithms/zstandard.adoc +0 -296
- data/docs/guides/creating-archives.adoc +0 -348
- data/docs/guides/extracting-archives.adoc +0 -53
- data/docs/guides/format-conversion.adoc +0 -64
- data/docs/guides/index.adoc +0 -49
- data/docs/guides/migration-rubyzip.adoc +0 -217
- data/docs/guides/parity-archives.adoc +0 -605
- data/docs/guides/performance-tuning.adoc +0 -88
- data/docs/index.adoc +0 -228
- data/docs/reference/api/overview.adoc +0 -187
- data/docs/reference/cli/compress-command.adoc +0 -114
- data/docs/reference/cli/overview.adoc +0 -140
- data/docs/reference/index.adoc +0 -26
- data/docs/resources/faq.adoc +0 -185
- data/docs/resources/quick-reference.adoc +0 -222
- data/docs/troubleshooting/index.adoc +0 -207
data/docs/compatibility.adoc
CHANGED
|
@@ -483,7 +483,7 @@ Zip::File.open('archive.zip') do |zip|
|
|
|
483
483
|
end
|
|
484
484
|
----
|
|
485
485
|
|
|
486
|
-
See link
|
|
486
|
+
See the migration notes in the Rubyzip compatibility sections of link:../readme-docs/api-usage.adoc[API usage]
|
|
487
487
|
|
|
488
488
|
=== From 7-Zip CLI to Omnizip API
|
|
489
489
|
|
|
@@ -574,6 +574,5 @@ This makes Omnizip ideal for Ruby applications needing advanced compression beyo
|
|
|
574
574
|
== See Also
|
|
575
575
|
|
|
576
576
|
* link:getting-started/installation.html[Installation Guide]
|
|
577
|
-
* link:guides/migration-rubyzip.html[Migration from Rubyzip]
|
|
578
577
|
* link:getting-started/quick-start.html[Quick Start Tutorial]
|
|
579
578
|
* link:reference/index.html[API Reference]
|
data/docs/lychee.toml
CHANGED
|
@@ -9,12 +9,9 @@ max_cache_age = "1d"
|
|
|
9
9
|
# Check both source files and built site
|
|
10
10
|
include_verbatim = true
|
|
11
11
|
|
|
12
|
-
# Recursively check all files
|
|
13
|
-
recursive = true
|
|
14
|
-
|
|
15
12
|
# File types to check
|
|
16
13
|
include = [
|
|
17
|
-
"
|
|
14
|
+
"site/**/*.html"
|
|
18
15
|
]
|
|
19
16
|
|
|
20
17
|
# Excluded paths
|
|
@@ -46,9 +43,6 @@ user_agent = "lychee/omnizip-docs-link-checker"
|
|
|
46
43
|
# Check HTTP, HTTPS, and file:// schemes
|
|
47
44
|
scheme = ["https", "http", "file"]
|
|
48
45
|
|
|
49
|
-
# Include file:// URLs for local link checking
|
|
50
|
-
include_file = true
|
|
51
|
-
|
|
52
46
|
# Handle different link types
|
|
53
47
|
include_mail = false # Don't check mailto: links
|
|
54
48
|
|
|
@@ -13,11 +13,13 @@ module Omnizip
|
|
|
13
13
|
BEST_COMPRESSION = Zlib::BEST_COMPRESSION
|
|
14
14
|
DEFAULT_COMPRESSION = Zlib::DEFAULT_COMPRESSION
|
|
15
15
|
|
|
16
|
-
# Compression strategies
|
|
16
|
+
# Compression strategies. RLE and FIXED are CRuby-only Zlib
|
|
17
|
+
# constants (JRuby's zlib omits them — found by the JRuby CI
|
|
18
|
+
# leg); the numeric values are fixed by the zlib spec.
|
|
17
19
|
FILTERED = Zlib::FILTERED
|
|
18
20
|
HUFFMAN_ONLY = Zlib::HUFFMAN_ONLY
|
|
19
|
-
RLE = Zlib::RLE
|
|
20
|
-
FIXED = Zlib::FIXED
|
|
21
|
+
RLE = Zlib.const_defined?(:RLE) ? Zlib::RLE : 3
|
|
22
|
+
FIXED = Zlib.const_defined?(:FIXED) ? Zlib::FIXED : 4
|
|
21
23
|
DEFAULT_STRATEGY = Zlib::DEFAULT_STRATEGY
|
|
22
24
|
|
|
23
25
|
# Buffer size for streaming operations
|
|
@@ -7,64 +7,45 @@ module Omnizip
|
|
|
7
7
|
# format (auto-detected), list entries, read entry bytes. This is
|
|
8
8
|
# the archive-level tier surface — the Ruby core remains the
|
|
9
9
|
# fallback (the cdylib unavailable ⇒ these raise LibraryMissing
|
|
10
|
-
# and callers fall back).
|
|
10
|
+
# and callers fall back). Binding-agnostic: dispatches through
|
|
11
|
+
# whichever backend loaded (Fiddle on MRI, ffi on
|
|
12
|
+
# JRuby/TruffleRuby).
|
|
11
13
|
class Archive
|
|
12
14
|
class LibraryMissing < StandardError; end
|
|
13
15
|
|
|
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
16
|
def initialize(data, password: nil)
|
|
19
17
|
lib = Library.instance
|
|
20
18
|
raise LibraryMissing, "omnizip-ffi cdylib not loaded" unless lib
|
|
21
19
|
|
|
22
20
|
@lib = lib
|
|
23
|
-
|
|
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
|
|
21
|
+
@handle = @lib.arch_open(data, password)
|
|
34
22
|
end
|
|
35
23
|
|
|
36
24
|
def entry_count
|
|
37
|
-
@
|
|
25
|
+
@lib.arch_count(@handle)
|
|
38
26
|
end
|
|
39
27
|
|
|
40
28
|
def entry_names
|
|
41
29
|
(0...entry_count).map do |i|
|
|
42
|
-
|
|
43
|
-
next nil if ptr.null?
|
|
44
|
-
|
|
45
|
-
ptr.to_s
|
|
30
|
+
@lib.arch_entry_name(@handle, i)
|
|
46
31
|
end
|
|
47
32
|
end
|
|
48
33
|
|
|
49
34
|
def entry_size(index)
|
|
50
|
-
@
|
|
35
|
+
@lib.arch_entry_size(@handle, index)
|
|
51
36
|
end
|
|
52
37
|
|
|
53
38
|
# Read one entry's uncompressed bytes.
|
|
54
39
|
#
|
|
55
40
|
# @return [String] binary
|
|
56
41
|
def read_entry(index)
|
|
57
|
-
|
|
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"))
|
|
42
|
+
@lib.arch_read_entry(@handle, index)
|
|
62
43
|
end
|
|
63
44
|
|
|
64
45
|
def close
|
|
65
46
|
return unless @handle
|
|
66
47
|
|
|
67
|
-
@
|
|
48
|
+
@lib.arch_close(@handle)
|
|
68
49
|
@handle = nil
|
|
69
50
|
end
|
|
70
51
|
|
|
@@ -74,19 +55,6 @@ module Omnizip
|
|
|
74
55
|
ensure
|
|
75
56
|
archive&.close
|
|
76
57
|
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
58
|
end
|
|
91
59
|
end
|
|
92
60
|
end
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Omnizip
|
|
4
|
+
module Implementations
|
|
5
|
+
module Rust
|
|
6
|
+
# The ffi-gem binding of the cdylib's C ABI — the JRuby /
|
|
7
|
+
# TruffleRuby acceleration path (leptris-ruby's model): those
|
|
8
|
+
# engines ship no Fiddle, so the Fiddle tier cannot load.
|
|
9
|
+
# Mirrors FiddleLibrary's public surface exactly, so the Library
|
|
10
|
+
# facade and the Archive class are binding-agnostic.
|
|
11
|
+
class FfiLibrary
|
|
12
|
+
attr_reader :mod
|
|
13
|
+
|
|
14
|
+
# Attach via a fresh module: FFI::Library is an extend-time
|
|
15
|
+
# DSL — one module per loaded image, like one Fiddle::Handle.
|
|
16
|
+
def self.attach(path)
|
|
17
|
+
mod = Module.new do
|
|
18
|
+
extend ::FFI::Library
|
|
19
|
+
|
|
20
|
+
ffi_lib path.to_s
|
|
21
|
+
end
|
|
22
|
+
mod.attach_function :ozip_compress,
|
|
23
|
+
%i[string buffer_in size_t int pointer], :pointer
|
|
24
|
+
mod.attach_function :ozip_decompress,
|
|
25
|
+
%i[string buffer_in size_t size_t pointer], :pointer
|
|
26
|
+
mod.attach_function :ozip_last_error, [], :pointer
|
|
27
|
+
mod.attach_function :ozip_free, %i[pointer size_t], :void
|
|
28
|
+
mod.attach_function :ozip_version, [], :pointer
|
|
29
|
+
mod.attach_function :ozip_arch_open,
|
|
30
|
+
%i[buffer_in size_t string], :pointer
|
|
31
|
+
mod.attach_function :ozip_arch_count, [:pointer], :size_t
|
|
32
|
+
mod.attach_function :ozip_arch_entry_name,
|
|
33
|
+
%i[pointer size_t], :pointer
|
|
34
|
+
mod.attach_function :ozip_arch_entry_size,
|
|
35
|
+
%i[pointer size_t], :uint64
|
|
36
|
+
mod.attach_function :ozip_arch_read_entry,
|
|
37
|
+
%i[pointer size_t pointer], :pointer
|
|
38
|
+
mod.attach_function :ozip_arch_close, [:pointer], :void
|
|
39
|
+
mod
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def initialize(path)
|
|
43
|
+
@mod = self.class.attach(path)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def compress(codec, data, level)
|
|
47
|
+
out_len = ::FFI::MemoryPointer.new(:size_t)
|
|
48
|
+
ptr = mod.ozip_compress(codec, data, data.bytesize, level, out_len)
|
|
49
|
+
raise Rust::Error, last_error if ptr.null?
|
|
50
|
+
|
|
51
|
+
take(ptr, out_len.read_ulong)
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def decompress(codec, data, expected_len)
|
|
55
|
+
out_len = ::FFI::MemoryPointer.new(:size_t)
|
|
56
|
+
ptr = mod.ozip_decompress(codec, data, data.bytesize,
|
|
57
|
+
expected_len, out_len)
|
|
58
|
+
raise Rust::Error, last_error if ptr.null?
|
|
59
|
+
|
|
60
|
+
take(ptr, out_len.read_ulong)
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def last_error
|
|
64
|
+
ptr = mod.ozip_last_error
|
|
65
|
+
ptr.null? ? "unknown error" : ptr.read_string
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
# Copy out + free one returned buffer: the buffer belongs to
|
|
69
|
+
# the Rust allocator and must never outlive ozip_free.
|
|
70
|
+
def take(ptr, len)
|
|
71
|
+
s = ptr.read_string(len)
|
|
72
|
+
mod.ozip_free(ptr, len)
|
|
73
|
+
s
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def take_buffer(ptr, len)
|
|
77
|
+
take(ptr, len)
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
# The Rust release this cdylib was built from, or nil when the
|
|
81
|
+
# symbol is absent (dylibs older than 0.21.109).
|
|
82
|
+
def dylib_version
|
|
83
|
+
ptr = mod.ozip_version
|
|
84
|
+
ptr.null? ? nil : ptr.read_string
|
|
85
|
+
rescue ::FFI::NotFoundError
|
|
86
|
+
nil
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
# ---- whole-archive surface (ArchHandle) ----
|
|
90
|
+
# FFI auto-converts a Ruby String for a :pointer argument and
|
|
91
|
+
# nil to NULL, and the handle travels as a naked pointer.
|
|
92
|
+
|
|
93
|
+
def arch_open(data, password)
|
|
94
|
+
ptr = mod.ozip_arch_open(data, data.bytesize, password)
|
|
95
|
+
raise Rust::Error, last_error if ptr.null?
|
|
96
|
+
|
|
97
|
+
ptr
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def arch_count(handle)
|
|
101
|
+
mod.ozip_arch_count(handle)
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def arch_entry_name(handle, index)
|
|
105
|
+
ptr = mod.ozip_arch_entry_name(handle, index)
|
|
106
|
+
ptr.null? ? nil : ptr.read_string
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
def arch_entry_size(handle, index)
|
|
110
|
+
mod.ozip_arch_entry_size(handle, index)
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
def arch_read_entry(handle, index)
|
|
114
|
+
out_len = ::FFI::MemoryPointer.new(:size_t)
|
|
115
|
+
ptr = mod.ozip_arch_read_entry(handle, index, out_len)
|
|
116
|
+
raise Rust::Error, last_error if ptr.null?
|
|
117
|
+
|
|
118
|
+
take(ptr, out_len.read_ulong)
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
def arch_close(handle)
|
|
122
|
+
mod.ozip_arch_close(handle)
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
end
|
|
127
|
+
end
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Omnizip
|
|
4
|
+
module Implementations
|
|
5
|
+
module Rust
|
|
6
|
+
# The Fiddle binding of the cdylib's C ABI — the MRI fast path
|
|
7
|
+
# (stdlib, zero gems). One public surface shared with
|
|
8
|
+
# FfiLibrary: codec methods, whole-archive methods, error and
|
|
9
|
+
# version accessors — the binding Library's facade and the
|
|
10
|
+
# Archive class dispatch through.
|
|
11
|
+
class FiddleLibrary
|
|
12
|
+
attr_reader :handle
|
|
13
|
+
|
|
14
|
+
# The shared function table (Fiddle::TYPE_* constants resolve
|
|
15
|
+
# only after a successful require — same reason Library keeps
|
|
16
|
+
# this out of the class body).
|
|
17
|
+
FUNCTION_TABLE = lambda {
|
|
18
|
+
{
|
|
19
|
+
last_error: ["ozip_last_error", [], Fiddle::TYPE_VOIDP],
|
|
20
|
+
free: ["ozip_free", %i[voidp size_t], Fiddle::TYPE_VOID],
|
|
21
|
+
compress: [
|
|
22
|
+
"ozip_compress",
|
|
23
|
+
%i[voidp voidp size_t int voidp],
|
|
24
|
+
Fiddle::TYPE_VOIDP,
|
|
25
|
+
],
|
|
26
|
+
decompress: [
|
|
27
|
+
"ozip_decompress",
|
|
28
|
+
%i[voidp voidp size_t size_t voidp],
|
|
29
|
+
Fiddle::TYPE_VOIDP,
|
|
30
|
+
],
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
FIDDLE_ARG_TYPES = {
|
|
35
|
+
pointer: ->(v) { v.nil? ? nil : Fiddle::Pointer[v] },
|
|
36
|
+
}.freeze
|
|
37
|
+
FIDDLE_RET_TYPES = {
|
|
38
|
+
pointer: Fiddle::TYPE_VOIDP,
|
|
39
|
+
void: Fiddle::TYPE_VOID,
|
|
40
|
+
size_t: Fiddle::TYPE_SIZE_T,
|
|
41
|
+
uint64: Fiddle::TYPE_VOIDP,
|
|
42
|
+
}.freeze
|
|
43
|
+
|
|
44
|
+
def initialize(path)
|
|
45
|
+
@handle = Fiddle.dlopen(path.to_s)
|
|
46
|
+
@funcs = FUNCTION_TABLE.call.each_with_object({}) do |(key, (name, args, ret)), h|
|
|
47
|
+
h[key] = Fiddle::Function.new(@handle[name], args, ret)
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def compress(codec, data, level)
|
|
52
|
+
out_len = [0].pack("Q")
|
|
53
|
+
ptr = @funcs[:compress].call(codec, data, data.bytesize, level, out_len)
|
|
54
|
+
raise Rust::Error, last_error if ptr.null?
|
|
55
|
+
|
|
56
|
+
take(ptr, out_len.unpack1("Q"))
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def decompress(codec, data, expected_len)
|
|
60
|
+
out_len = [0].pack("Q")
|
|
61
|
+
ptr = @funcs[:decompress].call(codec, data, data.bytesize, expected_len, out_len)
|
|
62
|
+
raise Rust::Error, last_error if ptr.null?
|
|
63
|
+
|
|
64
|
+
take(ptr, out_len.unpack1("Q"))
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def last_error
|
|
68
|
+
ptr = @funcs[:last_error].call
|
|
69
|
+
ptr.null? ? "unknown error" : ptr.to_s
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
# Copy out + free one returned buffer: the buffer belongs to
|
|
73
|
+
# the Rust allocator and must never outlive ozip_free.
|
|
74
|
+
def take(ptr, len)
|
|
75
|
+
s = ptr.to_s(len)
|
|
76
|
+
@funcs[:free].call(ptr, len)
|
|
77
|
+
s
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def take_buffer(ptr, len)
|
|
81
|
+
take(ptr, len)
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
# The Rust release this cdylib was built from, or nil when the
|
|
85
|
+
# symbol is absent (dylibs older than 0.21.109).
|
|
86
|
+
def dylib_version
|
|
87
|
+
func = Fiddle::Function.new(@handle["ozip_version"], [], Fiddle::TYPE_VOIDP)
|
|
88
|
+
ptr = func.call
|
|
89
|
+
ptr.null? ? nil : ptr.to_s
|
|
90
|
+
rescue StandardError
|
|
91
|
+
nil
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
# ---- whole-archive surface (ArchHandle) ----
|
|
95
|
+
|
|
96
|
+
def arch_open(data, password)
|
|
97
|
+
pw = password&.to_s
|
|
98
|
+
pw_ptr = pw && !pw.empty? ? Fiddle::Pointer[pw] : nil
|
|
99
|
+
ptr = Fiddle::Function.new(
|
|
100
|
+
@handle["ozip_arch_open"],
|
|
101
|
+
%i[voidp size_t voidp],
|
|
102
|
+
Fiddle::TYPE_VOIDP,
|
|
103
|
+
).call(Fiddle::Pointer[data], data.bytesize, pw_ptr)
|
|
104
|
+
raise Rust::Error, last_error if ptr.null?
|
|
105
|
+
|
|
106
|
+
ptr
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
def arch_count(handle)
|
|
110
|
+
fn = Fiddle::Function.new(@handle["ozip_arch_count"], [:voidp], Fiddle::TYPE_SIZE_T)
|
|
111
|
+
fn.call(handle)
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
def arch_entry_name(handle, index)
|
|
115
|
+
ptr = Fiddle::Function.new(
|
|
116
|
+
@handle["ozip_arch_entry_name"], %i[voidp size_t], Fiddle::TYPE_VOIDP
|
|
117
|
+
).call(handle, index)
|
|
118
|
+
ptr.null? ? nil : ptr.to_s
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
def arch_entry_size(handle, index)
|
|
122
|
+
Fiddle::Function.new(
|
|
123
|
+
@handle["ozip_arch_entry_size"], %i[voidp size_t], Fiddle::TYPE_VOIDP
|
|
124
|
+
).call(handle, index)
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
def arch_read_entry(handle, index)
|
|
128
|
+
out_len = [0].pack("Q")
|
|
129
|
+
ptr = Fiddle::Function.new(
|
|
130
|
+
@handle["ozip_arch_read_entry"], %i[voidp size_t voidp], Fiddle::TYPE_VOIDP
|
|
131
|
+
).call(handle, index, out_len)
|
|
132
|
+
raise Rust::Error, last_error if ptr.null?
|
|
133
|
+
|
|
134
|
+
take(ptr, out_len.unpack1("Q"))
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
def arch_close(handle)
|
|
138
|
+
Fiddle::Function.new(@handle["ozip_arch_close"], [:voidp], Fiddle::TYPE_VOID)
|
|
139
|
+
.call(handle)
|
|
140
|
+
end
|
|
141
|
+
end
|
|
142
|
+
end
|
|
143
|
+
end
|
|
144
|
+
end
|
|
@@ -11,23 +11,32 @@ end
|
|
|
11
11
|
module Omnizip
|
|
12
12
|
module Implementations
|
|
13
13
|
module Rust
|
|
14
|
-
# The loaded cdylib: one handle, lazily opened
|
|
15
|
-
#
|
|
16
|
-
#
|
|
14
|
+
# The loaded cdylib binding: one handle, lazily opened. The
|
|
15
|
+
# BINDING LAYER is chosen at load (leptris-ruby's model):
|
|
16
|
+
#
|
|
17
|
+
# 1. Fiddle (MRI stdlib — the zero-gem fast path)
|
|
18
|
+
# 2. the ffi gem (JRuby / TruffleRuby ship it bundled; MRI
|
|
19
|
+
# without fiddle — Ruby 4.0 without the gem — uses it too)
|
|
20
|
+
# 3. unavailable (pure-Ruby core everywhere)
|
|
21
|
+
#
|
|
22
|
+
# OMNIZIP_BINDING=fiddle|ffi forces a layer (spec seam; also the
|
|
23
|
+
# way to prove the ffi path on MRI). `instance` returns a
|
|
24
|
+
# FiddleLibrary or FfiLibrary — both expose the identical
|
|
25
|
+
# surface (compress / decompress / last_error / take_buffer /
|
|
26
|
+
# dylib_version / arch_*), so callers are binding-agnostic.
|
|
17
27
|
class Library
|
|
18
|
-
# Codecs the cdylib dispatches by name
|
|
19
|
-
#
|
|
20
|
-
#
|
|
21
|
-
# authority; Ruby is the fallback).
|
|
28
|
+
# Codecs the cdylib dispatches by name: the codec-level
|
|
29
|
+
# acceleration surface (both directions by default — Rust is
|
|
30
|
+
# the authority; Ruby is the fallback).
|
|
22
31
|
CODECS = %w[
|
|
23
32
|
bzip2 zstd lzma xz lzma-alone lzip
|
|
24
33
|
deflate deflate64 zlib gzip ppmd7 ppmd8
|
|
25
34
|
].freeze
|
|
26
35
|
|
|
27
36
|
class << self
|
|
28
|
-
# The shared
|
|
29
|
-
#
|
|
30
|
-
# condition, not something to re-attempt per call.
|
|
37
|
+
# The shared binding, or nil when no layer can load. Load
|
|
38
|
+
# failures are cached per layer: a missing library is a
|
|
39
|
+
# stable condition, not something to re-attempt per call.
|
|
31
40
|
def instance
|
|
32
41
|
return @instance if defined?(@instance)
|
|
33
42
|
|
|
@@ -38,6 +47,13 @@ module Omnizip
|
|
|
38
47
|
!instance.nil?
|
|
39
48
|
end
|
|
40
49
|
|
|
50
|
+
# Which binding loaded: "fiddle", "ffi", or nil.
|
|
51
|
+
def binding_layer
|
|
52
|
+
return nil if instance.nil?
|
|
53
|
+
|
|
54
|
+
instance.class.name.end_with?("FfiLibrary") ? "ffi" : "fiddle"
|
|
55
|
+
end
|
|
56
|
+
|
|
41
57
|
# Whether the cdylib dispatches this codec name — exact, or
|
|
42
58
|
# a param-carrying prefix ("ppmd7:o6:m16777216" under
|
|
43
59
|
# "ppmd7").
|
|
@@ -45,7 +61,7 @@ module Omnizip
|
|
|
45
61
|
CODECS.any? { |c| name == c || name.start_with?("#{c}:") }
|
|
46
62
|
end
|
|
47
63
|
|
|
48
|
-
# Drop the cached
|
|
64
|
+
# Drop the cached binding so resolution reruns (test seam:
|
|
49
65
|
# specs exercising fallback/absence paths need a clean
|
|
50
66
|
# slate; production never calls this).
|
|
51
67
|
def forget!
|
|
@@ -56,39 +72,12 @@ module Omnizip
|
|
|
56
72
|
nil
|
|
57
73
|
end
|
|
58
74
|
|
|
59
|
-
# Built lazily: the Fiddle::TYPE_* constants only resolve
|
|
60
|
-
# after a successful require; environments without fiddle
|
|
61
|
-
# (Ruby 4.0 without the gem) never touch this table.
|
|
62
|
-
def function_table
|
|
63
|
-
{
|
|
64
|
-
last_error: ["ozip_last_error", [], Fiddle::TYPE_VOIDP],
|
|
65
|
-
free: ["ozip_free", %i[voidp size_t], Fiddle::TYPE_VOID],
|
|
66
|
-
compress: [
|
|
67
|
-
"ozip_compress",
|
|
68
|
-
%i[voidp voidp size_t int voidp],
|
|
69
|
-
Fiddle::TYPE_VOIDP,
|
|
70
|
-
],
|
|
71
|
-
decompress: [
|
|
72
|
-
"ozip_decompress",
|
|
73
|
-
%i[voidp voidp size_t size_t voidp],
|
|
74
|
-
Fiddle::TYPE_VOIDP,
|
|
75
|
-
],
|
|
76
|
-
}
|
|
77
|
-
end
|
|
78
|
-
|
|
79
75
|
# The Rust release the loaded cdylib was built from (e.g.
|
|
80
|
-
# "0.21.
|
|
76
|
+
# "0.21.112"), read through the ozip_version symbol — the
|
|
81
77
|
# platform-gem smoke gate asserts it matches. nil when the
|
|
82
78
|
# library is absent or predates the symbol.
|
|
83
79
|
def dylib_version
|
|
84
|
-
|
|
85
|
-
return nil if handle.nil?
|
|
86
|
-
|
|
87
|
-
func = Fiddle::Function.new(handle["ozip_version"], [], Fiddle::TYPE_VOIDP)
|
|
88
|
-
ptr = func.call
|
|
89
|
-
ptr.null? ? nil : ptr.to_s
|
|
90
|
-
rescue StandardError
|
|
91
|
-
nil
|
|
80
|
+
instance&.dylib_version
|
|
92
81
|
end
|
|
93
82
|
|
|
94
83
|
# Resolve the cdylib path. Precedence:
|
|
@@ -133,12 +122,17 @@ module Omnizip
|
|
|
133
122
|
path = resolve_path
|
|
134
123
|
return nil if path.nil?
|
|
135
124
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
125
|
+
forced = ENV.fetch("OMNIZIP_BINDING", nil)
|
|
126
|
+
if forced != "ffi" && defined?(Fiddle)
|
|
127
|
+
return FiddleLibrary.new(path)
|
|
139
128
|
end
|
|
140
|
-
|
|
141
|
-
|
|
129
|
+
|
|
130
|
+
# The ffi path: JRuby/TruffleRuby bundle the gem; MRI
|
|
131
|
+
# needs it installed (it is a runtime dependency). A
|
|
132
|
+
# forced layer must not silently fall back to another.
|
|
133
|
+
require "ffi"
|
|
134
|
+
FfiLibrary.new(path)
|
|
135
|
+
rescue LoadError, StandardError => e # Fiddle::DLError, FFI::LoadError, ...
|
|
142
136
|
warn "omnizip: rust backend unavailable (#{e.message}); using pure Ruby" if ENV["OMNIZIP_BACKEND"] == "rust"
|
|
143
137
|
nil
|
|
144
138
|
end
|
|
@@ -152,65 +146,6 @@ module Omnizip
|
|
|
152
146
|
end
|
|
153
147
|
end
|
|
154
148
|
end
|
|
155
|
-
|
|
156
|
-
def initialize(functions, handle = nil)
|
|
157
|
-
@functions = functions.freeze
|
|
158
|
-
@handle = handle
|
|
159
|
-
end
|
|
160
|
-
|
|
161
|
-
def compress(codec, data, level)
|
|
162
|
-
out_len = [0].pack("Q")
|
|
163
|
-
ptr = call(:compress, codec, data, data.bytesize, level, out_len)
|
|
164
|
-
take(ptr, out_len.unpack1("Q"))
|
|
165
|
-
end
|
|
166
|
-
|
|
167
|
-
def decompress(codec, data, expected_len)
|
|
168
|
-
out_len = [0].pack("Q")
|
|
169
|
-
ptr = call(:decompress, codec, data, data.bytesize, expected_len, out_len)
|
|
170
|
-
take(ptr, out_len.unpack1("Q"))
|
|
171
|
-
end
|
|
172
|
-
|
|
173
|
-
# Bind one extra C symbol from the loaded cdylib (the archive
|
|
174
|
-
# surface uses this; Fiddle::Function is immutable once built).
|
|
175
|
-
def bind(symbol, args, ret)
|
|
176
|
-
handle = instance_handle
|
|
177
|
-
raise Error, "cdylib not loaded" if handle.nil?
|
|
178
|
-
|
|
179
|
-
Fiddle::Function.new(handle[symbol.to_s], args, ret)
|
|
180
|
-
end
|
|
181
|
-
|
|
182
|
-
def last_error
|
|
183
|
-
ptr = @functions[:last_error].call
|
|
184
|
-
ptr.null? ? "unknown error" : ptr.to_s
|
|
185
|
-
end
|
|
186
|
-
|
|
187
|
-
# The raw Fiddle::Handle (for extra symbol binds).
|
|
188
|
-
def instance_handle
|
|
189
|
-
@handle
|
|
190
|
-
end
|
|
191
|
-
|
|
192
|
-
# Copy out + free one returned buffer (public seam for the
|
|
193
|
-
# archive surface).
|
|
194
|
-
def take_buffer(ptr, len)
|
|
195
|
-
take(ptr, len)
|
|
196
|
-
end
|
|
197
|
-
|
|
198
|
-
private
|
|
199
|
-
|
|
200
|
-
def call(name, codec, input, input_len, a, out_len)
|
|
201
|
-
ptr = @functions[name].call(codec, input, input_len, a, out_len)
|
|
202
|
-
raise Error, last_error if ptr.null?
|
|
203
|
-
|
|
204
|
-
ptr
|
|
205
|
-
end
|
|
206
|
-
|
|
207
|
-
# Copy out + free in one breath: the buffer belongs to the
|
|
208
|
-
# Rust allocator and must never outlive ozip_free.
|
|
209
|
-
def take(ptr, len)
|
|
210
|
-
string = ptr.to_s(len)
|
|
211
|
-
@functions[:free].call(ptr, len)
|
|
212
|
-
string
|
|
213
|
-
end
|
|
214
149
|
end
|
|
215
150
|
|
|
216
151
|
# Raised when a codec call fails; #message carries the Rust
|
|
@@ -20,6 +20,8 @@ module Omnizip
|
|
|
20
20
|
# fallback on any Rust error.
|
|
21
21
|
module Rust
|
|
22
22
|
autoload :Library, "omnizip/implementations/rust/library"
|
|
23
|
+
autoload :FiddleLibrary, "omnizip/implementations/rust/fiddle_library"
|
|
24
|
+
autoload :FfiLibrary, "omnizip/implementations/rust/ffi_library"
|
|
23
25
|
autoload :Archive, "omnizip/implementations/rust/archive"
|
|
24
26
|
end
|
|
25
27
|
end
|
data/lib/omnizip/version.rb
CHANGED