lutaml-xsd 1.2.112 → 1.2.113

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: cdfdd257c5d78df578d997d512d39f17f9c83b10d4a8928708767b41bb6224b9
4
- data.tar.gz: c9a0bc6226dd8346324c44af5f93906f851b477895b145ed86a89cc2a70f8538
3
+ metadata.gz: 4ef5ebe654a09dce031fef237b193d2f537550b3f6e05e234214fe2c9bba8a21
4
+ data.tar.gz: bde3cb46ee3b74ea6a2812d98b69c73e8ea5ad5f7a89a838350a290a76917eec
5
5
  SHA512:
6
- metadata.gz: f503a443b9e205299c93eadd6c4f878f8fe025a2d0bd0514629fcdd38945f533b948afa656aa3e57bd8b5bfc2ade4213f9c77380d5bcc395c279a23b247c1da6
7
- data.tar.gz: 1f533117654e06d3e2acde45f3c053eb99c9c41030b9a844fa598482372db23e24029fe3eaf7514ce7033c8a4c94b9bcd11b62209d00e7ab0b56ccc9458e6e66
6
+ metadata.gz: df70a1340ca701bebd079dcd7b514f1657c5aa2a9e4117127a3003fdb0a63487aeb44e981da443b40801194e556be881f181d729cced3d0ed8fc07b08191e4f1
7
+ data.tar.gz: 90623814fed971c9c310f00ea3f1de694b624a15d634a0452e263786bdb3b934891023431af787bfe0cfdc2146bef9f9a3ce29f201d86e85401d3304648c68a5
data/TODO.conformance.md CHANGED
@@ -120,7 +120,7 @@ pinned commit) is measured via `rake conformance:w3c`:
120
120
 
121
121
  | mode | pass-rate |
122
122
  |---|---|
123
- | pipeline | 4644/4696 measured + 5 budget exclusions (98.96% of measured) |
123
+ | pipeline | 4649/4696 (98.91%) — FIRST FULL-DENOMINATOR MEASURE, all 94 batches in one rake run |
124
124
 
125
125
  (2026-11-02, bundled-stack measurement with the escape,
126
126
  hint-following, pattern-alternative, identity-crash, engine-set,
@@ -144,7 +144,7 @@ lockfile refresh) → 4411 (93.9%) → 4430 (94.3%) → 4435 (94.4%) →
144
144
  → 4557 (97.0%) → 4565 (97.2%) → 4569 (97.3%) → 4562 (drift raw) →
145
145
  4563 (97.17%, ref-namespace round) → 4573 (97.38%, group-content
146
146
  round) → 4578 (97.49%, attribute-prohibition round) → 4585 (97.64%,
147
- multi-document round) → 4588 (97.7%, type-binding round) → 4592 (97.79%, model-161 ride) → 4611 (98.19%, engine-406 ride) → 4613 (98.23%, temporal-enumeration round) → 4614 (98.25%, simple-type-children round) → 4617 (98.32%, ic-ancestor-scope round) → 4637 (parent-context rounds + chameleon round 1.2.109) → 4637 (nested-type-candidates round, 1.2.110) → 4637 (redefine-cycle round, 1.2.111) → 4644 measured (98.96% of measured, train-adoption round: moxml 0.6.6 + leptris 1.9.418.0), +683 net over the 1.2.43 baseline. The escapes round (PR #197 — \d→\p{Nd},
147
+ multi-document round) → 4588 (97.7%, type-binding round) → 4592 (97.79%, model-161 ride) → 4611 (98.19%, engine-406 ride) → 4613 (98.23%, temporal-enumeration round) → 4614 (98.25%, simple-type-children round) → 4617 (98.32%, ic-ancestor-scope round) → 4637 (parent-context rounds + chameleon round 1.2.109) → 4637 (nested-type-candidates round, 1.2.110) → 4637 (redefine-cycle round, 1.2.111) → 4644 measured (98.96% of measured, train-adoption round: moxml 0.6.6 + leptris 1.9.418.0) → 4649 (98.91%, quadratic-dispatch round — first FULL-denominator measure, the 5 reZ budget exclusions retired), +688 net over the 1.2.43 baseline. The escapes round (PR #197 — \d→\p{Nd},
148
148
  \w/\W/\s to their XSD value spaces) collapses Regex 120→69;
