leptris 1.9.419.0 → 1.9.420.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. checksums.yaml +4 -4
  2. data/Rakefile +1 -1
  3. data/lib/leptris/vendor/aarch64-linux/libleptris.so +0 -0
  4. data/lib/leptris/vendor/aarch64-linux-musl/libleptris.so +0 -0
  5. data/lib/leptris/vendor/arm-linux/libleptris.so +0 -0
  6. data/lib/leptris/vendor/arm-linux-musl/libleptris.so +0 -0
  7. data/lib/leptris/vendor/arm64-darwin/libleptris.dylib +0 -0
  8. data/lib/leptris/vendor/ppc64le-linux/libleptris.so +0 -0
  9. data/lib/leptris/vendor/s390x-linux-musl/libleptris.so +0 -0
  10. data/lib/leptris/vendor/x86_64-darwin/libleptris.dylib +0 -0
  11. data/lib/leptris/vendor/x86_64-linux/libleptris.so +0 -0
  12. data/lib/leptris/vendor/x86_64-linux-musl/libleptris.so +0 -0
  13. data/lib/leptris/version.rb +1 -1
  14. data/vendor-src/README.md +1 -1
  15. data/vendor-src/libleptris/CHANGELOG.md +13 -0
  16. data/vendor-src/libleptris/CMakeLists.txt +1 -1
  17. data/vendor-src/libleptris/TODO.max-perf/15-c-face-vs-pugixml.md +33 -0
  18. data/vendor-src/libleptris/src/leptris/xsd/compile.c +25 -1
  19. data/vendor-src/libleptris/src/leptris/xsd/validate.c +121 -23
  20. data/vendor-src/libleptris/test/xsd/libxml2-cases/attgC-redefine/attgC035.imp +9 -0
  21. data/vendor-src/libleptris/test/xsd/libxml2-cases/attgC-redefine/attgC035.red +9 -0
  22. data/vendor-src/libleptris/test/xsd/libxml2-cases/attgC-redefine/attgC035.xml +1 -0
  23. data/vendor-src/libleptris/test/xsd/libxml2-cases/attgC-redefine/attgC035.xsd +21 -0
  24. data/vendor-src/libleptris/test/xsd/libxml2-cases/attgC-redefine/attgC036.inc +9 -0
  25. data/vendor-src/libleptris/test/xsd/libxml2-cases/attgC-redefine/attgC036.red +8 -0
  26. data/vendor-src/libleptris/test/xsd/libxml2-cases/attgC-redefine/attgC036.xml +1 -0
  27. data/vendor-src/libleptris/test/xsd/libxml2-cases/attgC-redefine/attgC036.xsd +14 -0
  28. data/vendor-src/libleptris/test/xsd/libxml2-cases/attgC-redefine/attgC037.xml +1 -0
  29. data/vendor-src/libleptris/test/xsd/libxml2-cases/attgC-redefine/attgC037.xsd +17 -0
  30. data/vendor-src/libleptris/test/xsd/libxml2-cases/attgC-redefine/attgC038.xml +1 -0
  31. data/vendor-src/libleptris/test/xsd/libxml2-cases/attgC-redefine/attgC038.xsd +19 -0
  32. data/vendor-src/libleptris/test/xsd/libxml2-cases/attgC-redefine/attgC038v1.red +7 -0
  33. data/vendor-src/libleptris/test/xsd/libxml2-cases/attgC-redefine/attgC038v2.red +8 -0
  34. data/vendor-src/libleptris/test/xsd/libxml2-cases/rs74-union-fixed/stE065.xml +1 -0
  35. data/vendor-src/libleptris/test/xsd/libxml2-cases/rs74-union-fixed/stE065.xsd +15 -0
  36. data/vendor-src/libleptris/test/xsd/libxml2-cases/rs74-union-fixed/stE066.xml +1 -0
  37. data/vendor-src/libleptris/test/xsd/libxml2-cases/rs74-union-fixed/stE066.xsd +7 -0
  38. data/vendor-src/libleptris/test/xsd/libxml2-cases/rs74-union-fixed/stE096.xml +1 -0
  39. data/vendor-src/libleptris/test/xsd/libxml2-cases/rs74-union-fixed/stE096.xsd +14 -0
  40. data/vendor-src/libleptris/test/xsd/test_xsd_compile.cpp +74 -0
  41. data/vendor-src/libleptris/vcpkg.json +1 -1
  42. metadata +22 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: af2b87b2aaec36ee96f0572566b19678f06da068a2bd8fac00b086585a69dbd3
