leptris 1.9.53-x86_64-linux → 1.9.55-x86_64-linux
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 +82 -0
- data/README.adoc +5 -2
- data/Rakefile +1 -1
- data/lib/leptris/version.rb +1 -1
- data/lib/leptris/xml/document.rb +1 -1
- data/lib/leptris/xml/element.rb +11 -9
- data/lib/leptris/xml/ffi.rb +22 -5
- data/lib/leptris/xml/node.rb +8 -7
- data/lib/leptris/xml/serialization.rb +27 -6
- data/lib/libleptris.so +0 -0
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 166f46bd7874e3160fbe3131115c32e1695354d5d503fc6684076d6723a8f4f3
|
|
4
|
+
data.tar.gz: e203358c5847147d46de10c52950d9b3a81cd4e3610b4b59ce6e4a396e7b79ff
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: ccf26a2227f5fcdca9a639f676a2388b58c294c710795662522264a5ded8c07e6adcbb188133ed12b92bc766fc00b1c54d6726456d057c9d21b1b8f5797765fd
|
|
7
|
+
data.tar.gz: 9730c0d093216b2f96befd533a5360af1fe0124337b373392f77509cc4ccef6538b72b787ae7da1967b86f4dacdd2c261b65b6f3e87f987eaadf16392298ebe6
|
data/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,88 @@ 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.55] - 2026-09-02
|
|
9
|
+
|
|
10
|
+
### Changed
|
|
11
|
+
|
|
12
|
+
- **Lockstep with libleptris 1.9.50**: XPath 2.0 type operators —
|
|
13
|
+
`instance of`, `castable as`, `cast as`, `treat as` with
|
|
14
|
+
SequenceType v1 (`node()`, `item()`, occurrence `*`/`+`) —
|
|
15
|
+
resolving standalone from `#xpath`, specced against the Saxon
|
|
16
|
+
ground truth including the falsifiable negative
|
|
17
|
+
(`'x' castable as xs:integer` = false, `1.9 cast as xs:integer`
|
|
18
|
+
= 1). No new C surface (audit 255/255).
|
|
19
|
+
- **README**: the standalone-subset example block grows the type
|
|
20
|
+
operators; the #683 gap list drops sequence types (still out:
|
|
21
|
+
value comparators, quantifiers, `intersect`/`except`, string
|
|
22
|
+
templates, XQuery).
|
|
23
|
+
|
|
24
|
+
### Improved
|
|
25
|
+
|
|
26
|
+
- **Serialize + inner_html, one dispatch instead of two**: the
|
|
27
|
+
`_serialize_into` contract is snprintf-like (the return is
|
|
28
|
+
always the bytes needed incl. NUL; the copy happens only when
|
|
29
|
+
the buffer fits), so `serialize_into_string` now fills the
|
|
30
|
+
grow-only byte scratch opportunistically and probes the size
|
|
31
|
+
only when a child outgrows it — previously every call paid a
|
|
32
|
+
size-probe dispatch before the fill. The bound serializer
|
|
33
|
+
methods are hoisted to constants (`Element#to_xml` and
|
|
34
|
+
`Document#to_xml` allocated a Method object per call), and
|
|
35
|
+
`inner_html` serializes element children through a dedicated
|
|
36
|
+
default-options fast path (no kwargs, no options rebuild).
|
|
37
|
+
Measured on a 1.0 MB / 42k-element document: document serialize
|
|
38
|
+
1.1x → **3.2x faster** than Nokogiri 1.19.4; `inner_html` 0.8x →
|
|
39
|
+
**4.4x faster** (4.2 ms → 1.1 ms per 200 elements). Parse (11x),
|
|
40
|
+
attr reads (9x), text aggregation (11x), and element_children
|
|
41
|
+
walks (parity) all hold.
|
|
42
|
+
|
|
43
|
+
### Fixed
|
|
44
|
+
|
|
45
|
+
- **#120 closed with a guard**: text ampersands escape on
|
|
46
|
+
serialize — byte-parity specs (parse-and-serialize round-trip
|
|
47
|
+
plus a `content=` write path) now run on every CI leg including
|
|
48
|
+
both Linuxes, so a platform-build serializer divergence fails CI
|
|
49
|
+
instead of shipping. The 1.9.50 Linux build was superseded per
|
|
50
|
+
the verification on the issue.
|
|
51
|
+
- **#124 filed upstream** (leptris/leptris#745): the C-side
|
|
52
|
+
non-standard-entity pre-scan entry — the moxml +61% parse → ~+2%
|
|
53
|
+
case. The binding attaches and wraps it the day it lands.
|
|
54
|
+
|
|
55
|
+
## [1.9.54] - 2026-09-02
|
|
56
|
+
|
|
57
|
+
### Changed
|
|
58
|
+
|
|
59
|
+
- **Lockstep with libleptris 1.9.49** (1.9.47 → 1.9.49; no new C
|
|
60
|
+
surface — the audit holds at 255/255 attached == exported):
|
|
61
|
+
- 1.9.47: `leptris_element_copy` keeps namespaces (#721) —
|
|
62
|
+
prefixes, declarations, and resolution on the copied subtree;
|
|
63
|
+
sequence-use `xsl:key` (#720, use-after-free + each item
|
|
64
|
+
indexes).
|
|
65
|
+
- 1.9.48: `xsl:on-completion` sees the final iterate params
|
|
66
|
+
(#729); merge-level `xsl:merge-key` fallback (#731); #730/#732
|
|
67
|
+
triaged against Saxon (quoted `xpath="'$p'"` is the dynamic
|
|
68
|
+
with-param form; multi-top-level results serialize fully —
|
|
69
|
+
libxslt parity).
|
|
70
|
+
- 1.9.49: `xs:` atomic constructors (`xs:integer`/`xs:double`/
|
|
71
|
+
`xs:decimal`/`xs:boolean`/`xs:string`/`xs:anyURI`) resolve in
|
|
72
|
+
any expression — standalone `#xpath` included, specced against
|
|
73
|
+
the Saxon ground truth (`xs:integer('42') + 1` = 43).
|
|
74
|
+
- All three pending specs un-pending (#729/#730/#731); #730
|
|
75
|
+
rewritten as the Saxon-correct pair (quoted form evaluates,
|
|
76
|
+
unquoted raises); sequence-use keys and the constructors
|
|
77
|
+
specced.
|
|
78
|
+
|
|
79
|
+
### Changed
|
|
80
|
+
|
|
81
|
+
- **`dup` reverted to the C copy** (the payoff of #721's fix):
|
|
82
|
+
`Node#dup`/`Element#dup` and the element indent-unit path drop
|
|
83
|
+
the serialize → re-parse round-trip adopted in 1.9.50 — the C
|
|
84
|
+
copy now preserves every child kind, prefixed names, and
|
|
85
|
+
namespace declarations byte-for-byte, and is attached as the
|
|
86
|
+
fresh document's root so the tree is fully navigable (~436 µs
|
|
87
|
+
vs ~1.2 ms per dup of a 300-element subtree on this bench).
|
|
88
|
+
A new C-level spec pins the namespace fidelity.
|
|
89
|
+
|
|
8
90
|
## [1.9.53] - 2026-09-02
|
|
9
91
|
|
|
10
92
|
### Changed
|
data/README.adoc
CHANGED
|
@@ -239,6 +239,9 @@ doc.xpath("count(1 to 4)") # => 4.0
|
|
|
239
239
|
doc.xpath("//book => count()") # 3.1 arrow
|
|
240
240
|
doc.xpath("//title ! string(.)") # 3.1 simple map
|
|
241
241
|
doc.xpath("'v' || count(//book)") # 3.1 concat
|
|
242
|
+
doc.xpath("'42' castable as xs:integer") # 2.0 type ops
|
|
243
|
+
doc.xpath("1.9 cast as xs:integer") # => 1.0 (truncates)
|
|
244
|
+
doc.xpath("//title instance of node()+") # 2.0 sequence types
|
|
242
245
|
----
|
|
243
246
|
|
|
244
247
|
Two caveats: `for … return` sequences come back as opaque result
|
|
@@ -247,8 +250,8 @@ nodes — consume them through an aggregate (`count(...)`,
|
|
|
247
250
|
items; and XQuery-only syntax (`order by`, `try/catch`, map
|
|
248
251
|
constructors) raises `XPathError` rather than evaluating silently
|
|
249
252
|
wrong. The remaining grammar gaps — value comparators (`eq`),
|
|
250
|
-
quantifiers,
|
|
251
|
-
|
|
253
|
+
quantifiers, `intersect`/`except`, string templates — and XQuery
|
|
254
|
+
itself are
|
|
252
255
|
https://github.com/leptris/leptris/issues/683[tracked upstream].
|
|
253
256
|
|
|
254
257
|
=== Supported CSS selectors
|
data/Rakefile
CHANGED
|
@@ -8,7 +8,7 @@ RSpec::Core::RakeTask.new(:spec)
|
|
|
8
8
|
# Pin for `rake compile` and the platform-gem builds. Keep in lockstep
|
|
9
9
|
# with .github/workflows/build.yml (which calls `rake compile`) and the
|
|
10
10
|
# CHANGELOG when libleptris releases.
|
|
11
|
-
LIBLEPTRIS_VERSION = "1.9.
|
|
11
|
+
LIBLEPTRIS_VERSION = "1.9.50"
|
|
12
12
|
|
|
13
13
|
CMAKE_FLAGS = %w[
|
|
14
14
|
-DCMAKE_BUILD_TYPE=Release
|
data/lib/leptris/version.rb
CHANGED
data/lib/leptris/xml/document.rb
CHANGED
|
@@ -285,7 +285,7 @@ class Leptris::XML::Document
|
|
|
285
285
|
@c_ptr, indent: indent, no_decl: no_decl, encoding: encoding)
|
|
286
286
|
end
|
|
287
287
|
Leptris::XML::Serialization.to_xml(
|
|
288
|
-
Leptris::XML::
|
|
288
|
+
Leptris::XML::Serialization::DOCUMENT_SERIALIZE_INTO, @c_ptr,
|
|
289
289
|
indent: indent, no_decl: no_decl, encoding: encoding)
|
|
290
290
|
end
|
|
291
291
|
alias_method :to_s, :to_xml
|
data/lib/leptris/xml/element.rb
CHANGED
|
@@ -251,15 +251,16 @@ class Leptris::XML::Element < Leptris::XML::Node
|
|
|
251
251
|
self
|
|
252
252
|
end
|
|
253
253
|
|
|
254
|
-
# Deep copy in a NEW document
|
|
255
|
-
#
|
|
256
|
-
#
|
|
257
|
-
#
|
|
258
|
-
# every child kind, prefixed names, and namespace declarations,
|
|
259
|
-
# and re-parsing rebuilds them all.
|
|
254
|
+
# Deep copy in a NEW document via the C copy (both engine gaps
|
|
255
|
+
# closed: comment/PI children in 1.9.39, namespaces in 1.9.47 —
|
|
256
|
+
# leptris/leptris#696, #721), attached as the fresh document's
|
|
257
|
+
# root so the tree is fully navigable.
|
|
260
258
|
def dup
|
|
261
259
|
ensure_alive!
|
|
262
|
-
Leptris::XML::Document.
|
|
260
|
+
new_doc = Leptris::XML::Document.create
|
|
261
|
+
copy = Leptris::XML::FFI.leptris_element_copy(@c_ptr, new_doc.c_ptr)
|
|
262
|
+
raise Leptris::XML::Error, "leptris_element_copy failed" if copy.null?
|
|
263
|
+
new_doc.root = Leptris::XML::Node.wrap(copy, new_doc)
|
|
263
264
|
end
|
|
264
265
|
alias_method :clone, :dup
|
|
265
266
|
|
|
@@ -348,7 +349,8 @@ class Leptris::XML::Element < Leptris::XML::Node
|
|
|
348
349
|
def inner_html
|
|
349
350
|
children.map do |child|
|
|
350
351
|
case child
|
|
351
|
-
when Leptris::XML::Element
|
|
352
|
+
when Leptris::XML::Element
|
|
353
|
+
Leptris::XML::Serialization.element_xml_default(child.c_ptr)
|
|
352
354
|
when Leptris::XML::CDATA # before Text: CDATA subclasses it
|
|
353
355
|
"<![CDATA[#{child.content}]]>"
|
|
354
356
|
when Leptris::XML::Text
|
|
@@ -376,7 +378,7 @@ class Leptris::XML::Element < Leptris::XML::Node
|
|
|
376
378
|
return Leptris::XML::Serialization.to_xml_element_unit(
|
|
377
379
|
self, indent_text, indent: indent) if indent_text.is_a?(String)
|
|
378
380
|
Leptris::XML::Serialization.to_xml(
|
|
379
|
-
Leptris::XML::
|
|
381
|
+
Leptris::XML::Serialization::ELEMENT_SERIALIZE_INTO, @c_ptr,
|
|
380
382
|
indent: indent, no_decl: no_decl, encoding: encoding)
|
|
381
383
|
end
|
|
382
384
|
|
data/lib/leptris/xml/ffi.rb
CHANGED
|
@@ -894,12 +894,29 @@ module Leptris
|
|
|
894
894
|
# leptris_element_serialize_into. The buffer holds the
|
|
895
895
|
# serialization + NUL and XML output is NUL-free, so the
|
|
896
896
|
# bounded read is exact.
|
|
897
|
+
# snprintf contract (leptris.h #535): the return is ALWAYS the
|
|
898
|
+
# bytes needed incl. the NUL; the copy happens only when the
|
|
899
|
+
# buffer fits. So the size probe is paid only when the child
|
|
900
|
+
# outgrows the grow-only scratch — one dispatch per call on
|
|
901
|
+
# the fast path instead of two (inner_html serializes each
|
|
902
|
+
# child; the probe was most of the per-child FFI cost).
|
|
903
|
+
SERIALIZE_INITIAL = 256
|
|
904
|
+
private_constant :SERIALIZE_INITIAL
|
|
905
|
+
|
|
897
906
|
def self.serialize_into_string(ffi_function, c_ptr, options)
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
buffer
|
|
901
|
-
|
|
902
|
-
|
|
907
|
+
scratch = (Thread.current[:leptris_scratch] ||= {})
|
|
908
|
+
buffer = scratch[:bytes]
|
|
909
|
+
if buffer.nil?
|
|
910
|
+
buffer = scratch[:bytes] =
|
|
911
|
+
::FFI::MemoryPointer.new(:char, SERIALIZE_INITIAL)
|
|
912
|
+
end
|
|
913
|
+
needed = ffi_function.call(c_ptr, buffer, buffer.size, nil, options)
|
|
914
|
+
if needed > buffer.size
|
|
915
|
+
buffer = scratch_bytes(needed)
|
|
916
|
+
needed = ffi_function.call(c_ptr, buffer, buffer.size, nil, options)
|
|
917
|
+
end
|
|
918
|
+
return "" if needed <= 1
|
|
919
|
+
buffer.read_string(needed - 1).force_encoding(Encoding::UTF_8)
|
|
903
920
|
end
|
|
904
921
|
|
|
905
922
|
# Byte scratch for serialize-into cycles: the size+fill pair
|
data/lib/leptris/xml/node.rb
CHANGED
|
@@ -369,17 +369,18 @@ class Leptris::XML::Node
|
|
|
369
369
|
result
|
|
370
370
|
end
|
|
371
371
|
|
|
372
|
-
# Deep copy in a NEW document
|
|
373
|
-
#
|
|
374
|
-
#
|
|
375
|
-
#
|
|
376
|
-
# the serializer preserves every child kind, prefixed names, and
|
|
377
|
-
# namespace declarations, and re-parsing rebuilds them all.
|
|
372
|
+
# Deep copy in a NEW document via the C copy (both engine gaps
|
|
373
|
+
# closed: comment/PI children in 1.9.39, namespaces in 1.9.47 —
|
|
374
|
+
# leptris/leptris#696, #721). The copy is attached as the fresh
|
|
375
|
+
# document's root so the tree is fully navigable.
|
|
378
376
|
def dup
|
|
379
377
|
ensure_alive!
|
|
380
378
|
elem_ptr = Leptris::XML::FFI.leptris_node_as_element(@c_ptr)
|
|
381
379
|
raise Leptris::XML::Error, "dup is only supported for element nodes" if elem_ptr.null?
|
|
382
|
-
Leptris::XML::Document.
|
|
380
|
+
new_doc = Leptris::XML::Document.create
|
|
381
|
+
copy = Leptris::XML::FFI.leptris_element_copy(elem_ptr, new_doc.c_ptr)
|
|
382
|
+
raise Leptris::XML::Error, "leptris_element_copy failed" if copy.null?
|
|
383
|
+
new_doc.root = Leptris::XML::Node.wrap(copy, new_doc)
|
|
383
384
|
end
|
|
384
385
|
alias_method :clone, :dup
|
|
385
386
|
|
|
@@ -18,6 +18,16 @@ module Leptris::XML::Serialization
|
|
|
18
18
|
end
|
|
19
19
|
private_constant :DEFAULT_OPTIONS
|
|
20
20
|
|
|
21
|
+
# Bound serializer entries, hoisted: Element#to_xml and the
|
|
22
|
+
# inner_html per-child loop passed FFI.method(:...) — one Method
|
|
23
|
+
# allocation per serialized child.
|
|
24
|
+
ELEMENT_SERIALIZE_INTO =
|
|
25
|
+
Leptris::XML::FFI.method(:leptris_element_serialize_into)
|
|
26
|
+
DOCUMENT_SERIALIZE_INTO =
|
|
27
|
+
Leptris::XML::FFI.method(:leptris_document_serialize_into)
|
|
28
|
+
# Public within the library: Element#to_xml and Document#to_xml
|
|
29
|
+
# pass the hoisted bound methods straight through.
|
|
30
|
+
|
|
21
31
|
# +ffi_function+ is a bound _serialize_into function. The buffer
|
|
22
32
|
# cycle lives at the FFI seam (FFI.serialize_into_string); this
|
|
23
33
|
# module owns only options selection and construction.
|
|
@@ -33,6 +43,14 @@ module Leptris::XML::Serialization
|
|
|
33
43
|
Leptris::XML::FFI.serialize_into_string(ffi_function, c_ptr, opts.pointer)
|
|
34
44
|
end
|
|
35
45
|
|
|
46
|
+
# Default-options element serialization for inner_html's per-child
|
|
47
|
+
# loop: no kwargs, no options rebuild, no method allocation —
|
|
48
|
+
# one FFI fast-path dispatch per child.
|
|
49
|
+
def self.element_xml_default(c_ptr)
|
|
50
|
+
Leptris::XML::FFI.serialize_into_string(
|
|
51
|
+
ELEMENT_SERIALIZE_INTO, c_ptr, DEFAULT_OPTIONS.pointer)
|
|
52
|
+
end
|
|
53
|
+
|
|
36
54
|
# Display-form document serialization (libleptris 1.9.9, #129):
|
|
37
55
|
# the ext struct's indent_text hands ALL whitespace to the
|
|
38
56
|
# formatter — text and mixed content indent too. Display-oriented:
|
|
@@ -97,14 +115,17 @@ module Leptris::XML::Serialization
|
|
|
97
115
|
|
|
98
116
|
# Element-face indent unit (leptris-ruby#109 residual 2): no
|
|
99
117
|
# element-level ext-serialize entry exists yet, so the unit path
|
|
100
|
-
#
|
|
101
|
-
#
|
|
102
|
-
# and serializes that document without a declaration —
|
|
103
|
-
# output to the element serializer for the standard
|
|
104
|
-
# the unit and every child kind intact.
|
|
118
|
+
# C-copies the subtree into a fresh document (the copy keeps
|
|
119
|
+
# every child kind and namespace since leptris 1.9.47 — #696,
|
|
120
|
+
# #721) and serializes that document without a declaration —
|
|
121
|
+
# identical output to the element serializer for the standard
|
|
122
|
+
# layout, with the unit and every child kind intact.
|
|
105
123
|
def self.to_xml_element_unit(element, unit, indent: 0)
|
|
106
|
-
document = Leptris::XML::Document.
|
|
124
|
+
document = Leptris::XML::Document.create
|
|
107
125
|
begin
|
|
126
|
+
copy = Leptris::XML::FFI.leptris_element_copy(element.c_ptr, document.c_ptr)
|
|
127
|
+
raise Leptris::XML::Error, "leptris_element_copy failed" if copy.null?
|
|
128
|
+
document.root = Leptris::XML::Node.wrap(copy, document)
|
|
108
129
|
to_xml_indent_unit(document.c_ptr, unit,
|
|
109
130
|
indent: indent, no_decl: true)
|
|
110
131
|
ensure
|
data/lib/libleptris.so
CHANGED
|
Binary file
|