omnizip 0.4.3 → 0.4.4

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: 34bab2534b75e9ca958c00130c6b5962dbd5422bd6e0f4683c8e756415d9961b
4
- data.tar.gz: 9089d88fbe8ce8bda17fecd2a21454db2daa23c86e930b69cf8bb86a495b1ce0
3
+ metadata.gz: 8cf4c62c7304ef792b48b96486685707c68010f0cc6529b6a7504454b94ea025
4
+ data.tar.gz: 7c0d5e62fc21a99ccc85520ee0cd3ea32afaae39e76f10826dd56697ffa8376b
5
5
  SHA512:
6
- metadata.gz: d92441da2c757c65e1e5865abd446cd2a296adafcdf56d5dabfc00f6c792fd6f9b8f3e51dda0231ce5d8829eaf4b81662cfdadc13cdbc9495c73d088968c6c81
7
- data.tar.gz: 30591da9a2e0af6922a49596133f98e5f05bf30e927b698e4b413c1f9f0d1907ab6f0f9a0cc32eff385772b7445aff36e271066690c85e3a134dd040e8185591
6
+ metadata.gz: 539124e9189208423daacdac7cad6735f4a1069358a6ee7e39bb09155524589a2b1a01a6b1c41af6b650648dee327b2c4d36e71ccbbba5ff5e4453168f6ec9ef
7
+ data.tar.gz: 02ae0fdb9844f006e5289d8bd351b4ac023e8fafd704bd6fc96b6a0489bc27c96d3e7d3e32ef06011a8123b37573d877191e93cdbaa7c0f6e63e139e8485c09e
@@ -68,8 +68,13 @@ module Omnizip
68
68
  # @return [void]
69
69
  def compress(input_stream, output_stream, options = nil)
70
70
  input_data = input_stream.read
71
- encoder = Encoder.new(output_stream, build_encoder_options(options))
72
- encoder.encode_stream(input_data)
71
+ level = build_encoder_options(options)[:level] || 6
72
+ output_stream.write(
73
+ Backends.compress("zlib", input_data, level) do
74
+ encoder = Encoder.new(output_stream, build_encoder_options(options))
75
+ encoder.encode_stream(input_data)
76
+ end,
77
+ )
73
78
  end
74
79
 
75
80
  # Decompress Deflate-compressed data
@@ -42,21 +42,22 @@ module Omnizip
42
42
  # @param options [Hash] Compression options
43
43
  # @option options [Integer] :level Compression level (1-9)
44
44
  def compress(input, output, options = {})
45
- level = options[:level] || Zlib::DEFAULT_COMPRESSION
46
-
45
+ raw_level = options[:level] || Zlib::DEFAULT_COMPRESSION
47
46
  data = input.read
48
47
  return if data.nil? || data.empty?
49
48
 
50
- # Use Zlib::Deflate with maximum window size
51
- deflater = Zlib::Deflate.new(
52
- level,
53
- Zlib::MAX_WBITS, # Maximum window size
54
- Zlib::MAX_MEM_LEVEL,
55
- )
56
-
57
- compressed = deflater.deflate(data, Zlib::FINISH)
58
- deflater.close
59
-
49
+ # Zlib::DEFAULT_COMPRESSION (-1) maps to the FFI's 6.
50
+ level = raw_level.negative? ? 6 : raw_level.clamp(0, 9)
51
+ compressed = Backends.compress("zlib", data, level) do
52
+ deflater = Zlib::Deflate.new(
53
+ raw_level,
54
+ Zlib::MAX_WBITS,
55
+ Zlib::MAX_MEM_LEVEL,
56
+ )
57
+ result = deflater.deflate(data, Zlib::FINISH)
58
+ deflater.close
59
+ result
60
+ end
60
61
  output.write(compressed)
61
62
  end
62
63
 
@@ -86,8 +86,13 @@ module Omnizip
86
86
  # @return [void]
87
87
  def compress(input_stream, output_stream, options = nil)
88
88
  input_data = input_stream.read
89
- encoder = Encoder.new(output_stream, build_encoder_options(options))
90
- encoder.encode_stream(input_data)
89
+ level = build_encoder_options(options)[:level] || 3
90
+ output_stream.write(
91
+ Backends.compress("zstd", input_data, level) do
92
+ encoder = Encoder.new(output_stream, build_encoder_options(options))
93
+ encoder.encode_stream(input_data)
94
+ end,
95
+ )
91
96
  end
92
97
 
93
98
  # Decompress Zstandard-compressed data
