moxml 0.5.24 → 0.5.25
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/.github/workflows/rake.yml +2 -1
- data/docs/_pages/performance.adoc +31 -0
- data/lib/moxml/entity.rb +32 -3
- data/lib/moxml/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 7962623114db23fce306ee38745c51ccf85fe8333c60a274322eb3d5a6dd6270
|
|
4
|
+
data.tar.gz: 00b614eed4a674d4ad43a1cbf1f4dd0483de2e929b7e78728c4aab7106662b56
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 9e49d1c70a4332def347256eccf22f7defa256eaec8290987a73079daa0bcfed0f141f4862a9b457fbbc6f606be5386c3d5717e35e767b1088d2e7f88e0fde5d
|
|
7
|
+
data.tar.gz: d3ad61616f0b7bf163fef6333c59595bbce7b40dc96fd01c7a64f8bf2e8db2419102f3633444d1a54453bff7f6ad170051f75c9756e1c406233e25716426f3c7
|
data/.github/workflows/rake.yml
CHANGED
|
@@ -33,3 +33,34 @@ doc.xpath('//title')
|
|
|
33
33
|
root.xpath('./*/title')
|
|
34
34
|
----
|
|
35
35
|
|
|
36
|
+
|
|
37
|
+
=== Run with YJIT
|
|
38
|
+
|
|
39
|
+
The wrapper layer is Ruby-dispatch-heavy — exactly what YJIT compiles
|
|
40
|
+
well. Enable it for any throughput-sensitive workload (Ruby >= 3.3;
|
|
41
|
+
production-ready since then):
|
|
42
|
+
|
|
43
|
+
[source,bash]
|
|
44
|
+
----
|
|
45
|
+
RUBY_YJIT_ENABLE=1 ruby your_app.rb # or: ruby --yjit
|
|
46
|
+
----
|
|
47
|
+
|
|
48
|
+
Measured against raw engine calls on a 52KB fixture (leptris,
|
|
49
|
+
min-of-N, 100 warm reads): without YJIT the facade adds +23% on tree
|
|
50
|
+
walks and +43% on warm attribute reads; **with YJIT those become
|
|
51
|
+
-12% (faster than the binding's own walk) and +10%**, and `.name`
|
|
52
|
+
reads reach parity. Parse, serialize and duplicate are C-bound
|
|
53
|
+
either way. No semantic surface changes — it is a runtime flag.
|
|
54
|
+
|
|
55
|
+
Consumers on CRuby 3.3+ (canon's pretty-print pipeline, batch
|
|
56
|
+
conversion) should treat it as the default; moxml's own CI
|
|
57
|
+
performance gates run with YJIT enabled.
|
|
58
|
+
|
|
59
|
+
=== Prefer bulk paths for per-node conversion
|
|
60
|
+
|
|
61
|
+
Per-node Ruby iteration pays the wrapper tax per element. When the
|
|
62
|
+
goal is conversion rather than manipulation, the bulk paths move the
|
|
63
|
+
loop into C: `materialize_fields` streams the flattened record
|
|
64
|
+
fields from one C call and measures faster than an equivalent
|
|
65
|
+
raw-Nokogiri DOM walk. See
|
|
66
|
+
link:conversion-apis[Conversion and memory lifecycle APIs].
|
data/lib/moxml/entity.rb
CHANGED
|
@@ -49,6 +49,34 @@ module Moxml
|
|
|
49
49
|
# marker gsub's full-buffer copy entirely.
|
|
50
50
|
NON_STANDARD_ENTITY_RE = /&(?!amp;|lt;|gt;|quot;|apos;)(#{NAME_PATTERN});/
|
|
51
51
|
|
|
52
|
+
# True when a NON-standard named entity exists somewhere (the
|
|
53
|
+
# gsub would then be needed). The leptris binding's C probe
|
|
54
|
+
# (leptris-ruby#124) answers in one pass at memcmp speed; it is
|
|
55
|
+
# resolved lazily because adapters load on demand. Otherwise:
|
|
56
|
+
# the cheap `&` pre-filter, then the Ruby regex.
|
|
57
|
+
def nonstandard_entity?(str)
|
|
58
|
+
probe = c_entity_probe
|
|
59
|
+
if probe
|
|
60
|
+
probe.call(str, str.bytesize) == 1
|
|
61
|
+
else
|
|
62
|
+
str.include?("&") && str.match?(NON_STANDARD_ENTITY_RE)
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def c_entity_probe
|
|
67
|
+
if @c_entity_probe.nil?
|
|
68
|
+
@c_entity_probe =
|
|
69
|
+
if defined?(::Leptris::XML::FFI) &&
|
|
70
|
+
::Leptris::XML::FFI.respond_to?(:leptris_str_has_nonstandard_entity)
|
|
71
|
+
::Leptris::XML::FFI.method(:leptris_str_has_nonstandard_entity)
|
|
72
|
+
else
|
|
73
|
+
false
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
@c_entity_probe || nil
|
|
77
|
+
end
|
|
78
|
+
module_function :nonstandard_entity?, :c_entity_probe
|
|
79
|
+
|
|
52
80
|
NAMED_DECODE_MAP = {
|
|
53
81
|
"amp" => "&", "lt" => "<", "gt" => ">",
|
|
54
82
|
"quot" => '"', "apos" => "'"
|
|
@@ -87,11 +115,12 @@ module Moxml
|
|
|
87
115
|
# Fast path: no `&` means no entity references to mark — skip
|
|
88
116
|
# the regex scan and string allocation entirely. The vast
|
|
89
117
|
# majority of XML payloads contain no entity references.
|
|
90
|
-
return [str, false] unless str.include?("&")
|
|
91
118
|
# Second fast path: only predefined entities — the gsub would
|
|
92
119
|
# pass every match through unchanged, so skip its full-buffer
|
|
93
|
-
# copy too.
|
|
94
|
-
|
|
120
|
+
# copy too. When the leptris binding is loaded, its C probe
|
|
121
|
+
# (leptris-ruby#124) answers in one memcmp-class pass; the
|
|
122
|
+
# binding loads lazily, so the probe is resolved on first use.
|
|
123
|
+
return [str, false] unless nonstandard_entity?(str)
|
|
95
124
|
|
|
96
125
|
marked = false
|
|
97
126
|
processed = str.gsub(NAME_RE) do |match|
|
data/lib/moxml/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: moxml
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.5.
|
|
4
|
+
version: 0.5.25
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Ribose Inc.
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-09-
|
|
11
|
+
date: 2026-09-03 00:00:00.000000000 Z
|
|
12
12
|
dependencies: []
|
|
13
13
|
description: |
|
|
14
14
|
Moxml is a unified XML manipulation library that provides a common API
|