leptris 1.9.162.6 → 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: d09354998670281f8e6519fa0cf7b35c467d67e7536879b31be2b3efa634250e
4
- data.tar.gz: 362d92cf0e463160f94ecab7639a60d867680e47610bed4cac408dc0e835c1fb
3
+ metadata.gz: ec940fa88dc62b9bd98c0268fb7769274c2b919cc34b63d15b8b9f846b4f6374
4
+ data.tar.gz: b8167cf9190c65b87d6cc91dd7c8a949343187eff563758c0b6c9c4c07c5c541
5
5
  SHA512:
6
- metadata.gz: 59ee84ed455a5621ab3be63c4fb54ddf3d9b42346cb32f8f9a7c967f884bb70a04cb4e3864528718b26dfe725540f05f27ec06f64c44a252f203b40ff9484d8f
7
- data.tar.gz: 66d52689d9be07a40fee5739959878556f393ec0c86fb94b5e36c95ab36c38d022be579ff7189561f96f91eac972035e77e645eff0b8dae272b345dbc2f9667d
6
+ metadata.gz: 3bbe8b668a992020233f5a7c5f9a26aed7701a227db29cf15e5e80209df45e5be0fbe0f32a9de6459dd9ebfb2e4f4c134c99c8282501a7291e7a8a1924118011
7
+ data.tar.gz: 39d84a702970ad1c02b369c3114c2d3abf0efbf98ffd5dd55e0c178326f68f9af1ac8946f84918a69b1fbc610e287f14c875feab8d24a339c7d01845a38a8ce0
data/CHANGELOG.md CHANGED
@@ -5,6 +5,36 @@ 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
+
25
+ ## [1.9.162.7] - 2026-09-14
26
+
27
+ ### Added
28
+
29
+ - **Native builder factories (#149)**: with the opt-in native
30
+ layer loaded, `Document#native_create_element` /
31
+ `#native_create_text` / `#native_root=` and
32
+ `NativeNode#create_child` / `#add_child` create TypedData nodes
33
+ in one C call — no FFI::Pointer, no wrap_fresh path. Measured
34
+ create_text 746->324ns (2.3x binding), 1052-node build 1948->273µs
35
+ (**2.3x faster than Nokogiri**). Separate `native_cache` keeps
36
+ binding Node.wrap identity intact.
37
+
8
38
  ## [1.9.162.6] - 2026-09-14
9
39
 
10
40
  ### Fixed
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.
@@ -22,6 +22,13 @@ typedef int (*node_type_fn)(void *);
22
22
  typedef void *(*next_sibling_fn)(void *);
23
23
  typedef void *(*parent_fn)(void *);
24
24
  typedef const char *(*element_text_fn)(void *);
25
+ typedef void *(*doc_create_fn)(void);
26
+ typedef void *(*elem_create_fn)(void *, const char *);
27
+ typedef void *(*text_create_fn)(void *, const char *);
28
+ typedef void *(*create_child_fn)(void *, const char *);
29
+ typedef int (*append_child_fn)(void *, void *);
30
+ typedef int (*set_root_fn)(void *, void *);
31
+ typedef void (*doc_free_fn)(void *);
25
32
 
26
33
  static elem_name_fn f_elem_name;
27
34
  static text_content_fn f_text_content;
@@ -31,6 +38,13 @@ static node_type_fn f_node_type;
31
38
  static next_sibling_fn f_next_sibling;
32
39
  static parent_fn f_parent;
33
40
  static element_text_fn f_element_text;
41
+ static doc_create_fn f_doc_create;
42
+ static elem_create_fn f_elem_create;
43
+ static text_create_fn f_text_create;
44
+ static create_child_fn f_create_child;
45
+ static append_child_fn f_append_child;
46
+ static set_root_fn f_set_root;
47
+ static doc_free_fn f_doc_free;
34
48
 
35
49
  static VALUE c_native_node;
36
50
  static ID id_wrapper_cache;
@@ -81,9 +95,17 @@ static void resolve_symbols(const char *lib_path)
81
95
  f_next_sibling = (next_sibling_fn)lib_sym(h, "leptris_node_next_sibling");
82
96
  f_parent = (parent_fn)lib_sym(h, "leptris_node_parent");
83
97
  f_element_text = (element_text_fn)lib_sym(h, "leptris_element_text");
98
+ f_doc_create = (doc_create_fn)lib_sym(h, "leptris_document_create");
99
+ f_elem_create = (elem_create_fn)lib_sym(h, "leptris_element_create");
100
+ f_text_create = (text_create_fn)lib_sym(h, "leptris_text_node_create");
101
+ f_create_child = (create_child_fn)lib_sym(h, "leptris_element_create_child");
102
+ f_append_child = (append_child_fn)lib_sym(h, "leptris_element_append_child");
103
+ f_set_root = (set_root_fn)lib_sym(h, "leptris_document_set_root");
104
+ f_doc_free = (doc_free_fn)lib_sym(h, "leptris_document_free");
84
105
  if (!f_elem_name || !f_text_content || !f_attr ||
85
106
  !f_children_ex || !f_node_type || !f_next_sibling || !f_parent ||
86
- !f_element_text)
107
+ !f_element_text || !f_doc_create || !f_elem_create || !f_text_create ||
108
+ !f_create_child || !f_append_child || !f_set_root || !f_doc_free)
87
109
  rb_raise(rb_eRuntimeError, "libleptris symbols missing");
