leptris 1.9.4 → 1.9.5
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/CHANGELOG.md +26 -0
- data/README.adoc +11 -0
- data/leptris.gemspec +2 -1
- data/lib/leptris/version.rb +1 -1
- data/lib/leptris/xml/cdata.rb +1 -0
- data/lib/leptris/xml/comment.rb +1 -0
- data/lib/leptris/xml/document.rb +6 -0
- data/lib/leptris/xml/element.rb +12 -1
- data/lib/leptris/xml/node.rb +27 -2
- data/lib/leptris/xml/processing_instruction.rb +2 -0
- data/lib/leptris/xml/text.rb +1 -0
- metadata +1 -5
- data/.rspec +0 -3
- data/.rubocop.yml +0 -8
- data/benchmark/README.md +0 -12
- data/benchmark/leptris_vs_nokogiri.rb +0 -105
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 996051e28883434c5ac9cf2041278a37a0ebc3ad628685838e6b743cc87e0b9d
|
|
4
|
+
data.tar.gz: 3909cce576ad3ce14fa65c4058997476ddd814ea9f819e00e0b4bbb969bd09cf
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 61f73189528f9c0ffaff803328304baa44af1e545d6618d252ff8e46b5279a229fb204df807815a22a360bffbf107a0c32e1a33f5110cb699ae8135e06d2777e
|
|
7
|
+
data.tar.gz: 4b5b197e569e23820b74b1596b3a8f8bf2feefba09af9858bb830cbcd6339d59c196e71177760ab35eb7daeac30bd5c3edb2b384d1ff43fc56726b838bfe0f0a
|
data/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,32 @@ All notable changes to Leptris will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [1.9.5] - 2026-08-26
|
|
9
|
+
|
|
10
|
+
### Fixed
|
|
11
|
+
|
|
12
|
+
- **Borrowed handles validate their lender**: every c_ptr-
|
|
13
|
+
dereferencing read and mutation on Node/Element/Text/Comment/
|
|
14
|
+
CDATA/PI raises `Leptris::XML::UseAfterFreeError` once the owning
|
|
15
|
+
document has been freed (previously `doc.free; node.name` read
|
|
16
|
+
freed memory silently — verified returning `""` — and stale
|
|
17
|
+
mutations risked segfaults; only Document's own methods checked).
|
|
18
|
+
Memoized readonly results stay check-free. `Document#freed?` is
|
|
19
|
+
the single state predicate.
|
|
20
|
+
|
|
21
|
+
### Added
|
|
22
|
+
|
|
23
|
+
- **`benchmark/read_paths.rb`**: the committed read-path harness
|
|
24
|
+
(readonly loops, SAX, NodeSet unions, css, parse-query-serialize)
|
|
25
|
+
with a README methodology note — review-round benchmarks are now
|
|
26
|
+
reproducible from the repo.
|
|
27
|
+
|
|
28
|
+
### Changed
|
|
29
|
+
|
|
30
|
+
- The published gem no longer ships dev tooling (`benchmark/`,
|
|
31
|
+
`.rspec`, `.rubocop.yml`); it carries lib, gemspec, Rakefile, and
|
|
32
|
+
documentation only.
|
|
33
|
+
|
|
8
34
|
## [1.9.4] - 2026-08-25
|
|
9
35
|
|
|
10
36
|
### Fixed
|
data/README.adoc
CHANGED
|
@@ -411,6 +411,17 @@ Notable differences:
|
|
|
411
411
|
|
|
412
412
|
== Performance
|
|
413
413
|
|
|
414
|
+
=== Internal read-path harness
|
|
415
|
+
|
|
416
|
+
`benchmark/read_paths.rb` measures the binding's own hot paths
|
|
417
|
+
(readonly read loops, SAX, NodeSet unions, css, parse-query-
|
|
418
|
+
serialize). Machine-relative — compare before/after runs on the same
|
|
419
|
+
machine:
|
|
420
|
+
|
|
421
|
+
bundle exec ruby benchmark/read_paths.rb
|
|
422
|
+
|
|
423
|
+
=== Full-field comparison
|
|
424
|
+
|
|
414
425
|
Measured with metanorma/serialbench (Ruby 3.4.8, leptris 1.6.0,
|
|
415
426
|
libleptris 1.6.0, macOS arm64) — Leptris vs the full field:
|
|
416
427
|
|
data/leptris.gemspec
CHANGED
|
@@ -28,7 +28,8 @@ Gem::Specification.new do |spec|
|
|
|
28
28
|
spec.files = Dir.chdir(__dir__) do
|
|
29
29
|
`git ls-files -z`.split("\x0").reject do |f|
|
|
30
30
|
(File.expand_path(f) == __FILE__) ||
|
|
31
|
-
f.start_with?(*%w[bin/ test/ spec/ features/ .git .github
|
|
31
|
+
f.start_with?(*%w[bin/ test/ spec/ features/ benchmark/ .git .github
|
|
32
|
+
appveyor Gemfile .rspec .rubocop.yml])
|
|
32
33
|
end
|
|
33
34
|
end
|
|
34
35
|
spec.bindir = "bin"
|
data/lib/leptris/version.rb
CHANGED
data/lib/leptris/xml/cdata.rb
CHANGED
data/lib/leptris/xml/comment.rb
CHANGED
data/lib/leptris/xml/document.rb
CHANGED
|
@@ -281,6 +281,12 @@ class Leptris::XML::Document
|
|
|
281
281
|
@readonly == true
|
|
282
282
|
end
|
|
283
283
|
|
|
284
|
+
# True once #free has run (or the GC finalizer fired) — borrowed
|
|
285
|
+
# handles check this before dereferencing their c_ptr.
|
|
286
|
+
def freed?
|
|
287
|
+
@freed.state == :freed || @c_ptr.nil?
|
|
288
|
+
end
|
|
289
|
+
|
|
284
290
|
# The most recent error recorded against this document, or nil.
|
|
285
291
|
def last_error
|
|
286
292
|
msg = Leptris::XML::FFI.leptris_document_last_error(@c_ptr)
|
data/lib/leptris/xml/element.rb
CHANGED
|
@@ -2,7 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
class Leptris::XML::Element < Leptris::XML::Node
|
|
4
4
|
def name
|
|
5
|
-
@name
|
|
5
|
+
return @name if @name
|
|
6
|
+
ensure_alive!
|
|
7
|
+
@name = Leptris::XML::FFI.leptris_element_name(@c_ptr)
|
|
6
8
|
end
|
|
7
9
|
alias_method :node_name, :name
|
|
8
10
|
|
|
@@ -16,6 +18,7 @@ class Leptris::XML::Element < Leptris::XML::Node
|
|
|
16
18
|
|
|
17
19
|
def content
|
|
18
20
|
return @content if readonly_cached?(:@content)
|
|
21
|
+
ensure_alive!
|
|
19
22
|
Leptris::XML::FFI.leptris_element_text(@c_ptr).tap do |text|
|
|
20
23
|
@content = text if @document&.readonly?
|
|
21
24
|
end
|
|
@@ -29,6 +32,7 @@ class Leptris::XML::Element < Leptris::XML::Node
|
|
|
29
32
|
end
|
|
30
33
|
|
|
31
34
|
def [](key)
|
|
35
|
+
ensure_alive!
|
|
32
36
|
Leptris::XML::FFI.leptris_element_attribute(@c_ptr, key.to_s)
|
|
33
37
|
end
|
|
34
38
|
alias_method :attr, :[]
|
|
@@ -43,6 +47,7 @@ class Leptris::XML::Element < Leptris::XML::Node
|
|
|
43
47
|
alias_method :set_attribute, :[]=
|
|
44
48
|
|
|
45
49
|
def key?(name)
|
|
50
|
+
ensure_alive!
|
|
46
51
|
Leptris::XML::FFI.leptris_element_has_attribute(@c_ptr, name.to_s) != 0
|
|
47
52
|
end
|
|
48
53
|
alias_method :has_attribute?, :key?
|
|
@@ -53,11 +58,13 @@ class Leptris::XML::Element < Leptris::XML::Node
|
|
|
53
58
|
# in-scope declarations. uri nil/"" matches no-namespace
|
|
54
59
|
# attributes only; xmlns declarations are invisible.
|
|
55
60
|
def attribute_ns(uri, local)
|
|
61
|
+
ensure_alive!
|
|
56
62
|
Leptris::XML::FFI.leptris_element_attribute_ns(
|
|
57
63
|
@c_ptr, uri&.to_s, local.to_s)
|
|
58
64
|
end
|
|
59
65
|
|
|
60
66
|
def has_attribute_ns?(uri, local)
|
|
67
|
+
ensure_alive!
|
|
61
68
|
Leptris::XML::FFI.leptris_element_has_attribute_ns(
|
|
62
69
|
@c_ptr, uri&.to_s, local.to_s) != 0
|
|
63
70
|
end
|
|
@@ -77,6 +84,7 @@ class Leptris::XML::Element < Leptris::XML::Node
|
|
|
77
84
|
# per-attribute namespace accessors (libleptris 1.8.0).
|
|
78
85
|
def each_attribute
|
|
79
86
|
return enum_for(:each_attribute) unless block_given?
|
|
87
|
+
ensure_alive!
|
|
80
88
|
attr = Leptris::XML::FFI.leptris_element_first_attribute(@c_ptr)
|
|
81
89
|
until attr.nil? || attr.null?
|
|
82
90
|
name = Leptris::XML::FFI.leptris_attribute_get_name(attr)
|
|
@@ -119,6 +127,7 @@ class Leptris::XML::Element < Leptris::XML::Node
|
|
|
119
127
|
# The element's own namespace prefix (e.g. "foo" for <foo:child/>),
|
|
120
128
|
# or nil when the element has none.
|
|
121
129
|
def prefix
|
|
130
|
+
ensure_alive!
|
|
122
131
|
Leptris::XML::FFI.leptris_element_prefix(@c_ptr)
|
|
123
132
|
end
|
|
124
133
|
|
|
@@ -231,6 +240,7 @@ class Leptris::XML::Element < Leptris::XML::Node
|
|
|
231
240
|
|
|
232
241
|
def namespace
|
|
233
242
|
return @namespace if readonly_cached?(:@namespace)
|
|
243
|
+
ensure_alive!
|
|
234
244
|
uri = Leptris::XML::FFI.leptris_element_namespace(@c_ptr)
|
|
235
245
|
result =
|
|
236
246
|
if uri.nil? || uri.empty?
|
|
@@ -249,6 +259,7 @@ class Leptris::XML::Element < Leptris::XML::Node
|
|
|
249
259
|
|
|
250
260
|
def namespace_definitions
|
|
251
261
|
return @namespace_definitions if readonly_cached?(:@namespace_definitions)
|
|
262
|
+
ensure_alive!
|
|
252
263
|
count = Leptris::XML::FFI.leptris_element_namespace_count(@c_ptr)
|
|
253
264
|
result = count.times.map do |i|
|
|
254
265
|
prefix = Leptris::XML::FFI.leptris_element_namespace_decl_prefix(@c_ptr, i)
|
data/lib/leptris/xml/node.rb
CHANGED
|
@@ -62,7 +62,9 @@ class Leptris::XML::Node
|
|
|
62
62
|
end
|
|
63
63
|
|
|
64
64
|
def type
|
|
65
|
-
@node_type
|
|
65
|
+
return @node_type if @node_type
|
|
66
|
+
ensure_alive!
|
|
67
|
+
@node_type = Leptris::XML::FFI.leptris_node_get_type(@c_ptr)
|
|
66
68
|
end
|
|
67
69
|
alias_method :node_type, :type
|
|
68
70
|
|
|
@@ -77,13 +79,26 @@ class Leptris::XML::Node
|
|
|
77
79
|
|
|
78
80
|
def parent
|
|
79
81
|
return @parent if @parent
|
|
82
|
+
ensure_alive!
|
|
80
83
|
ptr = Leptris::XML::FFI.leptris_node_parent(@c_ptr)
|
|
81
84
|
return nil if ptr.null?
|
|
82
85
|
Leptris::XML::Node.wrap(ptr, @document)
|
|
83
86
|
end
|
|
84
87
|
|
|
85
|
-
#
|
|
88
|
+
# Borrowed-handle lifetime: every c_ptr dereference is valid only
|
|
89
|
+
# while the owning document lives. Parentless nodes (iterparse
|
|
90
|
+
# yields) cannot validate and are skipped.
|
|
91
|
+
def ensure_alive!
|
|
92
|
+
if @document&.freed?
|
|
93
|
+
raise Leptris::XML::UseAfterFreeError,
|
|
94
|
+
"owning document has been freed — handle used on #{inspect}"
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
# Raises ReadOnlyError when the owning document was marked readonly,
|
|
99
|
+
# UseAfterFreeError when it was freed.
|
|
86
100
|
def ensure_writable!
|
|
101
|
+
ensure_alive!
|
|
87
102
|
if @document&.readonly?
|
|
88
103
|
raise Leptris::XML::ReadOnlyError,
|
|
89
104
|
"document is readonly — mutation attempted on #{inspect}"
|
|
@@ -91,16 +106,19 @@ class Leptris::XML::Node
|
|
|
91
106
|
end
|
|
92
107
|
|
|
93
108
|
def line
|
|
109
|
+
ensure_alive!
|
|
94
110
|
Leptris::XML::FFI.leptris_node_line(@c_ptr)
|
|
95
111
|
end
|
|
96
112
|
|
|
97
113
|
def <=>(other)
|
|
98
114
|
return nil unless other.is_a?(Leptris::XML::Node)
|
|
99
115
|
return nil unless @document == other.document
|
|
116
|
+
ensure_alive!
|
|
100
117
|
Leptris::XML::FFI.leptris_node_compare(@c_ptr, other.c_ptr)
|
|
101
118
|
end
|
|
102
119
|
|
|
103
120
|
def child
|
|
121
|
+
ensure_alive!
|
|
104
122
|
ptr = Leptris::XML::FFI.leptris_node_first_child(@c_ptr)
|
|
105
123
|
return nil if ptr.null?
|
|
106
124
|
Leptris::XML::Node.wrap(ptr, @document, parent: as_element_or_self)
|
|
@@ -110,6 +128,7 @@ class Leptris::XML::Node
|
|
|
110
128
|
# Immutable in readonly mode: the batch fetch plus wrapper
|
|
111
129
|
# construction is paid once.
|
|
112
130
|
return @children if readonly_cached?(:@children)
|
|
131
|
+
ensure_alive!
|
|
113
132
|
parent = as_element_or_self
|
|
114
133
|
nodes = Leptris::XML::FFI.fetch_children(@c_ptr).map do |ptr|
|
|
115
134
|
Leptris::XML::Node.wrap(ptr, @document, parent: parent)
|
|
@@ -120,6 +139,7 @@ class Leptris::XML::Node
|
|
|
120
139
|
end
|
|
121
140
|
|
|
122
141
|
def next_sibling
|
|
142
|
+
ensure_alive!
|
|
123
143
|
ptr = Leptris::XML::FFI.leptris_node_next_sibling(@c_ptr)
|
|
124
144
|
return nil if ptr.null?
|
|
125
145
|
Leptris::XML::Node.wrap(ptr, @document, parent: @parent)
|
|
@@ -127,6 +147,7 @@ class Leptris::XML::Node
|
|
|
127
147
|
alias_method :next, :next_sibling
|
|
128
148
|
|
|
129
149
|
def previous_sibling
|
|
150
|
+
ensure_alive!
|
|
130
151
|
ptr = Leptris::XML::FFI.leptris_node_previous_sibling(@c_ptr)
|
|
131
152
|
return nil if ptr.null?
|
|
132
153
|
Leptris::XML::Node.wrap(ptr, @document, parent: @parent)
|
|
@@ -134,6 +155,7 @@ class Leptris::XML::Node
|
|
|
134
155
|
alias_method :previous, :previous_sibling
|
|
135
156
|
|
|
136
157
|
def first_element_child
|
|
158
|
+
ensure_alive!
|
|
137
159
|
ptr = Leptris::XML::FFI.leptris_node_first_child(@c_ptr)
|
|
138
160
|
until ptr.nil? || ptr.null?
|
|
139
161
|
node = Leptris::XML::Node.wrap(ptr, @document, parent: as_element_or_self)
|
|
@@ -189,6 +211,7 @@ class Leptris::XML::Node
|
|
|
189
211
|
# callback (libleptris #273); the per-node FFI cost is the floor.
|
|
190
212
|
def traverse
|
|
191
213
|
return enum_for(:traverse) unless block_given?
|
|
214
|
+
ensure_alive!
|
|
192
215
|
callback = ::FFI::Function.new(:int, [:pointer, :pointer], blocking: true) do |node_ptr, _|
|
|
193
216
|
yield Leptris::XML::Node.wrap(node_ptr, @document)
|
|
194
217
|
0
|
|
@@ -199,6 +222,7 @@ class Leptris::XML::Node
|
|
|
199
222
|
|
|
200
223
|
def path
|
|
201
224
|
return @path if readonly_cached?(:@path)
|
|
225
|
+
ensure_alive!
|
|
202
226
|
str_ptr = Leptris::XML::FFI.leptris_node_get_xpath(@c_ptr)
|
|
203
227
|
result = str_ptr.null? ? nil : Leptris::XML::FFI.read_owned_string(str_ptr)
|
|
204
228
|
@path = result if @document&.readonly?
|
|
@@ -221,6 +245,7 @@ class Leptris::XML::Node
|
|
|
221
245
|
end
|
|
222
246
|
|
|
223
247
|
def dup
|
|
248
|
+
ensure_alive!
|
|
224
249
|
elem_ptr = Leptris::XML::FFI.leptris_node_as_element(@c_ptr)
|
|
225
250
|
raise Leptris::XML::Error, "dup is only supported for element nodes" if elem_ptr.null?
|
|
226
251
|
copy_ptr = Leptris::XML::FFI.leptris_element_copy(elem_ptr, @document.c_ptr)
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
class Leptris::XML::ProcessingInstruction < Leptris::XML::Node
|
|
4
4
|
def name
|
|
5
5
|
return @name if readonly_cached?(:@name)
|
|
6
|
+
ensure_alive!
|
|
6
7
|
Leptris::XML::FFI.leptris_pi_node_get_target(@c_ptr).tap do |target|
|
|
7
8
|
@name = target if @document&.readonly?
|
|
8
9
|
end
|
|
@@ -11,6 +12,7 @@ class Leptris::XML::ProcessingInstruction < Leptris::XML::Node
|
|
|
11
12
|
|
|
12
13
|
def content
|
|
13
14
|
return @content if readonly_cached?(:@content)
|
|
15
|
+
ensure_alive!
|
|
14
16
|
Leptris::XML::FFI.leptris_pi_node_get_data(@c_ptr).to_s.tap do |data|
|
|
15
17
|
@content = data if @document&.readonly?
|
|
16
18
|
end
|
data/lib/leptris/xml/text.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: leptris
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.9.
|
|
4
|
+
version: 1.9.5
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Ribose Inc.
|
|
@@ -63,15 +63,11 @@ executables: []
|
|
|
63
63
|
extensions: []
|
|
64
64
|
extra_rdoc_files: []
|
|
65
65
|
files:
|
|
66
|
-
- ".rspec"
|
|
67
|
-
- ".rubocop.yml"
|
|
68
66
|
- CHANGELOG.md
|
|
69
67
|
- CLAUDE.md
|
|
70
68
|
- LICENSE.md
|
|
71
69
|
- README.adoc
|
|
72
70
|
- Rakefile
|
|
73
|
-
- benchmark/README.md
|
|
74
|
-
- benchmark/leptris_vs_nokogiri.rb
|
|
75
71
|
- leptris.gemspec
|
|
76
72
|
- lib/leptris.rb
|
|
77
73
|
- lib/leptris/version.rb
|
data/.rspec
DELETED
data/.rubocop.yml
DELETED
data/benchmark/README.md
DELETED
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
# leptris vs the field
|
|
2
|
-
|
|
3
|
-
The authoritative cross-library numbers live in the README's
|
|
4
|
-
Performance section (measured with metanorma/serialbench).
|
|
5
|
-
|
|
6
|
-
This local benchmark compares against Nokogiri only:
|
|
7
|
-
|
|
8
|
-
LEPTRIS_LIB_PATH=/path/to/libleptris.dylib bundle exec ruby benchmark/leptris_vs_nokogiri.rb
|
|
9
|
-
|
|
10
|
-
For the full-field race (Ox, Nokogiri, Oga, REXML, Leptris), use
|
|
11
|
-
https://github.com/metanorma/serialbench — leptris's adapter ships
|
|
12
|
-
there under `lib/serialbench/serializers/xml/leptris_serializer.rb`.
|
|
@@ -1,105 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
# Ruby-level performance comparison: Leptris::XML (FFI → libleptris v0.12.0)
|
|
4
|
-
# vs Nokogiri (C extension → libxml2).
|
|
5
|
-
#
|
|
6
|
-
# Run with: bundle exec ruby -Ilib benchmark/leptris_vs_nokogiri.rb
|
|
7
|
-
|
|
8
|
-
require "benchmark"
|
|
9
|
-
require "leptris/xml"
|
|
10
|
-
require "nokogiri"
|
|
11
|
-
|
|
12
|
-
module Fixtures
|
|
13
|
-
SMALL = "<catalog>" +
|
|
14
|
-
(1..10).map { |i| "<book id='#{i}'><title>Book #{i}</title></book>" }.join +
|
|
15
|
-
"</catalog>"
|
|
16
|
-
|
|
17
|
-
MEDIUM = ("<catalog version='2.0'>" +
|
|
18
|
-
(1..100).map do |i|
|
|
19
|
-
"<book id='#{i}' lang='en'>" \
|
|
20
|
-
"<title>Book #{i}</title>" \
|
|
21
|
-
"<author id='a#{i}'>Author #{i}</author>" \
|
|
22
|
-
"<price currency='USD'>#{i}.99</price>" \
|
|
23
|
-
"</book>"
|
|
24
|
-
end.join +
|
|
25
|
-
"</catalog>").freeze
|
|
26
|
-
end
|
|
27
|
-
|
|
28
|
-
def time_it(label, n, &block)
|
|
29
|
-
GC.start
|
|
30
|
-
t0 = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
|
31
|
-
n.times { yield }
|
|
32
|
-
elapsed = Process.clock_gettime(Process::CLOCK_MONOTONIC) - t0
|
|
33
|
-
us_per_iter = (elapsed / n) * 1_000_000
|
|
34
|
-
printf " %-40s %8.2f µs/iter (%d iters in %.3fs)\n", label, us_per_iter, n, elapsed
|
|
35
|
-
us_per_iter
|
|
36
|
-
end
|
|
37
|
-
|
|
38
|
-
def ratio(label, leptris_us, nokogiri_us)
|
|
39
|
-
r = nokogiri_us / leptris_us
|
|
40
|
-
who = r > 1 ? "Leptris faster" : "Nokogiri faster"
|
|
41
|
-
printf " → %-30s Leptris/Nokogiri = %.2fx (%s)\n\n", label, r, who
|
|
42
|
-
end
|
|
43
|
-
|
|
44
|
-
N_PARSE_SMALL = 5_000
|
|
45
|
-
N_PARSE_MEDIUM = 1_000
|
|
46
|
-
N_QUERY = 10_000
|
|
47
|
-
N_TRAVERSE = 2_000
|
|
48
|
-
N_SERIALIZE = 1_000
|
|
49
|
-
|
|
50
|
-
puts "===== Parse — small (#{Fixtures::SMALL.bytesize} B) ====="
|
|
51
|
-
t = time_it("leptris parse small", N_PARSE_SMALL) { d = Leptris::XML::Document.parse(Fixtures::SMALL); d.free }
|
|
52
|
-
n = time_it("nokogiri parse small", N_PARSE_SMALL) { Nokogiri::XML(Fixtures::SMALL) }
|
|
53
|
-
ratio("parse small", t, n)
|
|
54
|
-
|
|
55
|
-
puts "===== Parse — medium (#{Fixtures::MEDIUM.bytesize} B) ====="
|
|
56
|
-
t = time_it("leptris parse medium", N_PARSE_MEDIUM) { d = Leptris::XML::Document.parse(Fixtures::MEDIUM); d.free }
|
|
57
|
-
n = time_it("nokogiri parse medium", N_PARSE_MEDIUM) { Nokogiri::XML(Fixtures::MEDIUM) }
|
|
58
|
-
ratio("parse medium", t, n)
|
|
59
|
-
|
|
60
|
-
# Pre-parse medium for query benchmarks
|
|
61
|
-
doc_t = Leptris::XML::Document.parse(Fixtures::MEDIUM)
|
|
62
|
-
doc_n = Nokogiri::XML(Fixtures::MEDIUM)
|
|
63
|
-
|
|
64
|
-
puts "===== XPath — count(//book) ====="
|
|
65
|
-
t = time_it("leptris xpath count()", N_QUERY) { doc_t.xpath("count(//book)") }
|
|
66
|
-
n = time_it("nokogiri xpath count()", N_QUERY) { doc_n.xpath("count(//book)") }
|
|
67
|
-
ratio("xpath count()", t, n)
|
|
68
|
-
|
|
69
|
-
puts "===== XPath — //book (nodeset of 100) ====="
|
|
70
|
-
t = time_it("leptris xpath //book", N_QUERY) { doc_t.xpath("//book") }
|
|
71
|
-
n = time_it("nokogiri xpath //book", N_QUERY) { doc_n.xpath("//book") }
|
|
72
|
-
ratio("xpath //book", t, n)
|
|
73
|
-
|
|
74
|
-
puts "===== XPath — predicate //book[@id='50'] ====="
|
|
75
|
-
t = time_it("leptris xpath predicate", N_QUERY) { doc_t.xpath("//book[@id='50']") }
|
|
76
|
-
n = time_it("nokogiri xpath predicate", N_QUERY) { doc_n.xpath("//book[@id='50']") }
|
|
77
|
-
ratio("xpath predicate", t, n)
|
|
78
|
-
|
|
79
|
-
puts "===== XPath — complex //book[price > 50] ====="
|
|
80
|
-
t = time_it("leptris xpath complex", N_QUERY) { doc_t.xpath("//book[price > 50]") }
|
|
81
|
-
n = time_it("nokogiri xpath complex", N_QUERY) { doc_n.xpath("//book[price > 50]") }
|
|
82
|
-
ratio("xpath complex", t, n)
|
|
83
|
-
|
|
84
|
-
puts "===== XPath — union //author | //title ====="
|
|
85
|
-
t = time_it("leptris xpath union", N_QUERY) { doc_t.xpath("//author | //title") }
|
|
86
|
-
n = time_it("nokogiri xpath union", N_QUERY) { doc_n.xpath("//author | //title") }
|
|
87
|
-
ratio("xpath union", t, n)
|
|
88
|
-
|
|
89
|
-
puts "===== Tree traversal (root.traverse) ====="
|
|
90
|
-
t = time_it("leptris traverse", N_TRAVERSE) { doc_t.root.traverse { |n| n.name } }
|
|
91
|
-
n = time_it("nokogiri traverse", N_TRAVERSE) { doc_n.root.traverse { |n| n.name } }
|
|
92
|
-
ratio("traverse", t, n)
|
|
93
|
-
|
|
94
|
-
puts "===== Serialize — Document#to_xml ====="
|
|
95
|
-
t = time_it("leptris serialize", N_SERIALIZE) { doc_t.to_xml }
|
|
96
|
-
n = time_it("nokogiri serialize", N_SERIALIZE) { doc_n.to_xml }
|
|
97
|
-
ratio("serialize", t, n)
|
|
98
|
-
|
|
99
|
-
doc_t.free
|
|
100
|
-
|
|
101
|
-
puts ""
|
|
102
|
-
puts "libleptris v0.12.0 — all upstream issues closed."
|
|
103
|
-
puts "All 176 Ruby specs passing, 0 pending."
|
|
104
|
-
|
|
105
|
-
|