leptris 1.9.385.0-ppc64le-linux → 1.9.386.0-ppc64le-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/Rakefile +1 -1
- data/lib/leptris/version.rb +1 -1
- data/lib/leptris/xml/document.rb +11 -1
- data/lib/leptris/xml/element.rb +15 -10
- data/lib/leptris/xml/ffi.rb +42 -0
- data/lib/leptris/xml/xsd.rb +35 -0
- data/lib/libleptris.so +0 -0
- data/vendor-src/README.md +1 -1
- data/vendor-src/libleptris/CHANGELOG.md +135 -0
- data/vendor-src/libleptris/CMakeLists.txt +1 -1
- data/vendor-src/libleptris/src/include/leptris/descriptor.h +16 -0
- data/vendor-src/libleptris/src/include/leptris.h +77 -0
- data/vendor-src/libleptris/src/leptris/descriptor/plan.c +82 -0
- data/vendor-src/libleptris/src/leptris/dom/element_modify.c +23 -2
- data/vendor-src/libleptris/src/leptris/dom/element_query.c +201 -0
- data/vendor-src/libleptris/src/leptris/memory/pool.c +27 -0
- data/vendor-src/libleptris/src/leptris/memory/pool.h +5 -0
- data/vendor-src/libleptris/src/leptris/xpath/evaluator_axes.c +15 -0
- data/vendor-src/libleptris/src/leptris/xsd/compile.c +232 -15
- data/vendor-src/libleptris/src/leptris/xsd/datatypes.c +180 -46
- data/vendor-src/libleptris/src/leptris/xsd/validate.c +69 -22
- data/vendor-src/libleptris/src/leptris/xsd/xsd_internal.h +10 -0
- data/vendor-src/libleptris/test/abi/test_descriptor.cpp +201 -0
- data/vendor-src/libleptris/test/dom/test_document_children.cpp +256 -0
- data/vendor-src/libleptris/test/dom/test_dom.cpp +263 -0
- data/vendor-src/libleptris/test/xsd/libxml2-cases/schema_set/f.xsd +13 -0
- data/vendor-src/libleptris/test/xsd/libxml2-cases/schema_set/g.xsd +9 -0
- data/vendor-src/libleptris/test/xsd/test_xsd_compile.cpp +293 -1
- data/vendor-src/libleptris/vcpkg.json +1 -1
- metadata +4 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 693b3140d5bc11495cb1fe2569a09502e30a93ffbd0174352428f5fa8db73843
|
|
4
|
+
data.tar.gz: b68d2e270256b7d92056189a417100fd01c0d71982e6f69a81b9f83cfeac8495
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 526a5ff1640e8adc2c107e0f5289e388eb9cd236fe6c47d8bc54c57a9e0bbf31dde18e0ed3d03ce9a8301f004cb51fef481d9c3b4bb820d9da418b338c96eed2
|
|
7
|
+
data.tar.gz: 6b0e71839d07aa3734bff6c110da078a7cfa597404bb891e14590756b230928a6058968d265bf6af1c30a24ec34ec01a588a703830d036a23b288de9d897d2a0
|
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.387"
|
|
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
data/lib/leptris/xml/document.rb
CHANGED
|
@@ -692,7 +692,17 @@ class Leptris::XML::Document
|
|
|
692
692
|
def absorbed?
|
|
693
693
|
!@absorbed_into.nil?
|
|
694
694
|
end
|
|
695
|
-
|
|
695
|
+
|
|
696
|
+
# leptris#1866: cross-document adoption lifetime anchor — the
|
|
697
|
+
# target document keeps the source alive so no wrapper/cache can
|
|
698
|
+
# outlive the source arena (standoc svg cleanup crash).
|
|
699
|
+
def retain_source_document!(source)
|
|
700
|
+
return unless source.is_a?(Leptris::XML::Document)
|
|
701
|
+
return if source.equal?(self)
|
|
702
|
+
(@retained_sources ||= []) << source
|
|
703
|
+
source
|
|
704
|
+
end
|
|
705
|
+
|
|
696
706
|
def free
|
|
697
707
|
return if @freed.state == :freed
|
|
698
708
|
@freed.state = :freed
|
data/lib/leptris/xml/element.rb
CHANGED
|
@@ -494,16 +494,19 @@ class Leptris::XML::Element < Leptris::XML::Node
|
|
|
494
494
|
prefix = key.start_with?("xmlns:") ? key.delete_prefix("xmlns:") : nil
|
|
495
495
|
node.add_namespace_definition(prefix, uri)
|
|
496
496
|
end
|
|
497
|
-
#
|
|
498
|
-
#
|
|
499
|
-
# is
|
|
500
|
-
#
|
|
501
|
-
# declarations
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
497
|
+
# leptris#1866: the own-declaration PRUNE that used to live here
|
|
498
|
+
# (libxml2 reparent parity, moxml #208 lineage) is REMOVED — it
|
|
499
|
+
# is unsound on this engine. The engine's cross-document deep
|
|
500
|
+
# copy resolves each copied element's namespace through the
|
|
501
|
+
# SOURCE's in-scope declarations at copy time: pruning an own
|
|
502
|
+
# declaration made every descendant copy lose its namespace URI
|
|
503
|
+
# (adopted svgs fell out of the SVG namespace; classupdate's
|
|
504
|
+
# namespaced queries then matched nothing and the attribute
|
|
505
|
+
# write path corrupted a sibling attr name — <use =""> and the
|
|
506
|
+
# standoc svg-cleanup segfaults). libxml2 gets away with the
|
|
507
|
+
# prune because its nodes keep per-node URIs on reparent; the
|
|
508
|
+
# redundant xmlns in the serialized copy is the correct output
|
|
509
|
+
# here.
|
|
507
510
|
nil
|
|
508
511
|
end
|
|
509
512
|
|
|
@@ -559,6 +562,7 @@ class Leptris::XML::Element < Leptris::XML::Node
|
|
|
559
562
|
Leptris::XML::Element.lift_namespaces_for_adoption(node, namespaces_for_sibling)
|
|
560
563
|
end
|
|
561
564
|
@document&.advance_version
|
|
565
|
+
@document&.retain_source_document!(node.document)
|
|
562
566
|
Leptris::XML::FFI.check_status(
|
|
563
567
|
Leptris::XML::FFI.leptris_element_insert_after(c_ptr, node.c_ptr))
|
|
564
568
|
Leptris::XML::Node.invalidate_cross_document!(node, @document)
|
|
@@ -595,6 +599,7 @@ class Leptris::XML::Element < Leptris::XML::Node
|
|
|
595
599
|
Leptris::XML::Element.lift_namespaces_for_adoption(node, namespaces_for_sibling)
|
|
596
600
|
end
|
|
597
601
|
@document&.advance_version
|
|
602
|
+
@document&.retain_source_document!(node.document)
|
|
598
603
|
Leptris::XML::FFI.check_status(
|
|
599
604
|
Leptris::XML::FFI.leptris_element_insert_before(c_ptr, node.c_ptr))
|
|
600
605
|
Leptris::XML::Node.invalidate_cross_document!(node, @document)
|
data/lib/leptris/xml/ffi.rb
CHANGED
|
@@ -442,6 +442,16 @@ module Leptris
|
|
|
442
442
|
PLAN_OP_END = 3
|
|
443
443
|
attach_function :leptris_plan_result_build,
|
|
444
444
|
[:leptris_plan, :pointer, :size_t, :pointer], :leptris_plan_result
|
|
445
|
+
# libleptris 1.9.387: the packed-blob lane of the same face
|
|
446
|
+
# (five uint32 per op plus name/value bytes). SOFT attach —
|
|
447
|
+
# engines before 1.9.387 lack the symbol.
|
|
448
|
+
begin
|
|
449
|
+
attach_function :leptris_plan_result_build_packed,
|
|
450
|
+
[:leptris_plan, :pointer, :size_t, :pointer],
|
|
451
|
+
:leptris_plan_result
|
|
452
|
+
rescue ::FFI::NotFoundError
|
|
453
|
+
nil
|
|
454
|
+
end
|
|
445
455
|
|
|
446
456
|
# libleptris 1.9.312 (#1551): serialize a walk result back
|
|
447
457
|
# to XML guided by its plan — the plan supplies element
|
|
@@ -750,6 +760,26 @@ attach_function :leptris_parse_string,
|
|
|
750
760
|
[:leptris_element, :pointer, :pointer, :size_t], :size_t
|
|
751
761
|
attach_function :leptris_element_children,
|
|
752
762
|
[:leptris_element, :pointer, :size_t], :size_t
|
|
763
|
+
# libleptris 1.9.387 engine faces not yet wrapped in Ruby —
|
|
764
|
+
# attached so the export audit stays in lockstep; adoption
|
|
765
|
+
# wraps them (leptris#1855 batch children: (addr, kind) pairs,
|
|
766
|
+
# document order only when cap >= count; #1874 namespace
|
|
767
|
+
# snapshot: one crossing per subtree, rows are seven uint32
|
|
768
|
+
# (depth, name_count, names_off, prefix_off, prefix_len,
|
|
769
|
+
# href_off, href_len) into a caller blob, 0xFFFFFFFF prefix_off
|
|
770
|
+
# = default-ns declaration; #1855 ask-2 name compare: strcmp
|
|
771
|
+
# semantics, non-elements compare as the empty string).
|
|
772
|
+
begin
|
|
773
|
+
attach_function :leptris_element_children_batch,
|
|
774
|
+
[:pointer, :pointer, :pointer, :size_t], :size_t
|
|
775
|
+
attach_function :leptris_node_name_compare,
|
|
776
|
+
[:pointer, :string, :size_t], :int
|
|
777
|
+
attach_function :leptris_element_namespace_declarations_snapshot,
|
|
778
|
+
[:leptris_element, :pointer, :size_t, :pointer,
|
|
779
|
+
:pointer, :size_t, :pointer], :size_t
|
|
780
|
+
rescue ::FFI::NotFoundError
|
|
781
|
+
nil
|
|
782
|
+
end
|
|
753
783
|
attach_function :leptris_node_as_element,
|
|
754
784
|
[:leptris_node_ref], :leptris_element
|
|
755
785
|
attach_function :leptris_element_as_node,
|
|
@@ -1391,6 +1421,18 @@ attach_function :leptris_parse_string,
|
|
|
1391
1421
|
# handles, exactly like the string face.
|
|
1392
1422
|
attach_function :leptris_xsd_compile_file,
|
|
1393
1423
|
[:string, :pointer], :leptris_xsd_schema
|
|
1424
|
+
# libleptris 1.9.387 (leptris#1881): compile a SET of schema
|
|
1425
|
+
# documents (NULL-terminated char* array) into one schema —
|
|
1426
|
+
# imports resolve across the whole set, so substitution-group
|
|
1427
|
+
# members declared in importing documents are visible to the
|
|
1428
|
+
# head's content model. SOFT attach: engines before 1.9.387
|
|
1429
|
+
# lack the symbol.
|
|
1430
|
+
begin
|
|
1431
|
+
attach_function :leptris_xsd_compile_set,
|
|
1432
|
+
[:pointer, :size_t, :pointer], :leptris_xsd_schema
|
|
1433
|
+
rescue ::FFI::NotFoundError
|
|
1434
|
+
nil
|
|
1435
|
+
end
|
|
1394
1436
|
attach_function :leptris_xsd_free,
|
|
1395
1437
|
[:leptris_xsd_schema], :void
|
|
1396
1438
|
attach_function :leptris_xsd_declaration_count,
|
data/lib/leptris/xml/xsd.rb
CHANGED
|
@@ -16,6 +16,12 @@ module Leptris::XML::XSD
|
|
|
16
16
|
Schema.compile_file(path)
|
|
17
17
|
end
|
|
18
18
|
|
|
19
|
+
# Compile a set of schema documents (paths) into one schema
|
|
20
|
+
# (libleptris >= 1.9.387): imports resolve across the whole set.
|
|
21
|
+
def compile_set(paths)
|
|
22
|
+
Schema.compile_set(paths)
|
|
23
|
+
end
|
|
24
|
+
|
|
19
25
|
# Slice 2 (libleptris 1.9.319, #1075): built-in lexical table.
|
|
20
26
|
# +builtin+ takes the "xs:NAME" reference spelling. Returns
|
|
21
27
|
# true/false; an unknown table name raises — a silent false
|
|
@@ -101,6 +107,35 @@ module Leptris::XML::XSD
|
|
|
101
107
|
new(Handle.new(raw))
|
|
102
108
|
end
|
|
103
109
|
|
|
110
|
+
# Compile a SET of schema documents (paths) into one schema:
|
|
111
|
+
# imports resolve across the whole set, so components declared
|
|
112
|
+
# in importing documents (substitution-group members, derived
|
|
113
|
+
# types) are visible to the head's content models — the
|
|
114
|
+
# one-document compile cannot see them (libleptris 1.9.387,
|
|
115
|
+
# leptris#1881). Requires the leptris_xsd_compile_set face.
|
|
116
|
+
def self.compile_set(paths)
|
|
117
|
+
paths = paths.map { |p| p.to_s }
|
|
118
|
+
raise ArgumentError, "schema set needs at least one path" if paths.empty?
|
|
119
|
+
unless Leptris::XML::FFI.respond_to?(:leptris_xsd_compile_set)
|
|
120
|
+
raise Leptris::XML::Error,
|
|
121
|
+
"Schema.compile_set requires libleptris 1.9.387+ " \
|
|
122
|
+
"(the leptris_xsd_compile_set face)"
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
strings = paths.map { |p| ::FFI::MemoryPointer.from_string(p) }
|
|
126
|
+
array = ::FFI::MemoryPointer.new(:pointer, strings.size)
|
|
127
|
+
array.write_array_of_pointer(strings)
|
|
128
|
+
status = ::FFI::MemoryPointer.new(:int)
|
|
129
|
+
raw = Leptris::XML::FFI.leptris_xsd_compile_set(array, strings.size,
|
|
130
|
+
status)
|
|
131
|
+
if raw.null?
|
|
132
|
+
raise Leptris::XML::Error,
|
|
133
|
+
"schema set compile failed (status #{status.read_int}): " \
|
|
134
|
+
"#{paths.join(', ')}"
|
|
135
|
+
end
|
|
136
|
+
new(Handle.new(raw))
|
|
137
|
+
end
|
|
138
|
+
|
|
104
139
|
# Number of top-level schema declarations the compiler
|
|
105
140
|
# recognized (elements, types, groups, ...).
|
|
106
141
|
def declaration_count
|
data/lib/libleptris.so
CHANGED
|
Binary file
|
data/vendor-src/README.md
CHANGED
|
@@ -1,5 +1,140 @@
|
|
|
1
1
|
## [Unreleased]
|
|
2
2
|
|
|
3
|
+
## [1.9.387] - 2026-10-28
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
|
|
7
|
+
- **XSD: schema-set compilation — `leptris_xsd_compile_set(paths, count,
|
|
8
|
+
status)` (leptris#1881).** A head document cannot see
|
|
9
|
+
substitution-group members declared in schemas that IMPORT it —
|
|
10
|
+
nothing loads those from the head alone, so the head's content model
|
|
11
|
+
could never match the member. The set face merges every listed
|
|
12
|
+
document into ONE schema: per-document checks and capture with
|
|
13
|
+
per-unit target namespaces, a shared visited-set so each unit
|
|
14
|
+
captures exactly once in either member order, and extension bases
|
|
15
|
+
resolving once after all units are in. The deferred extension-base
|
|
16
|
+
fold now records the base QName's RESOLVED namespace
|
|
17
|
+
(`ext_base_ns`, prefix resolved against the extension element's
|
|
18
|
+
in-scope declarations) — set compilation extends ACROSS units
|
|
19
|
+
(g:foo1 extends f:foo), which the old same-ns fallback silently
|
|
20
|
+
dropped into an empty model.
|
|
21
|
+
|
|
22
|
+
### Fixed
|
|
23
|
+
|
|
24
|
+
- **XSD: `processContents="skip"` wildcards impose no constraint on
|
|
25
|
+
matched children (leptris#1879).** XSD 1.0 §3.9.4.2 — the engine
|
|
26
|
+
found the child's global declaration and validated its lexical type
|
|
27
|
+
anyway (`<a>1</a>` under a skip wildcard rejected `1` as a bad
|
|
28
|
+
xs:ID; the W3C suite addB130/addB140 expect it valid).
|
|
29
|
+
`XsdCm` records the processContents level (strict/lax/skip) at both
|
|
30
|
+
wildcard capture sites; validate_children computes the strongest
|
|
31
|
+
level among the wildcards admitting each child (the attribute
|
|
32
|
+
wildcard's namespace-grammar matcher, unit-tns aware) and skips
|
|
33
|
+
skip-admitted children entirely. Lax keeps validating declared
|
|
34
|
+
children; wildcard-admitted undeclared names pass under every
|
|
35
|
+
level (the pinned moxml#460 notQName semantics).
|
|
36
|
+
|
|
37
|
+
- **XSD: attributeGroup refs resolve regardless of declaration order
|
|
38
|
+
(leptris#1880).** Top-level declarations form one symbol space, but
|
|
39
|
+
the ref expansion looked up `s->attr_groups` at capture time — a
|
|
40
|
+
group declared AFTER the referencing type was silently dropped
|
|
41
|
+
(W3C suite addB096). Top-level `xs:attributeGroup` definitions are
|
|
42
|
+
now captured in a pre-pass at the head of `xsd_capture_root`;
|
|
43
|
+
the main walk skips already-captured names instead of shadowing.
|
|
44
|
+
|
|
45
|
+
- **XSD: length facets measure the VALUE SPACE (leptris#1882).** On
|
|
46
|
+
list types the length family counts whitespace-separated ITEMS, not
|
|
47
|
+
characters (`xs:IDREFS` + `length=2` accepts `a b`, was rejected as
|
|
48
|
+
3 characters; the minLength/maxLength NMTOKENS rows flip likewise).
|
|
49
|
+
The list detection walks the base chain — a restriction OF a list
|
|
50
|
+
keeps `deriv=RESTRICT`, so `xsd_chain_is_list` resolves through
|
|
51
|
+
local bases and threads a flag into the facet gate. And
|
|
52
|
+
`xs:QName`/`xs:NOTATION` have no length family at all (Part 2
|
|
53
|
+
§4.3.1–4.3.3 applicability): the gate skips those comparisons
|
|
54
|
+
entirely.
|
|
55
|
+
|
|
56
|
+
- **XSD: the builtin lexical table converges on Datatypes Part 2
|
|
57
|
+
(leptris#1883)** — thirteen rows disagreed in both directions.
|
|
58
|
+
Over-strict, now valid: `xs:token` accepts any whitespace shape
|
|
59
|
+
(collapse is total); `xs:ID` collapses before the NCName check; the
|
|
60
|
+
unbounded integer family accepts arbitrary precision with the sign
|
|
61
|
+
class intact; `xs:float`/`xs:double` accept `+INF` (float018).
|
|
62
|
+
Too lenient, now invalid: `xs:long` rejects 2^63 (strtoll ERANGE)
|
|
63
|
+
and `xs:unsignedLong` string-compares against 2^64−1; `xs:date`
|
|
64
|
+
applies calendar-aware day bounds with leap years; `xs:time` bounds
|
|
65
|
+
hour 0–23 (`24:00:00` exact), minute 0–59, second 0–60 with 60
|
|
66
|
+
reserved for `23:59:60`; `xs:duration` rejects a bare `T` (P1Y2MT)
|
|
67
|
+
and fractions on anything but seconds (P200.5Y); `xs:base64Binary`
|
|
68
|
+
rejects a one-byte remainder (`abcde`) and data after padding;
|
|
69
|
+
`xs:QName` rejects the reserved `xmlns` prefix.
|
|
70
|
+
|
|
71
|
+
- **DOM: bulk namespace-declaration snapshot — one crossing per
|
|
72
|
+
subtree (leptris#1874).**
|
|
73
|
+
`leptris_element_namespace_declarations_snapshot(elem, ...)` walks
|
|
74
|
+
the subtree in pre-order once and returns one row per (element
|
|
75
|
+
carrying declarations, declaration) with path depth, ancestor name
|
|
76
|
+
chain, and NUL-terminated prefix/URI lanes — the same rows the
|
|
77
|
+
binding's collect_native_namespaces produces today with one
|
|
78
|
+
`namespace_definitions` FFI round trip per node plus a
|
|
79
|
+
whole-subtree Ruby recursion. attributes_snapshot retry discipline;
|
|
80
|
+
default-ns and xmlns="" undeclaration markers included.
|
|
81
|
+
|
|
82
|
+
- **DOM: address-Integer batch faces (leptris#1855).**
|
|
83
|
+
`leptris_element_children_batch(elem_addr, ...)` returns every
|
|
84
|
+
child's address + node kind in document order from ONE crossing per
|
|
85
|
+
parent (the binding's plan-path hydration measured ~35% FFI
|
|
86
|
+
materialization; `bulk_children` was 12.4% of self-time) — NODE-level
|
|
87
|
+
walk, all five child kinds ride the batch.
|
|
88
|
+
`leptris_node_name_compare(node_addr, name, len)` compares names
|
|
89
|
+
inside the engine with strcmp semantics — the `fast_name` band (4.5%)
|
|
90
|
+
was Ruby constructing Strings that mostly feed `==`.
|
|
91
|
+
|
|
92
|
+
- **XSD: test pin — a type-less element declaration defaults to
|
|
93
|
+
xs:anyType (leptris#1878).** XSD 1.0 §3.3.2: any attributes, lax
|
|
94
|
+
content, never an undeclared-element error. The moxml 0.5.153 repro
|
|
95
|
+
fails on the bundled engine 1.9.381.0 and passes since e5762cdc;
|
|
96
|
+
the moxml Gemfile already allows `~> 1.9.351` (a `bundle update
|
|
97
|
+
leptris` picks the fixed engine).
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
## [1.9.386] - 2026-10-28
|
|
102
|
+
|
|
103
|
+
### Fixed
|
|
104
|
+
|
|
105
|
+
- **DOM: cross-document subtree adoption no longer corrupts attribute
|
|
106
|
+
chains when the destination arena spills into extension blocks
|
|
107
|
+
(leptris#1866).** `copy_subtree_detached` wired the first-attribute
|
|
108
|
+
edge with a truncating `int32_t` cast; once the sized arena was
|
|
109
|
+
exhausted, copy allocations landed in tracked malloc blocks that on
|
|
110
|
+
64-bit hosts sit farther from the arena span than int32 can express
|
|
111
|
+
(~105 TB on macOS). The truncated delta decoded to a wild pointer —
|
|
112
|
+
blank attribute names (`<use ="">`) and a serializer SEGV in
|
|
113
|
+
mutation-heavy SVG-rewrite loops. The edge now goes through
|
|
114
|
+
`leptris_elem_set_first_attribute`, which spills out-of-range deltas
|
|
115
|
+
to the compact overflow table, and `leptris_pool_contains`
|
|
116
|
+
recognizes extension blocks as pool-owned.
|
|
117
|
+
|
|
118
|
+
- **XSD: a local element particle with its own type shadows a
|
|
119
|
+
same-named global element declaration (leptris#1871).** Per Element
|
|
120
|
+
Locally Valid (Complex Type), XSD 1.0 §3.4.4, the matched
|
|
121
|
+
particle's declaration governs its instance element — an inline
|
|
122
|
+
anonymous (or named `type=`) local type was previously assessed as
|
|
123
|
+
the global declaration's type, rejecting valid instances and
|
|
124
|
+
accepting invalid ones. Resolution order is particle-type-first
|
|
125
|
+
(with `xsi:type` substitution applied there); `ref` particles,
|
|
126
|
+
which carry no type, keep the global lookup unchanged.
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
## [1.9.385] - 2026-10-27
|
|
131
|
+
|
|
132
|
+
### Fixed
|
|
133
|
+
|
|
134
|
+
- the cross-doc adopt keeps its attribute chain; @id/.. names the owner
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
|
|
3
138
|
## [1.9.384] - 2026-10-27
|
|
4
139
|
|
|
5
140
|
### Fixed
|
|
@@ -293,6 +293,22 @@ LEPTRIS_API LeptrisPlanResult leptris_plan_result_build(
|
|
|
293
293
|
LeptrisPlan plan, const leptris_plan_row_op* ops, size_t count,
|
|
294
294
|
LeptrisStatus* status);
|
|
295
295
|
|
|
296
|
+
/* leptris#1854: build a result from a PACKED op blob — the whole
|
|
297
|
+
* emission crosses in one call and no per-op host struct writes
|
|
298
|
+
* (the #408 address-Integer law applied to the build side; the
|
|
299
|
+
* binding's emission loop was 37% FFI::Pointer#+). Same program
|
|
300
|
+
* semantics as leptris_plan_result_build. Blob layout, per op:
|
|
301
|
+
* five native-byte-order uint32_t (kind, plan_index, row_index,
|
|
302
|
+
* name_len, value_len), then name_len name bytes PLUS a NUL
|
|
303
|
+
* terminator (the ATTR path dup's names NUL-terminated), then
|
|
304
|
+
* value_len raw value bytes; a zero length omits the bytes and
|
|
305
|
+
* reads as NULL. Bounds-checked: a truncated blob fails
|
|
306
|
+
* INVALID_ARG. Strings are copied; the blob is only read during
|
|
307
|
+
* the call. Memory: free with leptris_plan_result_free. */
|
|
308
|
+
LEPTRIS_API LeptrisPlanResult leptris_plan_result_build_packed(
|
|
309
|
+
LeptrisPlan plan, const void* blob, size_t blob_len,
|
|
310
|
+
LeptrisStatus* status);
|
|
311
|
+
|
|
296
312
|
/* #1551: serialize a walk result back to XML, guided by its plan.
|
|
297
313
|
* The plan supplies element wrappers (row wire_name + ns_prefix)
|
|
298
314
|
* and the result supplies content; children emit in document order
|
|
@@ -913,6 +913,21 @@ LEPTRIS_API void leptris_rng_free(LeptrisRelaxNG rng);
|
|
|
913
913
|
LEPTRIS_API LeptrisXsdSchema leptris_xsd_compile_file(const char* path,
|
|
914
914
|
LeptrisStatus* status);
|
|
915
915
|
|
|
916
|
+
/* leptris#1881: compile a schema SET — every listed document's
|
|
917
|
+
* declarations merge into ONE schema, in order, with the shared
|
|
918
|
+
* visited-set closing include/import cycles across members. A head
|
|
919
|
+
* document cannot see substitution-group members declared in
|
|
920
|
+
* schemas that import it (nothing loads those from the head alone);
|
|
921
|
+
* a set compilation does. Per-unit target namespaces are honored
|
|
922
|
+
* during each document's capture; the FIRST document's
|
|
923
|
+
* targetNamespace remains the schema's. Relative schemaLocations
|
|
924
|
+
* resolve against each document's own directory. NULL paths or
|
|
925
|
+
* count 0 return NULL; an unreadable or malformed member yields an
|
|
926
|
+
* error-carrying handle like leptris_xsd_compile_file.
|
|
927
|
+
* (Memory: free with leptris_xsd_free.) */
|
|
928
|
+
LEPTRIS_API LeptrisXsdSchema leptris_xsd_compile_set(
|
|
929
|
+
const char* const* paths, size_t count, LeptrisStatus* status);
|
|
930
|
+
|
|
916
931
|
LEPTRIS_API LeptrisXsdSchema leptris_xsd_compile(const char* xsd_text,
|
|
917
932
|
size_t len,
|
|
918
933
|
LeptrisStatus* status);
|
|
@@ -2019,6 +2034,68 @@ LEPTRIS_API size_t leptris_element_attributes_snapshot(
|
|
|
2019
2034
|
char* ns_prefixes, size_t prefixes_cap, size_t* prefix_offsets,
|
|
2020
2035
|
char* ns_uris, size_t uris_cap, size_t* uri_offsets);
|
|
2021
2036
|
|
|
2037
|
+
/* leptris#1855 (ask 1): batched child materialization under the
|
|
2038
|
+
* #408 address-Integer law — one crossing per PARENT returns every
|
|
2039
|
+
* child's address and node kind, replacing the binding's per-child
|
|
2040
|
+
* FFI walk. Returns the element's child count; the (addr, kind)
|
|
2041
|
+
* pairs are written in document order only when cap >= count —
|
|
2042
|
+
* otherwise nothing is written and the count comes back so the
|
|
2043
|
+
* caller sizes and retries. A 0 address reads as "no children". */
|
|
2044
|
+
LEPTRIS_API size_t leptris_element_children_batch(
|
|
2045
|
+
uintptr_t elem_addr,
|
|
2046
|
+
uintptr_t* child_addrs, uint8_t* child_kinds, size_t cap);
|
|
2047
|
+
|
|
2048
|
+
/* leptris#1855 (ask 2): engine-side name comparison — strcmp
|
|
2049
|
+
* semantics (-1/0/1) between the node's name and the caller's
|
|
2050
|
+
* (name, len) lens, with no String constructed on either side.
|
|
2051
|
+
* Element nodes compare their name; non-element nodes compare as
|
|
2052
|
+
* the empty string. A 0 address is an error lane: -2. */
|
|
2053
|
+
LEPTRIS_API int leptris_node_name_compare(uintptr_t node_addr,
|
|
2054
|
+
const char* name, size_t len);
|
|
2055
|
+
|
|
2056
|
+
/* leptris#1874: bulk namespace-declaration snapshot — the whole
|
|
2057
|
+
* subtree's declared namespaces in ONE crossing (the binding's
|
|
2058
|
+
* collect_native_namespaces walk paid one namespace_definitions FFI
|
|
2059
|
+
* round trip per node). Pre-order DFS under `elem`; one row per
|
|
2060
|
+
* (element carrying declarations, declaration) in document order,
|
|
2061
|
+
* exactly the rows the binding's per-node namespace_definitions
|
|
2062
|
+
* union produces.
|
|
2063
|
+
*
|
|
2064
|
+
* Each row carries the element's path depth (0 = `elem` itself, 1 =
|
|
2065
|
+
* a child, ...) and blob offsets: the path names (depth NUL-
|
|
2066
|
+
* terminated components in ancestor-first order, `names_off`), the
|
|
2067
|
+
* prefix (`prefix_off`/`prefix_len`, NUL-terminated in the blob;
|
|
2068
|
+
* 0xFFFFFFFFu off = a default-namespace declaration, no prefix),
|
|
2069
|
+
* and the URI (`href_off`/`href_len`, NUL-terminated in the blob;
|
|
2070
|
+
* len 0 = an xmlns="" undeclaration). Legacy xmlns:* attributes
|
|
2071
|
+
* added through the attribute API are included (same union as
|
|
2072
|
+
* leptris_element_namespace_count).
|
|
2073
|
+
*
|
|
2074
|
+
* Contract (the attributes_snapshot discipline): with `rows == NULL`
|
|
2075
|
+
* returns 0 and writes the required row count to *out_row_count and
|
|
2076
|
+
* the required blob bytes to *blob_used (either pointer may be
|
|
2077
|
+
* NULL); with non-NULL buffers smaller than required returns
|
|
2078
|
+
* (size_t)-1 and writes nothing; on success returns 0 and writes
|
|
2079
|
+
* the used blob bytes to *blob_used when non-NULL.
|
|
2080
|
+
*
|
|
2081
|
+
* Memory: the snapshot copies strings into the caller's blob;
|
|
2082
|
+
* nothing borrows the document.
|
|
2083
|
+
*/
|
|
2084
|
+
typedef struct {
|
|
2085
|
+
uint32_t depth; /* 0 = elem itself, 1 = child, ... */
|
|
2086
|
+
uint32_t name_count; /* == depth */
|
|
2087
|
+
uint32_t names_off; /* blob offset of the first path name */
|
|
2088
|
+
uint32_t prefix_off; /* 0xFFFFFFFFu = default-ns declaration */
|
|
2089
|
+
uint32_t prefix_len;
|
|
2090
|
+
uint32_t href_off;
|
|
2091
|
+
uint32_t href_len;
|
|
2092
|
+
} leptris_ns_decl_row;
|
|
2093
|
+
|
|
2094
|
+
LEPTRIS_API size_t leptris_element_namespace_declarations_snapshot(
|
|
2095
|
+
LeptrisElement elem,
|
|
2096
|
+
leptris_ns_decl_row* rows, size_t rows_cap, size_t* out_row_count,
|
|
2097
|
+
char* blob, size_t blob_cap, size_t* blob_used);
|
|
2098
|
+
|
|
2022
2099
|
/**
|
|
2023
2100
|
* Get name of attribute by index
|
|
2024
2101
|
*
|
|
@@ -2126,6 +2126,88 @@ LEPTRIS_API LeptrisPlanResult leptris_plan_result_build(
|
|
|
2126
2126
|
return (LeptrisPlanResult)r;
|
|
2127
2127
|
}
|
|
2128
2128
|
|
|
2129
|
+
/* leptris#1854: decode the packed blob into borrowed op rows
|
|
2130
|
+
* (name/value point INTO the blob — the windowed builder dup's
|
|
2131
|
+
* every string) and hand the IDENTICAL op stream to the IDENTICAL
|
|
2132
|
+
* builder, so byte parity with leptris_plan_result_build holds by
|
|
2133
|
+
* construction. */
|
|
2134
|
+
LEPTRIS_API LeptrisPlanResult leptris_plan_result_build_packed(
|
|
2135
|
+
LeptrisPlan plan, const void* blob, size_t blob_len,
|
|
2136
|
+
LeptrisStatus* status) {
|
|
2137
|
+
if (status) *status = LEPTRIS_OK;
|
|
2138
|
+
if (!plan || !blob || blob_len < 5 * sizeof(uint32_t)) {
|
|
2139
|
+
if (status) *status = LEPTRIS_ERROR_INVALID_ARG;
|
|
2140
|
+
return NULL;
|
|
2141
|
+
}
|
|
2142
|
+
/* Two passes: count ops to size the row array, then decode with
|
|
2143
|
+
* a bounds check on every field. */
|
|
2144
|
+
const uint8_t* p = (const uint8_t*)blob;
|
|
2145
|
+
const uint8_t* const end = p + blob_len;
|
|
2146
|
+
size_t op_count = 0;
|
|
2147
|
+
for (const uint8_t* q = p; q < end;) {
|
|
2148
|
+
if ((size_t)(end - q) < 5 * sizeof(uint32_t)) {
|
|
2149
|
+
if (status) *status = LEPTRIS_ERROR_INVALID_ARG;
|
|
2150
|
+
return NULL;
|
|
2151
|
+
}
|
|
2152
|
+
uint32_t nl, vl;
|
|
2153
|
+
memcpy(&nl, q + 12, sizeof(nl));
|
|
2154
|
+
memcpy(&vl, q + 16, sizeof(vl));
|
|
2155
|
+
q += 5 * sizeof(uint32_t);
|
|
2156
|
+
/* name bytes + their NUL (none when nl == 0) + value bytes
|
|
2157
|
+
* <= remaining: size_t math, no overflow (lens < 4G) */
|
|
2158
|
+
size_t name_span = nl ? (size_t)nl + 1 : 0;
|
|
2159
|
+
if ((size_t)(end - q) < name_span + (size_t)vl) {
|
|
2160
|
+
if (status) *status = LEPTRIS_ERROR_INVALID_ARG;
|
|
2161
|
+
return NULL;
|
|
2162
|
+
}
|
|
2163
|
+
q += name_span + (size_t)vl;
|
|
2164
|
+
op_count++;
|
|
2165
|
+
}
|
|
2166
|
+
leptris_plan_row_op* ops =
|
|
2167
|
+
(leptris_plan_row_op*)malloc(op_count * sizeof(*ops));
|
|
2168
|
+
if (!ops) {
|
|
2169
|
+
if (status) *status = LEPTRIS_ERROR_MEMORY;
|
|
2170
|
+
return NULL;
|
|
2171
|
+
}
|
|
2172
|
+
const uint8_t* q = p;
|
|
2173
|
+
for (size_t i = 0; i < op_count; i++) {
|
|
2174
|
+
uint32_t kind, pi, ri, nl, vl;
|
|
2175
|
+
memcpy(&kind, q, sizeof(kind));
|
|
2176
|
+
memcpy(&pi, q + 4, sizeof(pi));
|
|
2177
|
+
memcpy(&ri, q + 8, sizeof(ri));
|
|
2178
|
+
memcpy(&nl, q + 12, sizeof(nl));
|
|
2179
|
+
memcpy(&vl, q + 16, sizeof(vl));
|
|
2180
|
+
q += 5 * sizeof(uint32_t);
|
|
2181
|
+
ops[i].kind = (LeptrisPlanRowOpKind)kind;
|
|
2182
|
+
ops[i].plan_index = pi;
|
|
2183
|
+
ops[i].row_index = ri;
|
|
2184
|
+
ops[i].name = nl ? (const char*)q : NULL; /* NUL at q[nl] */
|
|
2185
|
+
q += nl ? (size_t)nl + 1 : 0;
|
|
2186
|
+
ops[i].value = vl ? (const char*)q : NULL;
|
|
2187
|
+
ops[i].value_len = vl;
|
|
2188
|
+
q += vl;
|
|
2189
|
+
}
|
|
2190
|
+
/* Same arena/window discipline as the struct face (#1677). */
|
|
2191
|
+
struct dp_arena* arena = dp_arena_new(DP_ARENA_CHUNK);
|
|
2192
|
+
if (!arena) {
|
|
2193
|
+
free(ops);
|
|
2194
|
+
if (status) *status = LEPTRIS_ERROR_MEMORY;
|
|
2195
|
+
return NULL;
|
|
2196
|
+
}
|
|
2197
|
+
struct dp_arena* saved_arena = dp_cur_arena;
|
|
2198
|
+
dp_cur_arena = arena;
|
|
2199
|
+
struct leptris_plan_result* r =
|
|
2200
|
+
plan_result_build_windowed(plan, ops, op_count, status, arena);
|
|
2201
|
+
dp_cur_arena = saved_arena;
|
|
2202
|
+
free(ops);
|
|
2203
|
+
if (!r) {
|
|
2204
|
+
dp_arena_free_all(arena);
|
|
2205
|
+
return NULL;
|
|
2206
|
+
}
|
|
2207
|
+
if (r->arena == arena) r->arena_owner = 1;
|
|
2208
|
+
return (LeptrisPlanResult)r;
|
|
2209
|
+
}
|
|
2210
|
+
|
|
2129
2211
|
LEPTRIS_API void leptris_plan_result_free(LeptrisPlanResult result) {
|
|
2130
2212
|
dp_result_free_rec((struct leptris_plan_result*)result);
|
|
2131
2213
|
}
|
|
@@ -2475,6 +2475,16 @@ static LeptrisElement copy_subtree_detached(LeptrisElement source,
|
|
|
2475
2475
|
v ? leptris_sv_from_ptr(v, sav_.length)
|
|
2476
2476
|
: leptris_sv_from_cstr("");
|
|
2477
2477
|
leptris_element_add_attribute(copy, nv, vv, pool);
|
|
2478
|
+
/* leptris#1866: add_attribute appends via the element's
|
|
2479
|
+
* cached last_attribute — the LOCAL tail below is now
|
|
2480
|
+
* stale. A following direct placement linked onto the
|
|
2481
|
+
* stale tail and orphaned every attribute between the
|
|
2482
|
+
* two wirings (a `<use xlink:href id>` copy lost the id:
|
|
2483
|
+
* invisible to the @-axis XPath, dropped at serialize,
|
|
2484
|
+
* and the attr_count-vs-chain mismatch fed the O(1)
|
|
2485
|
+
* index an empty name — `<use ="">`). Resync from the
|
|
2486
|
+
* element's cached tail, which add_attribute maintains. */
|
|
2487
|
+
last_attr = leptris_elem_last_attribute(copy);
|
|
2478
2488
|
continue;
|
|
2479
2489
|
}
|
|
2480
2490
|
/* Direct placement: the source attr is already decoded and
|
|
@@ -2505,10 +2515,21 @@ static LeptrisElement copy_subtree_detached(LeptrisElement source,
|
|
|
2505
2515
|
if (copy->first_attribute_off != 0) {
|
|
2506
2516
|
leptris_attr_set_next(last_attr, attr);
|
|
2507
2517
|
} else {
|
|
2508
|
-
|
|
2509
|
-
|
|
2518
|
+
/* The destination pool may hand this allocation from an
|
|
2519
|
+
* arena EXTENSION block — malloc'd far from the arena
|
|
2520
|
+
* span (leptris#1866: macOS puts it ~105 TB away, beyond
|
|
2521
|
+
* int32). The raw cast truncated that delta and decode
|
|
2522
|
+
* produced a wild pointer (blank names, serializer SEGV);
|
|
2523
|
+
* the setter spills out-of-range deltas to the overflow
|
|
2524
|
+
* table. */
|
|
2525
|
+
leptris_elem_set_first_attribute(copy, attr);
|
|
2510
2526
|
}
|
|
2511
2527
|
last_attr = attr;
|
|
2528
|
+
/* leptris#1866: keep the element's cached tail in step with
|
|
2529
|
+
* the local chain tail — a following add_attribute appends
|
|
2530
|
+
* via the cached tail, and a stale one orphans everything
|
|
2531
|
+
* wired since. */
|
|
2532
|
+
leptris_elem_set_last_attribute(copy, attr);
|
|
2512
2533
|
/* add_attribute's increment — attr_count is what the count
|
|
2513
2534
|
* accessor and the O(1) index fast path read (uint8 wrap
|
|
2514
2535
|
* at 255 latches the walk, same convention). */
|