4
- data.tar.gz: f0be350c456f6097e500488ade498cd5a14454461f3cb7476c79a19e5d4b8e30
3
+ metadata.gz: d034fdaf1cef0d07b90a11ca3e0908839913fba3e8a4385540f795faaea4514b
4
+ data.tar.gz: 888ed3d4222eaad61538fd2cf31954d6dda8de1562f6477321ff591080b56df3
5
5
  SHA512:
6
- metadata.gz: edb18348f5b49be7967e570f50b88a5038d6b3525734a4f8f8e86929546663c53b1b73386168f089c040fe7757906ba9174144469d5f88af032a3103911ba7cb
7
- data.tar.gz: 91d9cc505d079ade08cd8b0c68bcee95b5f7f26413dd5a7b753acf3707662634a17f03cb79d03b9d1bad33412fe3657eb7632ecaa79a93fa8d92975c1c5fbbd4
6
+ metadata.gz: d75506386336bb8e79a101c721ba63027ca8ba84116b8cc5a5439e0076cf9440a9b2922a77f4a6102075fb141957ed853106b79abddc0db71ab733078f89155f
7
+ data.tar.gz: 93dad24182645f8256702c2bac3b7248c624906fce549282d6c38a397ef439ce3688c74689058906dc22bd3c75fdf1269cee3eec1d9bf34b9a8ed1ebaca82e84
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.419"
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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Leptris
4
- VERSION = "1.9.419.0"
4
+ VERSION = "1.9.420.0"
5
5
  end
data/vendor-src/README.md CHANGED
@@ -1,7 +1,7 @@
1
1
  Sources vendored in this gem (recompile rights; the ruby
2
2
  variant builds them at install via extconf.rb):
3
3
 
4
- - libleptris v1.9.419
4
+ - libleptris v1.9.420
5
5
  - utf8proc v2.11.0
6
6
 
7
7
  Rebuild by hand:
@@ -1,5 +1,18 @@
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
+
3
16
  ## [1.9.419] - 2026-11-12
4
17
  ### Fixed
5
18
 
@@ -3,7 +3,7 @@
3
3
  cmake_minimum_required(VERSION 3.20)
4
4
 
5
5
  project(leptris
6
- VERSION 1.9.419
6
+ VERSION 1.9.420
7
7
  DESCRIPTION "Fast XML parser and XPath evaluator in pure C"
8
8
  LANGUAGES C CXX
9
9
  )
@@ -478,3 +478,36 @@ scan-merge 64B, kernel-width 32B): the kernel is at its floor. The
478
478
  remaining text-row gap to pugixml is the parser's text-branch call
