leptris 1.9.418.1-ppc64le-linux → 1.9.420.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/ext/leptris/native/native.c +38 -0
- data/lib/leptris/version.rb +1 -1
- data/lib/leptris/xml/native.so +0 -0
- data/lib/leptris/xml/native_3_3.so +0 -0
- data/lib/libleptris.so +0 -0
- data/vendor-src/README.md +1 -1
- data/vendor-src/libleptris/CHANGELOG.md +26 -0
- data/vendor-src/libleptris/CMakeLists.txt +1 -1
- data/vendor-src/libleptris/TODO.max-perf/15-c-face-vs-pugixml.md +106 -0
- data/vendor-src/libleptris/src/leptris/xsd/compile.c +81 -31
- data/vendor-src/libleptris/src/leptris/xsd/validate.c +193 -23
- data/vendor-src/libleptris/src/leptris/xsd/xsd_internal.h +3 -0
- data/vendor-src/libleptris/test/xsd/libxml2-cases/attZ014-set/attZ014a-11.xsd +16 -0
- data/vendor-src/libleptris/test/xsd/libxml2-cases/attZ014-set/attZ014a.xml +1 -0
- data/vendor-src/libleptris/test/xsd/libxml2-cases/attZ014-set/attZ014a.xsd +16 -0
- data/vendor-src/libleptris/test/xsd/libxml2-cases/attZ014-set/attZ014b.xml +1 -0
- data/vendor-src/libleptris/test/xsd/libxml2-cases/attZ014-set/attZ014b.xsd +16 -0
- data/vendor-src/libleptris/test/xsd/libxml2-cases/attgC-redefine/attgC035.imp +9 -0
- data/vendor-src/libleptris/test/xsd/libxml2-cases/attgC-redefine/attgC035.red +9 -0
- data/vendor-src/libleptris/test/xsd/libxml2-cases/attgC-redefine/attgC035.xml +1 -0
- data/vendor-src/libleptris/test/xsd/libxml2-cases/attgC-redefine/attgC035.xsd +21 -0
- data/vendor-src/libleptris/test/xsd/libxml2-cases/attgC-redefine/attgC036.inc +9 -0
- data/vendor-src/libleptris/test/xsd/libxml2-cases/attgC-redefine/attgC036.red +8 -0
- data/vendor-src/libleptris/test/xsd/libxml2-cases/attgC-redefine/attgC036.xml +1 -0
- data/vendor-src/libleptris/test/xsd/libxml2-cases/attgC-redefine/attgC036.xsd +14 -0
- data/vendor-src/libleptris/test/xsd/libxml2-cases/attgC-redefine/attgC037.xml +1 -0
- data/vendor-src/libleptris/test/xsd/libxml2-cases/attgC-redefine/attgC037.xsd +17 -0
- data/vendor-src/libleptris/test/xsd/libxml2-cases/attgC-redefine/attgC038.xml +1 -0
- data/vendor-src/libleptris/test/xsd/libxml2-cases/attgC-redefine/attgC038.xsd +19 -0
- data/vendor-src/libleptris/test/xsd/libxml2-cases/attgC-redefine/attgC038v1.red +7 -0
- data/vendor-src/libleptris/test/xsd/libxml2-cases/attgC-redefine/attgC038v2.red +8 -0
- data/vendor-src/libleptris/test/xsd/libxml2-cases/rs74-union-fixed/stE065.xml +1 -0
- data/vendor-src/libleptris/test/xsd/libxml2-cases/rs74-union-fixed/stE065.xsd +15 -0
- data/vendor-src/libleptris/test/xsd/libxml2-cases/rs74-union-fixed/stE066.xml +1 -0
- data/vendor-src/libleptris/test/xsd/libxml2-cases/rs74-union-fixed/stE066.xsd +7 -0
- data/vendor-src/libleptris/test/xsd/libxml2-cases/rs74-union-fixed/stE096.xml +1 -0
- data/vendor-src/libleptris/test/xsd/libxml2-cases/rs74-union-fixed/stE096.xsd +14 -0
- data/vendor-src/libleptris/test/xsd/libxml2-cases/wildZ013-set/test328873.xsd +122 -0
- data/vendor-src/libleptris/test/xsd/libxml2-cases/wildZ013-set/test328873_imp.xsd +15 -0
- data/vendor-src/libleptris/test/xsd/libxml2-cases/wildZ013-set/test328873b.xml +3 -0
- data/vendor-src/libleptris/test/xsd/libxml2-cases/wildZ013-set/test328873c.xml +3 -0
- data/vendor-src/libleptris/test/xsd/test_xsd_compile.cpp +169 -0
- data/vendor-src/libleptris/vcpkg.json +1 -1
- metadata +31 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: bfd921282d6b7f1c27ed608e90ecd9e827f597ff5ee17453139d71e59f197a94
|
|
4
|
+
data.tar.gz: db3496c29df65697e748d121535355399ca2b1732022ebd03493461c1704de11
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: a70bb31f1cfbab7a36a96626e4b3ec578dbfd4e3b9a12d35d487a094356c445fdb2475c8e6d8122eb79bc2bf38f7b6b4856fc406465ea694a086b570a5bc61a9
|
|
7
|
+
data.tar.gz: 36acd6a66bcd6dc5d78115ef6461d53cddf627d06374f1631c5246affd2cd4b0e96e032d9bace7784a51d511202edb2574306fc24504219652689479c1da1cac
|
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.420"
|
|
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
|
@@ -1681,6 +1681,42 @@ static VALUE nn_set_root(VALUE klass, VALUE document, VALUE element)
|
|
|
1681
1681
|
return element;
|
|
1682
1682
|
}
|
|
1683
1683
|
|
|
1684
|
+
/* TODO.perf/39(a) (leptris-ruby#622 ask 1): hot readers
|
|
1685
|
+
* parameterized by subclass. Consumers whose wrapper classes SUBCLASS
|
|
1686
|
+
* NativeNode (klass injection, #340) shadow these C methods with
|
|
1687
|
+
* included Ruby modules — every shadowed read pays an
|
|
1688
|
+
* UnboundMethod#bind_call frame (~the whole difference between the
|
|
1689
|
+
* facade's ~331ns reads and the binding's ~160ns seam). This entry
|
|
1690
|
+
* defines the SAME C functions (same bodies, same walk-memos, same
|
|
1691
|
+
* TypedData type) DIRECTLY on the consumer's subclass, so lookup
|
|
1692
|
+
* answers in C with no module shadow in the chain.
|
|
1693
|
+
*
|
|
1694
|
+
* The consumer decides which classes get the hot surface: a
|
|
1695
|
+
* per-document klass map (#340) can route entity-bearing documents
|
|
1696
|
+
* to contract classes and clean documents to hot classes, keeping
|
|
1697
|
+
* any Ruby-side value post-processing exactly where it is still
|
|
1698
|
+
* needed.
|
|
1699
|
+
*
|
|
1700
|
+
* Re-defining a method that already exists on the class replaces it
|
|
1701
|
+
* (Ruby semantics); calling twice is harmless. The class must be the
|
|
1702
|
+
* consumer's NativeNode subclass or NativeNode itself — a non-
|
|
1703
|
+
* TypedData class would crash on first read, so the type is checked
|
|
1704
|
+
* here against the registry. */
|
|
1705
|
+
static VALUE nf_define_hot_readers(VALUE self, VALUE klass)
|
|
1706
|
+
{
|
|
1707
|
+
(void)self;
|
|
1708
|
+
if (!RB_TYPE_P(klass, T_CLASS))
|
|
1709
|
+
rb_raise(rb_eTypeError, "expected a class");
|
|
1710
|
+
if (klass != c_native_node &&
|
|
1711
|
+
rb_class_inherited_p(klass, c_native_node) != Qtrue)
|
|
1712
|
+
rb_raise(rb_eTypeError,
|
|
1713
|
+
"the class must subclass Leptris::XML::NativeNode");
|
|
1714
|
+
rb_define_method(klass, "name", nn_name, 0);
|
|
1715
|
+
rb_define_method(klass, "content", nn_content, 0);
|
|
1716
|
+
rb_define_method(klass, "attribute", nn_attribute, 1);
|
|
1717
|
+
return Qtrue;
|
|
1718
|
+
}
|
|
1719
|
+
|
|
1684
1720
|
/* NativeNode surface completion (TODO.perf/05): bulk attribute
|
|
1685
1721
|
* hash (name => value, one C walk, no per-attr Ruby frames) and
|
|
1686
1722
|
* the position readers. */
|
|
@@ -4350,6 +4386,8 @@ static void leptris_native_setup(void)
|
|
|
4350
4386
|
nf_append_binding_child, 3);
|
|
4351
4387
|
rb_define_module_function(m_native, "set_binding_attribute",
|
|
4352
4388
|
nf_set_binding_attribute, 4);
|
|
4389
|
+
rb_define_module_function(m_native, "define_hot_readers",
|
|
4390
|
+
nf_define_hot_readers, 1);
|
|
4353
4391
|
rb_define_module_function(m_native, "insert_binding_child",
|
|
4354
4392
|
nf_insert_binding_child, 4);
|
|
4355
4393
|
rb_define_module_function(m_native, "at_xpath_first",
|
data/lib/leptris/version.rb
CHANGED
data/lib/leptris/xml/native.so
CHANGED
|
Binary file
|
|
Binary file
|
data/lib/libleptris.so
CHANGED
|
Binary file
|
data/vendor-src/README.md
CHANGED
|
@@ -1,5 +1,31 @@
|
|
|
1
1
|
## [Unreleased]
|
|
2
2
|
|
|
3
|
+
## [1.9.420] - 2026-11-13
|
|
4
|
+
### Fixed
|
|
5
|
+
|
|
6
|
+
- **xsd: the element fixed constraint compares in the value space** (leptris-rs#74, W3C stE065/066/096). Union-typed elements' final equality ran strcmp-level lexicals — the accepting member's whitespace semantics never applied. The token family and normalizedString now equate collapse-equal spellings (`fixed="abcd edfgh "` admits `abcd edfgh`, `fixed="ENU "` admits `ENU`), and LIST-typed elements itemize both lexicals on whitespace runs before per-item union comparison (a list of gYear/gMonth/gDay equates single- and double-space item separation). The collapse normalization is factored and shared with the attribute fixed path; preserve-types (xs:string) still compare exactly.
|
|
7
|
+
|
|
8
|
+
- **xsd: the §4.2.2 redefine hook splices the original attributeGroup** (the engine root of lutaml/moxml#545, W3C attgC035-038). An attributeGroup-redefine body that refs its OWN name means the ORIGINAL group — the name-keyed slot held the redefining body itself, so expanding it looped through the depth guard and the original's members never joined the composition (strict admission false-reports "not declared (strict)"). The rows capture threads the def being expanded; a self-ref resolves to the shadowed def. attgC038's chained double-redefine composes through the same path.
|
|
9
|
+
|
|
10
|
+
## Tests
|
|
11
|
+
|
|
12
|
+
- stE065/066/096 + attgC035/036/037/038 vendored from w3c/xsdtests as pins; the union fixed change is falsified by a no-op-collapse mutation (fails exactly the value-space pin, passes the teeth pin — a genuinely different value still fails). Full ctest 2269/2269.
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
## [1.9.419] - 2026-11-12
|
|
17
|
+
### Fixed
|
|
18
|
+
|
|
19
|
+
- **xsd: wildcards ride attributeGroup refs across units** (leptris-rs#72, W3C wildZ013b/c). The #1964 deep capture left two holes: the extension branch still scanned shallow, so a wildcard living inside a REFERENCED attributeGroup under the extension element was invisible (strict admission rejected attributes the composed union admits); and a wildcard found in a group from ANOTHER unit (a namespace-less import) resolved against the CONSUMING schema's targetNamespace instead of its declaring unit's — `##other` flipped meaning across the boundary and the union collapsed to admitting only half the instance's attribute namespaces. The capture now follows refs in the extension branch and carries the declaring group's own unit tns (absent for the import).
|
|
20
|
+
|
|
21
|
+
- **xsd: a bare `<xs:anyAttribute />` is a live ##any wildcard** (leptris-rs#73, W3C attZ014a/b). Absent @namespace means ##any (the moxml#460 posture) — the capture mapped it to NULL, an invisible wildcard, so wildcard-admitted attributes fell to strict rejection for the wrong reason. With admission live, XSD 1.0's one-ID-per-element rule fires: an element assessed with more than one ID-typed attribute (declared rows AND wildcard-admitted names whose global declaration is ID-derived) is invalid; XSD 1.1 lifted the constraint and reads the same shape valid. The rule is version-gated (`xsd_schema_version_11`).
|
|
22
|
+
|
|
23
|
+
## Tests
|
|
24
|
+
|
|
25
|
+
- wildZ013b/c + attZ014a/b fixtures vendored from w3c/xsdtests (msData/wildcards, msData/attribute); attZ014a also pinned under `@version="1.1"` (valid). Falsified: an always-false ID test failed exactly the two 1.0 pins and passed the 1.1 reading.
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
|
|
3
29
|
## [1.9.418] - 2026-11-12
|
|
4
30
|
## Fixed
|
|
5
31
|
|
|
@@ -405,3 +405,109 @@ the loop-body surgery slice — it inherits the region-wrap probe
|
|
|
405
405
|
|
|
406
406
|
RANKED CUT #1 therefore moves to the loop-surgery slice with the
|
|
407
407
|
scan-merge as its first step. Nothing ships from the kernel alone.
|
|
408
|
+
|
|
409
|
+
## Track D addendum 9 — the inline text-scan cut REJECTED by CI (PR #2018, closed)
|
|
410
|
+
|
|
411
|
+
The ranked-#1 cut (inline 32-byte scalar prefix scan for short text
|
|
412
|
+
runs, kernel for the remainder) went through the full gate: round 1
|
|
413
|
+
caught the restart-at-run-start flaw (the kernel rescanned the
|
|
414
|
+
scalar prefix — every leg regressed), round 2 fixed that (kernel
|
|
415
|
+
from the prefix end) and STILL regressed on all three legs vs the
|
|
416
|
+
A3d baselines (macos -39-46%, ubuntu-arm -26-27%, ubuntu-x86 -37-45%
|
|
417
|
+
vs its AVX2 run). Cause: the bench-class run lengths (33-64B) sit
|
|
418
|
+
exactly where the cut loses — 32 branchy scalar bytes plus a kernel
|
|
419
|
+
pass over a 1-32B remainder exceeds the kernel alone, and runs
|
|
420
|
+
<=32B are too few in these shapes to carry it.
|
|
421
|
+
|
|
422
|
+
THE MISSING DATUM: the run-length distribution of the real fixtures
|
|
423
|
+
and the scoreboard corpus. The next session's opening instrument =
|
|
424
|
+
a run-length histogram (one counting pass per fixture, reusable),
|
|
425
|
+
which decides whether the scan-merge has a paying window at all —
|
|
426
|
+
and at what threshold. Until then the kernel stands unchanged; the
|
|
427
|
+
PR closed with the full data (three legs, two rounds, CI artifacts).
|
|
428
|
+
|
|
429
|
+
## Track D addendum 10 — the scan-merge slice CLOSED (PR #2020, closed unmerged)
|
|
430
|
+
|
|
431
|
+
The 64-byte window (histogram-driven) went through the full gate and
|
|
432
|
+
regressed WORSE than the 32-byte attempt: macos -45/-39/-46%, arm
|
|
433
|
+
-32/-32/-29%, x86 -53/-53/-50% (vs the AVX2 run) — even for runs
|
|
434
|
+
fully inside the window. THE VERDICT: the branchy scalar loop
|
|
435
|
+
(~3-5 ops/byte) loses to the SIMD kernel (~1 op/byte) at every run
|
|
436
|
+
length on every platform; the kernel's per-run fixed cost is small
|
|
437
|
+
next to its per-byte advantage. The scan-merge slice is structurally
|
|
438
|
+
dead — two windows, two full CI runs, one consistent verdict.
|
|
439
|
+
|
|
440
|
+
THE TEXT-ROW LEVER REDIRECTS to the kernel's own per-byte
|
|
441
|
+
throughput: the text_scan_run NEON kernel processes ~1 byte/op
|
|
442
|
+
while pugixml's scanner sustains 2.1-2.7 GB/s — the Track A scanner
|
|
443
|
+
class. The next concrete candidate: process 32B chunks (NEON
|
|
444
|
+
registers ×2) per iteration instead of 16B, halving the reduction
|
|
445
|
+
frequency; and the parser's text-branch call overhead already
|
|
446
|
+
measures separably (the scalar wrapper at 2.2%).
|
|
447
|
+
|
|
448
|
+
The histogram instrument (probe/run-histogram) remains in the repo
|
|
449
|
+
history on probe/run-histogram; both rejected cut branches carry
|
|
450
|
+
their full data.
|
|
451
|
+
|
|
452
|
+
## Track D addendum 11 — the 32B kernel window measured-NEGATIVE (PR #2022, closed unmerged)
|
|
453
|
+
|
|
454
|
+
The addendum-10 candidate (NEON text_scan_run at 32B windows — two
|
|
455
|
+
uint8x16 registers, term masks OR-combined before ONE vmaxvq, halving
|
|
456
|
+
the horizontal-reduction frequency) passed the full CI suite but the
|
|
457
|
+
CI split was poisoned by fleet variance (x86 +31% on code the diff
|
|
458
|
+
does not touch). The decisive instrument was a same-machine
|
|
459
|
+
interleaved A/B on Apple silicon (Release+LTO, 3 rounds ABBA, pugixml
|
|
460
|
+
side flat ~2370/1800 MB/s across rounds — machine verified stable):
|
|
461
|
+
|
|
462
|
+
| shape | 16B (main) | 32B (PR) | delta |
|
|
463
|
+
|-------|-----------|----------|-------|
|
|
464
|
+
| 1.5MB | ~1341 MB/s | ~1144 MB/s | −15% |
|
|
465
|
+
| 5MB | ~1332 MB/s | ~1135 MB/s | −15% |
|
|
466
|
+
| 13MB | ~1029 MB/s | ~913 MB/s | −11% |
|
|
467
|
+
|
|
468
|
+
THE INVARIANT: horizontal-reduction frequency is NOT the binding term
|
|
469
|
+
in text_scan_run on M-series. The accumulator chain is — two extra
|
|
470
|
+
cmpeq+orr per register per window lengthen the dependency chain and
|
|
471
|
+
raise live-register pressure (9+ Q registers) without removing any
|
|
472
|
+
real per-byte work. Widening the window cannot pay; the kernel's
|
|
473
|
+
per-byte op count is already minimal for its contract (first-term
|
|
474
|
+
scan + f1/f2 seen-flags).
|
|
475
|
+
|
|
476
|
+
TEXT-ROW STATUS after three measured closures (scan-merge 32B,
|
|
477
|
+
scan-merge 64B, kernel-width 32B): the kernel is at its floor. The
|
|
478
|
+
remaining text-row gap to pugixml is the parser's text-branch call
|
|
479
|
+
overhead (the scalar wrapper measures separably at 2.2% — the only
|
|
480
|
+
untried lever in this class), not the kernel.
|
|
481
|
+
|
|
482
|
+
## Track D addendum 12 — the text-branch call-overhead class CLOSED (measured tie + the bound-vs-run datum)
|
|
483
|
+
|
|
484
|
+
The named remainder from addendum 11 ("the parser's text-branch call
|
|
485
|
+
overhead, the scalar wrapper at 2.2%") went through the full
|
|
486
|
+
instrumentation pass and closes the class:
|
|
487
|
+
|
|
488
|
+
1. **The microbench decomposition**: the dispatch entry costs 2-3x the
|
|
489
|
+
kernel's own work per call (7.1 vs 3.0 ns at 5B, 17.9 vs 6.9 ns at
|
|
490
|
+
31B) — the fn-pointer defeats thin-LTO and forces the full call ABI
|
|
491
|
+
plus per-call key re-materialization. A devirtualized cut (arch-
|
|
492
|
+
direct calls + a <16B scalar fast path) was implemented, pinned,
|
|
493
|
+
and mutation-falsified — and measured a DOC-LEVEL TIE in interleaved
|
|
494
|
+
A/B (−0.7/−2.3/−1.4%, inside the noise band). The microbench's
|
|
495
|
+
variant-B "win" was LTO INLINING the local kernel into the bench
|
|
496
|
+
loop — a zero-call comparison the real direct-call cut cannot
|
|
497
|
+
replicate across TUs.
|
|
498
|
+
|
|
499
|
+
2. **The bound-vs-run instrument datum** (the decisive one): the
|
|
500
|
+
parser's scan calls pass the REMAINING BUFFER as `len`
|
|
501
|
+
(`p.end - p.pos`), not the run length — so a length-gated fast path
|
|
502
|
+
never engages in the parser context (bucket census on the bench
|
|
503
|
+
text fixture: <16B = 0 of 2.1M calls, 64+ = 99.999%). The
|
|
504
|
+
run-length histogram that motivated the fast path describes the
|
|
505
|
+
RUN distribution, which the kernel only knows after scanning.
|
|
506
|
+
|
|
507
|
+
Disposition after four closures in the call-structure class
|
|
508
|
+
(scan-merge 32B/64B, kernel-width 32B, devirtualization): **the
|
|
509
|
+
text-branch call structure is at its floor.** The remaining text-row
|
|
510
|
+
gap to pugixml is the kernel's per-byte throughput itself — and the
|
|
511
|
+
accumulator chain that binds it was measured at addendum 11. Next
|
|
512
|
+
text-row work, if any, is a kernel redesign (Track A scanner class),
|
|
513
|
+
not call-structure rearrangement.
|
|
@@ -95,6 +95,10 @@ static XsdCm* xsd_cm_parse(struct leptris_xsd_schema* s,
|
|
|
95
95
|
static XsdAttrDecl* xsd_capture_attr_rows(struct leptris_xsd_schema* s,
|
|
96
96
|
LeptrisElement container,
|
|
97
97
|
int depth);
|
|
98
|
+
static XsdAttrDecl* xsd_capture_attr_rows_ex(struct leptris_xsd_schema* s,
|
|
99
|
+
LeptrisElement container,
|
|
100
|
+
int depth,
|
|
101
|
+
const XsdAttrGroupDef* owner);
|
|
98
102
|
XsdCm* xsd_find_complex_ns(struct leptris_xsd_schema* s,
|
|
99
103
|
const char* type_name, const char* uri);
|
|
100
104
|
extern XsdCm* xsd_find_complex(struct leptris_xsd_schema* s,
|
|
@@ -128,9 +132,9 @@ static char* xsd_capture_unit_tns(LeptrisElement e);
|
|
|
128
132
|
static void xsd_resolve_qname(LeptrisElement e, const char* qname,
|
|
129
133
|
char* uri, size_t urisz, char* local,
|
|
130
134
|
size_t localsz);
|
|
131
|
-
static char* xsd_capture_any_attr_ns(LeptrisElement container);
|
|
132
135
|
static char* xsd_capture_any_attr_ns_deep(
|
|
133
|
-
struct leptris_xsd_schema* s, LeptrisElement container, int depth
|
|
136
|
+
struct leptris_xsd_schema* s, LeptrisElement container, int depth,
|
|
137
|
+
char** out_unit);
|
|
134
138
|
static char* xsd_capture_any_attr_not_qname(LeptrisElement container);
|
|
135
139
|
|
|
136
140
|
/* #1626: prefixed QName refs (type="w:CT_Border") resolve by local
|
|
@@ -3257,13 +3261,26 @@ static XsdCm* xsd_capture_complex_model(struct leptris_xsd_schema* s,
|
|
|
3257
3261
|
/* XSD 1.1: asserts nested under the derivation */
|
|
3258
3262
|
if (s->version_11)
|
|
3259
3263
|
xsd_capture_assert_children(s, dq, root);
|
|
3260
|
-
if (!any_attr_ns)
|
|
3264
|
+
if (!any_attr_ns) {
|
|
3261
3265
|
if (!any_attr_unit_ns)
|
|
3262
3266
|
any_attr_unit_ns = xsd_capture_unit_tns(dq);
|
|
3263
|
-
|
|
3267
|
+
/* leptris-rs#72: the extension's wildcard rides
|
|
3268
|
+
* attributeGroup refs too — the shallow scan
|
|
3269
|
+
* misses attgD019/wildZ013 shapes. Cross-unit
|
|
3270
|
+
* found-groups carry their own tns. */
|
|
3271
|
+
char* deep_unit = NULL;
|
|
3272
|
+
any_attr_ns = xsd_capture_any_attr_ns_deep(
|
|
3273
|
+
s, dq, 0, &deep_unit);
|
|
3274
|
+
if (any_attr_ns && deep_unit) {
|
|
3275
|
+
free(any_attr_unit_ns);
|
|
3276
|
+
any_attr_unit_ns = deep_unit;
|
|
3277
|
+
} else {
|
|
3278
|
+
free(deep_unit);
|
|
3279
|
+
}
|
|
3264
3280
|
if (!any_attr_not_qname)
|
|
3265
3281
|
any_attr_not_qname =
|
|
3266
3282
|
xsd_capture_any_attr_not_qname(dq);
|
|
3283
|
+
}
|
|
3267
3284
|
}
|
|
3268
3285
|
} else if (strcmp(local, "simpleContent") == 0) {
|
|
3269
3286
|
for (LeptrisNodeRef q = leptris_node_first_child(
|
|
@@ -3344,8 +3361,19 @@ static XsdCm* xsd_capture_complex_model(struct leptris_xsd_schema* s,
|
|
|
3344
3361
|
if (!attrs) attrs = xsd_capture_attr_rows(s, ct, 0);
|
|
3345
3362
|
if (!any_attr_unit_ns)
|
|
3346
3363
|
any_attr_unit_ns = xsd_capture_unit_tns(ct);
|
|
3347
|
-
if (!any_attr_ns)
|
|
3348
|
-
|
|
3364
|
+
if (!any_attr_ns) {
|
|
3365
|
+
char* deep_unit = NULL;
|
|
3366
|
+
any_attr_ns = xsd_capture_any_attr_ns_deep(s, ct, 0,
|
|
3367
|
+
&deep_unit);
|
|
3368
|
+
if (any_attr_ns && deep_unit) {
|
|
3369
|
+
/* the wildcard lives in a REFERENCED group from
|
|
3370
|
+
* another unit — its declaring tns wins */
|
|
3371
|
+
free(any_attr_unit_ns);
|
|
3372
|
+
any_attr_unit_ns = deep_unit;
|
|
3373
|
+
} else {
|
|
3374
|
+
free(deep_unit);
|
|
3375
|
+
}
|
|
3376
|
+
}
|
|
3349
3377
|
if (!any_attr_not_qname)
|
|
3350
3378
|
any_attr_not_qname = xsd_capture_any_attr_not_qname(ct);
|
|
3351
3379
|
}
|
|
@@ -3455,7 +3483,9 @@ XsdGroupDef* xsd_find_group(struct leptris_xsd_schema* s,
|
|
|
3455
3483
|
return NULL;
|
|
3456
3484
|
}
|
|
3457
3485
|
|
|
3458
|
-
|
|
3486
|
+
/* leptris-rs#73: the global attribute declaration for a local name
|
|
3487
|
+
* (the ID-count pass resolves wildcard-admitted names against it). */
|
|
3488
|
+
const XsdAttrDecl* xsd_find_top_attr(
|
|
3459
3489
|
struct leptris_xsd_schema* s, const char* name) {
|
|
3460
3490
|
if (!s || !name) return NULL;
|
|
3461
3491
|
for (XsdAttrDecl* a = s->top_attrs; a; a = a->next)
|
|
@@ -3563,6 +3593,13 @@ static const char* xsd_inline_attr_type(
|
|
|
3563
3593
|
static XsdAttrDecl* xsd_capture_attr_rows(struct leptris_xsd_schema* s,
|
|
3564
3594
|
LeptrisElement container,
|
|
3565
3595
|
int depth) {
|
|
3596
|
+
return xsd_capture_attr_rows_ex(s, container, depth, NULL);
|
|
3597
|
+
}
|
|
3598
|
+
|
|
3599
|
+
static XsdAttrDecl* xsd_capture_attr_rows_ex(struct leptris_xsd_schema* s,
|
|
3600
|
+
LeptrisElement container,
|
|
3601
|
+
int depth,
|
|
3602
|
+
const XsdAttrGroupDef* owner) {
|
|
3566
3603
|
XsdAttrDecl* head = NULL;
|
|
3567
3604
|
XsdAttrDecl* tail = NULL;
|
|
3568
3605
|
if (depth > 8) return NULL;
|
|
@@ -3635,6 +3672,18 @@ static XsdAttrDecl* xsd_capture_attr_rows(struct leptris_xsd_schema* s,
|
|
|
3635
3672
|
XsdAttrGroupDef* g = s->attr_groups;
|
|
3636
3673
|
for (; g; g = g->next)
|
|
3637
3674
|
if (strcmp(g->name, ref) == 0) break;
|
|
3675
|
+
/* XSD 1.0 §4.2.2 redefine hook (moxml#545, attgC035-038):
|
|
3676
|
+
* a body redefining group N refs N to mean the ORIGINAL
|
|
3677
|
+
* group. The name-keyed slot may hold the redefining
|
|
3678
|
+
* body itself — when expanding THAT def and the ref
|
|
3679
|
+
* names it again, take the shadowed def instead, or the
|
|
3680
|
+
* original's members never splice into the composition. */
|
|
3681
|
+
if (g && g == owner) {
|
|
3682
|
+
XsdAttrGroupDef* alt = g->next;
|
|
3683
|
+
for (; alt; alt = alt->next)
|
|
3684
|
+
if (strcmp(alt->name, g->name) == 0) break;
|
|
3685
|
+
g = alt;
|
|
3686
|
+
}
|
|
3638
3687
|
if (!g) continue;
|
|
3639
3688
|
/* leptris#1957: expand lazily through the def's own
|
|
3640
3689
|
* element when its rows are not captured yet (a group
|
|
@@ -3643,7 +3692,8 @@ static XsdAttrDecl* xsd_capture_attr_rows(struct leptris_xsd_schema* s,
|
|
|
3643
3692
|
* the cycle guard. */
|
|
3644
3693
|
XsdAttrDecl* copied =
|
|
3645
3694
|
g->attrs ? xsd_copy_attr_rows(g->attrs)
|
|
3646
|
-
:
|
|
3695
|
+
: xsd_capture_attr_rows_ex(s, g->elem, depth + 1,
|
|
3696
|
+
g);
|
|
3647
3697
|
if (!copied) continue;
|
|
3648
3698
|
if (!head)
|
|
3649
3699
|
head = copied;
|
|
@@ -3686,7 +3736,7 @@ static char* xsd_capture_unit_tns(LeptrisElement e) {
|
|
|
3686
3736
|
* (depth-bounded); the group's OWN element is the walk root. */
|
|
3687
3737
|
static char* xsd_capture_any_attr_ns_deep(
|
|
3688
3738
|
struct leptris_xsd_schema* s, LeptrisElement container,
|
|
3689
|
-
int depth) {
|
|
3739
|
+
int depth, char** out_unit) {
|
|
3690
3740
|
if (depth > 8) return NULL;
|
|
3691
3741
|
for (LeptrisNodeRef ac = leptris_node_first_child(
|
|
3692
3742
|
leptris_element_as_node(container));
|
|
@@ -3697,9 +3747,14 @@ static char* xsd_capture_any_attr_ns_deep(
|
|
|
3697
3747
|
const char* al = NULL, *au = NULL;
|
|
3698
3748
|
leptris_element_expanded_name(ae, &al, NULL, &au);
|
|
3699
3749
|
if (!al || (au && strcmp(au, XSD_NS) != 0)) continue;
|
|
3700
|
-
if (strcmp(al, "anyAttribute") == 0)
|
|
3701
|
-
|
|
3702
|
-
|
|
3750
|
+
if (strcmp(al, "anyAttribute") == 0) {
|
|
3751
|
+
/* leptris-rs#73: absent @namespace means ##any — a
|
|
3752
|
+
* bare <xs:anyAttribute /> is a live wildcard, not an
|
|
3753
|
+
* invisible one. */
|
|
3754
|
+
const char* ns =
|
|
3755
|
+
leptris_element_attribute(ae, "namespace");
|
|
3756
|
+
return xsd_strdup(ns ? ns : "##any");
|
|
3757
|
+
}
|
|
3703
3758
|
if (strcmp(al, "attributeGroup") == 0) {
|
|
3704
3759
|
const char* ref = xsd_local_name(
|
|
3705
3760
|
leptris_element_attribute(ae, "ref"));
|
|
@@ -3708,31 +3763,26 @@ static char* xsd_capture_any_attr_ns_deep(
|
|
|
3708
3763
|
for (; g; g = g->next)
|
|
3709
3764
|
if (strcmp(g->name, ref) == 0) break;
|
|
3710
3765
|
if (g && g->elem) {
|
|
3711
|
-
char*
|
|
3712
|
-
|
|
3713
|
-
|
|
3766
|
+
char* inner_unit = NULL;
|
|
3767
|
+
char* r = xsd_capture_any_attr_ns_deep(
|
|
3768
|
+
s, g->elem, depth + 1, &inner_unit);
|
|
3769
|
+
if (r) {
|
|
3770
|
+
/* leptris-rs#72: the wildcard resolves against
|
|
3771
|
+
* its DECLARING unit's tns — the group's own
|
|
3772
|
+
* unit ("" for a namespace-less import), which
|
|
3773
|
+
* may differ from the consuming type's. */
|
|
3774
|
+
if (out_unit)
|
|
3775
|
+
*out_unit = inner_unit
|
|
3776
|
+
? inner_unit
|
|
3777
|
+
: xsd_capture_unit_tns(g->elem);
|
|
3778
|
+
return r;
|
|
3779
|
+
}
|
|
3714
3780
|
}
|
|
3715
3781
|
}
|
|
3716
3782
|
}
|
|
3717
3783
|
return NULL;
|
|
3718
3784
|
}
|
|
3719
3785
|
|
|
3720
|
-
static char* xsd_capture_any_attr_ns(LeptrisElement container) {
|
|
3721
|
-
for (LeptrisNodeRef ac = leptris_node_first_child(
|
|
3722
|
-
leptris_element_as_node(container));
|
|
3723
|
-
ac; ac = leptris_node_next_sibling(ac)) {
|
|
3724
|
-
if (leptris_node_get_type(ac) != LEPTRIS_NODE_TYPE_ELEMENT)
|
|
3725
|
-
continue;
|
|
3726
|
-
LeptrisElement ae = (LeptrisElement)ac;
|
|
3727
|
-
const char* al = NULL;
|
|
3728
|
-
leptris_element_expanded_name(ae, &al, NULL, NULL);
|
|
3729
|
-
if (al && strcmp(al, "anyAttribute") == 0)
|
|
3730
|
-
return xsd_strdup(
|
|
3731
|
-
leptris_element_attribute(ae, "namespace"));
|
|
3732
|
-
}
|
|
3733
|
-
return NULL;
|
|
3734
|
-
}
|
|
3735
|
-
|
|
3736
3786
|
/* moxml#460: the anyAttribute's XSD 1.1 @notQName (same walk as the
|
|
3737
3787
|
* namespace capture). NULL when absent. */
|
|
3738
3788
|
static char* xsd_capture_any_attr_not_qname(LeptrisElement container) {
|