leptris 1.9.232.0-arm-linux → 1.9.237.0-arm-linux

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.
Files changed (32) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +25 -0
  3. data/Rakefile +1 -1
  4. data/ext/leptris/native/native.c +144 -3
  5. data/lib/leptris/version.rb +1 -1
  6. data/lib/leptris/xml/document.rb +20 -3
  7. data/lib/leptris/xml/ffi.rb +91 -62
  8. data/lib/leptris/xml/native.so +0 -0
  9. data/lib/libleptris.so +0 -0
  10. data/vendor-src/README.md +1 -1
  11. data/vendor-src/libleptris/CHANGELOG.md +113 -4
  12. data/vendor-src/libleptris/CMakeLists.txt +1 -1
  13. data/vendor-src/libleptris/benchmarks/dom/bench_leptris.c +14 -6
  14. data/vendor-src/libleptris/src/include/leptris.h +27 -0
  15. data/vendor-src/libleptris/src/leptris/dom/element_modify.c +20 -0
  16. data/vendor-src/libleptris/src/leptris/leptris_internal.h +5 -0
  17. data/vendor-src/libleptris/src/leptris/xpath/evaluator.c +7 -0
  18. data/vendor-src/libleptris/src/leptris/xpath/evaluator_operators.c +38 -4
  19. data/vendor-src/libleptris/src/leptris/xpath/evaluator_types.c +5 -1
  20. data/vendor-src/libleptris/src/leptris/xpath/functions.c +86 -7
  21. data/vendor-src/libleptris/src/leptris/xpath/functions_ext31.c +237 -24
  22. data/vendor-src/libleptris/src/leptris/xpath/xpath_public.c +16 -1
  23. data/vendor-src/libleptris/test/dom/test_dom.cpp +80 -0
  24. data/vendor-src/libleptris/test/xquery/qt3/fn/count.xml +3117 -0
  25. data/vendor-src/libleptris/test/xquery/qt3/fn/empty.xml +502 -0
  26. data/vendor-src/libleptris/test/xquery/qt3/fn/exists.xml +537 -0
  27. data/vendor-src/libleptris/test/xquery/qt3/fn/head.xml +90 -0
  28. data/vendor-src/libleptris/test/xquery/qt3/fn/tail.xml +68 -0
  29. data/vendor-src/libleptris/test/xquery/test_qt3.cpp +124 -93
  30. data/vendor-src/libleptris/test/xquery/test_xquery.cpp +28 -0
  31. data/vendor-src/libleptris/vcpkg.json +1 -1
  32. metadata +6 -1
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: '059fd6d664035f84522042a8f01d847000b9a03bd6b379c635df6de21c78966b'
4
- data.tar.gz: f28cdfb00cb59d3077965ac76eb98c7a8804594b2b0695ff562f9c086ab8be60
3
+ metadata.gz: deb8e8ccbebd0bcca9030a8c605a7590e8a035b1c59422a29762b90d2834cc1c
4
+ data.tar.gz: 3124a00c5d8c965dd798c84ee19b3d6d0a020b1ff624a38c2ea5eb3002e58520
5
5
  SHA512:
