leptris 1.9.162.7 → 1.9.162.8

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: 57f929ff4841376940854db6d4c56ed6e5d781ca031b2c24d6d431da168e58c5
4
- data.tar.gz: 31770ff680d92ae5d6ab85ca295cab3c5786fcd443cef80acfb8b20d437d6db3
3
+ metadata.gz: ec940fa88dc62b9bd98c0268fb7769274c2b919cc34b63d15b8b9f846b4f6374
4
+ data.tar.gz: b8167cf9190c65b87d6cc91dd7c8a949343187eff563758c0b6c9c4c07c5c541
5
5
  SHA512:
6
- metadata.gz: f3e15b12e18df6ed283c9aef8971b10a779155ca823ccffe0389493776d8c03735f1130a5c47e7c4057f8c8baf1864736971fcc9a7154dec865979544452e2ea
7
- data.tar.gz: e5fcb4356b6ab0e18a97f89b14db6a7da02c49f79edb95661155c2858391a75eb27b1fb49617d325730e576dbc8896debcc91ec504543f68cdbec5b92e3a3cf4
6
+ metadata.gz: 3bbe8b668a992020233f5a7c5f9a26aed7701a227db29cf15e5e80209df45e5be0fbe0f32a9de6459dd9ebfb2e4f4c134c99c8282501a7291e7a8a1924118011
7
+ data.tar.gz: 39d84a702970ad1c02b369c3114c2d3abf0efbf98ffd5dd55e0c178326f68f9af1ac8946f84918a69b1fbc610e287f14c875feab8d24a339c7d01845a38a8ce0
data/CHANGELOG.md CHANGED
@@ -5,6 +5,23 @@ All notable changes to Leptris will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [1.9.162.8] - 2026-09-14
9
+
10
+ ### Added
11
+
12
+ - **Zero-setup TruffleRuby/JRuby (#160)**: the `ruby`-platform gem
13
+ now vendors `libleptris` + `libutf8proc` for arm64/x86_64-darwin
14
+ and x86_64/aarch64-linux (plus musl fallbacks) under
15
+ `lib/leptris/vendor/<platform>/`; `FFI.libleptris_candidates`
16
+ probes them first at require time. Both engines activate and
17
+ load with no system library and no env vars — the release
18
+ assembly gathers the binaries from every platform build leg.
19
+ MRI behavior is unchanged (platform gems resolve first); a
20
+ forced `--platform ruby` MRI install also works via the vendor
21
+ tree. Fixed a split-library hazard along the way: the native
22
+ read layer now resolves the exact image the FFI layer loads
23
+ (two differently-pathed copies split per-document state).
24
+
8
25
  ## [1.9.162.7] - 2026-09-14
9
26
 
10
27
  ### Added
data/README.adoc CHANGED
@@ -60,20 +60,21 @@ export LEPTRIS_LIB_PATH=/usr/local/lib/libleptris.dylib
60
60
  If Leptris can't find the library at startup, every `parse` call raises
61
61
  `LoadError`.
62
62
 
63
- === Alternative engines: TruffleRuby and JRuby (#160)
63
+ === Alternative engines: TruffleRuby and JRuby (#160) — zero setup
64
64
 
65
- The gem ships a plain `ruby`-platform variant alongside the precompiled
66
- platform gems, so Bundler activates `leptris` on TruffleRuby and JRuby
67
- (the binding is FFI-based — no MRI C extension anywhere). Those engines
68
- filter out precompiled platform gems at activation and always resolve
69
- the `ruby` variant, which does not vendor the native library:
65
+ The `ruby`-platform variant (what TruffleRuby and JRuby resolve)
66
+ vendors precompiled `libleptris` + `libutf8proc` binaries for the
67
+ common engine platforms under `lib/leptris/vendor/`:
70
68
 
71
- 1. Install `libleptris` on the host — build from source (above) or set
72
- `LEPTRIS_LIB_PATH=/path/to/libleptris.{so,dylib}`.
73
- 2. `gem install leptris` (or `bundle install`) — activation succeeds
74
- like on MRI, and FFI loads the system library at require time.
69
+ - `arm64-darwin`, `x86_64-darwin`
70
+ - `x86_64-linux`, `aarch64-linux` (glibc; the musl variants are
71
+ tried as fallback for Alpine hosts)
75
72
 
76
- The startup `LoadError` names this remedy when the library is missing.
73
+ `gem install leptris` just works on both engines — the FFI layer
74
+ selects the matching vendored binary at require time (the binding
75
+ is FFI-based; no MRI C extension anywhere). `LEPTRIS_LIB_PATH`
76
+ still overrides, and a system library remains the fallback for
77
+ OSes the variant does not carry.
77
78
 
78
79
  == Parsing
79
80
 
data/Rakefile CHANGED
@@ -189,9 +189,15 @@ task "gem:native:any" do
189
189
  sh "rake platform:any gem"
190
190
  end
191
191
 
192
- desc "Define the gem task to build the pure-Ruby gem"
192
+ desc "Define the gem task to build the ruby-platform gem"
193
193
  task "platform:any" do
194
194
  spec = Gem::Specification::load("leptris.gemspec").dup
195
+ # Zero-setup TruffleRuby/JRuby (#160): per-OS binaries staged
196
+ # under lib/leptris/vendor/<platform>/ by the release assembly
197
+ # (or by hand: cp from each platform build). Absent on a plain
198
+ # checkout — the variant then carries no binaries and the
199
+ # system-library / LEPTRIS_LIB_PATH path applies as before.
200
+ spec.files += Dir.glob("lib/leptris/vendor/**/*")
195
201
  task = Gem::PackageTask.new(spec)
196
202
  task.define
197
203
  end
@@ -0,0 +1,18 @@
1
+ # 01 — Ext-accelerated default reads (no FFI marshaling on hot paths)
2
+
3
+ Status: TODO
4
+
5
+ The default binding's hot reads each pay FFI per-call marshaling
6
+ (:string returns allocate through FFI, args marshal through the
7
+ boundary — ~200-500ns vs ~50ns for a direct ext call). The native
8
+ bundle already dlsyms every reader; add address-based C functions
9
+ the DEFAULT binding classes call when the bundle is loaded:
10
+
11
+ - Element#name / #content / #[]
12
+ - Node#children / #element_children (bulk: one C pass allocating
13
+ binding wrappers — the bulk_wrap shape, 1.27-1.30x measured)
14
+ - Element#expanded_name (three out-params today)
15
+
16
+ Gate: suite green with AND without the bundle; drift-record rows
17
+ (attr reads 1.88x, text 6.18x) must improve; cold-walk gate must
18
+ not regress (cold-walk-regression-gate memory).
@@ -0,0 +1,15 @@
1
+ # 02 — Auto-enable the native layer when the bundle is present
2
+
3
+ Status: TODO
4
+
5
+ Today `require "leptris/xml/native_layer"` is opt-in. End-state:
6
+ `require "leptris"` quietly probes for the vendored native.bundle
7
+ and, when present, activates the acceleration (01 makes the
8
+ default classes benefit). Silent fallback to FFI when absent
9
+ (ruby-variant on TruffleRuby/JRuby, custom builds). The explicit
10
+ native_layer require remains the parallel-API entry
11
+ (NativeNode builders).
12
+
13
+ Requires: 01 (so auto-enable accelerates the DEFAULT API, not a
14
+ parallel one), and a divergence test — same specs pass in both
15
+ modes (the suite already runs both; make it explicit).
@@ -0,0 +1,13 @@
1
+ # 03 — Bulk XPath result materialization
2
+
3
+ Status: TODO
4
+
5
+ NodeSet#fetch materializes results per node (wrap per item +
6
+ ResultText/ResultAttr captures). The #185 profile counted
7
+ NodeSet#initialize at 2.8% and Class#new 11.7% of walk wall —
8
+ result materialization shares that class of cost. Batch via the
9
+ ext: one C pass over leptris_xpath_result_get_node(i) building
10
+ the Ruby array with kinds, capturing text values in C.
11
+
12
+ Gate: xpath-heavy specs unchanged; xpath row (28,878 ips small)
13
+ improves on the battery.
@@ -0,0 +1,11 @@
1
+ # 04 — Serialization buffer cycle in C
2
+
3
+ Status: TODO
4
+
5
+ FFI.serialize_into_string does the two-call sized protocol
6
+ (needed probe, then copy) with Ruby-side buffer management per
7
+ to_xml call. Bind the cycle in the ext: one C function taking the
8
+ ffi_function pointer + node address, returning a Ruby String.
9
+
10
+ Gate: serialize row (3.24x baseline) improves; c14n specs
11
+ byte-identical.
@@ -0,0 +1,12 @@
1
+ # 05 — Complete the NativeNode read surface
2
+
3
+ Status: TODO
4
+
5
+ Missing on NativeNode vs Element: #attributes (bulk attr hash),
6
+ #attribute_nodes, #namespaces / #prefix, #line / #byte_offset,
7
+ #inner_html / #to_xml (delegate to binding serialization), write
8
+ faces beyond add_child (set_attribute, set_text, remove). Reads
9
+ first; writes stay FFI (cold) unless measured otherwise.
10
+
11
+ Gate: native_layer specs mirror the binding feature specs;
12
+ descriptor_walk parity where applicable.
@@ -0,0 +1,10 @@
1
+ # 06 — Native-layer legs in the perf battery + drift record
2
+
3
+ Status: TODO
4
+
5
+ benchmark/ gains native-layer legs (walk/build/attr rows via
6
+ NativeNode beside the binding rows) so the drift record tracks
7
+ both surfaces; TODO.restructure/17's record gains a native table.
8
+
9
+ Gate: one command (rake benchmark:all or the two scripts) emits
10
+ both tables; load-gated per the shared-machine discipline.
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Leptris
4
- VERSION = "1.9.162.7"
4
+ VERSION = "1.9.162.8"
5
5
  end
@@ -7,6 +7,50 @@ module Leptris
7
7
  module FFI
8
8
  extend ::FFI::Library
9
9
 
10
+ # The per-OS vendor directory of the ruby-platform gem
11
+ # (zero-setup TruffleRuby/JRuby, #160): binaries for the
12
+ # common engines' OSes under lib/leptris/vendor/<platform>/.
13
+ # Returns [] in platform gems (no vendor tree) and on
14
+ # OSes the variant does not carry.
15
+ # Dir-name candidates for a RUBY_PLATFORM string, in
16
+ # preference order (glibc before musl — dlopen falls through
17
+ # on musl hosts).
18
+ def self.vendor_platforms_for(ruby_platform)
19
+ case ruby_platform
20
+ when /arm64.*darwin|aarch64.*darwin/ then %w[arm64-darwin]
21
+ when /x86_64.*darwin/ then %w[x86_64-darwin]
22
+ when /aarch64.*linux/ then %w[aarch64-linux aarch64-linux-musl]
23
+ when /x86_64.*linux/ then %w[x86_64-linux x86_64-linux-musl]
24
+ else []
25
+ end
26
+ end
27
+
28
+ def self.vendor_dirs
29
+ vendor_platforms_for(RUBY_PLATFORM)
30
+ .map { |d| File.expand_path("../../leptris/vendor/#{d}", __dir__) }
31
+ .select { |dir| File.directory?(dir) }
32
+ end
33
+
34
+ # THE libleptris candidate list, in order — shared with the
35
+ # native read layer's resolve!. Single source of truth is
36
+ # load-bearing: two differently-pathed copies of the library
37
+ # load as two images with split per-document state.
38
+ def self.libleptris_candidates
39
+ [
40
+ ENV["LEPTRIS_LIB_PATH"],
41
+ *vendor_dirs.flat_map { |d|
42
+ %w[libleptris.dylib libleptris.so libleptris.dll]
43
+ .map { |n| File.join(d, n) }
44
+ },
45
+ File.expand_path("../../libleptris.dylib", __dir__),
46
+ File.expand_path("../../libleptris.so", __dir__),
47
+ File.expand_path("../../libleptris.dll", __dir__),
48
+ "/usr/local/lib/libleptris.dylib",
49
+ "/usr/local/lib/libleptris.so",
50
+ "leptris",
51
+ ].compact
52
+ end
53
+
10
54
  # libleptris links utf8proc via @rpath (TODO.restructure/20
11
55
  # — fn:normalize-unicode). dlopen the VENDORED utf8proc
12
56
  # FIRST: once its install name is loaded, dyld resolves
@@ -17,6 +61,10 @@ module Leptris
17
61
  # never consults it.
18
62
  begin
19
63
  ffi_lib [
64
+ *Leptris::XML::FFI.vendor_dirs.flat_map { |d|
65
+ %w[libutf8proc.3.dylib libutf8proc.so.3 libutf8proc.so
66
+ utf8proc.dll].map { |n| File.join(d, n) }
67
+ },
20
68
  File.expand_path("../../libutf8proc.3.dylib", __dir__),
21
69
  File.expand_path("../../libutf8proc.so.3", __dir__),
22
70
  File.expand_path("../../libutf8proc.so", __dir__),
@@ -29,15 +77,7 @@ module Leptris
29
77
  # Issue leptris-ruby#49: name the remedy when the library is
30
78
  # missing (ruby-platform gem without a vendored libleptris).
31
79
  begin
32
- ffi_lib [
33
- ENV["LEPTRIS_LIB_PATH"],
34
- File.expand_path("../../libleptris.dylib", __dir__),
35
- File.expand_path("../../libleptris.so", __dir__),
36
- File.expand_path("../../libleptris.dll", __dir__),
37
- "/usr/local/lib/libleptris.dylib",
38
- "/usr/local/lib/libleptris.so",
39
- "leptris",
40
- ].compact
80
+ ffi_lib Leptris::XML::FFI.libleptris_candidates
41
81
  rescue LoadError => e
42
82
  raise LoadError, <<~MSG
43
83
  leptris: cannot load the vendored libleptris library.
@@ -29,29 +29,26 @@ rescue LoadError => e
29
29
  end
30
30
 
31
31
  module Leptris::XML::Native
32
- LIB_CANDIDATES = [
33
- ENV["LEPTRIS_LIB_PATH"],
34
- File.expand_path("../../libleptris.dylib", __dir__),
35
- File.expand_path("../../libleptris.so", __dir__),
36
- File.expand_path("../../libleptris.dll", __dir__),
37
- "libleptris",
38
- ].compact.freeze
32
+ # FFI.libleptris_candidates is the single source of truth — the
33
+ # native layer MUST dlsym the exact image the FFI layer loaded,
34
+ # or two library instances split per-document state.
35
+ def self.lib_candidates
36
+ Leptris::XML::FFI.libleptris_candidates
37
+ end
39
38
  end
40
39
 
41
- candidates = Leptris::XML::Native::LIB_CANDIDATES
40
+ candidates = Leptris::XML::Native.lib_candidates
42
41
  resolved = candidates.any? do |path|
43
- resolved = Leptris::XML::Native::LIB_CANDIDATES.any? do |path|
44
- begin
45
- Leptris::XML::NativeNode.resolve!(path)
46
- true
47
- rescue RuntimeError
48
- false
49
- end
42
+ begin
43
+ Leptris::XML::NativeNode.resolve!(path)
44
+ true
45
+ rescue RuntimeError
46
+ false
50
47
  end
51
- raise LoadError,
52
- "leptris: native layer loaded but libleptris could not be " \
53
- "resolved from #{candidates.inspect}" unless resolved
54
48
  end
49
+ raise LoadError,
50
+ "leptris: native layer loaded but libleptris could not be " \
51
+ "resolved from #{candidates.inspect}" unless resolved
55
52
 
56
53
  class Leptris::XML::Document
57
54
  # Separate identity cache for NativeNodes so binding Node.wrap
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: leptris
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.9.162.7
4
+ version: 1.9.162.8
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ribose Inc.
@@ -69,6 +69,12 @@ files:
69
69
  - LICENSE.md
70
70
  - README.adoc
71
71
  - Rakefile
72
+ - TODO.perf/01-ext-accelerated-default-reads.md
73
+ - TODO.perf/02-auto-enable-native-layer.md
74
+ - TODO.perf/03-bulk-xpath-result-materialization.md
75
+ - TODO.perf/04-ext-bound-serialization.md
76
+ - TODO.perf/05-native-surface-completion.md
77
+ - TODO.perf/06-perf-battery-native-legs.md
72
78
  - TODO.restructure/01-constraint-compliance-audit.md
73
79
  - TODO.restructure/02-deep-copy-seam.md
74
80
  - TODO.restructure/03-evaluation-context-seam.md
@@ -101,6 +107,18 @@ files:
101
107
  - ext/leptris/native/native.c
102
108
  - leptris.gemspec
103
109
  - lib/leptris.rb
110
+ - lib/leptris/vendor/aarch64-linux-musl/libleptris.so
111
+ - lib/leptris/vendor/aarch64-linux-musl/libutf8proc.so.3
112
+ - lib/leptris/vendor/aarch64-linux/libleptris.so
113
+ - lib/leptris/vendor/aarch64-linux/libutf8proc.so.3
114
+ - lib/leptris/vendor/arm64-darwin/libleptris.dylib
115
+ - lib/leptris/vendor/arm64-darwin/libutf8proc.3.dylib
116
+ - lib/leptris/vendor/x86_64-darwin/libleptris.dylib
117
+ - lib/leptris/vendor/x86_64-darwin/libutf8proc.3.dylib
118
+ - lib/leptris/vendor/x86_64-linux-musl/libleptris.so
119
+ - lib/leptris/vendor/x86_64-linux-musl/libutf8proc.so.3
120
+ - lib/leptris/vendor/x86_64-linux/libleptris.so
121
+ - lib/leptris/vendor/x86_64-linux/libutf8proc.so.3
104
122
  - lib/leptris/version.rb
105
123
  - lib/leptris/xml.rb
106
124
  - lib/leptris/xml/attr.rb