leptris 1.9.285.1 → 1.9.290.0
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 +26 -0
- data/Rakefile +1 -1
- data/ext/leptris/native/native.c +59 -4
- 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/descriptor.rb +12 -0
- data/lib/leptris/xml/element.rb +36 -1
- data/lib/leptris/xml/ffi.rb +15 -2
- data/lib/leptris/xml/xslt.rb +52 -6
- data/vendor-src/README.md +1 -1
- data/vendor-src/libleptris/CHANGELOG.md +74 -0
- data/vendor-src/libleptris/CMakeLists.txt +1 -1
- data/vendor-src/libleptris/src/include/leptris/descriptor.h +12 -0
- data/vendor-src/libleptris/src/include/leptris.h +44 -1
- data/vendor-src/libleptris/src/leptris/common/chartype.c +4 -0
- data/vendor-src/libleptris/src/leptris/common/chartype.h +10 -0
- data/vendor-src/libleptris/src/leptris/descriptor/plan.c +72 -1
- data/vendor-src/libleptris/src/leptris/dom/element.c +6 -4
- data/vendor-src/libleptris/src/leptris/dom/element.h +79 -36
- data/vendor-src/libleptris/src/leptris/dom/element_modify.c +18 -14
- data/vendor-src/libleptris/src/leptris/dom/root_doc_map.c +3 -3
- data/vendor-src/libleptris/src/leptris/flat/direct_parse.c +49 -21
- data/vendor-src/libleptris/src/leptris/html/html_parse.c +191 -152
- data/vendor-src/libleptris/src/leptris/sax/recorder.c +3 -0
- data/vendor-src/libleptris/src/leptris/sax/records.c +8 -1
- data/vendor-src/libleptris/src/leptris/serialize/serialize.c +11 -8
- data/vendor-src/libleptris/src/leptris/xpath/vm.c +5 -3
- data/vendor-src/libleptris/src/leptris/xslt/xslt_exec.c +76 -8
- data/vendor-src/libleptris/src/leptris/xslt/xslt_internal.h +16 -1
- data/vendor-src/libleptris/src/leptris/xslt/xslt_public.c +38 -8
- data/vendor-src/libleptris/test/abi/test_descriptor.cpp +74 -0
- data/vendor-src/libleptris/test/abi/test_header_hygiene.cpp +35 -10
- data/vendor-src/libleptris/test/sax/test_recorder.cpp +24 -0
- data/vendor-src/libleptris/test/sax/test_records.cpp +25 -0
- data/vendor-src/libleptris/test/xpath/test_xslt.cpp +151 -0
- data/vendor-src/libleptris/test/xquery/qt3/fn/not.xml +771 -0
- data/vendor-src/libleptris/test/xquery/test_qt3.cpp +11 -0
- data/vendor-src/libleptris/vcpkg.json +1 -1
- metadata +2 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 6838f7a55c89398226cfac7d4b10daaff8714a145c3837241e620b5498e04a83
|
|
4
|
+
data.tar.gz: d468f7c6ed71c26264ea82989e277d15385e0d0d1d467def593e74002ac64fd9
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: da53446281f453422977498301f5581f81c428f0af970ba66ee88bc733de8580e0a6c031023a1a0aab8388a238919d0200a5c97ed29a1e389034a99e5dfc27df
|
|
7
|
+
data.tar.gz: 3517c14069987df979ea335882af3f9ed1375f78e9fc3b9efe3767072a4fa5cdc22f30e2d849917f13292a55768ec637029c40b051717a15b8ce03c66aaf58fa
|
data/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,29 @@
|
|
|
1
|
+
## [1.9.287.0] - 2026-10-02
|
|
2
|
+
|
|
3
|
+
### Added
|
|
4
|
+
|
|
5
|
+
- **Stylesheet top-level params (#360)**: `Stylesheet#apply_to(doc,
|
|
6
|
+
params: {name => value})` and `#serialize(doc, params: {...})`
|
|
7
|
+
thread XSLT `<xsl:param>` overrides through the new engine faces
|
|
8
|
+
(libleptris 1.9.287, leptris#1478): a supplied name binds its
|
|
9
|
+
string directly and never evaluates select/@default; absent names
|
|
10
|
+
keep them. Values bind as XPath strings — the caller owns
|
|
11
|
+
numeric/boolean quoting (nokogiri quote_params conventions for
|
|
12
|
+
moxml parity). nil/empty params keeps the flagless v1 faces.
|
|
13
|
+
|
|
14
|
+
### Fixed
|
|
15
|
+
|
|
16
|
+
- **NativeNode#content on comment nodes (#344)**: the C-side walk
|
|
17
|
+
accessor now dispatches by node kind — comment and CDATA payloads
|
|
18
|
+
read through their dedicated engine accessors (comment-kind nodes
|
|
19
|
+
answered nil while text/CDATA worked); element aggregation and
|
|
20
|
+
text reads unchanged.
|
|
21
|
+
|
|
22
|
+
### Engine sync (1.9.287)
|
|
23
|
+
|
|
24
|
+
- leptris_xslt_apply_params / leptris_xslt_apply_string_params
|
|
25
|
+
(leptris#1478) — the param overrides ride above.
|
|
26
|
+
|
|
1
27
|
## [1.9.285.0] - 2026-10-01
|
|
2
28
|
|
|
3
29
|
### Added — streaming Door A opt-outs (libleptris 1.9.283, #1459)
|
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.290"
|
|
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
|
@@ -17,7 +17,6 @@
|
|
|
17
17
|
|
|
18
18
|
typedef const char *(*elem_name_fn)(void *);
|
|
19
19
|
typedef const char *(*text_content_fn)(void *);
|
|
20
|
-
|
|
21
20
|
/* Engine node-kind ids (leptris_node_get_type) — hoisted above the
|
|
22
21
|
* read accessors so every dispatch shares one spelling. */
|
|
23
22
|
#define WS_NODE_ELEMENT 0
|
|
@@ -330,9 +329,9 @@ static VALUE nn_content(VALUE self)
|
|
|
330
329
|
if (doc != Qnil && n->content_ver != Qnil &&
|
|
331
330
|
n->content_ver == rb_ivar_get(doc, id_iv_version))
|
|
332
331
|
return n->content_val;
|
|
333
|
-
/*
|
|
334
|
-
*
|
|
335
|
-
*
|
|
332
|
+
/* every kind through its own accessor (#344): elements
|
|
333
|
+
* aggregate, text/comment/CDATA/PI carry their payload
|
|
334
|
+
* directly; unknown kinds answer nil. */
|
|
336
335
|
switch (f_node_type(n->ptr)) {
|
|
337
336
|
case WS_NODE_ELEMENT: s = f_element_text(n->ptr); break;
|
|
338
337
|
case WS_NODE_TEXT: s = f_text_content(n->ptr); break;
|
|
@@ -795,6 +794,54 @@ static VALUE nf_append_binding_child(VALUE self, VALUE document,
|
|
|
795
794
|
return Qtrue;
|
|
796
795
|
}
|
|
797
796
|
|
|
797
|
+
/* Batch append (leptris-ruby#366): one crossing for N children.
|
|
798
|
+
* Same gates and provable no-op lift predicate as
|
|
799
|
+
* nf_append_binding_child; ONE version bump covers the batch (a
|
|
800
|
+
* failed mutation discards memos either way). Children are passed
|
|
801
|
+
* as pre-flattened element ADDRESSES (Integer) — the Ruby side
|
|
802
|
+
* keeps the node objects for the cross-document invalidation
|
|
803
|
+
* sweep, which is pure Ruby and needs no crossing.
|
|
804
|
+
*
|
|
805
|
+
* Returns the index of the first child that needs the Ruby-side
|
|
806
|
+
* namespace-lift path (source order preserved: the caller runs
|
|
807
|
+
* the full per-child path from there); n when every child was
|
|
808
|
+
* appended. Status failures raise at the failing child — earlier
|
|
809
|
+
* appends stay appended, exactly like the per-child face. */
|
|
810
|
+
static VALUE nf_append_binding_children(VALUE self, VALUE document,
|
|
811
|
+
VALUE parent_addr, VALUE addrs)
|
|
812
|
+
{
|
|
813
|
+
void *parent;
|
|
814
|
+
const char *uri;
|
|
815
|
+
long i, n;
|
|
816
|
+
int st;
|
|
817
|
+
|
|
818
|
+
(void)self;
|
|
819
|
+
resolve_binding_classes();
|
|
820
|
+
if (NIL_P(rb_ivar_get(document, id_iv_c_address)))
|
|
821
|
+
rb_raise(c_use_after_free_error,
|
|
822
|
+
"owning document has been freed");
|
|
823
|
+
if (rb_ivar_get(document, id_iv_readonly) == Qtrue)
|
|
824
|
+
rb_raise(c_readonly_error,
|
|
825
|
+
"document is readonly — mutation attempted");
|
|
826
|
+
Check_Type(addrs, T_ARRAY);
|
|
827
|
+
parent = (void *)(uintptr_t)NUM2ULL(parent_addr);
|
|
828
|
+
n = RARRAY_LEN(addrs);
|
|
829
|
+
rb_ivar_set(document, id_iv_version,
|
|
830
|
+
LONG2FIX(FIX2LONG(rb_ivar_get(document, id_iv_version)) + 1));
|
|
831
|
+
for (i = 0; i < n; i++) {
|
|
832
|
+
void *child = (void *)(uintptr_t)NUM2ULL(RARRAY_AREF(addrs, i));
|
|
833
|
+
if (f_node_type(child) == NT_ELEMENT) {
|
|
834
|
+
if (f_elem_ns_count(child) > 0)
|
|
835
|
+
return LONG2NUM(i);
|
|
836
|
+
uri = f_elem_ns(child);
|
|
837
|
+
if (uri && *uri)
|
|
838
|
+
return LONG2NUM(i);
|
|
839
|
+
}
|
|
840
|
+
st = f_append_child(parent, child);
|
|
841
|
+
check_status_c(st);
|
|
842
|
+
}
|
|
843
|
+
return LONG2NUM(n);
|
|
844
|
+
}
|
|
798
845
|
/* C-bound set_attribute (TODO.perf/11, #204 gap row 0.25x): the
|
|
799
846
|
* gates, the version bump (which drops the version-stamped
|
|
800
847
|
* attribute memos on both surfaces), and the engine write in one
|
|
@@ -1984,6 +2031,12 @@ static VALUE nf_fast_element_xml(VALUE self, VALUE addr,
|
|
|
1984
2031
|
* construction — the consumer iterates rows and builds their
|
|
1985
2032
|
* typed objects directly. */
|
|
1986
2033
|
/* Node kinds (mirror types.h). Documented in descriptor comment. */
|
|
2034
|
+
#define WS_NODE_ELEMENT 0
|
|
2035
|
+
#define WS_NODE_TEXT 1
|
|
2036
|
+
#define WS_NODE_COMMENT 2
|
|
2037
|
+
#define WS_NODE_CDATA 3
|
|
2038
|
+
#define WS_NODE_PI 4
|
|
2039
|
+
#define WS_NODE_DOCTYPE 5
|
|
1987
2040
|
|
|
1988
2041
|
static VALUE build_element_row(VALUE doc, void *elem, int depth)
|
|
1989
2042
|
{
|
|
@@ -3030,6 +3083,8 @@ LEPTRIS_INIT_EXPORT void Init_native(void)
|
|
|
3030
3083
|
rb_define_module_function(m_native, "fast_prefix", nf_prefix, 1);
|
|
3031
3084
|
rb_define_module_function(m_native, "fast_attribute2", nf_fast_attribute2, 2);
|
|
3032
3085
|
rb_define_module_function(m_native, "ns_lift_needed?", nf_ns_lift_needed, 1);
|
|
3086
|
+
rb_define_module_function(m_native, "append_binding_children",
|
|
3087
|
+
nf_append_binding_children, 3);
|
|
3033
3088
|
rb_define_module_function(m_native, "append_binding_child",
|
|
3034
3089
|
nf_append_binding_child, 3);
|
|
3035
3090
|
rb_define_module_function(m_native, "set_binding_attribute",
|
|
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
|
@@ -167,6 +167,18 @@ class Leptris::XML::Descriptor
|
|
|
167
167
|
ap[:wire_name] = anchor_string(anchors, row.fetch(:name))
|
|
168
168
|
ap[:kind] = kind_code(row[:kind] || :scalar)
|
|
169
169
|
ap[:type_tag] = type_tag_code(row)
|
|
170
|
+
# Attribute-level namespace form (libleptris 1.9.289, #1486):
|
|
171
|
+
# ns rows match by (URI, local) — the wire prefix is irrelevant.
|
|
172
|
+
# Mirrors the child-row packing.
|
|
173
|
+
ap[:ns_form] =
|
|
174
|
+
case row[:ns]
|
|
175
|
+
when :any then Leptris::XML::FFI::PLAN_NS_ANY
|
|
176
|
+
when Hash then Leptris::XML::FFI::PLAN_NS_EXACT
|
|
177
|
+
else Leptris::XML::FFI::PLAN_NS_NONE
|
|
178
|
+
end
|
|
179
|
+
ap[:ns_uri] =
|
|
180
|
+
row[:ns].is_a?(Hash) ?
|
|
181
|
+
anchor_string(anchors, row[:ns].fetch(:exact)) : nil
|
|
170
182
|
pack_predicates(ap, row, anchors)
|
|
171
183
|
end
|
|
172
184
|
ep[:attribute_count] = attrs.size
|
data/lib/leptris/xml/element.rb
CHANGED
|
@@ -490,7 +490,42 @@ class Leptris::XML::Element < Leptris::XML::Node
|
|
|
490
490
|
# Remove existing children, then attach the new ones in source order.
|
|
491
491
|
Leptris::XML::FFI.check_status(
|
|
492
492
|
Leptris::XML::FFI.leptris_element_remove_children(c_ptr))
|
|
493
|
-
Array(node_or_nodes)
|
|
493
|
+
append_children(Array(node_or_nodes))
|
|
494
|
+
end
|
|
495
|
+
|
|
496
|
+
# Batch append (leptris-ruby#366): one C crossing for N children.
|
|
497
|
+
# The C loop runs the same gates and provable no-op lift
|
|
498
|
+
# predicate as the per-child fast path with ONE version bump for
|
|
499
|
+
# the batch; it stops at the first child needing the Ruby-side
|
|
500
|
+
# namespace lift (source order preserved — the remainder goes
|
|
501
|
+
# through the full per-child path). Strings and non-Nodes fall
|
|
502
|
+
# back to add_child. The cross-document memo invalidation sweep
|
|
503
|
+
# is pure Ruby and stays on this side.
|
|
504
|
+
def append_children(nodes)
|
|
505
|
+
ensure_writable!
|
|
506
|
+
return if nodes.empty?
|
|
507
|
+
unless nodes.all? { |n| n.is_a?(Leptris::XML::Node) }
|
|
508
|
+
nodes.each { |n| add_child(n) }
|
|
509
|
+
return
|
|
510
|
+
end
|
|
511
|
+
if native_fast_children?
|
|
512
|
+
addrs = nodes.map { |n| n.c_ptr.address }
|
|
513
|
+
done = Leptris::XML::Native.append_binding_children(
|
|
514
|
+
@document, @c_address, addrs)
|
|
515
|
+
done = 0 unless done.is_a?(Integer)
|
|
516
|
+
if done >= nodes.size
|
|
517
|
+
nodes.each do |n|
|
|
518
|
+
Leptris::XML::Node.invalidate_cross_document!(n, @document)
|
|
519
|
+
end
|
|
520
|
+
return nodes
|
|
521
|
+
end
|
|
522
|
+
nodes[0, done].each do |n|
|
|
523
|
+
Leptris::XML::Node.invalidate_cross_document!(n, @document)
|
|
524
|
+
end
|
|
525
|
+
nodes = nodes[done..]
|
|
526
|
+
end
|
|
527
|
+
nodes.each { |n| add_child(n) }
|
|
528
|
+
nodes
|
|
494
529
|
end
|
|
495
530
|
|
|
496
531
|
# Replace this element with +new_node+ in the parent's child list.
|
data/lib/leptris/xml/ffi.rb
CHANGED
|
@@ -299,7 +299,13 @@ module Leptris
|
|
|
299
299
|
:type_tag, :uint8,
|
|
300
300
|
:predicate_count, :uint16,
|
|
301
301
|
:pad_pred, :uint16,
|
|
302
|
-
:predicates, :pointer
|
|
302
|
+
:predicates, :pointer,
|
|
303
|
+
# libleptris 1.9.289 (#1486): attribute-level namespace
|
|
304
|
+
# form — ns rows match by (URI, local), wire_name is the
|
|
305
|
+
# LOCAL name; NONE keeps wire-name-exact matching.
|
|
306
|
+
:ns_form, :uint8,
|
|
307
|
+
:pad_ns, :uint8,
|
|
308
|
+
:ns_uri, :pointer
|
|
303
309
|
end
|
|
304
310
|
class ChildPlan < ::FFI::Struct
|
|
305
311
|
layout :wire_name, :pointer,
|
|
@@ -1272,7 +1278,14 @@ attach_function :leptris_parse_string,
|
|
|
1272
1278
|
# apply + serialize in one call, keeping top-level text nodes
|
|
1273
1279
|
# and result fragments the tree API would flatten.
|
|
1274
1280
|
attach_function :leptris_xslt_apply_string,
|
|
1275
|
-
[:leptris_xslt, :leptris_document], :pointer
|
|
1281
|
+
[:leptris_xslt, :leptris_document], :pointer # Top-level xsl:param overrides (#360): flat name/value pairs,
|
|
1282
|
+
# 2*pair_count entries; supplied names skip select/@default.
|
|
1283
|
+
attach_function :leptris_xslt_apply_params,
|
|
1284
|
+
[:leptris_xslt, :leptris_document, :pointer, :size_t],
|
|
1285
|
+
:leptris_document
|
|
1286
|
+
attach_function :leptris_xslt_apply_string_params,
|
|
1287
|
+
[:leptris_xslt, :leptris_document, :pointer, :size_t],
|
|
1288
|
+
:pointer
|
|
1276
1289
|
attach_function :leptris_c14n_canonicalize,
|
|
1277
1290
|
|
|
1278
1291
|
[:leptris_document, :int, :int], :pointer
|
data/lib/leptris/xml/xslt.rb
CHANGED
|
@@ -28,6 +28,9 @@ module Leptris::XML::XSLT
|
|
|
28
28
|
# # text nodes preserved)
|
|
29
29
|
#
|
|
30
30
|
class Stylesheet
|
|
31
|
+
FFI_PTR_SIZE = ::FFI.type_size(:pointer)
|
|
32
|
+
private_constant :FFI_PTR_SIZE
|
|
33
|
+
|
|
31
34
|
# GC-managed compiled handle.
|
|
32
35
|
class Handle < ::FFI::AutoPointer
|
|
33
36
|
def self.release(ptr)
|
|
@@ -65,8 +68,22 @@ module Leptris::XML::XSLT
|
|
|
65
68
|
|
|
66
69
|
# Apply to +document+ (not modified) and return the result tree
|
|
67
70
|
# as an owning Document — query it with xpath/css like any other.
|
|
68
|
-
|
|
69
|
-
|
|
71
|
+
#
|
|
72
|
+
# params (#360): top-level xsl:param overrides as a Hash of
|
|
73
|
+
# name => value. Values bind as XPath STRINGS verbatim — the
|
|
74
|
+
# caller owns numeric/boolean quoting (libxslt conventions:
|
|
75
|
+
# pass '7' for a number, matching nokogiri's quote_params).
|
|
76
|
+
# Absent names keep the param's select/@default.
|
|
77
|
+
def apply_to(document, params: nil)
|
|
78
|
+
raw, buffer, = with_param_pairs(params) do |pairs, count|
|
|
79
|
+
if pairs
|
|
80
|
+
Leptris::XML::FFI.leptris_xslt_apply_params(
|
|
81
|
+
@handle, document.c_ptr, pairs, count)
|
|
82
|
+
else
|
|
83
|
+
Leptris::XML::FFI.leptris_xslt_apply(@handle, document.c_ptr)
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
buffer&.free
|
|
70
87
|
if raw.null?
|
|
71
88
|
raise Leptris::XML::XPathError,
|
|
72
89
|
"transform failed: #{document.last_error || Leptris::XML::FFI.leptris_last_error}"
|
|
@@ -75,11 +92,40 @@ module Leptris::XML::XSLT
|
|
|
75
92
|
end
|
|
76
93
|
|
|
77
94
|
# Apply and serialize in one call — keeps top-level text nodes and
|
|
78
|
-
# result fragments that the tree API would flatten.
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
95
|
+
# result fragments that the tree API would flatten. params: as
|
|
96
|
+
# #apply_to.
|
|
97
|
+
def serialize(document, params: nil)
|
|
98
|
+
str_ptr, buffer, = with_param_pairs(params) do |pairs, count|
|
|
99
|
+
if pairs
|
|
100
|
+
Leptris::XML::FFI.leptris_xslt_apply_string_params(
|
|
101
|
+
@handle, document.c_ptr, pairs, count)
|
|
102
|
+
else
|
|
103
|
+
Leptris::XML::FFI.leptris_xslt_apply_string(
|
|
104
|
+
@handle, document.c_ptr)
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
buffer&.free
|
|
82
108
|
Leptris::XML::FFI.read_owned_string(str_ptr)
|
|
83
109
|
end
|
|
110
|
+
|
|
111
|
+
# Flattens a params Hash into the engine's flat char** pairs
|
|
112
|
+
# array (borrowed for the C call's lifetime — allocated here,
|
|
113
|
+
# freed by the caller; the per-string MemoryPointers stay
|
|
114
|
+
# anchored until the call returns).
|
|
115
|
+
def with_param_pairs(params)
|
|
116
|
+
return yield(nil, 0) if params.nil? || params.empty?
|
|
117
|
+
unless params.is_a?(Hash)
|
|
118
|
+
raise ArgumentError, "params must be a Hash of name => value"
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
entries = params.keys.map(&:to_s)
|
|
122
|
+
.zip(params.values.map { |v| v.nil? ? "" : v.to_s })
|
|
123
|
+
strings = entries.flatten.map { |s| ::FFI::MemoryPointer.from_string(s) }
|
|
124
|
+
buffer = ::FFI::MemoryPointer.new(:pointer, strings.size)
|
|
125
|
+
strings.each_with_index do |ptr, i|
|
|
126
|
+
buffer.put_pointer(i * FFI_PTR_SIZE, ptr)
|
|
127
|
+
end
|
|
128
|
+
[yield(buffer, entries.size), buffer, strings]
|
|
129
|
+
end
|
|
84
130
|
end
|
|
85
131
|
end
|
data/vendor-src/README.md
CHANGED
|
@@ -1,5 +1,79 @@
|
|
|
1
1
|
## [Unreleased]
|
|
2
2
|
|
|
3
|
+
## [1.9.290] - 2026-10-02
|
|
4
|
+
|
|
5
|
+
<!-- Edit this section with the actual release notes. -->
|
|
6
|
+
<!-- See https://keepachangelog.com for format guidance. -->
|
|
7
|
+
|
|
8
|
+
### Added
|
|
9
|
+
|
|
10
|
+
- namespace forms for attribute plan rows — `ns_form`/`ns_uri` on `leptris_attr_plan` (#1486)
|
|
11
|
+
- Extends #1115's child-row pattern to attribute rows: `ns_form = 0` (the zero value) keeps the historical wire-name lookup byte-for-byte and existing aggregate initializers stay valid; `LEPTRIS_PLAN_NS_EXACT` + `ns_uri` makes `wire_name` the LOCAL name and binds the (namespace URI, local) identity through the element's in-scope prefix bindings — prefix-spelling-independent, so a `w14:paraId`-typed row binds whatever prefix the document uses for the Word 2010 URI. `LEPTRIS_PLAN_NS_ANY` matches the local name in any namespace; unprefixed attributes have no namespace and never bind EXACT (XML Namespaces §5.2).
|
|
12
|
+
- Unlocks the plan fast path for typed-attribute models: lutaml-model's `attr_type_ns` opt-out (one `w14:paraId` leaf cascading the WordprocessingML paragraph chain out of the plan path, against a measured 33% parse-CPU saving) can now emit ns-exact attribute rows instead.
|
|
13
|
+
- Build deep-copies `ns_uri`, zero-guards the trailing fields for hosts that `malloc` the spec without value-initialization (#1272 lesson), rejects `EXACT` without a URI as `INVALID_ARG`; free releases the copies. RED-first spec plus the full battery (1773/1773). (#1487)
|
|
14
|
+
|
|
15
|
+
## [1.9.289] - 2026-10-02
|
|
16
|
+
|
|
17
|
+
### Performance
|
|
18
|
+
|
|
19
|
+
- open-id compares for the scope/table-context stack walks (#1218 slice 6, tranche 2) (html)
|
|
20
|
+
- The scope walks (p-in-button-scope, button-in-scope, AFE `kn`), the table-context searches, and the table-context dispatch block compare `b->open_id[k]` integers instead of fetching the element name and running case-insensitive literal chains per stack slot. The AFE furthest-block scan deliberately keeps the name fetch — it runs mid-AFE between stack splices, where the id stack is not yet authoritative (webkit02:14). Interleaved A/B vs the tranche-1 tree: 48.2-53.7 -> 51.0-58.2 MB/s (+11% median) on the 2.6MB table-rows shape. (#1485)
|
|
21
|
+
- id-LUT membership for the foster/formatting/stack checks (#1218 slice 6) (html)
|
|
22
|
+
- The classifier table grows the 22 compared-but-unlisted names (HTML4 formatting set + MathML/SVG integration points, inert flags); `h_is_formatting` (14-way) and `h_fosterable` (15-way) become id-LUT lookups; 15 insertion-mode stack checks read `open_id[]` directly. 56.1 -> 59.0 MB/s (+5.2%) interleaved. Cumulative slice 6: ~+15% on the issue's fixture shape; the 3.4-7x deficit the issue reported at 1.9.203 is now a 1.3-1.55x LEAD over htmlReadMemory locally. (#1484)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
## [1.9.288] - 2026-10-02
|
|
27
|
+
|
|
28
|
+
### Fixed
|
|
29
|
+
|
|
30
|
+
- top-level param values are XPath expressions (libxslt convention) — follow-up to #1478 (xslt)
|
|
31
|
+
- **Semantics correction to `leptris_xslt_apply_params` / `leptris_xslt_apply_string_params`, released briefly in 1.9.287:** value strings are now evaluated as XPath EXPRESSIONS (libxslt `params` convention; nokogiri's `quote_params` exists for exactly this), not bound as raw strings. Callers pass quoted literals — `{"n", "'7'"}` binds the string `7`; under 1.9.287 the quotes were part of the value. Numbers and source-derived expressions work (`7 * 6` → 42, `string(/r/@v)`), a later pair may reference an earlier one, a global `xsl:variable` may consume a caller value (§11 ordering), undeclared names bind into scope (libxslt parity), and a malformed expression fails the transform. Caller values bind before the globals run and a declared `xsl:param` default whose name a caller supplied never evaluates (§11.4). Specs ported from #1478 plus three RED-first expression cases; 1771/1771 and an external-consumer end-to-end probe. (#1481)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
## [1.9.287] - 2026-10-02
|
|
36
|
+
|
|
37
|
+
### Added
|
|
38
|
+
|
|
39
|
+
- top-level param overrides — leptris_xslt_apply_params / _apply_string_params (leptris-ruby#360) (xslt)
|
|
40
|
+
|
|
41
|
+
### Performance
|
|
42
|
+
|
|
43
|
+
- fused attr-loop dispatch on the shared chartype table (#1436) (parser)
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
## [1.9.286] - 2026-10-02
|
|
48
|
+
|
|
49
|
+
### Added
|
|
50
|
+
|
|
51
|
+
- top-level param overrides — leptris_xslt_apply_params / _apply_string_params (leptris-ruby#360) (xslt)
|
|
52
|
+
|
|
53
|
+
### Performance
|
|
54
|
+
|
|
55
|
+
- fused attr-loop dispatch on the shared chartype table (#1436) (parser)
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
## [1.9.286] - 2026-10-01
|
|
60
|
+
|
|
61
|
+
### Fixed
|
|
62
|
+
|
|
63
|
+
- Door A skip flags must survive recorder reset and be accepted by sax_records (sax)
|
|
64
|
+
- `leptris_sax_recorder_reset()` rebuilt the parser without the creation-time `LEPTRIS_PARSE_SKIP_DUP_DETECTION` / `LEPTRIS_PARSE_SKIP_SOURCE_POSITIONS` bits, silently re-enabling both probes mid-stream: record counts diverged between the first pass and post-reset passes on the same document. The recorder now stores its flags and re-applies them on reset.
|
|
65
|
+
- `leptris_sax_records_parse()` rejected every nonzero flag, so the bulk-records lane could not opt out of either probe. The two Door A bits are now accepted (no-ops on the interleaved lane, which carries neither probe); unknown bits still return `LEPTRIS_ERROR_INVALID_ARG`.
|
|
66
|
+
- Regression specs: `Recorder.DoorAFlagsSurviveReset`, `SaxRecords.DoorAFlagsAccepted`. (#1472)
|
|
67
|
+
|
|
68
|
+
### Changed
|
|
69
|
+
|
|
70
|
+
- element name stored as a self-relative int32 offset — `struct leptris_element` 48 → 44 bytes (#1285)
|
|
71
|
+
- The `char* name` was the struct's last 8-aligned member; replacing it with the tree-edge offset encoding drops the struct alignment to 4 and reclaims the tail padding.
|
|
72
|
+
- The dp parse lane packs element arrays at true size: peak RSS on a 15 MB / 400k-element document drops 253 → 239 MB (-5.4%) with parse and serialize times flat. Pool-allocated elements keep their stride (`LEPTRIS_POOL_ALIGNMENT` rounds up); ILP32 layouts are unchanged.
|
|
73
|
+
- Mutation/public-creator name stamps use the doc-tagged compact encoder (#1320 pattern) so far-mut-block spills register with the correct document. New specs: `ElementNameOffsetRoundTrip` plus a hard 44-byte size pin.
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
|
|
3
77
|
## [1.9.285] - 2026-10-01
|
|
4
78
|
|
|
5
79
|
### Fixed
|
|
@@ -95,6 +95,18 @@ typedef struct {
|
|
|
95
95
|
uint16_t predicate_count;
|
|
96
96
|
uint16_t pad_pred;
|
|
97
97
|
const leptris_attr_predicate* predicates;
|
|
98
|
+
/* #1486 (additive trailing fields, the #1115 child-row
|
|
99
|
+
* pattern): attribute-level namespace form. A row with
|
|
100
|
+
* ns_form set matches by (namespace URI, local name) —
|
|
101
|
+
* wire_name is the LOCAL name and the prefix spelling on the
|
|
102
|
+
* wire is irrelevant. NONE (0, the zero value) keeps the
|
|
103
|
+
* historical behavior: lookup by wire_name exactly as it
|
|
104
|
+
* appears on the wire (prefixed or bare). Unprefixed
|
|
105
|
+
* attributes have no namespace; EXACT requires a prefixed
|
|
106
|
+
* attribute whose prefix binds to ns_uri. */
|
|
107
|
+
uint8_t ns_form; /* LeptrisPlanNsForm */
|
|
108
|
+
uint8_t pad_ns;
|
|
109
|
+
const char* ns_uri; /* LEPTRIS_PLAN_NS_EXACT only */
|
|
98
110
|
} leptris_attr_plan;
|
|
99
111
|
|
|
100
112
|
typedef struct {
|
|
@@ -3096,7 +3096,6 @@ LEPTRIS_API void leptris_xslt_free(LeptrisXslt xslt);
|
|
|
3096
3096
|
*/
|
|
3097
3097
|
LEPTRIS_API LeptrisDocument leptris_xslt_apply(LeptrisXslt xslt,
|
|
3098
3098
|
LeptrisDocument doc);
|
|
3099
|
-
|
|
3100
3099
|
/**
|
|
3101
3100
|
* Apply a compiled stylesheet and serialize the result
|
|
3102
3101
|
*
|
|
@@ -3111,6 +3110,50 @@ LEPTRIS_API LeptrisDocument leptris_xslt_apply(LeptrisXslt xslt,
|
|
|
3111
3110
|
LEPTRIS_API char* leptris_xslt_apply_string(LeptrisXslt xslt,
|
|
3112
3111
|
LeptrisDocument doc);
|
|
3113
3112
|
|
|
3113
|
+
/**
|
|
3114
|
+
* Apply a compiled stylesheet with caller-supplied top-level params
|
|
3115
|
+
*
|
|
3116
|
+
* Binds the given xsl:param values BEFORE the stylesheet's globals
|
|
3117
|
+
* run (XSLT §11.4): a caller value replaces the declared default,
|
|
3118
|
+
* unspecified params keep their defaults, and the value expressions
|
|
3119
|
+
* may reference the source document and earlier pairs.
|
|
3120
|
+
*
|
|
3121
|
+
* pairs is a flat name/value array — [name0, value0, name1,
|
|
3122
|
+
* value1, ...] — and count is the number of PAIRS (the array holds
|
|
3123
|
+
* 2*count entries). Each value is an XPath EXPRESSION evaluated
|
|
3124
|
+
* with the source document node as context; callers pre-quote
|
|
3125
|
+
* string literals ("'text'"), matching the libxslt convention.
|
|
3126
|
+
*
|
|
3127
|
+
* @param xslt Compiled stylesheet
|
|
3128
|
+
* @param doc Source document (not modified)
|
|
3129
|
+
* @param pairs Flat name/expression array, or NULL with count 0
|
|
3130
|
+
* @param count Number of name/expression pairs
|
|
3131
|
+
* @return Result tree (free with leptris_document_free), or NULL on
|
|
3132
|
+
* error — including a value expression that fails to
|
|
3133
|
+
* compile (message via leptris_document_last_error)
|
|
3134
|
+
*/
|
|
3135
|
+
LEPTRIS_API LeptrisDocument leptris_xslt_apply_params(
|
|
3136
|
+
LeptrisXslt xslt, LeptrisDocument doc,
|
|
3137
|
+
const char* const* pairs, size_t count);
|
|
3138
|
+
|
|
3139
|
+
/**
|
|
3140
|
+
* Apply with caller-supplied top-level params and serialize
|
|
3141
|
+
*
|
|
3142
|
+
* leptris_xslt_apply_params + the leptris_xslt_apply_string
|
|
3143
|
+
* serialization contract (top-level text nodes and fragments
|
|
3144
|
+
* preserved).
|
|
3145
|
+
*
|
|
3146
|
+
* @param xslt Compiled stylesheet
|
|
3147
|
+
* @param doc Source document
|
|
3148
|
+
* @param pairs Flat name/expression array (see
|
|
3149
|
+
* leptris_xslt_apply_params), or NULL with count 0
|
|
3150
|
+
* @param count Number of name/expression pairs
|
|
3151
|
+
* @return Serialized result (free with leptris_free_string), or NULL
|
|
3152
|
+
*/
|
|
3153
|
+
LEPTRIS_API char* leptris_xslt_apply_string_params(
|
|
3154
|
+
LeptrisXslt xslt, LeptrisDocument doc,
|
|
3155
|
+
const char* const* pairs, size_t count);
|
|
3156
|
+
|
|
3114
3157
|
/**
|
|
3115
3158
|
* Custom XPath function handler (string-valued).
|
|
3116
3159
|
*
|
|
@@ -51,6 +51,10 @@ const uint8_t leptris_chartype_table[256] = {
|
|
|
51
51
|
['-']=CT_NAME, ['.']=CT_NAME,
|
|
52
52
|
/* Whitespace */
|
|
53
53
|
[' ']=CT_WS, ['\t']=CT_WS, ['\n']=CT_WS, ['\r']=CT_WS,
|
|
54
|
+
/* Attr-loop delimiters (#1436 fused scanner; see chartype.h) */
|
|
55
|
+
['>']=CT_ATTR_GT, ['/']=CT_ATTR_SLASH,
|
|
56
|
+
['=']=CT_ATTR_EQ,
|
|
57
|
+
['"']=CT_ATTR_QUOTE, ['\'']=CT_ATTR_QUOTE,
|
|
54
58
|
/* UTF-8 multibyte lead and continuation bytes — every byte
|
|
55
59
|
* >= 0x80 is CT_UTF8 (lenient name scanning, <café>). */
|
|
56
60
|
#define CT_U8(n) [n]=CT_UTF8
|
|
@@ -24,6 +24,16 @@
|
|
|
24
24
|
* Matches flat_parser's lenient UTF-8
|
|
25
25
|
* fallback (c >= 0xC0 for start, c >= 0x80
|
|
26
26
|
* for continuation). */
|
|
27
|
+
/* Attr-loop delimiter bits (#1436 fused scanner). The open-tag/
|
|
28
|
+
* attribute loop dispatches on ONE table load: the whitespace skip
|
|
29
|
+
* itself yields the stop byte's class — tag close, self-close, '=',
|
|
30
|
+
* or the value quote (pugixml ct_parse_attr's combined-mask shape).
|
|
31
|
+
* These five bytes carry none of the flags above, so every existing
|
|
32
|
+
* mask is unaffected. */
|
|
33
|
+
#define CT_ATTR_GT 0x10 /* '>' — end of the open tag */
|
|
34
|
+
#define CT_ATTR_SLASH 0x20 /* '/' — self-close (with '>' lookahead) */
|
|
35
|
+
#define CT_ATTR_EQ 0x40 /* '=' — name/value separator */
|
|
36
|
+
#define CT_ATTR_QUOTE 0x80 /* '"', '\'' — value delimiters */
|
|
27
37
|
|
|
28
38
|
/* The shared table. Defined once in chartype.c — fully static and
|
|
29
39
|
* const, including the CT_UTF8 entries for bytes >= 0x80 (no
|