leptris 1.9.36 → 1.9.38
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 +52 -0
- data/Rakefile +1 -1
- data/lib/leptris/version.rb +1 -1
- data/lib/leptris/xml/element.rb +22 -0
- data/lib/leptris/xml/ffi.rb +17 -6
- data/lib/leptris/xml/sax/dom_dispatch.rb +18 -2
- data/lib/leptris/xml/serialization.rb +13 -0
- 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: d6a20a13a634924317b339a27ecae165e6998c4d3de38854edd48d6ca68fa963
|
|
4
|
+
data.tar.gz: 87fdd1085af203af496cb625f02406073a000c1e5d6f1bcce046222fcc8d9a74
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: c635bbd6bff75845204d2e34665003f9652b68dfe834f1484b5da84dd26e4db5992b14bb9b5d51e5cb03d9ee2b8e4ca64d6190aeb3fffa631011fbf9b1316625
|
|
7
|
+
data.tar.gz: fdef1ebde1f5770b0379242ae258ef8fcc1331353bc25f0ad283a6c13fea23d0df9508fb4623ffd0dffd798298b3b324efc7d92dae3131063436682fa6762d7c
|
data/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,58 @@ 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.38] - 2026-08-29
|
|
9
|
+
|
|
10
|
+
### Changed
|
|
11
|
+
|
|
12
|
+
- **libleptris 1.9.13 lockstep**: pin bumped; audit 250/250, no
|
|
13
|
+
new C surface. Engine: document-level whitespace now chains as
|
|
14
|
+
TEXT children of the document node with libxml2's exact rule
|
|
15
|
+
(leading prolog ws dropped, ws after a prolog PI dropped, ws
|
|
16
|
+
after a comment or the root kept, trailing tail trimmed) — a
|
|
17
|
+
spec pins the rule through `Document#children`; XSLT suite
|
|
18
|
+
180 -> 181/205.
|
|
19
|
+
- **Element-only SAX handlers ride the element batch** in the
|
|
20
|
+
DOM-backed dispatcher (round XXIII's path): handlers overriding
|
|
21
|
+
only element events skip the per-child get_type walk entirely —
|
|
22
|
+
text/comments/PIs are never fetched. The document node keeps the
|
|
23
|
+
typed walk (it is not an element handle).
|
|
24
|
+
|
|
25
|
+
### Meta
|
|
26
|
+
|
|
27
|
+
- Nokogiri-compat audit against moxml's adapter surface: no gaps
|
|
28
|
+
(remove_attribute, add_namespace_definition, replace,
|
|
29
|
+
add_previous/next_sibling all present).
|
|
30
|
+
|
|
31
|
+
## [1.9.37] - 2026-08-29
|
|
32
|
+
|
|
33
|
+
### Added
|
|
34
|
+
|
|
35
|
+
- **`Element#inner_html`** (Nokogiri parity): the serialized
|
|
36
|
+
children — elements through the engine serializer, text
|
|
37
|
+
XML-escaped at the seam (`& < >` and CR → `
`, libxml2's
|
|
38
|
+
rules), comments/CDATA/PIs in literal forms. Well-formed by
|
|
39
|
+
construction: a spec re-parses the output to the same children.
|
|
40
|
+
(Nokogiri's inner_html HTML-serializes XML documents — SGML-style
|
|
41
|
+
PI closes, bare CDATA content — and its output does not
|
|
42
|
+
re-parse.) Measured 21 µs/element vs Nokogiri 15 — a first cut
|
|
43
|
+
on a new API with correct forms as the contract.
|
|
44
|
+
- **libleptris 1.9.12 lockstep** (with 1.9.11): pure XSLT
|
|
45
|
+
conformance upstream (libxslt suite 152 -> 180/205 — template
|
|
46
|
+
priority, xsl:number, attribute sets, namespace-declaration
|
|
47
|
+
ordering, strip/preserve-space, copy semantics); audit 250/250,
|
|
48
|
+
no new C surface.
|
|
49
|
+
|
|
50
|
+
### Changed
|
|
51
|
+
|
|
52
|
+
- **Serialize byte-scratch**: the size+fill serialization pair
|
|
53
|
+
allocated and freed a MemoryPointer per call — inner_html
|
|
54
|
+
serializes each child, and the allocation dominated per-child
|
|
55
|
+
cost. A grow-only thread-local byte scratch (the pointer-scratch
|
|
56
|
+
discipline) now serves every serialize call. Serialized output
|
|
57
|
+
is also forced UTF-8 at the seam (previously true only by
|
|
58
|
+
accident of ASCII-only content comparisons).
|
|
59
|
+
|
|
8
60
|
## [1.9.36] - 2026-08-29
|
|
9
61
|
|
|
10
62
|
### Fixed
|
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.13"
|
|
12
12
|
|
|
13
13
|
CMAKE_FLAGS = %w[
|
|
14
14
|
-DCMAKE_BUILD_TYPE=Release
|
data/lib/leptris/version.rb
CHANGED
data/lib/leptris/xml/element.rb
CHANGED
|
@@ -336,6 +336,28 @@ class Leptris::XML::Element < Leptris::XML::Node
|
|
|
336
336
|
scopes
|
|
337
337
|
end
|
|
338
338
|
|
|
339
|
+
# The serialized children (Nokogiri parity): elements serialize
|
|
340
|
+
# through the engine (correct escaping), text is XML-escaped,
|
|
341
|
+
# comments/CDATA/PIs render their literal forms. The complement
|
|
342
|
+
# of #inner_text, which returns the unescaped text content.
|
|
343
|
+
def inner_html
|
|
344
|
+
children.map do |child|
|
|
345
|
+
case child
|
|
346
|
+
when Leptris::XML::Element then child.to_xml
|
|
347
|
+
when Leptris::XML::CDATA # before Text: CDATA subclasses it
|
|
348
|
+
"<![CDATA[#{child.content}]]>"
|
|
349
|
+
when Leptris::XML::Text
|
|
350
|
+
Leptris::XML::Serialization.escape_text(child.content)
|
|
351
|
+
when Leptris::XML::Comment then "<!--#{child.content}-->"
|
|
352
|
+
when Leptris::XML::ProcessingInstruction
|
|
353
|
+
data = child.content
|
|
354
|
+
data.empty? ? "<?#{child.name}?>" : "<?#{child.name} #{data}?>"
|
|
355
|
+
else
|
|
356
|
+
Leptris::XML::Serialization.escape_text(child.content.to_s)
|
|
357
|
+
end
|
|
358
|
+
end.join
|
|
359
|
+
end
|
|
360
|
+
|
|
339
361
|
def to_xml(indent: 0, no_decl: false, encoding: nil)
|
|
340
362
|
Leptris::XML::Serialization.to_xml(
|
|
341
363
|
Leptris::XML::FFI.method(:leptris_element_serialize_into), @c_ptr,
|
data/lib/leptris/xml/ffi.rb
CHANGED
|
@@ -865,13 +865,24 @@ module Leptris
|
|
|
865
865
|
def self.serialize_into_string(ffi_function, c_ptr, options)
|
|
866
866
|
need = ffi_function.call(c_ptr, nil, 0, nil, options)
|
|
867
867
|
return "" if need.zero?
|
|
868
|
-
buffer =
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
868
|
+
buffer = scratch_bytes(need)
|
|
869
|
+
ffi_function.call(c_ptr, buffer, need, nil, options)
|
|
870
|
+
buffer.read_string.force_encoding(Encoding::UTF_8)
|
|
871
|
+
end
|
|
872
|
+
|
|
873
|
+
# Byte scratch for serialize-into cycles: the size+fill pair
|
|
874
|
+
# allocated and freed a buffer per call — inner_html
|
|
875
|
+
# serializes each child, and the allocation was most of the
|
|
876
|
+
# per-child cost. Grows to the largest serialization seen;
|
|
877
|
+
# no Ruby reentrancy during a serialize call.
|
|
878
|
+
def self.scratch_bytes(need)
|
|
879
|
+
scratch = (Thread.current[:leptris_scratch] ||= {})
|
|
880
|
+
ptr = scratch[:bytes]
|
|
881
|
+
if ptr.nil? || ptr.size < need
|
|
882
|
+
ptr&.free
|
|
883
|
+
ptr = scratch[:bytes] = ::FFI::MemoryPointer.new(:char, need)
|
|
874
884
|
end
|
|
885
|
+
ptr
|
|
875
886
|
end
|
|
876
887
|
|
|
877
888
|
# All-kind child handle batch (libleptris 1.7.0). Opportunistic
|
|
@@ -29,7 +29,10 @@ module Leptris::XML::SAX::DomDispatch
|
|
|
29
29
|
def dispatch(handler, dispatched, doc)
|
|
30
30
|
handler.start_document if dispatched[:start_document]
|
|
31
31
|
doc_node = doc.node
|
|
32
|
-
|
|
32
|
+
if doc_node
|
|
33
|
+
visit_children(doc_node.c_ptr, doc, handler, dispatched,
|
|
34
|
+
element_parent: false)
|
|
35
|
+
end
|
|
33
36
|
handler.end_document if dispatched[:end_document]
|
|
34
37
|
self
|
|
35
38
|
end
|
|
@@ -41,10 +44,23 @@ module Leptris::XML::SAX::DomDispatch
|
|
|
41
44
|
CDATA = Leptris::XML::FFI::NODE_CDATA
|
|
42
45
|
PI = Leptris::XML::FFI::NODE_PI
|
|
43
46
|
|
|
44
|
-
def visit_children(c_ptr, doc, handler, dispatched)
|
|
47
|
+
def visit_children(c_ptr, doc, handler, dispatched, element_parent: true)
|
|
45
48
|
wants_text_kinds = dispatched[:characters] ||
|
|
46
49
|
dispatched[:comment] ||
|
|
47
50
|
dispatched[:cdata] || dispatched[:pi]
|
|
51
|
+
if element_parent && !wants_text_kinds
|
|
52
|
+
# Element-only handlers under an element: the element batch
|
|
53
|
+
# hands out element pointers with no per-child get_type
|
|
54
|
+
# (round XXIII's path) — text/comments/PIs are never fetched.
|
|
55
|
+
# (The document node is not an element handle — the typed
|
|
56
|
+
# walk serves it.)
|
|
57
|
+
Leptris::XML::FFI.fetch_element_children(c_ptr).each do |child_ptr|
|
|
58
|
+
visit_element(
|
|
59
|
+
Leptris::XML::Node.wrap(child_ptr, doc, node_type: ELEMENT),
|
|
60
|
+
handler, dispatched)
|
|
61
|
+
end
|
|
62
|
+
return
|
|
63
|
+
end
|
|
48
64
|
Leptris::XML::FFI.fetch_children(c_ptr).each do |child_ptr|
|
|
49
65
|
case Leptris::XML::FFI.leptris_node_get_type(child_ptr)
|
|
50
66
|
when ELEMENT
|
|
@@ -62,6 +62,19 @@ module Leptris::XML::Serialization
|
|
|
62
62
|
Leptris::XML::FFI.read_owned_string(str_ptr)
|
|
63
63
|
end
|
|
64
64
|
|
|
65
|
+
# XML-escapes text content for inner_html: & first, then < >
|
|
66
|
+
# and CR (libxml2 emits 
 for a bare carriage return).
|
|
67
|
+
ESCAPE_TEXT = {
|
|
68
|
+
"&" => "&", "<" => "<", ">" => ">", "\r" => "
",
|
|
69
|
+
}.freeze
|
|
70
|
+
private_constant :ESCAPE_TEXT
|
|
71
|
+
ESCAPE_TEXT_RE = /[&<>\r]/.freeze
|
|
72
|
+
private_constant :ESCAPE_TEXT_RE
|
|
73
|
+
|
|
74
|
+
def self.escape_text(string)
|
|
75
|
+
string.gsub(ESCAPE_TEXT_RE, ESCAPE_TEXT)
|
|
76
|
+
end
|
|
77
|
+
|
|
65
78
|
# Builds a SerializeOptions struct. Returns [opts, encoding_anchor];
|
|
66
79
|
# the anchor must stay referenced while opts is in an FFI call.
|
|
67
80
|
def self.build_options(indent: 0, no_decl: false, encoding: nil)
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
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.38
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Ribose Inc.
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-08-
|
|
11
|
+
date: 2026-08-29 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: ffi
|