149
149
  same-step pattern facets now behave as alternatives and
150
150
  whitespace-only text reaches the facets intact (PR #200), Regex
@@ -612,6 +612,32 @@ tuple keys, node-set field violations and absent-field rows handled, cross-eleme
612
612
  the RAW validity boolean — expected-INVALID rows reading false
613
613
  are PASSES; the first assembly inverted them and manufactured 9
614
614
  phantom reZ regressions before the correction.
615
+ - QUADRATIC-DISPATCH ROUND (2026-11-12, release pending): the
616
+ per-rule profile on reZ003v (a trivial `[\w]` pattern over a
617
+ 232KB instance of ~65k `<value>` siblings) showed the RULES at
618
+ ~3.3s and the DISPATCH at ~100-370s — `same_name_sibling_index`
619
+ scanned the parent's raw children per element (O(N) per child,
620
+ O(N²) per parent) TIMES five category walks, and
621
+ `positional_children` re-walked the parent's particle tree per
622
+ element. THREE cuts, all semantics-preserving: (a) the sibling
623
+ ordinal is now a per-parent identity-keyed map built in ONE
624
+ O(N) pass (running same-name count — identical to the old
625
+ take(position).count reading); (b) the dispatch skips the scan
626
+ when the content model holds at most ONE same-named candidate
627
+ (positional binding only matters for DUPLICATE declarations,
628
+ isDefault079); (c) positional_children memoizes per
629
+ (declaration, name) on the resolver (schema immutable once
630
+ indexed). reZ003v validate 373.6s → 2.94s (127×); rspec 1663
631
+ green and 40s faster; rubocop clean. LEDGER: 4649/4696
632
+ (98.91%) — the FIRST FULL-DENOMINATOR MEASURE of the campaign:
633
+ ALL 94 batches completed in one rake run, the 5 reZ budget
634
+ exclusions RETIRED (reZ003v/005v/005i/006v/006i measured and
635
+ PASSING in-run), and the batch-03000 carry-over convention
636
+ RETIRED (reI003's tax was the same dispatch overhead, not regex
637
+ backtracking — 50/50 fresh). ZERO regressions vs the 1.2.112
638
+ ledger (47 still-failing in both — the standing remainder:
639
+ contested 24, the upstream filings' legs, the parked redesign
640
+ tails).
615
641
  - TRAIN ADOPTION REGRESSION — CHARACTER REFERENCES (2026-11-11):
616
642
  the 2026-11-10 adoption verdict ("conformance-IDENTICAL") was
617
643
  WRONG for one class: moxml 0.6.4 stopped decoding numeric
@@ -80,8 +80,17 @@ module Lutaml
80
80
  resolver.resolve(parent.name, parent.namespace_uri)
81
81
  candidates = resolver.positional_children(parent_declaration,
82
82
  xml_element.name)
83
- positional = candidates[same_name_sibling_index(xml_element,
84
- parent)]
83
+ # The scan only matters when the parent's content model
84
+ # holds DUPLICATE same-named declarations (isDefault079);
85
+ # a sole candidate binds regardless of position — skipping
86
+ # the sibling scan keeps wide documents linear.
87
+ positional = if candidates.size > 1
88
+ candidates[same_name_sibling_index(
89
+ xml_element, parent
90
+ )]
91
+ else
92
+ candidates.first
93
+ end
85
94
  # The binding must agree with the child's OWN namespace:
86
95
  # an unqualified ref's global namesake never serves a
