canon 0.3.32 → 0.3.33

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: 44f563b9b8ac9fa82cc08c9413ca4a17afd8080c524d6eb92536572356892539
4
- data.tar.gz: 4a7cbe635232ca07fa8b9924e46c027849145fbc6ac6846df3ec9de4c51edbc4
3
+ metadata.gz: 6094f49ebe4d3b32a5396a0db4d62dbc502156d277ed12115029d50eb36f37ff
4
+ data.tar.gz: 8cf4e6d0b0e1feaaddaed2b996d98f9fd36adb4b250c3460ac2ae20a5263689a
5
5
  SHA512:
6
- metadata.gz: a228ef2b06e3e59545aab338afd72cdcf4943c32b34fa24063e43884f01b41444558966cb121986513d28041a92f0918864106886875ef44da9b3f00e53dcdc2
7
- data.tar.gz: 9dbddd3a07e0d6076118ed7e2619de864b9bf3c99ff68c109fa31805a912a1839054d4cf843f712faa2df2a2ee619806d256a47bdce9399feb422721d6117e71
6
+ metadata.gz: 9b53fdc9296da8e354cbb99cc194f6dab11544a52c1cda09370a01f413ce57dc5b04d47d99d496d9a3dcb9558f934dd75a5124be10406de74106e6750b86f51a
7
+ data.tar.gz: b8cf46fd5d9380584db39732281d18e7186c33927518ac46e5a42fec09d7d3cc5aad167ca1a85c879f087a54a934e8a329b879c43b4cc3aade63512503d0fe80
data/lib/canon/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Canon
4
- VERSION = "0.3.32"
4
+ VERSION = "0.3.33"
5
5
  end
@@ -164,6 +164,21 @@ module Canon
164
164
  elems1 = children1.select { |n| n.node_type == :element }
165
165
  elems2 = children2.select { |n| n.node_type == :element }
166
166
 
167
+ # FAST PATH: pairwise-corresponding children. When both lists
168
+ # are the same length and every position pairs elements with
169
+ # equal name, namespace URI, and identity value, the full
170
+ # matcher's output is exactly the positional pairing: unique
171
+ # identity keys pair same-with-same, duplicate keys pair
172
+ # last-with-last in the identity phase and the rest pair in
173
+ # order in the positional phase. No deleted/inserted results
174
+ # either way. Skip the identity/positional/class machinery.
175
+ if !elems1.empty? && elems1.length == elems2.length &&
176
+ pairwise_corresponding?(elems1, elems2)
177
+ record_positional_matches(elems1, elems2, path,
178
+ recursive: recursive)
179
+ return
180
+ end
181
+
167
182
  # Positions by identity: elems.index(elem) was an O(n) scan per
168
183
  # recorded match — O(n²) per level on element-heavy parents.
169
184
  positions1 = {}
@@ -263,6 +278,48 @@ module Canon
263
278
  end
264
279
  end
265
280
 
281
+ # True when every position pairs elements with equal name,
282
+ # namespace URI, and identity value (nil-safe).
283
+ def pairwise_corresponding?(elems1, elems2)
284
+ elems1.each_index.all? do |i|
285
+ e1 = elems1[i]
286
+ e2 = elems2[i]
287
+ e1.name == e2.name && e1.namespace_uri == e2.namespace_uri &&
288
+ extract_identity(e1) == extract_identity(e2)
289
+ end
290
+ end
291
+
292
+ # Record the positional pairing as :matched MatchResults,
293
+ # descending per pair in recursive mode (match_trees' ordering
294
+ # contract).
295
+ def record_positional_matches(elems1, elems2, path, recursive:)
296
+ elems1.each_index do |i|
297
+ elem1 = elems1[i]
298
+ elem_path = element_path(path, elem1)
299
+ @matches << MatchResult.new(
300
+ status: :matched,
301
+ elem1: elem1,
302
+ elem2: elems2[i],
303
+ path: elem_path,
304
+ pos1: i,
305
+ pos2: i,
306
+ )
307
+ if recursive
308
+ match_children(elem1.children, elems2[i].children, elem_path)
309
+ end
310
+ end
311
+ end
312
+
313
+ # Path segment for an element: expanded under a namespace,
314
+ # bare otherwise.
315
+ def element_path(path, elem)
316
+ if elem.namespace_uri && !elem.namespace_uri.empty?
317
+ path + ["{#{elem.namespace_uri}}#{elem.name}"]
318
+ else
319
+ path + [elem.name]
320
+ end
321
+ end
322
+
266
323
  # Match remaining elements by name and position
267
324
  def match_by_position(elems1, elems2, path, matched1, matched2,
268
325
  recursive:)
@@ -225,6 +225,13 @@ compare_against: nil)
225
225
  failure_means: "Slow formatting affects serialization performance. C14N is critical for digital signatures and XML canonicalization.",
226
226
  compare_against: "Previous branch (main).",
227
227
  },
228
+ data_comparison: {
229
+ name: "Data Comparison",
230
+ icon: "🧮",
231
+ description: "JSON and YAML semantic comparison. Both formats load through their engine gateways (yeptris/stdlib), so this referees engine choice.",
232
+ failure_means: "Slow data comparison affects validation pipelines and test suites. A regression here can also signal an engine lane flip gone wrong.",
233
+ compare_against: "Previous branch (main). Inputs are freshly generated (different values), so the comparison does real work.",
234
+ },
228
235
  }.freeze
229
236
 
230
237
  # Test definitions
@@ -265,6 +272,12 @@ compare_against: nil)
265
272
  { name: "JSON", method: :json_format, desc: "JSON formatting" },
266
273
  { name: "YAML", method: :yaml_format, desc: "YAML formatting" },
267
274
  ],
275
+ data_comparison: [
276
+ { name: "JSON", method: :json_compare_equivalent,
277
+ desc: "JSON equivalence" },
278
+ { name: "YAML", method: :yaml_compare_equivalent,
279
+ desc: "YAML equivalence" },
280
+ ],
268
281
  }.freeze
269
282
 
270
283
  # Test data generators
@@ -555,6 +568,14 @@ compare_against: nil)
555
568
  yaml = DataGenerator.generate_yaml(items: @items)
556
569
  data = YAML.safe_load(yaml, permitted_classes: [Time])
557
570
  measure { Canon.format_yaml(data) }
571
+ when :json_compare_equivalent
572
+ json1 = DataGenerator.generate_json(items: @items)
573
+ json2 = DataGenerator.generate_json(items: @items)
574
+ measure { Canon::Comparison.equivalent?(json1, json2, format: :json) }
575
+ when :yaml_compare_equivalent
576
+ yaml1 = DataGenerator.generate_yaml(items: @items)
577
+ yaml2 = DataGenerator.generate_yaml(items: @items)
578
+ measure { Canon::Comparison.equivalent?(yaml1, yaml2, format: :yaml) }
558
579
  else
559
580
  raise "Unknown benchmark: #{method}"
560
581
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: canon
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.32
4
+ version: 0.3.33
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ribose Inc.