canon 0.3.47 → 0.3.49
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/CLAUDE.md +1 -1
- data/lib/canon/diff/diff_node_enricher.rb +37 -15
- data/lib/canon/version.rb +1 -1
- data/lib/canon/xml/c14n.rb +21 -23
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 8f3f2bed07760530a3f122f18e184ac0e925db9d6551bacc46ef896552fb79ce
|
|
4
|
+
data.tar.gz: 4de5e38b9be98f354a843bb3fdd1c725534fab28f6078cbf95a7c278d0c2596b
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: '002904526d2626e462005b4a3627540a1d6587c458b60adb871ad03134054485c2712707579106f727fa00066b213d8db903f96be5d695470aed08ec82157461'
|
|
7
|
+
data.tar.gz: 537566ace8ff29b3124bbd9fd2789250d95d539de60b0e8ba89a3d5ebf83dee871fe0e7525d8d76381286f8adf71886957a3c4a29b1887f861fc0201b44bd862
|
data/CLAUDE.md
CHANGED
|
@@ -145,7 +145,7 @@ Engine A/B testing: `CANON_XML_BACKEND=nokogiri bundle exec rspec` (or `=moxml`
|
|
|
145
145
|
|
|
146
146
|
### C14N Engines
|
|
147
147
|
|
|
148
|
-
`Canon::Xml::C14n.canonicalize`
|
|
148
|
+
`Canon::Xml::C14n.canonicalize` uses leptris' native C-side C14N 1.1 by DEFAULT (since libleptris 1.9.178 / gem 1.9.178.0) — 23x faster than the Ruby processor through canon's API (1MB document). The native parse runs `noblanks: true` (compact bytes, matching the Ruby lane's product), relative namespace URIs raise identically, and document-level PIs serialize in spec-correct document order (the Ruby processor's root-first order was non-conformant for prolog PIs). `CANON_C14N_BACKEND=ruby` forces the Ruby processor. `canonicalize_subset` and `with_comments: true` always use the Ruby processor. The parity spec (`spec/canon/xml/c14n_engine_parity_spec.rb`) pins byte-identity; its pending cases track intentional divergences (document-order PIs) and the canon-side CR-reference finding.
|
|
149
149
|
|
|
150
150
|
### YAML Engines
|
|
151
151
|
|
|
@@ -41,6 +41,11 @@ module Canon
|
|
|
41
41
|
# Track occurrences for text_content dimension to find correct element instance
|
|
42
42
|
@text_occurrence1 = Hash.new(0)
|
|
43
43
|
@text_occurrence2 = Hash.new(0)
|
|
44
|
+
# Opener-offset caches: one scan per unique element name per
|
|
45
|
+
# document replaces the per-call regex walk (which was also
|
|
46
|
+
# quadratic on repeated element names).
|
|
47
|
+
@opener_offsets1 = {}
|
|
48
|
+
@opener_offsets2 = {}
|
|
44
49
|
end
|
|
45
50
|
|
|
46
51
|
def enrich
|
|
@@ -1461,27 +1466,44 @@ range_start, range_end)
|
|
|
1461
1466
|
# tag (no inside-the-element correction — see
|
|
1462
1467
|
# locate_element_at_index).
|
|
1463
1468
|
def count_elements_before_position_open(text, char_offset, opener)
|
|
1464
|
-
|
|
1465
|
-
pos = 0
|
|
1466
|
-
while (hit = text.index(opener, pos)) && hit < char_offset
|
|
1467
|
-
count += 1
|
|
1468
|
-
pos = hit + 1
|
|
1469
|
-
end
|
|
1470
|
-
count
|
|
1469
|
+
count_openers_before(opener_offsets(text, opener), char_offset)
|
|
1471
1470
|
end
|
|
1472
1471
|
|
|
1473
1472
|
def count_elements_before_position(text, char_offset, element_name)
|
|
1474
|
-
#
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1473
|
+
# Subtract 1 because the count includes the element we are inside
|
|
1474
|
+
[count_openers_before(opener_offsets(text, opener_for(element_name)),
|
|
1475
|
+
char_offset) - 1, 0].max
|
|
1476
|
+
end
|
|
1477
|
+
|
|
1478
|
+
# Count of opener offsets strictly before char_offset — binary
|
|
1479
|
+
# search over the cached, ascending offset array.
|
|
1480
|
+
def count_openers_before(offsets, char_offset)
|
|
1481
|
+
idx = offsets.bsearch_index { |offset| offset >= char_offset }
|
|
1482
|
+
idx || offsets.length
|
|
1483
|
+
end
|
|
1484
|
+
|
|
1485
|
+
# All opener offsets for one opener regex in one document,
|
|
1486
|
+
# cached per text (one linear scan per unique element name per
|
|
1487
|
+
# document — callers previously re-scanned per occurrence
|
|
1488
|
+
# check, which was also quadratic on repeated element names).
|
|
1489
|
+
def opener_offsets(text, opener)
|
|
1490
|
+
if text.equal?(@text1)
|
|
1491
|
+
(@opener_offsets1[opener.source] ||= scan_opener_offsets(text, opener))
|
|
1492
|
+
elsif text.equal?(@text2)
|
|
1493
|
+
(@opener_offsets2[opener.source] ||= scan_opener_offsets(text, opener))
|
|
1494
|
+
else
|
|
1495
|
+
scan_opener_offsets(text, opener)
|
|
1496
|
+
end
|
|
1497
|
+
end
|
|
1498
|
+
|
|
1499
|
+
def scan_opener_offsets(text, opener)
|
|
1500
|
+
offsets = []
|
|
1478
1501
|
pos = 0
|
|
1479
|
-
while (hit = text.index(opener, pos))
|
|
1480
|
-
|
|
1502
|
+
while (hit = text.index(opener, pos))
|
|
1503
|
+
offsets << hit
|
|
1481
1504
|
pos = hit + 1
|
|
1482
1505
|
end
|
|
1483
|
-
|
|
1484
|
-
[count - 1, 0].max
|
|
1506
|
+
offsets
|
|
1485
1507
|
end
|
|
1486
1508
|
end
|
|
1487
1509
|
end
|
data/lib/canon/version.rb
CHANGED
data/lib/canon/xml/c14n.rb
CHANGED
|
@@ -23,39 +23,37 @@ module Canon
|
|
|
23
23
|
end
|
|
24
24
|
|
|
25
25
|
# leptris' C-side C14N 1.1 — 23x faster than the Ruby processor
|
|
26
|
-
# through canon's own API (1MB document) and
|
|
27
|
-
#
|
|
28
|
-
#
|
|
29
|
-
# (
|
|
30
|
-
#
|
|
31
|
-
#
|
|
32
|
-
#
|
|
33
|
-
#
|
|
34
|
-
#
|
|
35
|
-
#
|
|
36
|
-
#
|
|
37
|
-
#
|
|
38
|
-
#
|
|
39
|
-
#
|
|
40
|
-
# root-first order is non-conformant for prolog PIs).
|
|
41
|
-
# Relative namespace URIs raise exactly as the Ruby lane does.
|
|
42
|
-
# Comments mode keeps the Ruby path (the native seam exposes no
|
|
43
|
-
# with-comments form).
|
|
26
|
+
# through canon's own API (1MB document) and the DEFAULT lane
|
|
27
|
+
# since libleptris 1.9.178 (gem 1.9.178.0): the leptris#1117
|
|
28
|
+
# families are closed by the native parse-policy knob
|
|
29
|
+
# (`noblanks: true` — same compact bytes as the Ruby lane's
|
|
30
|
+
# parse) and by the canonicalizer itself (whitespace-only PI
|
|
31
|
+
# data dropped, document-level "\n" separators inserted). The
|
|
32
|
+
# one residual byte difference vs the Ruby lane is intentional
|
|
33
|
+
# and a correctness improvement: document-level PIs now
|
|
34
|
+
# serialize in document order (REC-xml-c14n 2.1) instead of
|
|
35
|
+
# the Ruby lane's non-conformant root-first reorder. Relative
|
|
36
|
+
# namespace URIs raise exactly as the Ruby lane does.
|
|
37
|
+
# Comments mode keeps the Ruby path regardless (the native
|
|
38
|
+
# seam exposes no with-comments form). CANON_C14N_BACKEND=ruby
|
|
39
|
+
# forces the stdlib lane.
|
|
44
40
|
def self.native_canonicalize(xml, with_comments)
|
|
45
41
|
return nil if with_comments
|
|
46
42
|
return nil if RUBY_ENGINE == "opal"
|
|
47
|
-
return nil
|
|
43
|
+
return nil if ENV["CANON_C14N_BACKEND"].to_s.casecmp("ruby").zero?
|
|
48
44
|
return nil unless Canon::XmlBackend.moxml? &&
|
|
49
45
|
Canon::XmlParsing.moxml_adapter_name == :leptris
|
|
50
46
|
return nil unless native_c14n_available?
|
|
51
47
|
|
|
52
48
|
validate_relative_namespaces!(xml)
|
|
53
49
|
|
|
54
|
-
doc = Canon::XmlParsing.moxml_context.parse(xml,
|
|
55
|
-
|
|
50
|
+
doc = Canon::XmlParsing.moxml_context.parse(xml,
|
|
51
|
+
readonly: true,
|
|
52
|
+
strict: false,
|
|
53
|
+
noblanks: true)
|
|
56
54
|
begin
|
|
57
|
-
doc.native.
|
|
58
|
-
|
|
55
|
+
doc.native.c14n(::Leptris::XML::FFI::C14N_1_1, nil,
|
|
56
|
+
mode: ::Leptris::XML::FFI::C14N_MODE_CANONICAL)
|
|
59
57
|
ensure
|
|
60
58
|
doc.free
|
|
61
59
|
end
|