87
96
  # qualified local namesake (particlesQ016).
@@ -245,21 +254,31 @@ module Lutaml
245
254
  override && override.value.to_s.split(":").last
246
255
  end
247
256
 
248
- # The child's position among its same-named siblings — the index
257
+ # The child's ordinal among its same-named siblings — the index
249
258
  # the positional binding (duplicate same-named declarations,
250
- # isDefault079) binds to. Computed on the RAW moxml nodes: the
259
+ # isDefault079) binds to. Keyed on the RAW moxml nodes: the
251
260
  # wrapper objects are not identity-stable across accessor calls
252
- # (the IdUniquenessRule lesson).
261
+ # (the IdUniquenessRule lesson). The per-parent ordinal map is
262
+ # memoized — a document of N same-named siblings costs one
263
+ # O(N) pass per parent instead of an O(N) scan per child
264
+ # (reZ003v: 65k siblings made every dispatch quadratic, times
265
+ # five category walks).
253
266
  #
254
267
  # @return [Integer]
255
268
  def same_name_sibling_index(element, parent)
256
- raw_children = Array(parent.moxml_element.elements)
257
- position = raw_children.index do |node|
258
- node.equal?(element.moxml_element)
269
+ parent_raw = parent.moxml_element
270
+ cache = (@sibling_ordinals ||= {}.compare_by_identity)
271
+ ordinals = cache[parent_raw]
272
+ if ordinals.nil?
273
+ ordinals = {}.compare_by_identity
274
+ counts = Hash.new(0)
275
+ parent_raw.elements.each do |node|
276
+ ordinals[node] = counts[node.name]
277
+ counts[node.name] += 1
278
+ end
279
+ cache[parent_raw] = ordinals
259
280
  end
260
- return 0 if position.nil?
261
-
262
- raw_children.take(position).count { |node| node.name == element.name }
281
+ ordinals.fetch(element.moxml_element, 0)
263
282
  end
264
283
 
265
284
  # Whether the parent's content model holds a processContents
@@ -607,17 +607,28 @@ module Lutaml
607
607
  # @param parent_declaration [Lutaml::Xml::Schema::Xsd::Element, nil]
608
608
  # @param local_name [String]
609
609
  # @return [Array<Lutaml::Xml::Schema::Xsd::Element>]
610
+ #
611
+ # Memoized per (declaration, local name): the dispatch calls
612
+ # this for every element, and the particle-tree walk cost is
613
+ # constant across elements of the same parent type (the schema
614
+ # is immutable once indexed).
610
615
  def positional_children(parent_declaration, local_name)
616
+ key = [parent_declaration.object_id, local_name]
617
+ cache = (@positional_children ||= {})
618
+ cached = cache[key]
619
+ return cached if cached
620
+
611
621
  type = parent_declaration&.complex_type ||
612
622
  resolve_type(parent_declaration)
613
- return [] unless type.is_a?(Lutaml::Xml::Schema::Xsd::ComplexType)
614
-
615
- particles = []
616
- collect_named_particles(particle_for_type(type), local_name,
617
- particles, {}.compare_by_identity, 0)
618
- return [] unless particles.all? { |p| @namespaces.key?(p) }
619
-
620
- particles
623
+ computed = []
624
+ if type.is_a?(Lutaml::Xml::Schema::Xsd::ComplexType)
625
+ particles = []
626
+ collect_named_particles(particle_for_type(type), local_name,
627
+ particles, {}.compare_by_identity, 0)
628
+ computed = particles if particles.all? { |p| @namespaces.key?(p) }
629
+ end
630
+ cache[key] = computed
631
+ computed
621
632
  end
622
633
 
623
634
  private
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Lutaml
4
4
  module Xsd
5
- VERSION = "1.2.112"
5
+ VERSION = "1.2.113"
6
6
  end
7
7
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: lutaml-xsd
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.112
4
+ version: 1.2.113
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ribose Inc.