leptris 1.9.12 → 1.9.13
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 +19 -0
- data/lib/leptris/version.rb +1 -1
- data/lib/leptris/xml/document.rb +9 -3
- data/lib/leptris/xml/element.rb +1 -1
- data/lib/leptris/xml/node.rb +11 -1
- 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: 590285b33ed6aac05def5ff85268822fc6d5abf87cfd5b262d51b72868f70ac3
|
|
4
|
+
data.tar.gz: 8dc09c12f495c12177a5dd15ff8892340dc090d678eb342738571bfb0e142228
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 2781b4465ff07a85ea7ecc91824070da8412aa933bc96273a4b9159860c95a0e227e06630967685e66db9fd696a6e5648ec9474217ecae769b8163096adf18ed
|
|
7
|
+
data.tar.gz: 9d7173b435723be6f068700e27e05f796246143dcf22dd1319079c5770a71f3f933fdd7d517a71ee47cba58f1305ed2d2e270ffaee43c422c43d693dbb77f4f7
|
data/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,25 @@ 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.13] - 2026-08-27
|
|
9
|
+
|
|
10
|
+
### Changed
|
|
11
|
+
|
|
12
|
+
- **Cached-true readonly guard**: readonly is one-way, so a node
|
|
13
|
+
caches TRUE once observed (FALSE stays uncached — the document
|
|
14
|
+
may still flip). Per-read guards drop from a three-call document
|
|
15
|
+
round-trip to one ivar check. Harness attr loop 0.061 -> 0.048 s.
|
|
16
|
+
- **Lazy wrapper-cache allocation**: Documents no longer allocate
|
|
17
|
+
the wrapper-identity Hash up front; parse-heavy loops that free
|
|
18
|
+
before re-reading stop paying it (~9% on the tiny-doc
|
|
19
|
+
parse-query-serialize loop). Identity semantics unchanged.
|
|
20
|
+
|
|
21
|
+
### Measured dead
|
|
22
|
+
|
|
23
|
+
- Further readonly-[] trimming: the isolated read is at the Ruby
|
|
24
|
+
call-chain floor (~227 ns across ~7 calls); inlining the
|
|
25
|
+
attributes build would duplicate memo logic for ~10%.
|
|
26
|
+
|
|
8
27
|
## [1.9.12] - 2026-08-27
|
|
9
28
|
|
|
10
29
|
### Changed
|
data/lib/leptris/version.rb
CHANGED
data/lib/leptris/xml/document.rb
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
require "ffi"
|
|
4
4
|
|
|
5
5
|
class Leptris::XML::Document
|
|
6
|
-
attr_reader :c_ptr
|
|
6
|
+
attr_reader :c_ptr
|
|
7
7
|
|
|
8
8
|
# @api private
|
|
9
9
|
# Internal flag container shared between the Document instance and its
|
|
@@ -31,7 +31,13 @@ class Leptris::XML::Document
|
|
|
31
31
|
# the weak map — any GC sweep evicted it and the second call built
|
|
32
32
|
# a fresh object. A strong cache costs at most one wrapper per node
|
|
33
33
|
# actually visited, held until the document dies.
|
|
34
|
-
|
|
34
|
+
#
|
|
35
|
+
# Allocated lazily: parse-heavy loops stop paying one Hash per
|
|
36
|
+
# document for trees that are freed before any wrap.
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def wrapper_cache
|
|
40
|
+
@wrapper_cache ||= {}
|
|
35
41
|
end
|
|
36
42
|
|
|
37
43
|
def self.parse(xml_or_io, options: nil, readonly: false, recover: false)
|
|
@@ -230,7 +236,7 @@ class Leptris::XML::Document
|
|
|
230
236
|
@freed.state = :freed
|
|
231
237
|
Leptris::XML::FFI.leptris_document_free(@c_ptr) unless @c_ptr.nil?
|
|
232
238
|
@c_ptr = nil
|
|
233
|
-
@wrapper_cache
|
|
239
|
+
@wrapper_cache&.clear
|
|
234
240
|
end
|
|
235
241
|
|
|
236
242
|
# Enable the first-party EXSLT-style extension pack on this
|
data/lib/leptris/xml/element.rb
CHANGED
|
@@ -36,7 +36,7 @@ class Leptris::XML::Element < Leptris::XML::Node
|
|
|
36
36
|
# materializes on demand — repeated reads become hash lookups,
|
|
37
37
|
# with no dispatch and no lifetime guard (a hash read cannot
|
|
38
38
|
# use-after-free).
|
|
39
|
-
if
|
|
39
|
+
if readonly_document?
|
|
40
40
|
cached = attributes[key.to_s]
|
|
41
41
|
return cached.nil? ? nil : cached.value
|
|
42
42
|
end
|
data/lib/leptris/xml/node.rb
CHANGED
|
@@ -104,12 +104,22 @@ class Leptris::XML::Node
|
|
|
104
104
|
# UseAfterFreeError when it was freed.
|
|
105
105
|
def ensure_writable!
|
|
106
106
|
ensure_alive!
|
|
107
|
-
if
|
|
107
|
+
if readonly_document?
|
|
108
108
|
raise Leptris::XML::ReadOnlyError,
|
|
109
109
|
"document is readonly — mutation attempted on #{inspect}"
|
|
110
110
|
end
|
|
111
111
|
end
|
|
112
112
|
|
|
113
|
+
# Readonly is one-way, so caching TRUE is sound: once observed,
|
|
114
|
+
# the document is readonly forever. FALSE stays uncached (the
|
|
115
|
+
# document may still flip). Saves the document round-trip on
|
|
116
|
+
# per-read guards.
|
|
117
|
+
def readonly_document?
|
|
118
|
+
return true if instance_variable_defined?(:@readonly_document)
|
|
119
|
+
return false unless @document&.readonly?
|
|
120
|
+
@readonly_document = true
|
|
121
|
+
end
|
|
122
|
+
|
|
113
123
|
def line
|
|
114
124
|
ensure_alive!
|
|
115
125
|
Leptris::XML::FFI.leptris_node_line(@c_ptr)
|