leptris 1.9.36 → 1.9.37
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 +29 -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/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: 69c5e9ac12901717be224595df4f25d8f73deae9dd52d305025ce41ee90664e5
|
|
4
|
+
data.tar.gz: 35b825df17976e8afaf64adc879c77964bc91a80320fe4c6c205e51995a9bc3d
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: dd81428b349b52fc6133a089c589621dca7eaf6eba91070987cbfb79dbc8002ce3983ff79b8c76811e61215427a2e16f96b850ce2d28fe8d5fee29d39720ba5e
|
|
7
|
+
data.tar.gz: b996ea6b4ed1c5005741aab368f32a9902a2c83179c6e7c90d2617aef5bf01b0efddcaeac80836524b682d2001ca4bda53242874c96440b3ea0db688cdc66863
|
data/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,35 @@ 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.37] - 2026-08-29
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- **`Element#inner_html`** (Nokogiri parity): the serialized
|
|
13
|
+
children — elements through the engine serializer, text
|
|
14
|
+
XML-escaped at the seam (`& < >` and CR → `
`, libxml2's
|
|
15
|
+
rules), comments/CDATA/PIs in literal forms. Well-formed by
|
|
16
|
+
construction: a spec re-parses the output to the same children.
|
|
17
|
+
(Nokogiri's inner_html HTML-serializes XML documents — SGML-style
|
|
18
|
+
PI closes, bare CDATA content — and its output does not
|
|
19
|
+
re-parse.) Measured 21 µs/element vs Nokogiri 15 — a first cut
|
|
20
|
+
on a new API with correct forms as the contract.
|
|
21
|
+
- **libleptris 1.9.12 lockstep** (with 1.9.11): pure XSLT
|
|
22
|
+
conformance upstream (libxslt suite 152 -> 180/205 — template
|
|
23
|
+
priority, xsl:number, attribute sets, namespace-declaration
|
|
24
|
+
ordering, strip/preserve-space, copy semantics); audit 250/250,
|
|
25
|
+
no new C surface.
|
|
26
|
+
|
|
27
|
+
### Changed
|
|
28
|
+
|
|
29
|
+
- **Serialize byte-scratch**: the size+fill serialization pair
|
|
30
|
+
allocated and freed a MemoryPointer per call — inner_html
|
|
31
|
+
serializes each child, and the allocation dominated per-child
|
|
32
|
+
cost. A grow-only thread-local byte scratch (the pointer-scratch
|
|
33
|
+
discipline) now serves every serialize call. Serialized output
|
|
34
|
+
is also forced UTF-8 at the seam (previously true only by
|
|
35
|
+
accident of ASCII-only content comparisons).
|
|
36
|
+
|
|
8
37
|
## [1.9.36] - 2026-08-29
|
|
9
38
|
|
|
10
39
|
### 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.12"
|
|
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
|
|
@@ -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.37
|
|
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
|