leptris 1.9.311.2-aarch64-linux → 1.9.312.0-aarch64-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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +49 -0
- data/Rakefile +1 -1
- data/lib/leptris/version.rb +1 -1
- data/lib/leptris/xml/descriptor.rb +42 -1
- data/lib/leptris/xml/element.rb +7 -10
- data/lib/leptris/xml/ffi.rb +28 -3
- data/lib/leptris/xml/plan_value.rb +7 -0
- data/lib/libleptris.so +0 -0
- data/vendor-src/README.md +1 -1
- data/vendor-src/libleptris/CHANGELOG.md +45 -0
- data/vendor-src/libleptris/CMakeLists.txt +1 -1
- data/vendor-src/libleptris/bindings/rust/descriptor/src/lib.rs +14 -0
- data/vendor-src/libleptris/bindings/rust/tests/descriptor_abi.rs +5 -0
- data/vendor-src/libleptris/src/include/leptris/descriptor.h +45 -1
- data/vendor-src/libleptris/src/leptris/descriptor/plan.c +529 -23
- data/vendor-src/libleptris/src/leptris/dom/element_modify.c +50 -0
- data/vendor-src/libleptris/test/abi/test_descriptor.cpp +736 -1
- data/vendor-src/libleptris/test/abi/test_dom_mutation_bugs.cpp +67 -0
- data/vendor-src/libleptris/test/perf/test_perf.cpp +7 -3
- data/vendor-src/libleptris/test/xslt/libxslt_suite_general/bug-111.xml +17 -17
- data/vendor-src/libleptris/test/xslt/libxslt_suite_general/bug-111.xsl +45 -45
- data/vendor-src/libleptris/test/xslt/libxslt_suite_general/bug-115.xsl +19 -19
- data/vendor-src/libleptris/test/xslt/libxslt_suite_general/bug-156.imp1.imp +16 -16
- data/vendor-src/libleptris/test/xslt/libxslt_suite_general/bug-156.imp2.imp +10 -10
- data/vendor-src/libleptris/test/xslt/libxslt_suite_general/bug-156.xsl +12 -12
- data/vendor-src/libleptris/test/xslt/libxslt_suite_general/bug-165.xml +10 -10
- data/vendor-src/libleptris/test/xslt/libxslt_suite_general/bug-80.xml +6 -6
- data/vendor-src/libleptris/test/xslt/libxslt_suite_general/bug-80.xsl +29 -29
- data/vendor-src/libleptris/vcpkg.json +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 9de71b8c23036b66ec08f42ba8d76658c4e0b434d1310edd0f25b22c67685cfa
|
|
4
|
+
data.tar.gz: b6a93975b39dd5c601e021c4265ddc4ad50af9e4542c161d946d992110b02fbc
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: fbd674dc47c7220e0405a473dce2d261370d4ef45a8cca93475212250ddf4859206884638901974124e8f23aff5ea78b44e5f4a8e6e078647f5279efe0ab1544
|
|
7
|
+
data.tar.gz: 37082ea5af7b48056ea1d19cd2613d6f8d4696cce9dcb547ebc14c3ee30ef0af28d88a1d086d4857d07ef3431be03217b3ff0c9a0fa39511211890773525474c
|
data/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,52 @@
|
|
|
1
|
+
## [1.9.312.0] - 2026-10-07
|
|
2
|
+
|
|
3
|
+
### Added — engine sync (libleptris 1.9.312)
|
|
4
|
+
|
|
5
|
+
- **Descriptor#serialize — plan-guided serialization (#1551)**:
|
|
6
|
+
`leptris_plan_serialize` round-trips a walk result back to XML —
|
|
7
|
+
the plan supplies element wrappers (row wire_name + the new
|
|
8
|
+
`ns_prefix:` field on element/child/attr rows), the result the
|
|
9
|
+
content. Children emit in document order; rows with ns EXACT +
|
|
10
|
+
`ns_prefix:` emit prefixed names and every distinct (prefix,
|
|
11
|
+
uri) pair is declared exactly once, on the output root, in
|
|
12
|
+
first-encounter order; text/attr values escape; RAW members are
|
|
13
|
+
verbatim. The three plan structs take the additive trailing
|
|
14
|
+
`ns_prefix` field (attr row 40 → 48B; the row-size skew gate
|
|
15
|
+
holds). `PlanValue#result_ptr` exposes the whole-result handle
|
|
16
|
+
the face reads.
|
|
17
|
+
- **Wildcard child rows (#1552)**: `kind: :wildcard` binds every
|
|
18
|
+
element child no named sibling row bound — named rows win
|
|
19
|
+
regardless of row order; the row emits exactly one COLLECTION
|
|
20
|
+
(echoing wire_name/type_tag, even when empty); members keep
|
|
21
|
+
document order; an explicit `ns:` filters the remainder
|
|
22
|
+
(`ns: :none` binds no-namespace children alone; unset stays the
|
|
23
|
+
catch-all ANY default). PLAN_KIND_WILDCARD = 7.
|
|
24
|
+
|
|
25
|
+
### Fixed — engine sync (1.9.312)
|
|
26
|
+
|
|
27
|
+
- **Root-level leaf splices adopt single-node (#1539)**: a leaf
|
|
28
|
+
directly on a scratch document's children chain (prolog
|
|
29
|
+
comment / PI) no longer reports LEPTRIS_ERROR_MEMORY on
|
|
30
|
+
cross-document append — spec-pinned in the adoption suite.
|
|
31
|
+
|
|
32
|
+
## [1.9.311.3] - 2026-10-06
|
|
33
|
+
|
|
34
|
+
### Performance
|
|
35
|
+
|
|
36
|
+
- **Same-document splices stop paying the cross-document sweep
|
|
37
|
+
(#374, the fresh-doc 0.80x follow-up)**: every fast splice path
|
|
38
|
+
is reached only after `foreign_to?` said no — the child's
|
|
39
|
+
document IS this one — so the post-splice
|
|
40
|
+
`invalidate_cross_document!` (a method call plus two ivar reads
|
|
41
|
+
that always returned at the equal check) was dead weight on the
|
|
42
|
+
hottest construction loop. Removed from the add_child /
|
|
43
|
+
prepend_child / sibling-insert fast paths and their FFI tails
|
|
44
|
+
(the foreign and batch paths keep their sweeps); `add_child` no
|
|
45
|
+
longer runs the Ruby-side readonly/version gate twice when the
|
|
46
|
+
C face self-gates. Measured under load with interleaved
|
|
47
|
+
CPU-time medians (3 rounds, 15k appends): add_child 1.69 →
|
|
48
|
+
1.41 µs/call (~16%), programmatic build ~14%.
|
|
49
|
+
|
|
1
50
|
## [1.9.311.2] - 2026-10-06
|
|
2
51
|
|
|
3
52
|
### 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.312"
|
|
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/lib/leptris/version.rb
CHANGED
|
@@ -74,6 +74,12 @@ class Leptris::XML::Descriptor
|
|
|
74
74
|
raw: Leptris::XML::FFI::PLAN_KIND_RAW,
|
|
75
75
|
content: Leptris::XML::FFI::PLAN_KIND_CONTENT,
|
|
76
76
|
callback: Leptris::XML::FFI::PLAN_KIND_CALLBACK,
|
|
77
|
+
# libleptris 1.9.312 (#1552): the remainder bucket — binds
|
|
78
|
+
# every element child no named sibling row bound. Emits one
|
|
79
|
+
# COLLECTION (even when empty); named rows win regardless of
|
|
80
|
+
# row order. child_plan_index >= 0 walks members through that
|
|
81
|
+
# plan; -1 (the default) emits RAW serialized subtrees.
|
|
82
|
+
wildcard: Leptris::XML::FFI::PLAN_KIND_WILDCARD,
|
|
77
83
|
}.freeze
|
|
78
84
|
private_constant :KINDS
|
|
79
85
|
|
|
@@ -177,6 +183,10 @@ class Leptris::XML::Descriptor
|
|
|
177
183
|
end
|
|
178
184
|
ep[:ns_form] = ns_form
|
|
179
185
|
ep[:ns_uri] = ns_uri
|
|
186
|
+
# libleptris 1.9.312 (#1551): the root plan's serialization
|
|
187
|
+
# prefix (ns_form EXACT rows emit "prefix:local" and the
|
|
188
|
+
# xmlns declaration lands once on the output root).
|
|
189
|
+
ep[:ns_prefix] = anchor_string(anchors, plan[:ns_prefix]) if plan[:ns_prefix]
|
|
180
190
|
attrs = plan[:attributes] || []
|
|
181
191
|
attr_memory = attrs.empty? ? nil :
|
|
182
192
|
::FFI::MemoryPointer.new(Leptris::XML::FFI::AttrPlan, attrs.size)
|
|
@@ -198,6 +208,9 @@ class Leptris::XML::Descriptor
|
|
|
198
208
|
ap[:ns_uri] =
|
|
199
209
|
row[:ns].is_a?(Hash) ?
|
|
200
210
|
anchor_string(anchors, row[:ns].fetch(:exact)) : nil
|
|
211
|
+
# libleptris 1.9.312 (#1551): serialization prefix — with
|
|
212
|
+
# ns_form EXACT the attribute emits as "prefix:local".
|
|
213
|
+
ap[:ns_prefix] = anchor_string(anchors, row[:ns_prefix]) if row[:ns_prefix]
|
|
201
214
|
pack_predicates(ap, row, anchors)
|
|
202
215
|
end
|
|
203
216
|
ep[:attribute_count] = attrs.size
|
|
@@ -214,16 +227,24 @@ class Leptris::XML::Descriptor
|
|
|
214
227
|
cp[:child_plan_index] = row[:child_plan_index] || -1
|
|
215
228
|
# Rule-level ns form (libleptris 1.9.178, #1115): siblings
|
|
216
229
|
# under one parent can require different URIs when set;
|
|
217
|
-
# defaults to NONE for backward compatibility.
|
|
230
|
+
# defaults to NONE for backward compatibility. WILDCARD
|
|
231
|
+
# rows (#1552) read an explicit :ns through pad0 (an
|
|
232
|
+
# explicit :none filters no-namespace remainder alone;
|
|
233
|
+
# unset stays the catch-all ANY default).
|
|
218
234
|
cp[:ns_form] =
|
|
219
235
|
case row[:ns]
|
|
220
236
|
when :any then Leptris::XML::FFI::PLAN_NS_ANY
|
|
221
237
|
when Hash then Leptris::XML::FFI::PLAN_NS_EXACT
|
|
222
238
|
else Leptris::XML::FFI::PLAN_NS_NONE
|
|
223
239
|
end
|
|
240
|
+
cp[:pad0] = 1 if row[:kind] == :wildcard && row.key?(:ns)
|
|
224
241
|
cp[:ns_uri] =
|
|
225
242
|
row[:ns].is_a?(Hash) ?
|
|
226
243
|
anchor_string(anchors, row[:ns].fetch(:exact)) : nil
|
|
244
|
+
# libleptris 1.9.312 (#1551): serialization prefix — with
|
|
245
|
+
# ns_form EXACT the element emits as "prefix:local" and
|
|
246
|
+
# its declaration lands once on the output root.
|
|
247
|
+
cp[:ns_prefix] = anchor_string(anchors, row[:ns_prefix]) if row[:ns_prefix]
|
|
227
248
|
pack_predicates(cp, row, anchors)
|
|
228
249
|
end
|
|
229
250
|
ep[:child_count] = children.size
|
|
@@ -318,6 +339,26 @@ class Leptris::XML::Descriptor
|
|
|
318
339
|
owner: true, plans: @plans, plan: @plans[0])
|
|
319
340
|
end
|
|
320
341
|
|
|
342
|
+
# libleptris 1.9.312 (#1551): serialize a walk result back to
|
|
343
|
+
# XML guided by this plan — the plan supplies element wrappers
|
|
344
|
+
# (row wire_name + ns_prefix), the result the content; children
|
|
345
|
+
# emit in document order. Rows with ns_form EXACT + ns_prefix
|
|
346
|
+
# emit prefixed names and every distinct (prefix, uri) pair is
|
|
347
|
+
# declared exactly once, on the output root, first-encounter
|
|
348
|
+
# order. Text and attribute values are escaped; RAW members are
|
|
349
|
+
# verbatim.
|
|
350
|
+
def serialize(plan_value)
|
|
351
|
+
result_ptr = plan_value.result_ptr
|
|
352
|
+
status = ::FFI::MemoryPointer.new(:int)
|
|
353
|
+
raw = Leptris::XML::FFI.leptris_plan_serialize(
|
|
354
|
+
@handle, result_ptr, status)
|
|
355
|
+
if raw.null?
|
|
356
|
+
raise Leptris::XML::Error,
|
|
357
|
+
"plan serialize failed (status=#{status.read_int})"
|
|
358
|
+
end
|
|
359
|
+
Leptris::XML::FFI.read_owned_string(raw)
|
|
360
|
+
end
|
|
361
|
+
|
|
321
362
|
# The fused loop (#1269b): source bytes → typed rows in ONE C
|
|
322
363
|
# call — leptris_plan_materialize runs parse, walk, and free
|
|
323
364
|
# natively; the returned PlanValue tree is standalone and the
|
data/lib/leptris/xml/element.rb
CHANGED
|
@@ -446,7 +446,6 @@ class Leptris::XML::Element < Leptris::XML::Node
|
|
|
446
446
|
if native_fast_children?
|
|
447
447
|
unless Leptris::XML::Native.insert_binding_child(
|
|
448
448
|
@document, @c_address, node.c_ptr.address, 1).nil?
|
|
449
|
-
Leptris::XML::Node.invalidate_cross_document!(node, @document)
|
|
450
449
|
return node
|
|
451
450
|
end
|
|
452
451
|
end
|
|
@@ -456,7 +455,6 @@ class Leptris::XML::Element < Leptris::XML::Node
|
|
|
456
455
|
end
|
|
457
456
|
Leptris::XML::FFI.check_status(
|
|
458
457
|
Leptris::XML::FFI.leptris_element_prepend_child(c_ptr, node.c_ptr))
|
|
459
|
-
Leptris::XML::Node.invalidate_cross_document!(node, @document)
|
|
460
458
|
node
|
|
461
459
|
end
|
|
462
460
|
|
|
@@ -481,7 +479,6 @@ class Leptris::XML::Element < Leptris::XML::Node
|
|
|
481
479
|
if native_fast_children?
|
|
482
480
|
unless Leptris::XML::Native.insert_binding_child(
|
|
483
481
|
@document, @c_address, node.c_ptr.address, 2).nil?
|
|
484
|
-
Leptris::XML::Node.invalidate_cross_document!(node, @document)
|
|
485
482
|
return node
|
|
486
483
|
end
|
|
487
484
|
end
|
|
@@ -491,7 +488,6 @@ class Leptris::XML::Element < Leptris::XML::Node
|
|
|
491
488
|
end
|
|
492
489
|
Leptris::XML::FFI.check_status(
|
|
493
490
|
Leptris::XML::FFI.leptris_element_insert_after(c_ptr, node.c_ptr))
|
|
494
|
-
Leptris::XML::Node.invalidate_cross_document!(node, @document)
|
|
495
491
|
node
|
|
496
492
|
end
|
|
497
493
|
|
|
@@ -516,7 +512,6 @@ class Leptris::XML::Element < Leptris::XML::Node
|
|
|
516
512
|
if native_fast_children?
|
|
517
513
|
unless Leptris::XML::Native.insert_binding_child(
|
|
518
514
|
@document, @c_address, node.c_ptr.address, 3).nil?
|
|
519
|
-
Leptris::XML::Node.invalidate_cross_document!(node, @document)
|
|
520
515
|
return node
|
|
521
516
|
end
|
|
522
517
|
end
|
|
@@ -526,7 +521,6 @@ class Leptris::XML::Element < Leptris::XML::Node
|
|
|
526
521
|
end
|
|
527
522
|
Leptris::XML::FFI.check_status(
|
|
528
523
|
Leptris::XML::FFI.leptris_element_insert_before(c_ptr, node.c_ptr))
|
|
529
|
-
Leptris::XML::Node.invalidate_cross_document!(node, @document)
|
|
530
524
|
node
|
|
531
525
|
end
|
|
532
526
|
|
|
@@ -672,7 +666,6 @@ class Leptris::XML::Element < Leptris::XML::Node
|
|
|
672
666
|
end
|
|
673
667
|
|
|
674
668
|
def add_child(node_or_markup)
|
|
675
|
-
ensure_writable!
|
|
676
669
|
case node_or_markup
|
|
677
670
|
when Leptris::XML::Node
|
|
678
671
|
# Foreign-child adoption (1.9.304, #1528): the engine
|
|
@@ -686,22 +679,24 @@ class Leptris::XML::Element < Leptris::XML::Node
|
|
|
686
679
|
# readonly/liveness gates, the provable no-op lift
|
|
687
680
|
# predicate, the version bump, and the engine append. Qnil
|
|
688
681
|
# means the child needs the namespace lift — fall through
|
|
689
|
-
# to the full path.
|
|
682
|
+
# to the full path. Same-document children cannot reach
|
|
683
|
+
# here from another document (foreign ones routed above),
|
|
684
|
+
# so there is no cross-document sweep to run (#374): the
|
|
685
|
+
# C-side version bump invalidates this document's memos.
|
|
690
686
|
if native_fast_children?
|
|
691
687
|
# The face raises on failure (TODO.perf/36); Qnil = the
|
|
692
688
|
# child needs the lift — the full path handles it.
|
|
693
689
|
unless Leptris::XML::Native.append_binding_child(
|
|
694
690
|
@document, @c_address, node_or_markup.c_ptr.address).nil?
|
|
695
|
-
Leptris::XML::Node.invalidate_cross_document!(node_or_markup, @document)
|
|
696
691
|
return node_or_markup
|
|
697
692
|
end
|
|
698
693
|
end
|
|
694
|
+
ensure_writable!
|
|
699
695
|
unless Leptris::XML::Element.skip_adoption_lift?(node_or_markup)
|
|
700
696
|
Leptris::XML::Element.lift_namespaces_for_adoption(node_or_markup, namespaces)
|
|
701
697
|
end
|
|
702
698
|
Leptris::XML::FFI.check_status(
|
|
703
699
|
Leptris::XML::FFI.leptris_element_append_child(c_ptr, node_or_markup.c_ptr))
|
|
704
|
-
Leptris::XML::Node.invalidate_cross_document!(node_or_markup, @document)
|
|
705
700
|
node_or_markup
|
|
706
701
|
when String
|
|
707
702
|
# TODO.perf/34: one dispatch parses the markup and appends
|
|
@@ -721,6 +716,7 @@ class Leptris::XML::Element < Leptris::XML::Node
|
|
|
721
716
|
# path (it re-fails fast)
|
|
722
717
|
Leptris::XML::DocumentFragment.parse(node_or_markup, @document)
|
|
723
718
|
end
|
|
719
|
+
ensure_writable!
|
|
724
720
|
frag = Leptris::XML::DocumentFragment.parse(node_or_markup, @document)
|
|
725
721
|
added = []
|
|
726
722
|
frag.children.each do |n|
|
|
@@ -741,6 +737,7 @@ class Leptris::XML::Element < Leptris::XML::Node
|
|
|
741
737
|
# original is removed after (the source loses the node, as
|
|
742
738
|
# every pre-304 caller observed).
|
|
743
739
|
def append_installed(node)
|
|
740
|
+
ensure_writable!
|
|
744
741
|
unless Leptris::XML::Element.skip_adoption_lift?(node)
|
|
745
742
|
Leptris::XML::Element.lift_namespaces_for_adoption(node, namespaces)
|
|
746
743
|
end
|
data/lib/leptris/xml/ffi.rb
CHANGED
|
@@ -306,7 +306,12 @@ module Leptris
|
|
|
306
306
|
# unset = the historical wire-name lookup.
|
|
307
307
|
:ns_form, :uint8,
|
|
308
308
|
:pad_ns, :uint8,
|
|
309
|
-
:ns_uri, :pointer
|
|
309
|
+
:ns_uri, :pointer,
|
|
310
|
+
# libleptris 1.9.312 (#1551): intended wire prefix
|
|
311
|
+
# for plan-path serialization (ns_form EXACT emits
|
|
312
|
+
# "ns_prefix:wire_name"). NULL keeps matching-only
|
|
313
|
+
# behavior.
|
|
314
|
+
:ns_prefix, :pointer
|
|
310
315
|
end
|
|
311
316
|
class ChildPlan < ::FFI::Struct
|
|
312
317
|
layout :wire_name, :pointer,
|
|
@@ -321,7 +326,13 @@ module Leptris
|
|
|
321
326
|
# #1272: element-side predicates (same semantics).
|
|
322
327
|
:predicate_count, :uint16,
|
|
323
328
|
:pad_pred, :uint16,
|
|
324
|
-
:predicates, :pointer
|
|
329
|
+
:predicates, :pointer,
|
|
330
|
+
# libleptris 1.9.312 (#1551): serialization prefix,
|
|
331
|
+
# same contract as AttrPlan's. For WILDCARD rows
|
|
332
|
+
# (libleptris 1.9.312, #1552) pad0 marks an EXPLICIT
|
|
333
|
+
# ns_form (0 then filters no-namespace remainder);
|
|
334
|
+
# unset keeps the catch-all ANY default.
|
|
335
|
+
:ns_prefix, :pointer
|
|
325
336
|
end
|
|
326
337
|
class ElementPlan < ::FFI::Struct
|
|
327
338
|
layout :element_name, :pointer,
|
|
@@ -333,7 +344,10 @@ module Leptris
|
|
|
333
344
|
:attribute_plans, :pointer,
|
|
334
345
|
:child_plans, :pointer,
|
|
335
346
|
:flags, :uint16,
|
|
336
|
-
:pad1, :uint16
|
|
347
|
+
:pad1, :uint16,
|
|
348
|
+
# libleptris 1.9.312 (#1551): the root plan's
|
|
349
|
+
# serialization prefix.
|
|
350
|
+
:ns_prefix, :pointer
|
|
337
351
|
end
|
|
338
352
|
class PlanSpec < ::FFI::Struct
|
|
339
353
|
layout :abi_version, :uint32,
|
|
@@ -349,6 +363,11 @@ module Leptris
|
|
|
349
363
|
PLAN_KIND_RAW = 4
|
|
350
364
|
PLAN_KIND_CONTENT = 5
|
|
351
365
|
PLAN_KIND_CALLBACK = 6
|
|
366
|
+
# libleptris 1.9.312 (#1552): catch-all row — binds every
|
|
367
|
+
# element child no named sibling row bound (named rows win
|
|
368
|
+
# regardless of order); emits one COLLECTION echoing the
|
|
369
|
+
# row's wire_name/type_tag, members in document order.
|
|
370
|
+
PLAN_KIND_WILDCARD = 7
|
|
352
371
|
|
|
353
372
|
PLAN_FLAG_MIXED_CONTENT = 0x1
|
|
354
373
|
PLAN_FLAG_ORDERED = 0x2
|
|
@@ -391,6 +410,12 @@ module Leptris
|
|
|
391
410
|
:leptris_plan_result
|
|
392
411
|
attach_function :leptris_plan_result_free,
|
|
393
412
|
[:leptris_plan_result], :void
|
|
413
|
+
# libleptris 1.9.312 (#1551): serialize a walk result back
|
|
414
|
+
# to XML guided by its plan — the plan supplies element
|
|
415
|
+
# wrappers (row wire_name + ns_prefix), the result the
|
|
416
|
+
# content. Caller-freed string (leptris_free_string).
|
|
417
|
+
attach_function :leptris_plan_serialize,
|
|
418
|
+
[:leptris_plan, :leptris_plan_result, :pointer], :pointer
|
|
394
419
|
attach_function :leptris_plan_value_kind,
|
|
395
420
|
[:leptris_plan_result], :int
|
|
396
421
|
attach_function :leptris_plan_value_name,
|
|
@@ -30,6 +30,13 @@ class Leptris::XML::PlanValue
|
|
|
30
30
|
@counter = counter || [0]
|
|
31
31
|
end
|
|
32
32
|
|
|
33
|
+
# The engine value handle — the ROOT value's is the whole-result
|
|
34
|
+
# handle whole-result faces (leptris_plan_serialize) read. Pass
|
|
35
|
+
# the value Descriptor#walk/#materialize returned.
|
|
36
|
+
def result_ptr
|
|
37
|
+
@ptr
|
|
38
|
+
end
|
|
39
|
+
|
|
33
40
|
def kind
|
|
34
41
|
@counter[0] += 1
|
|
35
42
|
VALUE_KINDS.fetch(Leptris::XML::FFI.leptris_plan_value_kind(@ptr))
|
data/lib/libleptris.so
CHANGED
|
Binary file
|
data/vendor-src/README.md
CHANGED
|
@@ -1,5 +1,50 @@
|
|
|
1
1
|
## [Unreleased]
|
|
2
2
|
|
|
3
|
+
## [1.9.312] - 2026-10-06
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
|
|
7
|
+
- **Plan-path serialization emits namespace declarations (#1551,
|
|
8
|
+
PR #1558).** New `leptris_plan_serialize(plan, result)`: the plan
|
|
9
|
+
supplies element wrappers, the result the content — children emit
|
|
10
|
+
in document order, collections are transparent, RAW members
|
|
11
|
+
verbatim, text and attributes escaped. Rows carrying
|
|
12
|
+
`ns_form = EXACT` plus the new trailing `ns_prefix` field emit
|
|
13
|
+
prefixed wire names, and every distinct (prefix, uri) pair is
|
|
14
|
+
declared exactly once on the output root in first-encounter
|
|
15
|
+
order. `leptris_attr_plan` takes the same field, so #1486-matched
|
|
16
|
+
attributes emit the plan-chosen spelling. Byte-parity gates on a
|
|
17
|
+
`w:document` corpus; additive trailing fields keep
|
|
18
|
+
`PLAN_ABI_VERSION` at 1 (64-bit attr row 40 -> 48 bytes). Rust
|
|
19
|
+
mirror (`leptris-descriptor`) updated in lockstep.
|
|
20
|
+
- **Wildcard (catch-all) child rows (#1552, PR #1555).**
|
|
21
|
+
`LEPTRIS_PLAN_KIND_WILDCARD` binds every element child no named
|
|
22
|
+
sibling row bound — named rows take precedence regardless of row
|
|
23
|
+
order (a two-pass walk over a completed `bound[]` map; a named
|
|
24
|
+
row that rejected a child does not reserve it). Namespace-form
|
|
25
|
+
aware (`pad0` marks the form explicit so `NS_NONE` is
|
|
26
|
+
expressible; unset keeps the catch-all ANY default), emits one
|
|
27
|
+
COLLECTION echoing the row wire_name/type_tag even when empty,
|
|
28
|
+
members in document order; `child_plan_index >= 0` walks members
|
|
29
|
+
through that plan. Rust mirror gains `PLAN_KIND_WILDCARD = 7`.
|
|
30
|
+
|
|
31
|
+
### Fixed
|
|
32
|
+
|
|
33
|
+
- **Root-level leaf splice arguments adopt single-node (#1539,
|
|
34
|
+
PR #1554).** A leaf directly on a scratch document's children
|
|
35
|
+
chain (prolog/epilog comment or PI) reported
|
|
36
|
+
`LEPTRIS_ERROR_MEMORY` on cross-document append: the chain
|
|
37
|
+
copier hit the root element and misread it as OOM. The seam now
|
|
38
|
+
copies exactly the argument for the doc-level shape; in-element
|
|
39
|
+
leaves keep the #540 chain shape.
|
|
40
|
+
- **Emulated-i386 perf-gate stability (PR #1561).** The
|
|
41
|
+
indexed-child-access shape budget widens 6x -> 7.5x: the qemu
|
|
42
|
+
timer noise of the ILP32 leg flaked past 6x three times in one
|
|
43
|
+
day while every native leg measured ~3x. Linear ~3x, quadratic
|
|
44
|
+
~9x, O(N^3) ~27x — the gate keeps its discrimination.
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
|
|
3
48
|
## [1.9.311] - 2026-10-06
|
|
4
49
|
|
|
5
50
|
### Added
|
|
@@ -31,6 +31,11 @@ pub const PLAN_KIND_NESTED: PlanKind = 3;
|
|
|
31
31
|
pub const PLAN_KIND_RAW: PlanKind = 4;
|
|
32
32
|
pub const PLAN_KIND_CONTENT: PlanKind = 5;
|
|
33
33
|
pub const PLAN_KIND_CALLBACK: PlanKind = 6;
|
|
34
|
+
/// #1552: catch-all row — binds every element child no named
|
|
35
|
+
/// sibling row bound; emits one COLLECTION echoing the row
|
|
36
|
+
/// wire_name/type_tag; `child_plan_index >= 0` walks members
|
|
37
|
+
/// through that plan, else members are RAW serialized subtrees.
|
|
38
|
+
pub const PLAN_KIND_WILDCARD: PlanKind = 7;
|
|
34
39
|
|
|
35
40
|
/// Element plan flags.
|
|
36
41
|
pub const PLAN_FLAG_MIXED_CONTENT: u32 = 0x1;
|
|
@@ -83,6 +88,9 @@ pub struct AttrPlan {
|
|
|
83
88
|
pub pad_ns: u8,
|
|
84
89
|
/// `PLAN_NS_EXACT` only.
|
|
85
90
|
pub ns_uri: *const c_char,
|
|
91
|
+
/// #1551: intended wire prefix for serialized output
|
|
92
|
+
/// (`leptris_plan_serialize`); meaningful with EXACT.
|
|
93
|
+
pub ns_prefix: *const c_char,
|
|
86
94
|
}
|
|
87
95
|
|
|
88
96
|
/// Child binding row.
|
|
@@ -107,6 +115,9 @@ pub struct ChildPlan {
|
|
|
107
115
|
pub predicate_count: u16,
|
|
108
116
|
pub pad_pred: u16,
|
|
109
117
|
pub predicates: *const AttrPredicate,
|
|
118
|
+
/// #1551: intended wire prefix for serialized output
|
|
119
|
+
/// (`leptris_plan_serialize`); meaningful with EXACT.
|
|
120
|
+
pub ns_prefix: *const c_char,
|
|
110
121
|
}
|
|
111
122
|
|
|
112
123
|
/// One element plan: the attribute and child rows bound to a name.
|
|
@@ -127,6 +138,9 @@ pub struct ElementPlan {
|
|
|
127
138
|
/// `PLAN_FLAG_*`.
|
|
128
139
|
pub flags: u16,
|
|
129
140
|
pub pad1: u16,
|
|
141
|
+
/// #1551: the root plan's intended wire prefix for serialized
|
|
142
|
+
/// output (`leptris_plan_serialize`); meaningful with EXACT.
|
|
143
|
+
pub ns_prefix: *const c_char,
|
|
130
144
|
}
|
|
131
145
|
|
|
132
146
|
/// Top-level plan specification handed to the engine.
|
|
@@ -86,6 +86,7 @@ fn plan_build_and_walk_round_trip() {
|
|
|
86
86
|
ns_form: descriptor::PLAN_NS_NONE,
|
|
87
87
|
pad_ns: 0,
|
|
88
88
|
ns_uri: ptr::null(),
|
|
89
|
+
ns_prefix: ptr::null(),
|
|
89
90
|
};
|
|
90
91
|
let child_row = descriptor::ChildPlan {
|
|
91
92
|
wire_name: title.as_ptr(),
|
|
@@ -98,6 +99,7 @@ fn plan_build_and_walk_round_trip() {
|
|
|
98
99
|
predicate_count: 0,
|
|
99
100
|
pad_pred: 0,
|
|
100
101
|
predicates: ptr::null(),
|
|
102
|
+
ns_prefix: ptr::null(),
|
|
101
103
|
};
|
|
102
104
|
let book_plan = descriptor::ElementPlan {
|
|
103
105
|
element_name: book.as_ptr(),
|
|
@@ -110,6 +112,7 @@ fn plan_build_and_walk_round_trip() {
|
|
|
110
112
|
child_plans: &child_row,
|
|
111
113
|
flags: 0,
|
|
112
114
|
pad1: 0,
|
|
115
|
+
ns_prefix: ptr::null(),
|
|
113
116
|
};
|
|
114
117
|
let root_child = descriptor::ChildPlan {
|
|
115
118
|
wire_name: book.as_ptr(),
|
|
@@ -124,6 +127,7 @@ fn plan_build_and_walk_round_trip() {
|
|
|
124
127
|
predicate_count: 0,
|
|
125
128
|
pad_pred: 0,
|
|
126
129
|
predicates: ptr::null(),
|
|
130
|
+
ns_prefix: ptr::null(),
|
|
127
131
|
};
|
|
128
132
|
let root_plan = descriptor::ElementPlan {
|
|
129
133
|
element_name: catalog.as_ptr(),
|
|
@@ -136,6 +140,7 @@ fn plan_build_and_walk_round_trip() {
|
|
|
136
140
|
child_plans: &root_child,
|
|
137
141
|
flags: 0,
|
|
138
142
|
pad1: 0,
|
|
143
|
+
ns_prefix: ptr::null(),
|
|
139
144
|
};
|
|
140
145
|
let plans = [root_plan, book_plan];
|
|
141
146
|
let spec = descriptor::PlanSpec {
|
|
@@ -65,7 +65,18 @@ typedef enum {
|
|
|
65
65
|
LEPTRIS_PLAN_KIND_NESTED = 3, /* recurse into plans[child_plan_index] */
|
|
66
66
|
LEPTRIS_PLAN_KIND_RAW = 4, /* verbatim serialized subtree */
|
|
67
67
|
LEPTRIS_PLAN_KIND_CONTENT = 5, /* mixed-content text runs, doc order */
|
|
68
|
-
LEPTRIS_PLAN_KIND_CALLBACK = 6
|
|
68
|
+
LEPTRIS_PLAN_KIND_CALLBACK = 6, /* escape hatch: raw value + position */
|
|
69
|
+
/* #1552: catch-all row. Binds every element child that no
|
|
70
|
+
* named sibling row bound — named rows take precedence
|
|
71
|
+
* regardless of row-list position. Emits exactly one
|
|
72
|
+
* COLLECTION echoing the row's wire_name/type_tag (even when
|
|
73
|
+
* the remainder is empty). Members keep document order and
|
|
74
|
+
* echo the row type_tag. The row's ns_form filters members;
|
|
75
|
+
* an unset form means ANY namespace (catch-all), unlike
|
|
76
|
+
* named rows' historical NONE. child_plan_index >= 0 walks
|
|
77
|
+
* each member through that plan (ELEMENT members); -1 emits
|
|
78
|
+
* RAW serialized subtrees. */
|
|
79
|
+
LEPTRIS_PLAN_KIND_WILDCARD = 7 /* remainder bucket, named rows win */
|
|
69
80
|
} LeptrisPlanKind;
|
|
70
81
|
|
|
71
82
|
/* Element plan flags. */
|
|
@@ -118,6 +129,12 @@ typedef struct {
|
|
|
118
129
|
uint8_t ns_form; /* LeptrisPlanNsForm */
|
|
119
130
|
uint8_t pad_ns;
|
|
120
131
|
const char* ns_uri; /* LEPTRIS_PLAN_NS_EXACT only */
|
|
132
|
+
/* #1551 (additive trailing field, the #1115 pattern): the
|
|
133
|
+
* intended wire prefix for serialized output. With ns_form
|
|
134
|
+
* EXACT, plan-path serialization emits the attribute as
|
|
135
|
+
* "ns_prefix:wire_name" (wire_name is the LOCAL name). NULL
|
|
136
|
+
* keeps matching-only behavior. */
|
|
137
|
+
const char* ns_prefix;
|
|
121
138
|
} leptris_attr_plan;
|
|
122
139
|
|
|
123
140
|
typedef struct {
|
|
@@ -137,6 +154,10 @@ typedef struct {
|
|
|
137
154
|
* zero value) keeps the historical no-namespace+NS_LENIENT
|
|
138
155
|
* behavior — the fields are consulted only when set. */
|
|
139
156
|
uint8_t ns_form; /* LeptrisPlanNsForm */
|
|
157
|
+
/* #1552 WILDCARD rows only: nonzero marks ns_form as
|
|
158
|
+
* explicitly set — ns_form 0 then binds no-namespace
|
|
159
|
+
* remainder alone. Unset (0), the row is the catch-all ANY
|
|
160
|
+
* default. Ignored by named rows. */
|
|
140
161
|
uint8_t pad0;
|
|
141
162
|
const char* ns_uri; /* LEPTRIS_PLAN_NS_EXACT only */
|
|
142
163
|
/* #1272: element-side predicates. Same semantics as attr_predicate
|
|
@@ -144,6 +165,12 @@ typedef struct {
|
|
|
144
165
|
uint16_t predicate_count;
|
|
145
166
|
uint16_t pad_pred;
|
|
146
167
|
const leptris_attr_predicate* predicates;
|
|
168
|
+
/* #1551 (additive trailing field): the intended wire prefix
|
|
169
|
+
* for serialized output. With ns_form EXACT, the walk stamps
|
|
170
|
+
* the binding on values and plan-path serialization emits
|
|
171
|
+
* "ns_prefix:local" and the xmlns declaration (once, on the
|
|
172
|
+
* output root). NULL keeps matching-only behavior. */
|
|
173
|
+
const char* ns_prefix;
|
|
147
174
|
} leptris_child_plan;
|
|
148
175
|
|
|
149
176
|
typedef struct {
|
|
@@ -159,6 +186,9 @@ typedef struct {
|
|
|
159
186
|
const leptris_child_plan* child_plans;
|
|
160
187
|
uint16_t flags; /* LEPTRIS_PLAN_FLAG_* */
|
|
161
188
|
uint16_t pad1;
|
|
189
|
+
/* #1551 (additive trailing field): the root plan's intended
|
|
190
|
+
* wire prefix, same contract as the child-row field. */
|
|
191
|
+
const char* ns_prefix;
|
|
162
192
|
} leptris_element_plan;
|
|
163
193
|
|
|
164
194
|
typedef struct {
|
|
@@ -222,6 +252,20 @@ LEPTRIS_API LeptrisPlanResult leptris_plan_walk(LeptrisDocument doc,
|
|
|
222
252
|
LeptrisStatus* status);
|
|
223
253
|
LEPTRIS_API void leptris_plan_result_free(LeptrisPlanResult result);
|
|
224
254
|
|
|
255
|
+
/* #1551: serialize a walk result back to XML, guided by its plan.
|
|
256
|
+
* The plan supplies element wrappers (row wire_name + ns_prefix)
|
|
257
|
+
* and the result supplies content; children emit in document order
|
|
258
|
+
* (the walk's order_index). Rows carrying ns_form EXACT +
|
|
259
|
+
* ns_prefix emit prefixed names, and every distinct (prefix, uri)
|
|
260
|
+
* pair used anywhere in the tree is declared exactly once, on the
|
|
261
|
+
* output root element, in first-encounter order. Attribute rows
|
|
262
|
+
* with ns_prefix emit "prefix:local". Text and attribute values
|
|
263
|
+
* are escaped; RAW members are verbatim. Returns a caller-freed
|
|
264
|
+
* string (leptris_free_string); NULL + status on failure. */
|
|
265
|
+
LEPTRIS_API char* leptris_plan_serialize(LeptrisPlan plan,
|
|
266
|
+
const LeptrisPlanResult result,
|
|
267
|
+
LeptrisStatus* status);
|
|
268
|
+
|
|
225
269
|
/* ---- Result accessors (NULL-tolerant: NULL in, 0/NULL out) ------- */
|
|
226
270
|
|
|
227
271
|
LEPTRIS_API LeptrisPlanValueKind leptris_plan_value_kind(const LeptrisPlanResult v);
|