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 +4 -4
- data/CHANGELOG.md +30 -0
- data/README.adoc +12 -11
- data/Rakefile +7 -1
- data/TODO.perf/01-ext-accelerated-default-reads.md +18 -0
- data/TODO.perf/02-auto-enable-native-layer.md +15 -0
- data/TODO.perf/03-bulk-xpath-result-materialization.md +13 -0
- data/TODO.perf/04-ext-bound-serialization.md +11 -0
- data/TODO.perf/05-native-surface-completion.md +12 -0
- data/TODO.perf/06-perf-battery-native-legs.md +10 -0
- data/ext/leptris/native/native.c +121 -2
- data/lib/leptris/vendor/aarch64-linux/libleptris.so +0 -0
- data/lib/leptris/vendor/aarch64-linux/libutf8proc.so.3 +0 -0
- data/lib/leptris/vendor/aarch64-linux-musl/libleptris.so +0 -0
- data/lib/leptris/vendor/aarch64-linux-musl/libutf8proc.so.3 +0 -0
- data/lib/leptris/vendor/arm64-darwin/libleptris.dylib +0 -0
- data/lib/leptris/vendor/arm64-darwin/libutf8proc.3.dylib +0 -0
- data/lib/leptris/vendor/x86_64-darwin/libleptris.dylib +0 -0
- data/lib/leptris/vendor/x86_64-darwin/libutf8proc.3.dylib +0 -0
- data/lib/leptris/vendor/x86_64-linux/libleptris.so +0 -0
- data/lib/leptris/vendor/x86_64-linux/libutf8proc.so.3 +0 -0
- data/lib/leptris/vendor/x86_64-linux-musl/libleptris.so +0 -0
- data/lib/leptris/vendor/x86_64-linux-musl/libutf8proc.so.3 +0 -0
- data/lib/leptris/version.rb +1 -1
- data/lib/leptris/xml/ffi.rb +49 -9
- data/lib/leptris/xml/native_layer.rb +38 -21
- metadata +19 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: ec940fa88dc62b9bd98c0268fb7769274c2b919cc34b63d15b8b9f846b4f6374
|
|
4
|
+
data.tar.gz: b8167cf9190c65b87d6cc91dd7c8a949343187eff563758c0b6c9c4c07c5c541
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
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
|
|
66
|
-
|
|
67
|
-
|
|
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
|
-
|
|
72
|
-
|
|
73
|
-
|
|
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
|
-
|
|
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
|
|
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.
|
data/ext/leptris/native/native.c
CHANGED
|
@@ -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("
|
|
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);
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
data/lib/leptris/version.rb
CHANGED
data/lib/leptris/xml/ffi.rb
CHANGED
|
@@ -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
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
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
|
|
40
|
+
candidates = Leptris::XML::Native.lib_candidates
|
|
42
41
|
resolved = candidates.any? do |path|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
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
|
|
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,
|
|
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.
|
|
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
|