88
110
  }
89
111
 
@@ -243,6 +265,96 @@ static VALUE nn_parent(VALUE self)
243
265
  return par ? wrap_cached(n, par) : Qnil;
244
266
  }
245
267
 
268
+ /* Wrap a freshly-created C node as a NativeNode and register it
269
+ * in the document's identity cache. */
270
+ static VALUE wrap_new(VALUE document, void *ptr)
271
+ {
272
+ struct native_node *n;
273
+ VALUE node, cache, key;
274
+
275
+ node = nn_allocate(c_native_node);
276
+ TypedData_Get_Struct(node, struct native_node, &nn_type, n);
277
+ n->ptr = ptr;
278
+ n->document = document;
279
+ cache = rb_funcall(document, id_wrapper_cache, 0);
280
+ key = ULL2NUM((uint64_t)(uintptr_t)ptr);
281
+ rb_hash_aset(cache, key, node);
282
+ return node;
283
+ }
284
+
285
+ /* Document address from the binding Document's #c_ptr. */
286
+ static void *doc_ptr_of(VALUE document)
287
+ {
288
+ VALUE c_ptr = rb_funcall(document, rb_intern("c_ptr"), 0);
289
+ if (NIL_P(c_ptr))
290
+ rb_raise(rb_eRuntimeError, "document has been freed");
291
+ return (void *)(uintptr_t)NUM2ULL(rb_funcall(c_ptr, rb_intern("address"), 0));
292
+ }
293
+
294
+ /* Builder factories (#149): create in C, wrap as NativeNode — no
295
+ * FFI::Pointer, no Ruby wrap_fresh path. */
296
+ static VALUE nn_create_element(VALUE klass, VALUE document, VALUE name)
297
+ {
298
+ void *ptr;
299
+ (void)klass;
300
+ ptr = f_elem_create(doc_ptr_of(document), StringValueCStr(name));
301
+ if (!ptr)
302
+ rb_raise(rb_eRuntimeError, "leptris_element_create failed");
303
+ return wrap_new(document, ptr);
304
+ }
305
+
306
+ static VALUE nn_create_text(VALUE klass, VALUE document, VALUE content)
307
+ {
308
+ void *ptr;
309
+ (void)klass;
310
+ ptr = f_text_create(doc_ptr_of(document), StringValueCStr(content));
311
+ if (!ptr)
312
+ rb_raise(rb_eRuntimeError, "leptris_text_node_create failed");
313
+ return wrap_new(document, ptr);
314
+ }
315
+
316
+ static VALUE nn_create_child(VALUE self, VALUE name)
317
+ {
318
+ struct native_node *n;
319
+ void *ptr;
320
+ TypedData_Get_Struct(self, struct native_node, &nn_type, n);
321
+ ptr = f_create_child(n->ptr, StringValueCStr(name));
322
+ if (!ptr)
323
+ rb_raise(rb_eRuntimeError, "leptris_element_create_child failed");
324
+ return wrap_new(n->document, ptr);
325
+ }
326
+
327
+ static VALUE nn_append_child(VALUE self, VALUE child)
328
+ {
329
+ struct native_node *n, *c;
330
+ int rc;
331
+ TypedData_Get_Struct(self, struct native_node, &nn_type, n);
332
+ TypedData_Get_Struct(child, struct native_node, &nn_type, c);
333
+ rc = f_append_child(n->ptr, c->ptr);
334
+ if (rc != 0)
335
+ rb_raise(rb_eRuntimeError, "leptris_element_append_child failed (%d)", rc);
336
+ return child;
337
+ }
338
+
339
+ static VALUE nn_set_root(VALUE klass, VALUE document, VALUE element)
340
+ {
341
+ struct native_node *n;
342
+ int rc;
343
+ (void)klass;
344
+ TypedData_Get_Struct(element, struct native_node, &nn_type, n);
345
+ rc = f_set_root(doc_ptr_of(document), n->ptr);
346
+ if (rc != 0)
347
+ rb_raise(rb_eRuntimeError, "leptris_document_set_root failed (%d)", rc);
348
+ return element;
349
+ }
350
+
351
+ static VALUE nn_address(VALUE self)
352
+ {
353
+ struct native_node *n;
354
+ TypedData_Get_Struct(self, struct native_node, &nn_type, n);
355
+ return ULL2NUM((uint64_t)(uintptr_t)n->ptr);
356
+ }
357
+
246
358
  static VALUE nn_from(VALUE klass, VALUE document, VALUE element)