@@ -27,19 +27,23 @@ module Omnizip
27
27
  # over frame stability.
28
28
  module Backends
29
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
30
+ # 2026-09-20 policy flip (owner directive): RUST IS THE AUTHORITY
31
+ # — its encoders/decoders are the ports validated against the
32
+ # reference C/C++ implementations (bzip2, xz/liblzma, zstd,
33
+ # libdeflate), and they are determinism-contracted (same input +
34
+ # level => same bytes on every machine). :auto therefore routes
35
+ # BOTH directions through Rust whenever the cdylib loads; encode
36
+ # falls back to the Ruby core on any Rust error, so :auto is
37
+ # never worse than pure Ruby. The pure-Ruby cores remain the
38
+ # portable fallback and the OMNIZIP_BACKEND=ruby escape hatch.
39
+ RUST_AUTHORITATIVE = true
36
40
 
37
41
  # usize::MAX — the FFI's "caller does not know the plaintext
38
42
  # size" sentinel (streaming decode contract).
39
43
  UNKNOWN_LENGTH = 0xFFFF_FFFF_FFFF_FFFF
40
44
 
41
45
  class << self
42
- def for(codec, direction)
46
+ def for(codec, _direction)
43
47
  mode = ENV.fetch("OMNIZIP_BACKEND", "auto")
44
48
 
45
49
  return backend_rust if mode == "rust"
@@ -48,17 +52,26 @@ module Omnizip
48
52
  return backend_ruby unless Implementations::Rust::Library.available?
49
53
  return backend_ruby unless Implementations::Rust::Library::CODECS.include?(codec)
50
54
 
51
- direction == :encode && !RUST_ENCODE_IDENTICAL.fetch(codec, false) ? backend_ruby : backend_rust
55
+ backend_rust
52
56
  end
53
57
 
54
58
  # Compress through the tier-selected backend. The block is
55
59
  # the pure-Ruby core — evaluated ONLY on the Ruby path, so
56
- # the accelerated path never pays for it.
60
+ # the accelerated path never pays for it. A Rust encode error
61
+ # falls back to the Ruby core (auto is never worse than pure
62
+ # Ruby); only the forced `rust` mode propagates.
57
63
  def compress(codec, data, level, &ruby_core)
58
64
  backend = Backends.for(codec, :encode)
59
65
  return yield if backend == RubyBackend
60
66
 
61
- backend.compress(codec, data, level)
67
+ begin
68
+ backend.compress(codec, data, level)
69
+ rescue Implementations::Rust::Error => e
70
+ raise if ENV.fetch("OMNIZIP_BACKEND", "auto") == "rust"
71
+
72
+ warn "omnizip: rust #{codec} encode failed (#{e.message}); using ruby" if ENV["OMNIZIP_BACKEND_DEBUG"]
73
+ yield
74
+ end
62
75
  end
63
76
 
64
77
  # Decompress through the tier-selected backend (output-
@@ -16,9 +16,9 @@ module Omnizip
16
16
  # built; compress/decompress are pure C calls).
17
17
  class Library
18
18
  # Codecs the cdylib dispatches by name.
19
- # Names the cdylib dispatches: the codec-level acceleration surface
20
- # (decode tier by default; encode only under forced rust mode —
21
- # RUST_ENCODE_IDENTICAL gates the auto tier).
19
+ # Names the cdylib dispatches: the codec-level acceleration
20
+ # surface (both directions by default — Rust is the
21
+ # authority; Ruby is the fallback).
22
22
  CODECS = %w[
23
23
  bzip2 zstd lzma xz lzma-alone lzip
24
24
  deflate deflate64 zlib gzip
@@ -13,11 +13,11 @@ module Omnizip
13
13
  # 1. ENV['OMNIZIP_FFI_DYLIB'] (explicit path)
14
14
  # 2. ../target/release/ inside a sibling omnizip-rs checkout
15
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.
16
+ # Tier policy (see Omnizip::Backends): Rust is the authority —
17
+ # validated against the reference C/C++ implementations and
18
+ # determinism-contracted — so `auto` routes BOTH directions
19
+ # through Rust whenever the library loads, with Ruby-core
20
+ # fallback on any Rust error.
21
21
  module Rust
22
22
  autoload :Library, "omnizip/implementations/rust/library"
23
23
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Omnizip
4
- VERSION = "0.4.3"
4
+ VERSION = "0.4.4"
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: omnizip
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.3
4
+ version: 0.4.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ribose Inc.