6
- metadata.gz: b255f974356722b350c48ea587590308c548f53d7b27686594d139b40202c028e609a4981bfab2db28adbf1375c3a5bc2f75ee86c060573342ba41c5122ef7c1
7
- data.tar.gz: 7f08ae010ae2909f75b12a396a370b8c36d70833182c5af515f05b0776e61d34d55396bd3e8b05d431644359a2a1bbe648d7ddb415fb49152f2ae44e07c35b55
6
+ metadata.gz: 240fa4a1440c291f6cdbc8093fc8a14f43cd38ae81ef1a84519b7ae38be84c5d6ef1a3223c911035aa3f8b52811557829dd4aed456a2524cf55799034dffcf93
7
+ data.tar.gz: 69e0a10c76deb0970fd40b5bdd81ab57799c9d4ef090b1a4545414b8b0aa7fe343bdf28fa420c613f56946421367cc8898795e88661c19a5bb639019ba6e993c
data/CHANGELOG.md CHANGED
@@ -5,6 +5,31 @@ 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.237.0] - 2026-09-24
9
+
10
+ ### Added
11
+
12
+ - `Document#create_element` accepts an attributes hash:
13
+ `doc.create_element("book", "id" => "1", "lang" => "en")` creates
14
+ the element with every attribute in ONE engine crossing
15
+ (leptris_element_new_with_attributes, #1344) — builder paths
16
+ collapse from 1 + N crossings to one. Duplicate names replace,
17
+ last wins; `nil` values store the empty string (engine
18
+ semantics). The native face falls back to create + per-pair sets
19
+ on pins predating #1344.
20
+
21
+ ### Performance — engine sync (1.9.237)
22
+
23
+ - Vendored libleptris 1.9.237: typed atoms for QT3 equality
24
+ (typed constructors, float32, the xs: constructor surface),
25
+ per-function equality semantics (index-of/distinct-values/
26
+ deep-equal), the F&O fn:subsequence window with NaN safety and
27
+ member-type preservation, the xpath per-eval registry init fix,
28
+ QT3 slice 3 (405 gated cases), the matched-shape traversal
29
+ benchmark correction (#1341: the structural gap vs libxml2 is
30
+ 1.5-1.7x, not 1.8-4.2x), and the single-crossing element
31
+ constructor (#1344).
32
+
8
33
  ## [1.9.232.0] - 2026-09-24
9
34
 
10
35
  ### Added
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.232"
11
+ LIBLEPTRIS_VERSION = "1.9.237"
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
@@ -42,6 +42,8 @@ typedef size_t (*node_offset_fn)(void *);
42
42
  typedef const char *(*element_text_fn)(void *);
43
43
  typedef void *(*doc_create_fn)(void);
44
44
  typedef void *(*elem_create_fn)(void *, const char *);
45
+ typedef void *(*elem_new_attrs_fn)(void *, const char *, const char **,
46
+ const char **, size_t);
45
47
  typedef void *(*text_create_fn)(void *, const char *);
46
48
  typedef void *(*create_child_fn)(void *, const char *);
47
49
  typedef int (*append_child_fn)(void *, void *);
@@ -75,10 +77,14 @@ static node_offset_fn f_node_offset;
75
77
  static element_text_fn f_element_text;
76
78
  static doc_create_fn f_doc_create;
77
79
  static elem_create_fn f_elem_create;
80
+ static elem_new_attrs_fn f_elem_new_with_attrs;
78
81
  static text_create_fn f_text_create;
79
82
  static create_child_fn f_create_child;
80
83
  static append_child_fn f_append_child;
81
84
  static append_child_fn f_prepend_child, f_insert_after, f_insert_before;
85
+ typedef void *(*elem_new_attrs_fn)(void *, const char *, const char **,
86
+ const char **, size_t);
87
+ static elem_new_attrs_fn f_elem_new_with_attrs;
82
88
  static set_attr_fn f_set_attr;
83
89
  typedef int (*xp_type_fn)(void *);
84
90
  typedef void (*xp_free_fn)(void *);
@@ -218,6 +224,11 @@ static void resolve_symbols(const char *lib_path)
218
224
  f_element_text = (element_text_fn)lib_sym(h, "leptris_element_text");
219
225
  f_doc_create = (doc_create_fn)lib_sym(h, "leptris_document_create");
220
226
  f_elem_create = (elem_create_fn)lib_sym(h, "leptris_element_create");
227
+ /* Single-crossing element construction (libleptris 1.9.237,
228
+ * #1344): NULL in older engine pins; the create path falls
229
+ * back to create + per-pair sets. */
230
+ f_elem_new_with_attrs =
231
+ (elem_new_attrs_fn)lib_sym(h, "leptris_element_new_with_attributes");
221
232
  f_text_create = (text_create_fn)lib_sym(h, "leptris_text_node_create");
222
233
  f_create_child = (create_child_fn)lib_sym(h, "leptris_element_create_child");
223
234
  f_append_child = (append_child_fn)lib_sym(h, "leptris_element_append_child");
@@ -225,6 +236,11 @@ static void resolve_symbols(const char *lib_path)
225
236
  f_insert_after = (append_child_fn)lib_sym(h, "leptris_element_insert_after");
226
237
  f_insert_before = (append_child_fn)lib_sym(h, "leptris_element_insert_before");
227
238
  f_set_attr = (set_attr_fn)lib_sym(h, "leptris_element_set_attribute");
239
+ /* Engine single-crossing construction (leptris#1344, 1.9.236+):
240
+ * optional; absent engines use the create_child + set_attr
241
+ * loop, which is still one Ruby->C crossing. */
242
+ f_elem_new_with_attrs =
243
+ (elem_new_attrs_fn)lib_sym(h, "leptris_element_new_with_attributes");
228
244
  f_xp_type = (xp_type_fn)lib_sym(h, "leptris_xpath_result_type");
229
245
  f_xp_free = (xp_free_fn)lib_sym(h, "leptris_xpath_result_free");
230
246
  f_first_child = (first_child_fn)lib_sym(h, "leptris_node_first_child");
@@ -1638,15 +1654,88 @@ static VALUE nf_bulk_attributes(VALUE self, VALUE addr)
1638
1654
  /* ---- Binding create via C (TODO.perf/09): one call creates the
1639
1655
  * node AND constructs the binding wrapper (class dispatch, ivars,
1640
1656
  * identity-cache store) — no FFI marshaling, no wrap_fresh path. */
1657
+ static int set_attr_pair(VALUE key, VALUE value, VALUE data)
1658
+ {
1659
+ void *elem =
1660
+ (void *)(uintptr_t)NUM2ULL(rb_ary_entry(data, 0));
1661
+ VALUE k = RB_TYPE_P(key, T_STRING) ? key : rb_obj_as_string(key);
1662
+ VALUE v = NIL_P(value) ? rb_usascii_str_new(0, 0)
1663
+ : (RB_TYPE_P(value, T_STRING) ? value
1664
+ : rb_obj_as_string(value));
1665
+
1666
+ rb_ary_push(rb_ary_entry(data, 1), k);
1667
+ rb_ary_push(rb_ary_entry(data, 2), v);
1668
+ f_set_attr(elem, RSTRING_PTR(k), RSTRING_PTR(v));
1669
+ return ST_CONTINUE;
1670
+ }
1671
+
1641
1672
  static VALUE nf_create_binding_element(VALUE self, VALUE document,
1642
- VALUE name)
1673
+ VALUE name, VALUE attrs)
1643
1674
  {
1644
1675
  void *doc = doc_ptr_of(document);
1645
- void *ptr = f_elem_create(doc, RSTRING_PTR(StringValue(name)));
1676
+ void *ptr;
1677
+ VALUE name_str = StringValue(name);
1646
1678
  VALUE node, cache, key;
1647
1679
 
1648
1680
  (void)self;
1649
1681
  resolve_binding_classes();
1682
+ if (attrs == Qnil ||
1683
+ (RB_TYPE_P(attrs, T_HASH) && RHASH_SIZE(attrs) == 0)) {
1684
+ ptr = f_elem_create(doc, RSTRING_PTR(name_str));
1685
+ } else if (f_elem_new_with_attrs != NULL) {
1686
+ /* #1344: element + every attribute in ONE crossing.
1687
+ * Duplicate names replace, last wins; NULL values store
1688
+ * the empty string — engine semantics, identical to
1689
+ * per-pair set_attribute.
1690
+ *
1691
+ * Iteration goes through keys + aref (no rb_hash_foreach
1692
+ * state pointer): every converted key/value is pushed into
1693
+ * Ruby arrays BEFORE its pointer enters the C arrays, so
1694
+ * temporaries (non-String keys, nil -> "") survive until
1695
+ * the engine call returns — no C pointer rides through a
1696
+ * boxed VALUE (long is 32-bit on Windows/LLP64; the
1697
+ * foreach+boxed-struct shape truncated it and segfaulted
1698
+ * the windows CI leg). */
1699
+ VALUE keys, names, values;
1700
+ const char **np, **vp;
1701
+ long cap, i;
1702
+ Check_Type(attrs, T_HASH);
1703
+ cap = (long)RHASH_SIZE(attrs);
1704
+ keys = rb_funcall(attrs, rb_intern("keys"), 0);
1705
+ names = rb_ary_new_capa(cap);
1706
+ values = rb_ary_new_capa(cap);
1707
+ np = ruby_xmalloc(sizeof(char *) * (cap + 1));
1708
+ vp = ruby_xmalloc(sizeof(char *) * (cap + 1));
1709
+ for (i = 0; i < cap; i++) {
1710
+ VALUE k = rb_ary_entry(keys, i);
1711
+ VALUE v = rb_hash_aref(attrs, k);
1712
+ k = RB_TYPE_P(k, T_STRING) ? k : rb_obj_as_string(k);
1713
+ v = NIL_P(v) ? rb_usascii_str_new(0, 0)
1714
+ : (RB_TYPE_P(v, T_STRING) ? v
1715
+ : rb_obj_as_string(v));
1716
+ rb_ary_push(names, k);
1717
+ rb_ary_push(values, v);
1718
+ np[i] = RSTRING_PTR(k);
1719
+ vp[i] = RSTRING_PTR(v);
1720
+ }
1721
+ ptr = f_elem_new_with_attrs(doc, RSTRING_PTR(name_str),
1722
+ np, vp, (size_t)cap);
1723
+ ruby_xfree(np);
1724
+ ruby_xfree(vp);
1725
+ } else {
1726
+ /* Older pin: create + per-pair sets (N+1 crossings). */
1727
+ VALUE addr = Qnil;
1728
+ VALUE anchor;
1729
+ Check_Type(attrs, T_HASH);
1730
+ anchor = rb_ary_new_capa((long)RHASH_SIZE(attrs));
1731
+ ptr = f_elem_create(doc, RSTRING_PTR(name_str));
1732
+ if (!ptr)
1733
+ return Qnil;
1734
+ addr = ULL2NUM((uint64_t)(uintptr_t)ptr);
1735
+ rb_hash_foreach(attrs, set_attr_pair,
1736
+ rb_ary_new3(3, addr, anchor,
1737
+ rb_ary_new_capa(0)));
1738
+ }
1650
1739
  if (!ptr)
1651
1740
  return Qnil; /* caller raises with the error channel */
1652
1741
  node = rb_obj_alloc(c_b_element);
@@ -2194,6 +2283,56 @@ static VALUE nf_plan_structs_typed_p(VALUE self)
2194
2283
  * to the per-attribute FFI chain (first/next/get_name/get_value,
2195
2284
  * 4 crossings each) — the win amplifies with attribute density
2196
2285
  * (40-attr elements: ~7x on the listing). */
2286
+ /* ---- Single-crossing element construction (leptris#1344) --------
2287
+ * Creates an element attached under parent (parent_addr 0 = free
2288
+ *floating, owner document only) with all attributes set — ONE
2289
+ * Ruby->C crossing for the whole construction. Internal calls are
2290
+ * C->C dlsym dispatches (create_child = create+attach in one
2291
+ * engine call; set_attr per attribute). Returns the new node's
2292
+ * address for wrapper minting. */
2293
+ static VALUE nf_create_element_with_attrs(VALUE self, VALUE document,
2294
+ VALUE parent_addr, VALUE name,
2295
+ VALUE attrs)
2296
+ {
2297
+ (void)self;
2298
+ (void)document;
2299
+ void *parent = NIL_P(parent_addr)
2300
+ ? NULL
2301
+ : (void *)(uintptr_t)NUM2ULL(parent_addr);
2302
+ const char *cname = StringValueCStr(name);
2303
+
2304
+ if (f_elem_new_with_attrs && parent && RARRAY_LEN(attrs) > 0 &&
2305
+ RARRAY_LEN(attrs) % 2 == 0) {
2306
+ /* Engine single-crossing construction (1.9.236+): arena
2307
+ * create + all attributes with no per-attr calls at all. */
2308
+ long n = RARRAY_LEN(attrs) / 2;
2309
+ const char **names = ALLOCA_N(const char *, n);
2310
+ const char **values = ALLOCA_N(const char *, n);
2311
+ for (long i = 0; i < n; i++) {
2312
+ /* Hoist to lvalues: truffleruby's StringValueCStr takes
2313
+ * the address of its argument (RARRAY_AREF is not one). */
2314
+ VALUE an = RARRAY_AREF(attrs, 2 * i);
2315
+ VALUE av = RARRAY_AREF(attrs, 2 * i + 1);
2316
+ names[i] = StringValueCStr(an);
2317
+ values[i] = StringValueCStr(av);
2318
+ }
2319
+ void *elem = f_elem_new_with_attrs(parent, cname, names, values,
2320
+ n);
2321
+ return elem ? ULL2NUM((uintptr_t)elem) : Qnil;
2322
+ }
2323
+
2324
+ void *elem = parent ? f_create_child(parent, cname) : NULL;
2325
+ if (!elem) return Qnil;
2326
+
2327
+ long i, n = RARRAY_LEN(attrs);
2328
+ for (i = 0; i + 1 < n; i += 2) {
2329
+ VALUE an = RARRAY_AREF(attrs, i);
2330
+ VALUE av = RARRAY_AREF(attrs, i + 1);
2331
+ f_set_attr(elem, StringValueCStr(an), StringValueCStr(av));
2332
+ }
2333
+ return ULL2NUM((uintptr_t)elem);
2334
+ }
2335
+
2197
2336
  static VALUE nf_attribute_rows(VALUE self, VALUE document, VALUE addr)
2198
2337
  {
2199
2338
  (void)self;
@@ -2808,6 +2947,8 @@ LEPTRIS_INIT_EXPORT void Init_native(void)
2808
2947
  nf_plan_structs, 3);
2809
2948
  rb_define_module_function(m_native, "attribute_rows",
2810
2949
  nf_attribute_rows, 2);
2950
+ rb_define_module_function(m_native, "create_element_with_attrs",
2951
+ nf_create_element_with_attrs, 4);
2811
2952
  rb_define_module_function(m_native, "plan_structs_typed?",
2812
2953
  nf_plan_structs_typed_p, 0);
2813
2954
  rb_define_module_function(m_native, "doc_handle_attach",
@@ -2822,7 +2963,7 @@ LEPTRIS_INIT_EXPORT void Init_native(void)
2822
2963
  rb_define_module_function(m_native, "bulk_attr_faces",
2823
2964
  nf_bulk_attr_faces, 2);
2824
2965
  rb_define_module_function(m_native, "create_binding_element",
2825
- nf_create_binding_element, 2);
2966
+ nf_create_binding_element, 3);
2826
2967
  rb_define_module_function(m_native, "create_binding_text",
2827
2968
  nf_create_binding_text, 2);
2828
2969
  rb_define_module_function(m_native, "bulk_children", nf_bulk_children, 2);
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Leptris
4
- VERSION = "1.9.232.0"
4
+ VERSION = "1.9.237.0"
5
5
  end
@@ -389,15 +389,32 @@ class Leptris::XML::Document
389
389
  element
390
390
  end
391
391
 
392
- def create_element(name)
392
+ # attributes: optional Hash of {name => value} applied in the
393
+ # SAME engine crossing as construction
394
+ # (leptris_element_new_with_attributes, #1344) — builder paths
395
+ # collapse from 1 + N crossings to one. nil values store the
396
+ # empty string; duplicate names replace, last wins (engine
397
+ # semantics, identical to per-pair set_attribute).
398
+ def create_element(name, attributes = nil)
393
399
  # TODO.perf/09: one C call creates + wraps when the ext is
394
400
  # loaded — no FFI marshaling, no wrap_fresh frames.
395
401
  if defined?(Leptris::XML::NATIVE_FAST)
396
- node = Leptris::XML::Native.create_binding_element(self, name.to_s)
402
+ node = Leptris::XML::Native.create_binding_element(
403
+ self, name.to_s, attributes)
397
404
  raise Leptris::XML::Error, "leptris_element_create failed" if node.nil?
398
405
  return node
399
406
  end
400
- ptr = Leptris::XML::FFI.leptris_element_create(c_ptr, name)
407
+ ptr =
408
+ if attributes && !attributes.empty?
409
+ names = attributes.map { |k, _| k.to_s }
410
+ values = attributes.map { |_, v| v.nil? ? "" : v.to_s }
411
+ nb, na = Leptris::XML::CStringArray.to_c(names)
412
+ vb, va = Leptris::XML::CStringArray.to_c(values)
413
+ Leptris::XML::FFI.leptris_element_new_with_attributes(
414
+ c_ptr, name.to_s, nb, vb, names.size)
415
+ else
416
+ Leptris::XML::FFI.leptris_element_create(c_ptr, name)
417
+ end
401
418
  raise Leptris::XML::Error, "leptris_element_create failed" if ptr.null?
402
419
  Leptris::XML::Node.wrap_fresh(ptr, self, Leptris::XML::FFI::NODE_ELEMENT)
403
420
  end