247
359
  {
248
360
  /* Element address comes through the binding's #c_ptr address;
@@ -278,9 +390,16 @@ void Init_native(void)
278
390
  c_native_node = rb_define_class_under(m_xml, "NativeNode", rb_cObject);
279
391
  rb_undef_alloc_func(c_native_node);
280
392
 
281
- id_wrapper_cache = rb_intern("wrapper_cache");
393
+ id_wrapper_cache = rb_intern("native_cache");
282
394
 
283
395
  rb_define_singleton_method(c_native_node, "from", nn_from, 2);
396
+ rb_define_singleton_method(c_native_node, "create_element", nn_create_element, 2);
397
+ rb_define_singleton_method(c_native_node, "create_text", nn_create_text, 2);
398
+ rb_define_singleton_method(c_native_node, "set_root", nn_set_root, 2);
399
+ rb_define_method(c_native_node, "create_child", nn_create_child, 1);
400
+ rb_define_method(c_native_node, "append_child", nn_append_child, 1);
401
+ rb_define_method(c_native_node, "add_child", nn_append_child, 1);
402
+ rb_define_method(c_native_node, "address", nn_address, 0);
284
403
  rb_define_method(c_native_node, "name", nn_name, 0);
285
404
  rb_define_method(c_native_node, "content", nn_content, 0);
286
405
  rb_define_method(c_native_node, "attribute", nn_attribute, 1);
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Leptris
4
- VERSION = "1.9.162.6"
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,34 +29,51 @@ 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
- # The native-node view of the document root (opt-in read layer).
54
+ # Separate identity cache for NativeNodes so binding Node.wrap
55
+ # and the native layer never overwrite each other.
56
+ def native_cache
57
+ @native_cache ||= {}
58
+ end
59
+
58
60
  def native_node
59
- root or raise Leptris::XML::Error, "document has no root element"
60
- Leptris::XML::NativeNode.from(self, root.c_ptr)
61
+ r = root or raise Leptris::XML::Error, "document has no root element"
62
+ Leptris::XML::NativeNode.from(self, r.c_ptr)
63
+ end
64
+
65
+ # Builder factories that return NativeNodes (#149): one C call
66
+ # and a TypedData wrap — no FFI::Pointer, no wrap_fresh path.
67
+ def native_create_element(name)
68
+ Leptris::XML::NativeNode.create_element(self, name.to_s)
69
+ end
70
+
71
+ def native_create_text(content)
72
+ Leptris::XML::NativeNode.create_text(self, content.to_s)
73
+ end
74
+
75
+ def native_root=(element)
76
+ Leptris::XML::NativeNode.set_root(self, element)
77
+ element
61
78
  end
62
79
  end
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.6
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