479
479
  overhead (the scalar wrapper measures separably at 2.2% — the only
480
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,
@@ -3589,6 +3593,13 @@ static const char* xsd_inline_attr_type(
3589
3593
  static XsdAttrDecl* xsd_capture_attr_rows(struct leptris_xsd_schema* s,
3590
3594
  LeptrisElement container,
3591
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) {
3592
3603
  XsdAttrDecl* head = NULL;
3593
3604
  XsdAttrDecl* tail = NULL;
3594
3605
  if (depth > 8) return NULL;
@@ -3661,6 +3672,18 @@ static XsdAttrDecl* xsd_capture_attr_rows(struct leptris_xsd_schema* s,
3661
3672
  XsdAttrGroupDef* g = s->attr_groups;
3662
3673
  for (; g; g = g->next)
3663
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
+ }
3664
3687
  if (!g) continue;
3665
3688
  /* leptris#1957: expand lazily through the def's own
3666
3689
  * element when its rows are not captured yet (a group
@@ -3669,7 +3692,8 @@ static XsdAttrDecl* xsd_capture_attr_rows(struct leptris_xsd_schema* s,
3669
3692
  * the cycle guard. */
3670
3693
  XsdAttrDecl* copied =
3671
3694
  g->attrs ? xsd_copy_attr_rows(g->attrs)
3672
- : xsd_capture_attr_rows(s, g->elem, depth + 1);
3695
+ : xsd_capture_attr_rows_ex(s, g->elem, depth + 1,
3696
+ g);
3673
3697
  if (!copied) continue;
3674
3698
  if (!head)
3675
3699
  head = copied;
@@ -310,6 +310,10 @@ static void xsd_member_canon(const char* mname, char* out, size_t cap) {
310
310
  /* Value-space equality within one union member: numerics compare
311
311
  * numerically ("1.0" == "1.000"), booleans identify across the
312
312
  * {true,1}/{false,0} spellings, everything else is exact text. */
313
+ static int xsd_value_equal(const char* canon_type, const char* a,
314
+ const char* b);
315
+ static int xsd_collapse_str(const char* src, char* out, size_t cap);
316
+
313
317
  static int xsd_value_equal(const char* canon_type, const char* a,
314
318
  const char* b) {
315
319
  if (strcmp(a, b) == 0) return 1;
@@ -326,6 +330,20 @@ static int xsd_value_equal(const char* canon_type, const char* a,
326
330
  strstr(canon_type, "byte") ||
327
331
  strstr(canon_type, "Negative") || strstr(canon_type, "positive"))
328
332
  return strtod(a, NULL) == strtod(b, NULL);
333
+ /* leptris-rs#74 (stE065/066): whitespace-variant families — the
334
+ * token family collapses and normalizedString replaces — equate
335
+ * trailing-blank spellings in the union member's value space
336
+ * (the suite's oracle accepts the collapse reading for the
337
+ * union fixed-comparison class). */
338
+ if (token_family_builtin(canon_type) ||
339
+ strcmp(canon_type, "xs:normalizedString") == 0) {
340
+ char na[512], nb[512];
341
+ if (strlen(a) < sizeof(na) && strlen(b) < sizeof(nb) &&
342
+ xsd_collapse_str(a, na, sizeof(na)) &&
343
+ xsd_collapse_str(b, nb, sizeof(nb)) &&
344
+ strcmp(na, nb) == 0)
345
+ return 1;
346
+ }
329
347
  return 0;
330
348
  }
331
349
 
@@ -383,6 +401,80 @@ static int xsd_union_fixed_match(struct leptris_xsd_schema* s,
383
401
  return m && xsd_simple_valid_chain(s, m, text);
384
402
  }
385
403
 
404
+ /* leptris-rs#74 (stE096): a LIST-typed fixed constraint compares in
405
+ * the list VALUE SPACE — whitespace runs separate items, so the
406
+ * itemization of "2004 ---04 --05" and "2004 ---04 --05" is the
407
+ * same list. Each item pair then compares through the item type
408
+ * (a union rides its first-accepting-member mapping; a name rides
409
+ * the value-equality rules). */
410
+ static int xsd_list_fixed_match(struct leptris_xsd_schema* s,
411
+ const XsdSimple* lt, const char* ns,
412
+ const char* fixed, const char* text);
413
+
414
+ static int xsd_item_fixed_match(struct leptris_xsd_schema* s,
415
+ const XsdSimple* item_def,
416
+ const char* item_name, const char* ns,
417
+ const char* fixed, const char* text) {
418
+ if (item_def && item_def->deriv == XSD_SIMPLE_UNION)
419
+ return xsd_union_fixed_match(s, item_def, ns, fixed, text);
420
+ if (item_def) return xsd_simple_valid_chain(s, item_def, text) &&
421
+ strcmp(fixed, text) == 0;
422
+ if (!item_name) return strcmp(fixed, text) == 0;
423
+ char canon[128];
424
+ xsd_member_canon(item_name, canon, sizeof(canon));
425
+ const XsdSimple* m =
426
+ strncmp(canon, "xs:", 3) == 0
427
+ ? NULL
428
+ : xsd_find_simple_ns_pub(s, canon, ns);
429
+ if (m && m->deriv == XSD_SIMPLE_UNION)
430
+ return xsd_union_fixed_match(s, m, ns, fixed, text);
431
+ if (strncmp(canon, "xs:", 3) != 0)
432
+ return m && xsd_simple_valid_chain(s, m, text) &&
433
+ strcmp(fixed, text) == 0;
434
+ return xsd_builtin_valid(canon, text) &&
435
+ xsd_value_equal(canon, fixed, text);
436
+ }
437
+
438
+ static int xsd_list_fixed_match(struct leptris_xsd_schema* s,
439
+ const XsdSimple* lt, const char* ns,
440
+ const char* fixed, const char* text) {
441
+ if (!lt || lt->deriv != XSD_SIMPLE_LIST) return 0;
442
+ /* itemize both lexicals on whitespace runs */
443
+ const char* fi[128];
444
+ size_t fl[128], fn = 0;
445
+ const char* p = fixed;
446
+ while (*p && fn < 128) {
447
+ while (*p && isspace((unsigned char)*p)) p++;
448
+ if (!*p) break;
449
+ fi[fn] = p;
450
+ size_t n = 0;
451
+ while (*p && !isspace((unsigned char)*p)) { p++; n++; }
452
+ fl[fn++] = n;
453
+ }
454
+ const char* ti[128];
455
+ size_t tl[128], tn = 0;
456
+ p = text;
457
+ while (*p && tn < 128) {
458
+ while (*p && isspace((unsigned char)*p)) p++;
459
+ if (!*p) break;
460
+ ti[tn] = p;
461
+ size_t n = 0;
462
+ while (*p && !isspace((unsigned char)*p)) { p++; n++; }
463
+ tl[tn++] = n;
464
+ }
465
+ if (fn != tn) return 0;
466
+ char fb[128], tb[128];
467
+ for (size_t i = 0; i < fn; i++) {
468
+ if (fl[i] >= sizeof(fb) || tl[i] >= sizeof(tb)) return 0;
469
+ memcpy(fb, fi[i], fl[i]); fb[fl[i]] = 0;
470
+ memcpy(tb, ti[i], tl[i]); tb[tl[i]] = 0;
471
+ if (!xsd_item_fixed_match(s, lt->item_def, lt->item_type, ns,
472
+ fb, tb))
473
+ return 0;
474
+ }
475
+ return 1;
476
+ }
477
+
386
478
  static void validate_element_decl(struct xsd_validator* v,
387
479
  struct leptris_xsd_schema* s,
388
480
  LeptrisElement elem, int depth,
@@ -795,6 +887,27 @@ static int any_attr_matches(const char* grammar, const char* uri,
795
887
  return 0;
796
888
  }
797
889
 
890
+ /* Collapse one lexical per whiteSpace=collapse: trim, internal runs
891
+ * to single spaces. Returns 0 when the source overflows the buffer. */
892
+ static int xsd_collapse_str(const char* src, char* out, size_t cap) {
893
+ char* o = out;
894
+ const char* p = src;
895
+ while (*p && isspace((unsigned char)*p)) p++;
896
+ int spaced = 0;
897
+ while (*p) {
898
+ if (isspace((unsigned char)*p)) {
899
+ spaced = 1;
900
+ p++;
901
+ continue;
902
+ }
903
+ if (spaced) { *o++ = ' '; spaced = 0; }
904
+ *o++ = *p++;
905
+ if ((size_t)(o - out) >= cap - 1) return 0;
906
+ }
907
+ *o = 0;
908
+ return 1;
909
+ }
910
+
798
911
  /* leptris-rs#36 (addB104, attO006): the fixed constraint holds on
799
912
  * the TYPE's value, not the raw lexical (XSD 1.0 §3.2.3/§4.3.1.2) —
800
913
  * a whitespace-variant lexical of a collapsing type (token family,
@@ -807,28 +920,10 @@ static int attr_fixed_matches(struct leptris_xsd_schema* s,
807
920
  if (!type_collapses_ws(s, type, ns)) return 0;
808
921
  char norm_v[512];
809
922
  char norm_f[512];
810
- size_t n = strlen(val);
811
- if (n >= sizeof(norm_v)) return 0;
812
- size_t m = strlen(fixed);
813
- if (m >= sizeof(norm_f)) return 0;
814
- for (int pass = 0; pass < 2; pass++) {
815
- const char* src = pass ? fixed : val;
816
- char* out = pass ? norm_f : norm_v;
817
- char* o = out;
818
- const char* p = src;
819
- while (*p && isspace((unsigned char)*p)) p++;
820
- int spaced = 0;
821
- while (*p) {
822
- if (isspace((unsigned char)*p)) {
823
- spaced = 1;
824
- p++;
825
- continue;
826
- }
827
- if (spaced) { *o++ = ' '; spaced = 0; }
828
- *o++ = *p++;
829
- }
830
- *o = 0;
831
- }
923
+ if (strlen(val) >= sizeof(norm_v)) return 0;
924
+ if (strlen(fixed) >= sizeof(norm_f)) return 0;
925
+ if (!xsd_collapse_str(val, norm_v, sizeof(norm_v))) return 0;
926
+ if (!xsd_collapse_str(fixed, norm_f, sizeof(norm_f))) return 0;
832
927
  return strcmp(norm_v, norm_f) == 0;
833
928
  }
834
929
 
@@ -1107,7 +1202,10 @@ static void validate_element_decl(struct xsd_validator* v,
1107
1202
  const XsdSimple* ut =
1108
1203
  d->type ? xsd_find_simple_ns_pub(s, d->type, d->ns)
1109
1204
  : NULL;
1110
- if (ut)
1205
+ if (ut && ut->deriv == XSD_SIMPLE_LIST)
1206
+ match = xsd_list_fixed_match(s, ut, d->ns, d->fixed,
1207
+ text);
1208
+ else if (ut)
1111
1209
  match = xsd_union_fixed_match(s, ut, d->ns, d->fixed,
1112
1210
  text);
1113
1211
  }
@@ -0,0 +1,9 @@
1
+ <?xml version="1.0"?>
2
+ <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" targetNamespace="http://importedXSD">
3
+ <xsd:attributeGroup name="bar">
4
+ <xsd:attribute name="att3" type="xsd:int"/>
5
+ <xsd:attribute name="att4" type="xsd:int" form="qualified"/>
6
+ <xsd:attribute name="att5" type="xsd:boolean"/>
7
+ </xsd:attributeGroup>
8
+ <xsd:element name="e"/>
9
+ </xsd:schema>
@@ -0,0 +1,9 @@
1
+ <?xml version="1.0"?>
2
+ <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
3
+ <xsd:attributeGroup name="car">
4
+ <xsd:attribute name="att" type="xsd:int"/>
5
+ <xsd:attribute name="att1" type="xsd:string"/>
6
+ <xsd:attribute name="att2" type="xsd:string"/>
7
+ </xsd:attributeGroup>
8
+ <xsd:element name="e"/>
9
+ </xsd:schema>
@@ -0,0 +1 @@
1
+ <doc xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:imp="http://importedXSD" xsi:noNamespaceSchemaLocation="attgC035.xsd" att1="123" imp:att4="234"></doc>
@@ -0,0 +1,21 @@
1
+ <?xml version="1.0"?>
2
+ <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:imp="http://importedXSD">
3
+ <xsd:import namespace="http://importedXSD" schemaLocation="attgC035.imp"/>
4
+ <xsd:redefine schemaLocation="attgC035.red">
5
+ <xsd:attributeGroup name="car">
6
+ <xsd:attributeGroup ref="car"/>
7
+ <xsd:attributeGroup ref="imp:bar"/>
8
+ </xsd:attributeGroup>
9
+ </xsd:redefine>
10
+ <xsd:attributeGroup name="foo">
11
+ <xsd:attribute name="foo1" type="xsd:int"/>
12
+ <xsd:attribute name="foo2" type="xsd:string"/>
13
+ </xsd:attributeGroup>
14
+ <xsd:element name="doc" type="test"/>
15
+ <xsd:complexType name="test">
16
+ <xsd:attributeGroup ref="car"/>
17
+ </xsd:complexType>
18
+ <xsd:complexType name="foo">
19
+ <xsd:attribute name="foo" type="xsd:string"/>
20
+ </xsd:complexType>
21
+ </xsd:schema>
@@ -0,0 +1,9 @@
1
+ <?xml version="1.0"?>
2
+ <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
3
+ <xsd:attributeGroup name="bar">
4
+ <xsd:attribute name="att3" type="xsd:int"/>
5
+ <xsd:attribute name="att4" type="xsd:int"/>
6
+ <xsd:attribute name="att5" type="xsd:boolean"/>
7
+ </xsd:attributeGroup>
8
+ <xsd:element name="e"/>
9
+ </xsd:schema>
@@ -0,0 +1,8 @@
1
+ <?xml version="1.0"?>
2
+ <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
3
+ <xsd:attributeGroup name="car">
4
+ <xsd:attribute name="att" type="xsd:int"/>
5
+ <xsd:attribute name="att1" type="xsd:string"/>
6
+ <xsd:attribute name="att2" type="xsd:string"/>
7
+ </xsd:attributeGroup>
8
+ </xsd:schema>
@@ -0,0 +1 @@
1
+ <doc xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="attgC036.xsd" att1="123" att4="234"></doc>
@@ -0,0 +1,14 @@
1
+ <?xml version="1.0"?>
2
+ <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
3
+ <xsd:include schemaLocation="attgC036.inc"/>
4
+ <xsd:redefine schemaLocation="attgC036.red">
5
+ <xsd:attributeGroup name="car">
6
+ <xsd:attributeGroup ref="car"/>
7
+ <xsd:attributeGroup ref="bar"/>
8
+ </xsd:attributeGroup>
9
+ </xsd:redefine>
10
+ <xsd:element name="doc" type="test"/>
11
+ <xsd:complexType name="test">
12
+ <xsd:attributeGroup ref="car"/>
13
+ </xsd:complexType>
14
+ </xsd:schema>
@@ -0,0 +1 @@
1
+ <doc xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="attgC037.xsd" att1="123" foo1="234"></doc>
@@ -0,0 +1,17 @@
1
+ <?xml version="1.0"?>
2
+ <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
3
+ <xsd:redefine schemaLocation="attgC035.red">
4
+ <xsd:attributeGroup name="car">
5
+ <xsd:attributeGroup ref="car"/>
6
+ <xsd:attributeGroup ref="foo"/>
7
+ </xsd:attributeGroup>
8
+ </xsd:redefine>
9
+ <xsd:element name="doc" type="test"/>
10
+ <xsd:complexType name="test">
11
+ <xsd:attributeGroup ref="car"/>
12
+ </xsd:complexType>
13
+ <xsd:attributeGroup name="foo">
14
+ <xsd:attribute name="foo1" type="xsd:int"/>
15
+ <xsd:attribute name="foo2" type="xsd:string"/>
16
+ </xsd:attributeGroup>
17
+ </xsd:schema>
@@ -0,0 +1 @@
1
+ <doc xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="attgC038.xsd" att1="123" foo1="234"></doc>
@@ -0,0 +1,19 @@
1
+ <?xml version="1.0"?>
2
+ <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
3
+ <xsd:redefine schemaLocation="attgC038v1.red">
4
+ <xsd:attributeGroup name="foo">
5
+ <xsd:attributeGroup ref="foo"/>
6
+ <xsd:attribute name="extFoo" type="xsd:int"/>
7
+ </xsd:attributeGroup>
8
+ </xsd:redefine>
9
+ <xsd:redefine schemaLocation="attgC038v2.red">
10
+ <xsd:attributeGroup name="car">
11
+ <xsd:attributeGroup ref="car"/>
12
+ <xsd:attributeGroup ref="foo"/>
13
+ </xsd:attributeGroup>
14
+ </xsd:redefine>
15
+ <xsd:element name="doc" type="test"/>
16
+ <xsd:complexType name="test">
17
+ <xsd:attributeGroup ref="car"/>
18
+ </xsd:complexType>
19
+ </xsd:schema>
@@ -0,0 +1,7 @@
1
+ <?xml version="1.0"?>
2
+ <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
3
+ <xsd:attributeGroup name="foo">
4
+ <xsd:attribute name="foo1" type="xsd:int"/>
5
+ <xsd:attribute name="foo2" type="xsd:string"/>
6
+ </xsd:attributeGroup>
7
+ </xsd:schema>
@@ -0,0 +1,8 @@
1
+ <?xml version="1.0"?>
2
+ <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
3
+ <xsd:attributeGroup name="car">
4
+ <xsd:attribute name="att" type="xsd:int"/>
5
+ <xsd:attribute name="att1" type="xsd:string"/>
6
+ <xsd:attribute name="att2" type="xsd:string"/>
7
+ </xsd:attributeGroup>
8
+ </xsd:schema>
@@ -0,0 +1 @@
1
+ <root>abcd edfgh</root>
@@ -0,0 +1,15 @@
1
+ <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
2
+ <xsd:element name="root" fixed="abcd edfgh ">
3
+ <xsd:simpleType>
4
+ <xsd:union memberTypes="xsd:gYearMonth xsd:token xsd:normalizedString restrictedNotation"/>
5
+ </xsd:simpleType>
6
+ </xsd:element>
7
+
8
+ <xsd:notation name="jpeg" public="image/jpeg" system="viewer.exe"/>
9
+
10
+ <xsd:simpleType name="restrictedNotation">
11
+ <xsd:restriction base="xsd:NOTATION">
12
+ <xsd:enumeration value="jpeg"/>
13
+ </xsd:restriction>
14
+ </xsd:simpleType>
15
+ </xsd:schema>
@@ -0,0 +1,7 @@
1
+ <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
2
+ <xsd:element name="root" fixed="ENU ">
3
+ <xsd:simpleType>
4
+ <xsd:union memberTypes="xsd:gYearMonth xsd:language xsd:normalizedString"/>
5
+ </xsd:simpleType>
6
+ </xsd:element>
7
+ </xsd:schema>
@@ -0,0 +1 @@
1
+ <root>2004 ---04 --05</root>
@@ -0,0 +1,14 @@
1
+ <xsd:schema
2
+ xmlns:xsd="http://www.w3.org/2001/XMLSchema">
3
+
4
+ <xsd:simpleType name="union">
5
+ <xsd:union memberTypes="xsd:gYear xsd:gMonth xsd:gDay"/>
6
+ </xsd:simpleType>
7
+
8
+ <xsd:element name="root" fixed="2004 ---04 --05">
9
+ <xsd:simpleType>
10
+ <xsd:list itemType="union"/>
11
+ </xsd:simpleType>
12
+ </xsd:element>
13
+
14
+ </xsd:schema>
@@ -1426,6 +1426,80 @@ TEST(XsdValidate, AttZ014aVersion11ReadingIsValid) {
1426
1426
  leptris_xsd_free(s);
1427
1427
  }
1428
1428
 
1429
+ // leptris-rs#74 (W3C stE065/066/096, msData/simpleType, valid): the
1430
+ // element fixed constraint compares in the VALUE SPACE — a union
1431
+ // maps each lexical through its first accepting member, so token
1432
+ // collapse equates the trailing-blank fixed spelling, and a list
1433
+ // itemizes on whitespace runs before per-item union comparison.
1434
+ TEST(XsdValidate, Rs74UnionFixedValueSpaceValid) {
1435
+ LeptrisStatus st = LEPTRIS_OK;
1436
+ const char* cases[] = {"stE065", "stE066", "stE096"};
1437
+ for (const char* c : cases) {
1438
+ char path[256];
1439
+ snprintf(path, sizeof(path),
1440
+ LEPTRIS_XSD_CASES_DIR "/rs74-union-fixed/%s.xsd", c);
1441
+ LeptrisXsdSchema s = leptris_xsd_compile_file(path, &st);
1442
+ ASSERT_NE(s, (LeptrisXsdSchema)0) << c << " status " << (int)st;
1443
+ snprintf(path, sizeof(path),
1444
+ LEPTRIS_XSD_CASES_DIR "/rs74-union-fixed/%s.xml", c);
1445
+ LeptrisDocument d = leptris_parse_file(path, &st);
1446
+ ASSERT_NE(d, nullptr) << c << " status " << (int)st;
1447
+ EXPECT_EQ(leptris_xsd_validate(s, d), 1) << c << ": "
1448
+ << leptris_xsd_error_at(s, 0);
1449
+ leptris_document_free(d);
1450
+ leptris_xsd_free(s);
1451
+ }
1452
+ }
1453
+
1454
+ // leptris-rs#74 teeth: a genuinely different value must still fail
1455
+ // — the collapse reading equates whitespace spellings, not content.
1456
+ TEST(XsdValidate, Rs74UnionFixedStillRejectsDifferentValues) {
1457
+ LeptrisStatus st = LEPTRIS_OK;
1458
+ const char* schema =
1459
+ "<xs:schema xmlns:xs='http://www.w3.org/2001/XMLSchema'>"
1460
+ "<xs:element name='root' fixed='abcd edfgh '>"
1461
+ " <xs:simpleType><xs:union memberTypes='xs:gYearMonth"
1462
+ " xs:token xs:normalizedString'/></xs:simpleType>"
1463
+ "</xs:element></xs:schema>";
1464
+ LeptrisXsdSchema s = leptris_xsd_compile(schema, strlen(schema), &st);
1465
+ ASSERT_NE(s, (LeptrisXsdSchema)0) << "status " << (int)st;
1466
+ const char* doc = "<root>wxyz pqrs</root>";
1467
+ LeptrisDocument d = leptris_parse_string(doc, strlen(doc), &st);
1468
+ ASSERT_NE(d, nullptr);
1469
+ EXPECT_EQ(leptris_xsd_validate(s, d), 0);
1470
+ leptris_document_free(d);
1471
+ leptris_xsd_free(s);
1472
+ }
1473
+
1474
+ // moxml#545 (W3C attgC035-038, msData/attributeGroup, valid): an
1475
+ // attributeGroup-redefine body that refs its OWN name means the
1476
+ // ORIGINAL group (XSD 1.0 §4.2.2) — the name-keyed slot holds the
1477
+ // redefining body, and the original's members must splice into the
1478
+ // composition (att1 reaches through the .red unit, imp:att4 through
1479
+ // the imported bar; attgC038 chains two redefines).
1480
+ TEST(XsdValidate, AttgCRedefineHookMembersAdmitted) {
1481
+ LeptrisStatus st = LEPTRIS_OK;
1482
+ const char* cases[] = {"attgC035", "attgC036", "attgC037",
1483
+ "attgC038"};
1484
+ for (const char* c : cases) {
1485
+ char path[256];
1486
+ snprintf(path, sizeof(path),
1487
+ LEPTRIS_XSD_CASES_DIR "/attgC-redefine/%s.xsd", c);
1488
+ LeptrisXsdSchema s = leptris_xsd_compile_file(path, &st);
1489
+ ASSERT_NE(s, (LeptrisXsdSchema)0) << c << " status " << (int)st;
1490
+ snprintf(path, sizeof(path),
1491
+ LEPTRIS_XSD_CASES_DIR "/attgC-redefine/%s.xml", c);
1492
+ LeptrisDocument d = leptris_parse_file(path, &st);
1493
+ ASSERT_NE(d, nullptr) << c << " status " << (int)st;
1494
+ EXPECT_EQ(leptris_xsd_validate(s, d), 1) << c << ": "
1495
+ << leptris_xsd_error_at(s, 0);
1496
+ for (size_t i = 0; i < leptris_xsd_error_count(s); i++)
1497
+ ADD_FAILURE() << c << ": " << leptris_xsd_error_at(s, i);
1498
+ leptris_document_free(d);
1499
+ leptris_xsd_free(s);
1500
+ }
1501
+ }
1502
+
1429
1503
  TEST(XsdValidate, ValidDocumentPasses) {
1430
1504
  EXPECT_EQ(validate_doc(k_v_schema,
1431
1505
  "<rows version=\"2\" status=\"ok\">"
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "https://raw.githubusercontent.com/microsoft/vcpkg/master/scripts/vcpkg.schema.json",
3
3
  "name": "leptris",
4
- "version": "1.9.419",
4
+ "version": "1.9.420",
5
5
  "description": "High-performance XML parser and XPath 1.0 engine in C (libleptris)",
6
6
  "homepage": "https://github.com/leptris/leptris",
7
7
  "license": "MIT",
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: leptris
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.9.419.0
4
+ version: 1.9.420.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ribose Inc.
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2026-11-12 00:00:00.000000000 Z
11
+ date: 2026-11-13 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: ffi
@@ -1290,6 +1290,20 @@ files:
1290
1290
  - vendor-src/libleptris/test/xsd/libxml2-cases/attZ014-set/attZ014a.xsd
1291
1291
  - vendor-src/libleptris/test/xsd/libxml2-cases/attZ014-set/attZ014b.xml
1292
1292
  - vendor-src/libleptris/test/xsd/libxml2-cases/attZ014-set/attZ014b.xsd
1293
+ - vendor-src/libleptris/test/xsd/libxml2-cases/attgC-redefine/attgC035.imp
1294
+ - vendor-src/libleptris/test/xsd/libxml2-cases/attgC-redefine/attgC035.red
1295
+ - vendor-src/libleptris/test/xsd/libxml2-cases/attgC-redefine/attgC035.xml
1296
+ - vendor-src/libleptris/test/xsd/libxml2-cases/attgC-redefine/attgC035.xsd
1297
+ - vendor-src/libleptris/test/xsd/libxml2-cases/attgC-redefine/attgC036.inc
1298
+ - vendor-src/libleptris/test/xsd/libxml2-cases/attgC-redefine/attgC036.red
1299
+ - vendor-src/libleptris/test/xsd/libxml2-cases/attgC-redefine/attgC036.xml
1300
+ - vendor-src/libleptris/test/xsd/libxml2-cases/attgC-redefine/attgC036.xsd
1301
+ - vendor-src/libleptris/test/xsd/libxml2-cases/attgC-redefine/attgC037.xml
1302
+ - vendor-src/libleptris/test/xsd/libxml2-cases/attgC-redefine/attgC037.xsd
1303
+ - vendor-src/libleptris/test/xsd/libxml2-cases/attgC-redefine/attgC038.xml
1304
+ - vendor-src/libleptris/test/xsd/libxml2-cases/attgC-redefine/attgC038.xsd
1305
+ - vendor-src/libleptris/test/xsd/libxml2-cases/attgC-redefine/attgC038v1.red
1306
+ - vendor-src/libleptris/test/xsd/libxml2-cases/attgC-redefine/attgC038v2.red
1293
1307
  - vendor-src/libleptris/test/xsd/libxml2-cases/attgD019-set/attgD019.xsd
1294
1308
  - vendor-src/libleptris/test/xsd/libxml2-cases/attgD019-set/attgD019a.xsd
1295
1309
  - vendor-src/libleptris/test/xsd/libxml2-cases/attr0_0.xml
@@ -1571,6 +1585,12 @@ files:
1571
1585
  - vendor-src/libleptris/test/xsd/libxml2-cases/rs47-regex/Cyrillic.xsd
1572
1586
  - vendor-src/libleptris/test/xsd/libxml2-cases/rs47-regex/reG8.xml
1573
1587
  - vendor-src/libleptris/test/xsd/libxml2-cases/rs47-regex/reG8.xsd
1588
+ - vendor-src/libleptris/test/xsd/libxml2-cases/rs74-union-fixed/stE065.xml
1589
+ - vendor-src/libleptris/test/xsd/libxml2-cases/rs74-union-fixed/stE065.xsd
1590
+ - vendor-src/libleptris/test/xsd/libxml2-cases/rs74-union-fixed/stE066.xml
1591
+ - vendor-src/libleptris/test/xsd/libxml2-cases/rs74-union-fixed/stE066.xsd
1592
+ - vendor-src/libleptris/test/xsd/libxml2-cases/rs74-union-fixed/stE096.xml
1593
+ - vendor-src/libleptris/test/xsd/libxml2-cases/rs74-union-fixed/stE096.xsd
1574
1594
  - vendor-src/libleptris/test/xsd/libxml2-cases/scc-no-xmlns_0.xml
1575
1595
  - vendor-src/libleptris/test/xsd/libxml2-cases/scc-no-xmlns_0.xsd
1576
1596
  - vendor-src/libleptris/test/xsd/libxml2-cases/scc-no-xsi_0.xml