omnizip 0.4.8-arm-linux → 0.4.10-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.
data/.rubocop.yml CHANGED
@@ -1,7 +1,15 @@
1
1
  # Auto-generated by Cimas: Do not edit it manually!
2
2
  # See https://github.com/metanorma/cimas
3
+ #
4
+ # PINNED SNAPSHOT (2026-09-21): the config used to inherit the
5
+ # oss-guides file over HTTPS directly. A remote main that moves under
6
+ # us — combined with CI's fresh dependency resolution — made rubocop
7
+ # behavior change with zero repo-side diffs (the 2026-09-21
8
+ # Metrics/BlockLength + standard-custom CI breakage). The snapshot is
9
+ # refreshed deliberately: refresh with
10
+ # curl -sL https://raw.githubusercontent.com/riboseinc/oss-guides/main/ci/rubocop.yml -o .rubocop-oss.yml
3
11
  inherit_from:
4
- - https://raw.githubusercontent.com/riboseinc/oss-guides/main/ci/rubocop.yml
12
+ - .rubocop-oss.yml
5
13
  - .rubocop_todo.yml
6
14
 
7
15
  # local repo-specific modifications
@@ -10,6 +18,12 @@ inherit_from:
10
18
  AllCops:
11
19
  NewCops: enable
12
20
  TargetRubyVersion: 3.0
21
+ # rake rust:build clones omnizip-rs under lib/tasks/build (and a
22
+ # build/ dir may exist at the root) — never lint the vendored
23
+ # checkout's Ruby files.
24
+ Exclude:
25
+ - "build/**/*"
26
+ - "lib/tasks/build/**/*"
13
27
 
14
28
  # 7-Zip SDK uses is_char_state? naming convention (IsCharState in C)
15
29
  # We keep this naming to match the SDK reference
data/.rubocop_todo.yml CHANGED
@@ -138,6 +138,13 @@ Metrics/AbcSize:
138
138
  # AllowedMethods: refine
139
139
  Metrics/BlockLength:
140
140
  Max: 81
141
+ # Merge with the inherited exclusions (oss-guides excludes
142
+ # spec/**/* here) — a bare Exclude key REPLACES the inherited list,
143
+ # which un-excluded every spec and re-activated ~190 dormant
144
+ # offenses under rubocop 1.91 (2026-09-21 CI).
145
+ inherit_mode:
146
+ merge:
147
+ - Exclude
141
148
  Exclude:
142
149
  - "lib/tasks/platform_gems.rake"
143
150
 
data/CHANGELOG.md CHANGED
@@ -7,6 +7,33 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ### Added
11
+ - ffi tier — JRuby/TruffleRuby acceleration (leptris-ruby model):
12
+ the cdylib's C ABI is additionally bound through the `ffi` gem
13
+ (`FfiLibrary`, same surface as the Fiddle binding), with
14
+ `Library` dispatching Fiddle → ffi → pure Ruby and
15
+ `OMNIZIP_BINDING=fiddle|ffi` to force a layer. `ffi` becomes a
16
+ runtime dependency (JRuby/TruffleRuby bundle it).
17
+ - Owned CI build matrix (windows-11-arm, musl containers, armv7
18
+ qemu, JRuby ffi gate) replacing the cimas rake job, whose fresh
19
+ dependency resolution made rubocop break with zero repo-side
20
+ diffs. The installed-gem smoke reports its binding layer.
21
+
22
+ ### Fixed
23
+ - `Zlib::RLE` is CRuby-only: `require "omnizip"` raised NameError on
24
+ JRuby (and any engine without CRuby's zlib extension) as soon as
25
+ the Deflate algorithm loaded. RLE/FIXED strategy constants are now
26
+ guarded with their spec-defined values (3 and 4). Found by the new
27
+ JRuby CI leg — the matrix found a bug on day one.
28
+ - RPM archive-tier readiness: the dylib requirement is
29
+ **0.21.112**, not 0.21.111 — 0.21.111's `./`-stripping was half the
30
+ fix (the Ruby handler's names are ABSOLUTE, from the header's
31
+ DIRNAMES/BASENAMES; 0.21.112 normalizes the cpio names to
32
+ `/usr/…`). The tier spec version-guards accordingly, and with
33
+ 0.21.112 the rpm tier is fully active (list stays Ruby by design;
34
+ read_entry is accelerated). Ships with the rubocop exclusion for
35
+ the tier spec that a CI-skipped merge dropped.
36
+
10
37
  ### Added
11
38
  - Archive-level tier for every handler: tar, cpio, iso, xar, 7z and
12
39
  rar3/4/5 (with `password:` passthrough) route list(names) and
data/Gemfile CHANGED
@@ -9,7 +9,16 @@ gemspec
9
9
  gem "csv"
10
10
  gem "rake"
11
11
  gem "rspec"
12
- gem "rubocop"
12
+ # Pinned: CI resolves dependencies fresh (no lockfile), and rubocop
13
+ # 1.91 changed Metrics/BlockLength scoping + plugin loading, which
14
+ # resurrected ~190 dormant spec offenses and crashes on the inherited
15
+ # oss-guides `standard-custom` require. Re-pin after triage upstream.
16
+ gem "rubocop", "1.90.0"
17
+ # rubocop's plugin loader auto-declares this via standard's
18
+ # default_lint_roller_plugin metadata where the gem is present; on
19
+ # runners without it the require crashes rubocop outright. Ship it in
20
+ # the bundle so the load always resolves (its plugin is inert without
21
+ # a .standard.yml).
13
22
  gem "rubocop-performance"
14
23
  gem "rubocop-rake"
15
24
  gem "rubocop-rspec"
data/README.adoc CHANGED
@@ -38,9 +38,11 @@ automatically — no Rust toolchain, no compilation:
38
38
  * arm64-darwin, x86_64-darwin
39
39
  * x64-mingw-ucrt, aarch64-mingw-ucrt, x64-mingw32
40
40
 
41
- Everywhere else — including JRuby/TruffleRuby and any platform without
42
- a binary — the gem stays fully functional on its pure-Ruby core (the
43
- acceleration is strictly optional). Three escape hatches:
41
+ Everywhere else — including any platform without a binary — the gem
42
+ stays fully functional on its pure-Ruby core (the acceleration is
43
+ strictly optional). JRuby and TruffleRuby get acceleration too
44
+ whenever a cdylib is available: they bind through the bundled `ffi`
45
+ gem (MRI uses stdlib Fiddle). Three escape hatches:
44
46
 
45
47
  * `OMNIZIP_NO_RUST=1` — force the pure-Ruby core everywhere.
46
48
  * `OMNIZIP_FFI_DYLIB=/path/to/libomnizip_ffi` — load a specific build.
@@ -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
- 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
21
+ @handle = @lib.arch_open(data, password)
34
22
  end
35
23
 
36
24
  def entry_count
37
- @funcs[:arch_count].call(@handle)
25
+ @lib.arch_count(@handle)
38
26
  end
39
27
 
40
28
  def entry_names
41
29
  (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
30
+ @lib.arch_entry_name(@handle, i)
46
31
  end
47
32
  end
48
33
 
49
34
  def entry_size(index)
50
- @funcs[:arch_entry_size].call(@handle, index)
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
- 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"))
42
+ @lib.arch_read_entry(@handle, index)
62
43
  end
63
44
 
64
45
  def close
65
46
  return unless @handle
66
47
 
67
- @funcs[:arch_close].call(@handle)
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