canon 0.3.20 → 0.3.21

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: 581a483d81eba304f20fefc4e14777efced86bd7b829d1181209bee00c3ac7df
4
- data.tar.gz: ea8929a3686f268e5c99376a0aded4999714b298fe7a558c2ac713929fe3d6ac
3
+ metadata.gz: fd899c17d1512c815d189076fe08721dbe66b7d964f8bac69abde35f207b8c76
4
+ data.tar.gz: 205ad0b365288bf883f2ce57f68b612217151e30ae90fb0f959b29f252063826
5
5
  SHA512:
6
- metadata.gz: 19d37bd2c6fec3949791787ca2a64e24afce11588bf17d335efa81fba225d945d77e578a2eb8dae0b8df48904f0f824830f43018cd78830448e05da127498f99
7
- data.tar.gz: d3c3f990c6a133cb9cada5dbfae4bb752f524c972f3b75253c24c71226e7ad86138467559e533611b20beb95520f4441bfb80fafd4648bacf5b63e0717a69e70
6
+ metadata.gz: 5aa4d552592cda5a55417415efb225151eb0d39d899fe0471eb26f10ceeca64ae56de786d8e807e0085264ba800cfd5937a6ec15e182e71957381414f3036105
7
+ data.tar.gz: b52ceb55e3ca6c6affcca3c7091a0e3f6e22d1c077b65732e7073915c3f6914cb86a61e675b11636baeb1ea09e94c223c933e961c0b366fb3e621c592a50170b
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.20"
4
+ VERSION = "0.3.21"
5
5
  end
@@ -5,10 +5,18 @@ module Canon
5
5
  # Character encoder for C14N 1.1
6
6
  # Handles UTF-8 encoding and character reference encoding per spec
7
7
  class CharacterEncoder
8
+ # Most text and attribute values contain nothing to escape —
9
+ # the zero-allocation guard keeps those from paying the gsub
10
+ # copy on every render.
11
+ TEXT_ESCAPABLE = /[&<>\r]/
12
+ ATTRIBUTE_ESCAPABLE = /[&<"\t\n\r]/
13
+
8
14
  # Encode text node content
9
15
  # Replace: & → &amp;, < → &lt;, > → &gt;, #xD → &#xD;
10
16
  def encode_text(text)
11
- text.gsub(/[&<>\r]/) do |char|
17
+ return text unless text.match?(TEXT_ESCAPABLE)
18
+
19
+ text.gsub(TEXT_ESCAPABLE) do |char|
12
20
  case char
13
21
  when "&" then "&amp;"
14
22
  when "<" then "&lt;"
@@ -22,7 +30,9 @@ module Canon
22
30
  # Replace: & → &amp;, < → &lt;, " → &quot;,
23
31
  # #x9 → &#x9;, #xA → &#xA;, #xD → &#xD;
24
32
  def encode_attribute(value)
25
- value.gsub(/[&<"\t\n\r]/) do |char|
33
+ return value unless value.match?(ATTRIBUTE_ESCAPABLE)
34
+
35
+ value.gsub(ATTRIBUTE_ESCAPABLE) do |char|
26
36
  case char
27
37
  when "&" then "&amp;"
28
38
  when "<" then "&lt;"
@@ -14,7 +14,7 @@ module Canon
14
14
  def process_namespaces(element, output, parent_element = nil)
15
15
  return unless element.in_node_set?
16
16
 
17
- namespaces = element.sorted_namespace_nodes.select(&:in_node_set?)
17
+ namespaces = renderable_namespaces(element)
18
18
 
19
19
  # Check if we need to emit xmlns="" for empty default namespace
20
20
  if should_emit_empty_default_namespace?(element, namespaces,
@@ -61,26 +61,45 @@ module Canon
61
61
  end
62
62
 
63
63
  # Check if a namespace node should be skipped
64
- def should_skip_namespace?(ns, _element, parent_element)
64
+ def should_skip_namespace?(ns, element, parent_element)
65
65
  # Skip xml namespace with standard URI
66
66
  return true if ns.xml_namespace?
67
67
 
68
68
  # Skip if an ancestor already declared this namespace
69
- return true if namespace_declared_by_ancestor?(ns, parent_element)
69
+ return true if namespace_declared_by_ancestor?(ns, element,
70
+ parent_element)
70
71
 
71
72
  false
72
73
  end
73
74
 
74
75
  # Check if a namespace is already declared by an ancestor
75
- def namespace_declared_by_ancestor?(ns, parent_element)
76
+ def namespace_declared_by_ancestor?(ns, element, parent_element)
76
77
  return false unless parent_element
77
78
 
78
- parent_ns = parent_element.namespace_nodes.find do |parent_ns|
79
+ parent_nodes = parent_element.namespace_nodes
80
+ # Elements that declared nothing share their parent's scope
81
+ # object (TreeBuilder scope sharing), so identical arrays mean
82
+ # identical bindings — every namespace of this element is
83
+ # already declared above and will be skipped without a scan.
84
+ return true if parent_nodes.equal?(element.namespace_nodes)
85
+
86
+ parent_ns = parent_nodes.find do |parent_ns|
79
87
  parent_ns.prefix == ns.prefix && parent_ns.in_node_set?
80
88
  end
81
89
 
82
90
  parent_ns && parent_ns.uri == ns.uri
83
91
  end
92
+
93
+ # Sorted, in-node-set namespaces for one element. Undeclaring
94
+ # elements share one frozen array object, and node-set marking is
95
+ # fixed before processing runs, so the rendered form is cached
96
+ # per array identity for the lifetime of this handler (one
97
+ # canonicalization run).
98
+ def renderable_namespaces(element)
99
+ cache = (@renderable_namespaces ||= {}.compare_by_identity)
100
+ nodes = element.namespace_nodes
101
+ cache[nodes] ||= nodes.sort_by(&:local_name).select(&:in_node_set?)
102
+ end
84
103
  end
85
104
  end
86
105
  end
@@ -53,15 +53,26 @@ module Canon
53
53
  (@attribute_nodes ||= []) << attribute_node
54
54
  end
55
55
 
56
- # Get namespace nodes in sorted order (lexicographically by local name)
56
+ # Get namespace nodes in sorted order (lexicographically by local name).
57
57
  def sorted_namespace_nodes
58
- namespace_nodes.sort_by(&:local_name)
58
+ nodes = namespace_nodes
59
+ return nodes if nodes.empty?
60
+
61
+ nodes.sort_by(&:local_name)
59
62
  end
60
63
 
61
- # Get attribute nodes in sorted order (by namespace URI then local name)
64
+ # Get attribute nodes in sorted order (by namespace URI then local
65
+ # name). A comparator block instead of sort_by keys allocates no
66
+ # per-attribute key arrays; (uri, name) pairs are unique per
67
+ # element (duplicates are resolved at build), so the order is
68
+ # identical.
62
69
  def sorted_attribute_nodes
63
- attribute_nodes.sort_by do |attr|
64
- [attr.namespace_uri.to_s, attr.local_name]
70
+ attrs = attribute_nodes
71
+ return attrs if attrs.empty?
72
+
73
+ attrs.sort do |a, b|
74
+ (a.namespace_uri.to_s <=> b.namespace_uri.to_s).nonzero? ||
75
+ (a.local_name <=> b.local_name)
65
76
  end
66
77
  end
67
78
 
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.20
4
+ version: 0.3.21
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ribose Inc.