moxml 0.5.1 → 0.5.3

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: ca6fefbf114afcaf13d9c241b4df5081eef1e004019618ca12d31a655d89dd53
4
- data.tar.gz: d361373f16dafff997d4ca53b5dbc23c332b09331e8f49ef5abed09f526bdea7
3
+ metadata.gz: 6cadb7739071fa9a823f6a08fe5daa33c2313d22e4c4454aa491e975f62da5ee
4
+ data.tar.gz: b6682343586e2bf873c9067fccc68f03f2c67c8b10eafc418fcad6663320f2f2
5
5
  SHA512:
6
- metadata.gz: 0a2e61a613511970e4c6945c074a578aefcb12591def797a726bf0959796c43dbf2e71b77509271b1dad15b9f0f1e81f5ec3b8b0af0e384bccad35745bc2dbb6
7
- data.tar.gz: 183964ef083c888b3b7d58b896a547acf25c0414d997040c10b805b8379088825c2ec996b149aa9e8fe51b6419135d1341d8ebe9c88ddb0656101e25d3291557
6
+ metadata.gz: 67da3161b095e47aafe6d7e7327c2125a4458a9ef557c41c5f9d6465fccb01374d29c409178910b39426cf0cf7a42140d6858d4177c3f63b529690f397303796
7
+ data.tar.gz: c88f55e02c026e18f4c1a25578d9e27094835288e6ccc0dccff40633ff31029975125d9fcfb51ae5353f00ad5fa54fd8bef135d9e44c64da794b45513f728903
@@ -546,6 +546,10 @@ module Moxml
546
546
  # returns those without name accessors). Both engines resolve
547
547
  # expression prefixes against document-declared namespaces.
548
548
  NATIVE_XPATH_CACHE = XPath::Cache.new(100)
549
+ # The gate decision is expression-intrinsic; re-walking the
550
+ # cached AST three times per xpath call cost ~23% of repeated
551
+ # selective queries, so the boolean caches beside it.
552
+ NATIVE_GATE_CACHE = XPath::Cache.new(100)
549
553
 
550
554
  def xpath(node, expression, namespaces = {})
551
555
  native = native_xpath(node, expression, namespaces)
@@ -623,11 +627,13 @@ module Moxml
623
627
  # attribute-node results, or the nokogiri-compat xmlns:
624
628
  # reserved prefix convention.
625
629
  def native_expression?(expression)
626
- ast = XPath::Parser.parse_with_cache(expression)
627
- return false if ast_contains_type?(ast, :variable)
628
- return false if uses_xmlns_prefix?(ast)
630
+ NATIVE_GATE_CACHE.get_or_set(expression) do
631
+ ast = XPath::Parser.parse_with_cache(expression)
632
+ next false if ast_contains_type?(ast, :variable)
633
+ next false if uses_xmlns_prefix?(ast)
629
634
 
630
- !selects_attribute_results?(ast)
635
+ !selects_attribute_results?(ast)
636
+ end
631
637
  rescue XPath::SyntaxError
632
638
  false
633
639
  end
@@ -7,6 +7,11 @@ require "libxml"
7
7
  module Moxml
8
8
  module Adapter
9
9
  class Libxml < Base
10
+ # DOCTYPE prolog sniffer; see parse for the index-then-match
11
+ # strategy that keeps this off the hot path.
12
+ DOCTYPE_RE = /<!DOCTYPE\s+([^\s>]+)(?:\s+PUBLIC\s+"([^"]+)"\s+"([^"]+)"|\s+SYSTEM\s+"([^"]+)")?\s*>/i
13
+ private_constant :DOCTYPE_RE
14
+
10
15
  autoload :EntityRefRegistry, "moxml/adapter/libxml/entity_ref_registry"
11
16
 
12
17
  # Wrapper class to store DOCTYPE information
@@ -89,8 +94,15 @@ module Moxml
89
94
  # parameter or XML declaration for byte interpretation.
90
95
  xml_string = preprocess_entities(xml_string)
91
96
 
92
- # Extract DOCTYPE before parsing
93
- doctype_match = xml_string.match(/<!DOCTYPE\s+([^\s>]+)(?:\s+PUBLIC\s+"([^"]+)"\s+"([^"]+)"|\s+SYSTEM\s+"([^"]+)")?\s*>/i)
97
+ # Extract DOCTYPE before parsing. Locate the literal first:
98
+ # a plain memscan is ~2x cheaper than the regex's literal
99
+ # prefix scan on doctype-less documents (the common case),
100
+ # and matching from the found offset keeps captures
101
+ # identical.
102
+ doctype_idx = xml_string.index("<!DOCTYPE")
103
+ doctype_match = if doctype_idx
104
+ xml_string.match(DOCTYPE_RE, doctype_idx)
105
+ end
94
106
 
95
107
  native_doc = begin
96
108
  # Handle both string and file inputs
@@ -39,11 +39,15 @@ module Moxml
39
39
  return nil if uri.nil?
40
40
 
41
41
  element.attributes.find do |attr|
42
- local_name(attr.name) == local && attribute_uri(element, attr) == uri
42
+ # attr.name allocates on several adapters (native string,
43
+ # plus leptris' force_encoding dup) — read it once per probe
44
+ attr_name = attr.name
45
+ local_name(attr_name) == local && attribute_uri(element, attr, attr_name) == uri
43
46
  end
44
47
  else
45
48
  element.attributes.find do |attr|
46
- local_name(attr.name) == name && attribute_uri(element, attr).nil?
49
+ attr_name = attr.name
50
+ local_name(attr_name) == name && attribute_uri(element, attr, attr_name).nil?
47
51
  end
48
52
  end
49
53
  end
@@ -170,11 +174,11 @@ module Moxml
170
174
  # adapters that report a prefix without resolving it (rexml/ox for
171
175
  # the prebound xml prefix) and adapters that expose neither
172
176
  # namespace nor separate prefix (leptris qualified names).
173
- def attribute_uri(element, attr)
177
+ def attribute_uri(element, attr, attr_name = nil)
174
178
  ns = attr.namespace
175
179
  return ns.uri unless ns.nil? || ns.uri.to_s.empty?
176
180
 
177
- prefix = ns&.prefix || prefix_part(attr.name)
181
+ prefix = ns&.prefix || prefix_part(attr_name || attr.name)
178
182
  return nil if prefix.nil? || prefix.empty?
179
183
  return XML_NAMESPACE_URI if prefix == "xml"
180
184
 
data/lib/moxml/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Moxml
4
- VERSION = "0.5.1"
4
+ VERSION = "0.5.3"
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: moxml
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.1
4
+ version: 0.5.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ribose Inc.