leptris 1.9.380.0 → 1.9.380.2
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 +19 -0
- data/Rakefile +1 -1
- data/ext/leptris/native/native.c +168 -7
- data/lib/leptris/vendor/aarch64-linux/libleptris.so +0 -0
- data/lib/leptris/vendor/aarch64-linux-musl/libleptris.so +0 -0
- data/lib/leptris/vendor/arm-linux/libleptris.so +0 -0
- data/lib/leptris/vendor/arm-linux-musl/libleptris.so +0 -0
- data/lib/leptris/vendor/arm64-darwin/libleptris.dylib +0 -0
- data/lib/leptris/vendor/ppc64le-linux/libleptris.so +0 -0
- data/lib/leptris/vendor/s390x-linux-musl/libleptris.so +0 -0
- data/lib/leptris/vendor/x86_64-darwin/libleptris.dylib +0 -0
- data/lib/leptris/vendor/x86_64-linux/libleptris.so +0 -0
- data/lib/leptris/vendor/x86_64-linux-musl/libleptris.so +0 -0
- data/lib/leptris/version.rb +1 -1
- data/lib/leptris/xml/native_layer.rb +20 -2
- data/vendor-src/README.md +1 -1
- data/vendor-src/libleptris/CHANGELOG.md +10 -0
- data/vendor-src/libleptris/CMakeLists.txt +1 -1
- data/vendor-src/libleptris/TODO.SOTA.md +82 -56
- data/vendor-src/libleptris/benchmarks/baseline.json +1 -0
- data/vendor-src/libleptris/bindings/rust/Cargo.lock +1 -1
- data/vendor-src/libleptris/bindings/rust/descriptor/Cargo.toml +1 -1
- data/vendor-src/libleptris/cli/output.c +11 -0
- data/vendor-src/libleptris/docs/MISSION-PLAN.md +12 -9
- data/vendor-src/libleptris/docs/guide/memory-model.md +229 -0
- data/vendor-src/libleptris/docs/guide/migrating-from-libxml2.md +131 -0
- data/vendor-src/libleptris/packaging/README.md +120 -0
- data/vendor-src/libleptris/packaging/conan/conanfile.py +120 -0
- data/vendor-src/libleptris/packaging/conan/test_package/CMakeLists.txt +7 -0
- data/vendor-src/libleptris/packaging/conan/test_package/conanfile.py +27 -0
- data/vendor-src/libleptris/packaging/conan/test_package/src/example.c +22 -0
- data/vendor-src/libleptris/packaging/debian/README.md +87 -0
- data/vendor-src/libleptris/packaging/debian/changelog +8 -0
- data/vendor-src/libleptris/packaging/debian/control +55 -0
- data/vendor-src/libleptris/packaging/debian/copyright +26 -0
- data/vendor-src/libleptris/packaging/debian/leptris.install +1 -0
- data/vendor-src/libleptris/packaging/debian/libleptris-dev.install +5 -0
- data/vendor-src/libleptris/packaging/debian/libleptris1.install +1 -0
- data/vendor-src/libleptris/packaging/debian/rules +34 -0
- data/vendor-src/libleptris/packaging/debian/source/format +1 -0
- data/vendor-src/libleptris/packaging/homebrew/leptris.rb +75 -0
- data/vendor-src/libleptris/scripts/perf_gate.py +51 -0
- data/vendor-src/libleptris/scripts/verify_manifest.py +182 -0
- data/vendor-src/libleptris/src/leptris/dtd/content_check.c +31 -23
- data/vendor-src/libleptris/src/leptris/flat/direct_parse.c +52 -8
- data/vendor-src/libleptris/src/leptris/xinclude/xinclude.c +359 -244
- data/vendor-src/libleptris/src/leptris/xpath/functions_ext31.c +17 -11
- data/vendor-src/libleptris/src/leptris/xsd/compile.c +110 -7
- data/vendor-src/libleptris/src/leptris/xsd/datatypes.c +14 -4
- data/vendor-src/libleptris/src/leptris/xsd/validate.c +179 -19
- data/vendor-src/libleptris/src/leptris/xsd/xsd_internal.h +4 -0
- data/vendor-src/libleptris/test/differential/README.md +100 -0
- data/vendor-src/libleptris/test/differential/corpus/basic.xml +13 -0
- data/vendor-src/libleptris/test/differential/corpus/cdata.xml +5 -0
- data/vendor-src/libleptris/test/differential/corpus/comments_pis.xml +1 -0
- data/vendor-src/libleptris/test/differential/corpus/dtd_model.xml +1 -0
- data/vendor-src/libleptris/test/differential/corpus/entities_dtd.xml +1 -0
- data/vendor-src/libleptris/test/differential/corpus/mixed_deep.xml +1 -0
- data/vendor-src/libleptris/test/differential/corpus/namespaces.xml +11 -0
- data/vendor-src/libleptris/test/differential/corpus/xpath_target.xml +1 -0
- data/vendor-src/libleptris/test/differential/package.json +9 -0
- data/vendor-src/libleptris/test/differential/run.mjs +52 -0
- data/vendor-src/libleptris/test/differential/run.py +61 -0
- data/vendor-src/libleptris/test/differential/run.rb +48 -0
- data/vendor-src/libleptris/test/dtd/test_dtd_validate.cpp +39 -0
- data/vendor-src/libleptris/test/xinclude/test_xinclude.cpp +274 -10
- data/vendor-src/libleptris/test/xpath/test_xpath.cpp +5 -0
- data/vendor-src/libleptris/test/xquery/qt3-dispositions/README.md +47 -0
- data/vendor-src/libleptris/test/xquery/qt3-dispositions/assert-deep-eq.md +187 -0
- data/vendor-src/libleptris/test/xquery/qt3-dispositions/assert-kinds-misc.md +48 -0
- data/vendor-src/libleptris/test/xquery/qt3-dispositions/assert-type.md +285 -0
- data/vendor-src/libleptris/test/xquery/qt3-dispositions/dep-gates.md +57 -0
- data/vendor-src/libleptris/test/xquery/qt3-dispositions/empty-query.md +11 -0
- data/vendor-src/libleptris/test/xquery/qt3-dispositions/env-inline-no-params.md +48 -0
- data/vendor-src/libleptris/test/xquery/qt3-dispositions/env-unsupported.md +143 -0
- data/vendor-src/libleptris/test/xquery/qt3-dispositions/error-channel.md +782 -0
- data/vendor-src/libleptris/test/xquery/qt3-dispositions/named-exclude.md +85 -0
- data/vendor-src/libleptris/test/xquery/qt3-dispositions/query-exclude.md +379 -0
- data/vendor-src/libleptris/test/xquery/test_qt3.cpp +192 -26
- data/vendor-src/libleptris/test/xsd/test_xsd_compile.cpp +524 -2
- data/vendor-src/libleptris/test/xsd/xsd-dispositions/README.md +59 -0
- data/vendor-src/libleptris/vcpkg.json +1 -1
- metadata +46 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 213b5096ca73813ee05e8816632f5f3f0e0cbae5744b477b5cecb7acae15ad2b
|
|
4
|
+
data.tar.gz: 23ea9d5f3ddb5f067e5bc90b29599e47df1418336bff8caa1b3fb139202079fe
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: ca38fd56c66cf502830190aed494c9371571477c2d4609d93dcc942ff98b7c34503ffe34f61dda5148aa63306f1386b4b99c022d587be5cb6b19e79e103ef8f2
|
|
7
|
+
data.tar.gz: 07ff974cc3fee133d576d76cbf205422235d6cc7656ade52f0d50453c8cc0232f6d648969e6aa2d3778f15e3417f1a30b75b07e93e8124054b75b47a0c876f62
|
data/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,22 @@
|
|
|
1
|
+
## [Unreleased]
|
|
2
|
+
|
|
3
|
+
### Fixed — engine ride
|
|
4
|
+
|
|
5
|
+
- **Ride libleptris 1.9.380**: the xsi:type conformance fix
|
|
6
|
+
(leptris/leptris#1832, lutaml/moxml#433) — an element carrying
|
|
7
|
+
`xsi:type` is assessed against the NAMED type (XSD 1.0
|
|
8
|
+
§2.6.2/§3.3.4), so valid xsi:type-derived instances validate
|
|
9
|
+
instead of being rejected against the declared type's content
|
|
10
|
+
model. Released 1.9.380.0 with the pin still at 1.9.379 (the
|
|
11
|
+
vendored dylib missed the fix); this pin bump is the real ride.
|
|
12
|
+
|
|
13
|
+
### Fixed — engine ride (1.9.380.2)
|
|
14
|
+
|
|
15
|
+
- **Bulk XPath priming face rides (leptris/leptris-ruby#537)**:
|
|
16
|
+
`xpath_result_rows_bulk` gains the mx_context/mx_adapter birth-prime
|
|
17
|
+
parameters and marks all-native results; 1.9.380.1 shipped before
|
|
18
|
+
the PR merged, so the face rides in 1.9.380.2.
|
|
19
|
+
|
|
1
20
|
## [1.9.379.3] - 2026-10-22
|
|
2
21
|
|
|
3
22
|
### Fixed
|
data/Rakefile
CHANGED
|
@@ -8,7 +8,7 @@ RSpec::Core::RakeTask.new(:spec)
|
|
|
8
8
|
# Pin for `rake compile` and the platform-gem builds. Keep in lockstep
|
|
9
9
|
# with .github/workflows/build.yml (which calls `rake compile`) and the
|
|
10
10
|
# CHANGELOG when libleptris releases.
|
|
11
|
-
LIBLEPTRIS_VERSION = "1.9.
|
|
11
|
+
LIBLEPTRIS_VERSION = "1.9.380"
|
|
12
12
|
# Vendored alongside libleptris for fn:normalize-unicode (TODO
|
|
13
13
|
# .restructure/20): built per platform with a RELOCATABLE @rpath
|
|
14
14
|
# install name, loaded by ffi.rb before libleptris so the
|
data/ext/leptris/native/native.c
CHANGED
|
@@ -203,6 +203,10 @@ static VALUE binding_klass_for(int kind);
|
|
|
203
203
|
static VALUE c_iteration_scope;
|
|
204
204
|
static VALUE c_leptris_error;
|
|
205
205
|
static VALUE c_b_document, c_b_freed, c_ffi_pointer;
|
|
206
|
+
/* BulkIdentityRows resolves lazily like the binding classes: the
|
|
207
|
+
* module lives in native_layer.rb, which optional consumers load. */
|
|
208
|
+
static VALUE c_bulk_rows_mark = Qundef;
|
|
209
|
+
static ID id_bulk_rows_mark;
|
|
206
210
|
static ID id_ptr_new, id_freed_new, id_alive;
|
|
207
211
|
static ID id_iv_c_address, id_iv_document, id_iv_parent;
|
|
208
212
|
static ID id_iv_mx_context, id_iv_mx_adapter, id_iv_mx_native;
|
|
@@ -221,8 +225,23 @@ struct native_node {
|
|
|
221
225
|
* Slot beats the hash memo on repeat reads of one name. */
|
|
222
226
|
VALUE attr_key, attr_val, attr_ver;
|
|
223
227
|
VALUE content_val, content_ver;
|
|
228
|
+
/* Walk-read memos (leptris-ruby#530): the element name and the
|
|
229
|
+
* children list, the two reads a tree walk pays per node. The
|
|
230
|
+
* children memo is what the subtree prime (#527 machinery)
|
|
231
|
+
* fills; the version stamp retires both on any mutation. The
|
|
232
|
+
* children list is SHARED with callers until the version moves
|
|
233
|
+
* (the mutation that would invalidate it also retires the
|
|
234
|
+
* memo) — treat it as read-only, like an interned String. */
|
|
235
|
+
VALUE name_val, name_ver;
|
|
236
|
+
VALUE children_val, children_ver;
|
|
224
237
|
};
|
|
225
238
|
|
|
239
|
+
static VALUE columnar_node_for(VALUE document, VALUE owner_klass,
|
|
240
|
+
void *ptr, int kind);
|
|
241
|
+
static VALUE doc_klass_for_kind(VALUE document, int kind);
|
|
242
|
+
static VALUE nn_prime_children_of(VALUE document, VALUE owner_klass,
|
|
243
|
+
void *node, VALUE ver);
|
|
244
|
+
|
|
226
245
|
static void nn_mark(void *p)
|
|
227
246
|
{
|
|
228
247
|
struct native_node *n = p;
|
|
@@ -234,6 +253,10 @@ static void nn_mark(void *p)
|
|
|
234
253
|
rb_gc_mark(n->attr_val);
|
|
235
254
|
if (n->content_val != Qnil)
|
|
236
255
|
rb_gc_mark(n->content_val);
|
|
256
|
+
if (n->name_val != Qnil)
|
|
257
|
+
rb_gc_mark(n->name_val);
|
|
258
|
+
if (n->children_val != Qnil)
|
|
259
|
+
rb_gc_mark(n->children_val);
|
|
237
260
|
}
|
|
238
261
|
|
|
239
262
|
static size_t nn_size(const void *p) { (void)p; return sizeof(struct native_node); }
|
|
@@ -367,7 +390,19 @@ static VALUE nn_name(VALUE self)
|
|
|
367
390
|
{
|
|
368
391
|
struct native_node *n;
|
|
369
392
|
const char *s;
|
|
393
|
+
VALUE doc, ver;
|
|
394
|
+
|
|
370
395
|
TypedData_Get_Struct(self, struct native_node, &nn_type, n);
|
|
396
|
+
/* Walk-memo hit (leptris-ruby#530): a repeat name read is two
|
|
397
|
+
* struct reads and a Fixnum compare — the subtree prime fills
|
|
398
|
+
* this slot ahead of the walk, so walked names never recompute
|
|
399
|
+
* (or re-intern) until the next mutation. */
|
|
400
|
+
doc = n->document;
|
|
401
|
+
if (doc != Qnil) {
|
|
402
|
+
ver = rb_ivar_get(doc, id_iv_version);
|
|
403
|
+
if (n->name_ver == ver)
|
|
404
|
+
return n->name_val;
|
|
405
|
+
}
|
|
371
406
|
s = f_elem_name(n->ptr);
|
|
372
407
|
/* Interned (shared, frozen) fstring: element names repeat
|
|
373
408
|
* massively across a document — one RString per distinct name
|
|
@@ -375,7 +410,13 @@ static VALUE nn_name(VALUE self)
|
|
|
375
410
|
* ~18% of consumer materialize allocations were fresh name
|
|
376
411
|
* strings). Names are identifiers; nothing downstream mutates
|
|
377
412
|
* them in place (renames mint new strings). */
|
|
378
|
-
|
|
413
|
+
VALUE name = s ? rb_enc_interned_str(s, strlen(s), rb_utf8_encoding())
|
|
414
|
+
: Qnil;
|
|
415
|
+
if (doc != Qnil) {
|
|
416
|
+
n->name_val = name;
|
|
417
|
+
n->name_ver = ver;
|
|
418
|
+
}
|
|
419
|
+
return name;
|
|
379
420
|
}
|
|
380
421
|
|
|
381
422
|
static VALUE nn_content(VALUE self)
|
|
@@ -563,14 +604,43 @@ static VALUE nn_children(VALUE self)
|
|
|
563
604
|
void **buf;
|
|
564
605
|
int *kinds;
|
|
565
606
|
int count, i;
|
|
566
|
-
VALUE cache, out, owner_klass;
|
|
607
|
+
VALUE cache, out, owner_klass, doc, ver;
|
|
567
608
|
|
|
568
609
|
TypedData_Get_Struct(self, struct native_node, &nn_type, n);
|
|
610
|
+
/* Walk-memo hit (leptris-ruby#530): the subtree prime (or an
|
|
611
|
+
* earlier read) filled this node's list against the current
|
|
612
|
+
* mutation version — serve it, engine walk skipped. */
|
|
613
|
+
doc = n->document;
|
|
614
|
+
if (doc != Qnil) {
|
|
615
|
+
ver = rb_ivar_get(doc, id_iv_version);
|
|
616
|
+
if (n->children_ver == ver)
|
|
617
|
+
return n->children_val;
|
|
618
|
+
/* Registered-consumer documents (#340 doc-klass map = the
|
|
619
|
+
* moxml mint registration) walk whole subtrees top-down:
|
|
620
|
+
* the FIRST cold children_read trades itself for ONE
|
|
621
|
+
* subtree crossing that fills every node's name + children
|
|
622
|
+
* memo (walk_columnar machinery, native-cache identity —
|
|
623
|
+
* the primed wrappers ARE the answers later reads give).
|
|
624
|
+
* Unregistered documents keep the per-node fetch; both
|
|
625
|
+
* paths memo-store below. */
|
|
626
|
+
if (!NIL_P(doc_klass_for_kind(doc, NT_ELEMENT))) {
|
|
627
|
+
out = nn_prime_children_of(doc, rb_obj_class(self), n->ptr,
|
|
628
|
+
ver);
|
|
629
|
+
n->children_val = out;
|
|
630
|
+
n->children_ver = ver;
|
|
631
|
+
return out;
|
|
632
|
+
}
|
|
633
|
+
}
|
|
569
634
|
count = fetch_children_two_call(n->ptr, &buf, &kinds);
|
|
570
635
|
if (count <= 0) {
|
|
571
636
|
ruby_xfree(buf);
|
|
572
637
|
ruby_xfree(kinds);
|
|
573
|
-
|
|
638
|
+
out = rb_ary_new2(0);
|
|
639
|
+
if (doc != Qnil) {
|
|
640
|
+
n->children_val = out;
|
|
641
|
+
n->children_ver = ver;
|
|
642
|
+
}
|
|
643
|
+
return out;
|
|
574
644
|
}
|
|
575
645
|
|
|
576
646
|
cache = native_cache_of(n->document);
|
|
@@ -592,6 +662,10 @@ static VALUE nn_children(VALUE self)
|
|
|
592
662
|
}
|
|
593
663
|
ruby_xfree(buf);
|
|
594
664
|
ruby_xfree(kinds);
|
|
665
|
+
if (doc != Qnil) {
|
|
666
|
+
n->children_val = out;
|
|
667
|
+
n->children_ver = ver;
|
|
668
|
+
}
|
|
595
669
|
return out;
|
|
596
670
|
}
|
|
597
671
|
|
|
@@ -1940,18 +2014,28 @@ static VALUE nf_at_xpath_first(VALUE self, VALUE document,
|
|
|
1940
2014
|
* Nodesets free the handle here (TODO.perf/22 eager discipline);
|
|
1941
2015
|
* non-nodeset results return Qnil UNFREED — the caller keeps the
|
|
1942
2016
|
* scalar path, whose wrap owns the free. */
|
|
1943
|
-
static VALUE nf_xpath_result_rows_bulk(VALUE
|
|
1944
|
-
VALUE result_addr)
|
|
2017
|
+
static VALUE nf_xpath_result_rows_bulk(int argc, VALUE *argv, VALUE self)
|
|
1945
2018
|
{
|
|
1946
|
-
|
|
2019
|
+
VALUE document, result_addr, mx_context, mx_adapter;
|
|
2020
|
+
void *result;
|
|
1947
2021
|
void **buf;
|
|
1948
2022
|
int *kinds;
|
|
1949
2023
|
size_t scount;
|
|
1950
2024
|
long i, n;
|
|
1951
2025
|
VALUE cache, out;
|
|
2026
|
+
int all_native;
|
|
1952
2027
|
|
|
1953
2028
|
(void)self;
|
|
1954
2029
|
resolve_binding_classes();
|
|
2030
|
+
/* Optional (context, adapter): when the consumer passes its
|
|
2031
|
+
* contract state, every registered-klass row is born primed
|
|
2032
|
+
* (@native is the node itself, @context/@adapter written here)
|
|
2033
|
+
* and an all-native result extends with BulkIdentityRows —
|
|
2034
|
+
* the consumer's per-row wrap prime disappears from the
|
|
2035
|
+
* iterate-every-result lane (same triple as nn_create_element). */
|
|
2036
|
+
rb_scan_args(argc, argv, "22", &document, &result_addr,
|
|
2037
|
+
&mx_context, &mx_adapter);
|
|
2038
|
+
result = (void *)(uintptr_t)NUM2ULL(result_addr);
|
|
1955
2039
|
if (f_xp_type(result) != 0) /* XPATH_NODESET only */
|
|
1956
2040
|
return Qnil;
|
|
1957
2041
|
|
|
@@ -1969,6 +2053,7 @@ static VALUE nf_xpath_result_rows_bulk(VALUE self, VALUE document,
|
|
|
1969
2053
|
n = (long)scount;
|
|
1970
2054
|
out = rb_ary_new_capa(n);
|
|
1971
2055
|
cache = binding_cache_of(document);
|
|
2056
|
+
all_native = !NIL_P(mx_context);
|
|
1972
2057
|
for (i = 0; i < n; i++) {
|
|
1973
2058
|
void *ptr = buf[i];
|
|
1974
2059
|
int tag;
|
|
@@ -1988,10 +2073,17 @@ static VALUE nf_xpath_result_rows_bulk(VALUE self, VALUE document,
|
|
|
1988
2073
|
node = rb_hash_aref(native_cache_of(document), key);
|
|
1989
2074
|
if (NIL_P(node))
|
|
1990
2075
|
node = mint_consumer_node(mk, document, ptr);
|
|
2076
|
+
if (all_native &&
|
|
2077
|
+
!rb_ivar_defined(node, id_iv_mx_context)) {
|
|
2078
|
+
rb_ivar_set(node, id_iv_mx_native, node);
|
|
2079
|
+
rb_ivar_set(node, id_iv_mx_context, mx_context);
|
|
2080
|
+
rb_ivar_set(node, id_iv_mx_adapter, mx_adapter);
|
|
2081
|
+
}
|
|
1991
2082
|
rb_ary_push(out, node);
|
|
1992
2083
|
continue;
|
|
1993
2084
|
}
|
|
1994
2085
|
}
|
|
2086
|
+
all_native = 0;
|
|
1995
2087
|
node = materialize_xp_entry(document, cache, result, ptr,
|
|
1996
2088
|
kinds[i], (int)i);
|
|
1997
2089
|
if (node == m_native_sentinel) {
|
|
@@ -2005,6 +2097,22 @@ static VALUE nf_xpath_result_rows_bulk(VALUE self, VALUE document,
|
|
|
2005
2097
|
ruby_xfree(buf);
|
|
2006
2098
|
ruby_xfree(kinds);
|
|
2007
2099
|
f_xp_free(result);
|
|
2100
|
+
if (all_native) {
|
|
2101
|
+
/* Certificate for the consumer's fast row loop: every row
|
|
2102
|
+
* is a born-primed, registered-klass identity native. The
|
|
2103
|
+
* module lives in the Ruby layer (native_layer.rb) and may
|
|
2104
|
+
* not be loaded on faces used without it — an unresolved
|
|
2105
|
+
* mark degrades to the unmarked Array, never an error. */
|
|
2106
|
+
if (c_bulk_rows_mark == Qundef) {
|
|
2107
|
+
VALUE m_xml = rb_path2class("Leptris::XML");
|
|
2108
|
+
c_bulk_rows_mark =
|
|
2109
|
+
rb_const_defined_at(m_xml, id_bulk_rows_mark)
|
|
2110
|
+
? rb_const_get_at(m_xml, id_bulk_rows_mark)
|
|
2111
|
+
: Qnil;
|
|
2112
|
+
}
|
|
2113
|
+
if (c_bulk_rows_mark != Qnil)
|
|
2114
|
+
rb_extend_object(out, c_bulk_rows_mark);
|
|
2115
|
+
}
|
|
2008
2116
|
return out;
|
|
2009
2117
|
}
|
|
2010
2118
|
|
|
@@ -2599,6 +2707,58 @@ static void walk_columnar_impl(VALUE document, VALUE owner_klass,
|
|
|
2599
2707
|
}
|
|
2600
2708
|
}
|
|
2601
2709
|
|
|
2710
|
+
/* ---- Subtree memo priming (leptris-ruby#530) ---------------------
|
|
2711
|
+
* Registered-consumer documents (the #340 doc-klass map — moxml's
|
|
2712
|
+
* mint registration) walk whole subtrees top-down, so the FIRST
|
|
2713
|
+
* cold children_read fills every ELEMENT's name + children read
|
|
2714
|
+
* memos in ONE crossing, reusing the walk_columnar machinery and
|
|
2715
|
+
* minting through columnar_node_for (native-cache identity — the
|
|
2716
|
+
* primed wrappers ARE the answers later reads hand back). Memos
|
|
2717
|
+
* are version-stamped like the content memo: any mutation bumps
|
|
2718
|
+
* @version and retires them subtree-wide.
|
|
2719
|
+
*
|
|
2720
|
+
* Conservative by construction: only ELEMENT descendants get their
|
|
2721
|
+
* memos filled here (the one kind whose f_first_child chain IS its
|
|
2722
|
+
* children list, the #527 contract); leaf and shell kinds keep the
|
|
2723
|
+
* per-node fetch, which memo-stores the same way. The receiver's
|
|
2724
|
+
* own name memo is left to nn_name (one node, once). */
|
|
2725
|
+
static VALUE nn_prime_children_of(VALUE document, VALUE owner_klass,
|
|
2726
|
+
void *node, VALUE ver)
|
|
2727
|
+
{
|
|
2728
|
+
VALUE out = rb_ary_new();
|
|
2729
|
+
void *c;
|
|
2730
|
+
|
|
2731
|
+
for (c = f_first_child(node); c; c = f_next_sibling(c)) {
|
|
2732
|
+
int kind = f_node_type(c);
|
|
2733
|
+
VALUE wrapper = columnar_node_for(document, owner_klass, c, kind);
|
|
2734
|
+
struct native_node *nn;
|
|
2735
|
+
|
|
2736
|
+
TypedData_Get_Struct(wrapper, struct native_node, &nn_type, nn);
|
|
2737
|
+
if (kind == WS_NODE_ELEMENT) {
|
|
2738
|
+
const char *name = f_elem_name(c);
|
|
2739
|
+
nn->name_val = name ? rb_enc_interned_str(name, strlen(name),
|
|
2740
|
+
rb_utf8_encoding())
|
|
2741
|
+
: Qnil;
|
|
2742
|
+
nn->name_ver = ver;
|
|
2743
|
+
nn->children_val = nn_prime_children_of(document, owner_klass,
|
|
2744
|
+
c, ver);
|
|
2745
|
+
nn->children_ver = ver;
|
|
2746
|
+
} else if (kind != WS_NODE_DOCTYPE && kind != 9 /* DOCUMENT */) {
|
|
2747
|
+
/* leaf kinds: memo the name exactly as nn_name computes
|
|
2748
|
+
* it (f_elem_name answers "" for nameless kinds); their
|
|
2749
|
+
* (empty) children keep the fetch path, which
|
|
2750
|
+
* memo-stores the same way. */
|
|
2751
|
+
const char *name = f_elem_name(c);
|
|
2752
|
+
nn->name_val = name ? rb_enc_interned_str(name, strlen(name),
|
|
2753
|
+
rb_utf8_encoding())
|
|
2754
|
+
: Qnil;
|
|
2755
|
+
nn->name_ver = ver;
|
|
2756
|
+
}
|
|
2757
|
+
rb_ary_push(out, wrapper);
|
|
2758
|
+
}
|
|
2759
|
+
return out;
|
|
2760
|
+
}
|
|
2761
|
+
|
|
2602
2762
|
static VALUE nf_walk_columnar(VALUE self, VALUE root)
|
|
2603
2763
|
{
|
|
2604
2764
|
struct native_node *n;
|
|
@@ -3715,6 +3875,7 @@ LEPTRIS_INIT_EXPORT void Init_native(void)
|
|
|
3715
3875
|
id_iv_mx_context = rb_intern("@context");
|
|
3716
3876
|
id_iv_mx_native = rb_intern("@native");
|
|
3717
3877
|
id_iv_mx_adapter = rb_intern("@adapter");
|
|
3878
|
+
id_bulk_rows_mark = rb_intern("BulkIdentityRows");
|
|
3718
3879
|
id_iv_parent = rb_intern("@parent");
|
|
3719
3880
|
id_iv_structure_memoizable = rb_intern("@structure_memoizable");
|
|
3720
3881
|
id_iv_native_fast = rb_intern("@native_fast");
|
|
@@ -3823,7 +3984,7 @@ LEPTRIS_INIT_EXPORT void Init_native(void)
|
|
|
3823
3984
|
rb_define_module_function(m_native, "materialize_xpath",
|
|
3824
3985
|
nf_materialize_xpath, 2);
|
|
3825
3986
|
rb_define_module_function(m_native, "xpath_result_rows_bulk",
|
|
3826
|
-
nf_xpath_result_rows_bulk,
|
|
3987
|
+
nf_xpath_result_rows_bulk, -1);
|
|
3827
3988
|
rb_define_module_function(m_native, "set_binding_name",
|
|
3828
3989
|
nf_set_binding_name, 3);
|
|
3829
3990
|
rb_define_module_function(m_native, "set_binding_text",
|
|
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
|
@@ -59,6 +59,19 @@ rescue LoadError => e
|
|
|
59
59
|
MSG
|
|
60
60
|
end
|
|
61
61
|
|
|
62
|
+
# Certificate module for the bulk XPath rows face: an Array extended
|
|
63
|
+
# with this carries ONLY born-primed registered-klass identity natives
|
|
64
|
+
# (xpath_result_rows_bulk answered with a consumer context+adapter, so
|
|
65
|
+
# @native is the node itself and @context/@adapter are already set).
|
|
66
|
+
# Consumers with a per-row wrap in their result loop can key a fast
|
|
67
|
+
# path on it (moxml's NodeSet#each); one object per native address
|
|
68
|
+
# remains the binding's native-cache contract (#529) either way.
|
|
69
|
+
module Leptris::XML::BulkIdentityRows
|
|
70
|
+
def bulk_identity_rows?
|
|
71
|
+
true
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
|
|
62
75
|
module Leptris::XML::Native
|
|
63
76
|
# FFI.libleptris_candidates is the single source of truth — the
|
|
64
77
|
# native layer MUST dlsym the exact image the FFI layer loaded,
|
|
@@ -118,10 +131,15 @@ class Leptris::XML::Document
|
|
|
118
131
|
# (nodesets free the handle in C); a non-nodeset result answers
|
|
119
132
|
# nil and leaves the pointer with the caller — route it through
|
|
120
133
|
# Searchable.wrap_xpath_result for the scalar faces.
|
|
121
|
-
|
|
134
|
+
# Optional (context, adapter): when the consumer passes its
|
|
135
|
+
# contract state, every registered-klass row is born primed and an
|
|
136
|
+
# all-native result extends with BulkIdentityRows — the consumer's
|
|
137
|
+
# per-row wrap prime disappears from the iterate-every-result lane.
|
|
138
|
+
def xpath_result_rows_bulk(result_ptr, mx_context = nil, mx_adapter = nil)
|
|
122
139
|
Leptris::XML::Native.xpath_result_rows_bulk(
|
|
123
140
|
self, result_ptr.respond_to?(:address) ? result_ptr.address
|
|
124
|
-
: result_ptr
|
|
141
|
+
: result_ptr,
|
|
142
|
+
mx_context, mx_adapter)
|
|
125
143
|
end
|
|
126
144
|
|
|
127
145
|
# Builder factories that return NativeNodes (#149): one C call
|
data/vendor-src/README.md
CHANGED
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
## [Unreleased]
|
|
2
2
|
|
|
3
|
+
## [1.9.380] - 2026-10-22
|
|
4
|
+
|
|
5
|
+
### Fixed
|
|
6
|
+
|
|
7
|
+
- assess elements against the instance's xsi:type (moxml#433) (xsd)
|
|
8
|
+
- choice alternatives after a failed parenthesized branch were unreachable (dtd)
|
|
9
|
+
- iterative nested-include processing - no C-stack nesting (xinclude)
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
|
|
3
13
|
## [1.9.379] - 2026-10-21
|
|
4
14
|
|
|
5
15
|
### Fixed
|
|
@@ -27,54 +27,62 @@ docs/MISSION-PLAN.md and is updated at every release.
|
|
|
27
27
|
|
|
28
28
|
## Gate A — Conformance: zero reds everywhere
|
|
29
29
|
|
|
30
|
-
### A1 WHATWG verdict parity:
|
|
31
|
-
**Measured 2026-10-21 (
|
|
32
|
-
1555/1555
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
(
|
|
48
|
-
-
|
|
49
|
-
|
|
50
|
-
EOF classification
|
|
51
|
-
- [ ] the after-body EOF phase rule (tests1:34 class; the exact
|
|
52
|
-
phaseAtBody.eof semantics)
|
|
53
|
-
- [ ] in-body `unexpected-start-tag` family (body/frameset/form
|
|
54
|
-
starts; the startTagBody/Forms/Frameset error sites)
|
|
55
|
-
- [ ] `unexpected-end-tag` family (p-in-button-scope synthesis,
|
|
56
|
-
form pointer, li/dd/dt scope, endTagOther walk)
|
|
57
|
-
- [ ] foster-parenting sites (start-tag/character/character-in-table)
|
|
58
|
-
- [ ] adoption-agency codes (`adoption-agency-1.3` et al.)
|
|
59
|
-
- [ ] in-table voodoo (`*-implies-table-voodoo`, cells, eof-in-table)
|
|
60
|
-
- [ ] unknown-doctype / unexpected-char-in-doctype (doctype
|
|
61
|
-
tokenization §13.2.5.28+)
|
|
62
|
-
- [ ] non-html-root, select-mode codes, heading/edge shapes
|
|
30
|
+
### A1 WHATWG verdict parity: DONE — 1555/1555
|
|
31
|
+
**Measured 2026-10-21 (PR #1803, tagged v1.9.378): verdict
|
|
32
|
+
1555/1555, ZERO misses; tree 1555/1555; floor pinned at the full
|
|
33
|
+
corpus count.** The gate is presence-only — every corpus document
|
|
34
|
+
now reports a parse error exactly when html5lib does. The final
|
|
35
|
+
tail closed at the html5lib source sites: adoption-agency-1.3
|
|
36
|
+
(7 cases), menuitem endTagOther pair, tokenizer
|
|
37
|
+
expected-dashes-or-doctype / solidus-in-tag / attributes-in-end-tag,
|
|
38
|
+
AfterAfterFrameset trailing chars, BeforeHead
|
|
39
|
+
end-tag-after-implied-root, BeforeHtml unexpected-end-tag-before-html,
|
|
40
|
+
foreign end-tag mismatch, form-in-table both flavors, </form> table
|
|
41
|
+
voodoo, template foster-parenting-character, two-heads. Earlier
|
|
42
|
+
slices: initial mode, raw-text EOF, unterminated close, frameset,
|
|
43
|
+
fenced walk, InBody EOF allowed-set, in-select, foster-by-kind,
|
|
44
|
+
unknown-doctype (#1778-#1791), raw-text strays, non-html-root,
|
|
45
|
+
end-tag-too-early, after-body/frameset phases, start-tag phases,
|
|
46
|
+
select/table/foreign, need-space-after-doctype (#1794),
|
|
47
|
+
misplaced/stray-p, after-html codes, image (#1797, #1800). The
|
|
48
|
+
~270 remaining code-set divergences are prose-dialect labels from
|
|
49
|
+
older corpus datasets — reported, not gated.
|
|
63
50
|
|
|
64
51
|
### A2 QT3 / XQuery 3.1: zero unexplained pins
|
|
65
|
-
- [
|
|
66
|
-
- [
|
|
52
|
+
- [x] re-pin fresh (2026-10-22: 1723 pins, 62/62 green, zero reds; 106 sets, 72 pins moved)
|
|
53
|
+
- [x] every remaining pin gets a disposition file or a fix
|
|
54
|
+
(test/xquery/qt3-dispositions/ — 10 files, 1723/1723 dispositioned;
|
|
55
|
+
fixes: fn:codepoint-equal empty-operand propagation, driver
|
|
56
|
+
assert-count/assert-empty, role="." context sources, QT3_REPORT
|
|
57
|
+
inventory channel)
|
|
67
58
|
- [ ] ctor node-yield family: owned_docs transfer completion
|
|
68
59
|
|
|
69
60
|
### A3 XSD conformance lane: suite-complete
|
|
70
|
-
- [
|
|
61
|
+
- [x] the lane's own queue to its dispositioned ceiling — **2026-10-22:
|
|
62
|
+
903 → 989**/1056 (PR #1825; the board's 795 was stale — five slices
|
|
63
|
+
had landed). 9 root-caused classes retired:
|
|
64
|
+
(2026-10-22: 903 → **989**/1056 — 9 root-caused classes retired:
|
|
65
|
+
xsi-attr exemption, empty content models, unprefixed builtins,
|
|
66
|
+
no-tns `##other`, xs:attribute 3.2.3 clauses (+55), g-regional
|
|
67
|
+
timezone, token whitespace, unknown-xs-builtin refs. 67 left,
|
|
68
|
+
every row dispositioned in test/xsd/xsd-dispositions/README.md:
|
|
69
|
+
substitution groups in content models, xsi:type, attribute form
|
|
70
|
+
defaults, identity-constraint checks, derivation-ok attribute
|
|
71
|
+
clauses; 1 oracle bug (bug322411) not chased)
|
|
71
72
|
|
|
72
73
|
### A4 DTD phase 8 (TODO 91)
|
|
73
|
-
- [
|
|
74
|
-
|
|
74
|
+
- [x] ENTITY/ENTITIES attribute resolution (undeclared entity, parsed
|
|
75
|
+
entity without NDATA, undeclared notation, bad ENTITIES token —
|
|
76
|
+
all falsifiably spec'd; shipped bd21acc6/9d3b3a0e)
|
|
77
|
+
- [x] parameter entities (internal, chained, ATTLIST/ELEMENT bodies,
|
|
78
|
+
SYSTEM PEs via loader, recursion cap, conditional sections) and
|
|
79
|
+
choice-model backtracking — group-alternative failure walker
|
|
80
|
+
fixed so a failed parenthesized alternative still reaches later
|
|
81
|
+
`|` branches (DtdPhase8.GroupAlternativeFailureStillTriesLaterAlternatives)
|
|
75
82
|
|
|
76
83
|
### A5 XInclude completeness (TODO 92)
|
|
77
|
-
- [
|
|
84
|
+
- [x] `parse="xml"` (ownership-transfer splice + canonical insert face,
|
|
85
|
+
depth-guard resource-error fallback; verified by specs)
|
|
78
86
|
- [ ] xpointer
|
|
79
87
|
|
|
80
88
|
### A6 The universality proof (differential corpus)
|
|
@@ -90,15 +98,15 @@ Ground every further slice in html5lib's actual source
|
|
|
90
98
|
## Gate B — Performance: every published row ≥1.00x
|
|
91
99
|
|
|
92
100
|
### B1 build row: ~2.9x → ≤1.0x (the #242 campaign)
|
|
93
|
-
**Measured 2026-10-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
101
|
+
**Measured 2026-10-22: ~1.47-1.54x median after NINE merged levers**
|
|
102
|
+
(honest baseline 2.72x; guard retirement −22%, namespace gate,
|
|
103
|
+
nil-ivar priming, children-cache guard, mint direct-dispatch +
|
|
104
|
+
native set_attribute −19% (PR #431), prepare_node inline (PR #432)).
|
|
105
|
+
Harness `wt/460-harness/build_bench.rb`; A/B pairs alternate within
|
|
106
|
+
ONE window (drift ±20%). Rejected within noise: fast_attach,
|
|
107
|
+
native-cache deletion, doc_klass memo, identity-probe deletion,
|
|
108
|
+
add_child alias. Residual: wrapper allocation (owner-level node
|
|
109
|
+
layout).
|
|
102
110
|
- [ ] per-element object budget: one Ruby object per created node
|
|
103
111
|
(drop the shell entirely post-create-priming; the mint cache
|
|
104
112
|
insert policy; the context registration)
|
|
@@ -113,8 +121,16 @@ scaffolding. Memo law holds (dropping @children memo = 2x GC).
|
|
|
113
121
|
- [ ] publish the per-face table (the honest much-faster story)
|
|
114
122
|
|
|
115
123
|
### B3 whole-pipeline rows (new, published)
|
|
116
|
-
-
|
|
117
|
-
-
|
|
124
|
+
**Measured 2026-10-21: parse→query→serialize is GREEN — leptris
|
|
125
|
+
finishes in 0.533-0.573x of Nokogiri's CPU time (~1.8x faster),
|
|
126
|
+
three interleaved-median runs (`wt/460-harness/pipeline_bench.rb`,
|
|
127
|
+
250 records, 31 reps; same document and query both sides).**
|
|
128
|
+
- [x] parse→query→serialize pipeline row vs nokogiri (green, dated)
|
|
129
|
+
- [x] build→transform (XSLT) pipeline row — **GREEN 2026-10-21:
|
|
130
|
+
build 250 records, XSLT 1.0 transform, serialize — leptris
|
|
131
|
+
finishes in 0.492-0.515x of Nokogiri's CPU time (~2x faster),
|
|
132
|
+
three interleaved-median runs
|
|
133
|
+
(`wt/460-harness/xslt_pipeline_bench.rb`)**
|
|
118
134
|
- [ ] both to serialbench as the public scoreboard
|
|
119
135
|
|
|
120
136
|
### B4 durability
|
|
@@ -136,19 +152,29 @@ scaffolding. Memo law holds (dropping @children memo = 2x GC).
|
|
|
136
152
|
- [ ] C3 reproducible builds everywhere: darwin toolchain pin done
|
|
137
153
|
(ruby#512) — extend to linux/windows legs; publish a per-
|
|
138
154
|
release build manifest (engine + binding hashes per platform)
|
|
139
|
-
- [
|
|
140
|
-
dependency-free attestation
|
|
155
|
+
- [x] C4 security.md: advisory channel, CVE-response process,
|
|
156
|
+
dependency-free attestation — **shipped 2026-10-21 (PR #1804,
|
|
157
|
+
v1.9.378)**: GitHub Security Advisories channel, 5-business-day
|
|
158
|
+
acknowledgment target, coordinated disclosure, CVE assignment,
|
|
159
|
+
scope table, hardening inventory tied to the lanes that actually
|
|
160
|
+
run
|
|
141
161
|
- [ ] C5 the memory story documented: pool arena model, leak gates,
|
|
142
162
|
the borrowed-slice lifetime contract
|
|
143
163
|
|
|
144
164
|
## Gate D — Universality: same semantics everywhere
|
|
145
165
|
|
|
146
|
-
- [
|
|
147
|
-
|
|
166
|
+
- [x] D1 leptris-descriptor published — **2026-10-22: live on
|
|
167
|
+
crates.io as leptris-descriptor v1.9.379** (lockstep with the
|
|
168
|
+
engine; #1827 fixed the workspace publish to target the
|
|
169
|
+
descriptor package; issue #1829 records the one-time manual
|
|
170
|
+
establishment — crates.io trusted publishing cannot create new
|
|
171
|
+
crates — after which versions publish via the OIDC workflow)
|
|
148
172
|
- [ ] D2 system packaging: homebrew, conan, deb (apt repo)
|
|
149
173
|
- [ ] D3 cross-runtime differential CI (= A6, wired)
|
|
150
|
-
- [
|
|
151
|
-
|
|
174
|
+
- [x] D4 libxml2→libleptris migration guide — **shipped 2026-10-21
|
|
175
|
+
(PR #1810): docs/guide/migrating-from-libxml2.md** (ownership
|
|
176
|
+
model, parse/tree/XPath/serialize/SAX/DTD mappings, deliberate
|
|
177
|
+
differences); nokogiri-compat surface documented in the guide
|
|
152
178
|
|
|
153
179
|
## Gate E — 1.0 (the seal)
|
|
154
180
|
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[package]
|
|
2
2
|
name = "leptris-descriptor"
|
|
3
|
-
version = "
|
|
3
|
+
version = "1.9.379"
|
|
4
4
|
edition = "2021"
|
|
5
5
|
description = "Shared descriptor-plan ABI for the leptris engine family (leptris, yeptris, teptris) — repr(C) mirrors of the tree-shaped descriptor plan records"
|
|
6
6
|
license = "MIT"
|