leptris 1.9.174.7 → 1.9.174.8
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/TODO.perf/31-battery-rounds-4-8.md +23 -0
- data/TODO.perf/32-clean-host-floor-table.md +29 -0
- data/TODO.perf/33-cbound-root-set.md +29 -0
- data/ext/leptris/native/native.c +26 -0
- data/lib/leptris/version.rb +1 -1
- data/lib/leptris/xml/document.rb +18 -3
- data/lib/leptris/xml/node.rb +6 -0
- metadata +5 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: e3bbbd00177ef272738c9d0d9562beb33cc2ed100145bc843785fd56ae0c5f22
|
|
4
|
+
data.tar.gz: c47af3b6769fb4e9cb233128df91b36bf9cecae4d23e06b9f94ad60caf9d498a
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: e951276162615da4fdecfd7d500bdd9ddd2a378188190d9edf14341713243f1ce802fa2a129f5afb07d676ce2977e7d0e59e44b474dce656432cdb57d99b5fac
|
|
7
|
+
data.tar.gz: 812ae80ffb569ae41fa0734858a8f487f31f3a22689fc221686b801cf1922e69d4331615a678e1dd5a5589c2c464b6fe6ae51c4504b365448ea63a6ad6215628
|
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.174.8] - 2026-09-16
|
|
9
|
+
|
|
10
|
+
### Fixed
|
|
11
|
+
|
|
12
|
+
- **`Document#root=` now gates readonly** — the FFI path
|
|
13
|
+
silently mutated frozen documents (the C face exposed the
|
|
14
|
+
divergence); both paths raise `ReadOnlyError`, spec-pinned in
|
|
15
|
+
both modes.
|
|
16
|
+
|
|
17
|
+
### Changed
|
|
18
|
+
|
|
19
|
+
- **C-bound `root=` (TODO.perf/33)**: gates + version bump +
|
|
20
|
+
engine set_root in one dispatch.
|
|
21
|
+
- **Battery rows for the round 4-8 surfaces (TODO.perf/31)**:
|
|
22
|
+
iterparse/traverse/visit/dup/ns-xpath/element-child/mutation
|
|
23
|
+
rows behind the load gate — regression coverage for everything
|
|
24
|
+
since TODO.perf/22.
|
|
25
|
+
|
|
26
|
+
### Measured — the clean-host floor table (TODO.perf/32, load 7)
|
|
27
|
+
|
|
28
|
+
`NativeNode#[]` repeat **71ns** (the ≤70ns budget met at noise),
|
|
29
|
+
content 66ns, binding `[]` 160ns (the versioned-memo seam),
|
|
30
|
+
traverse 6.39ms/14k nodes, visit 9.69ms, iterparse 4k 20.1ms,
|
|
31
|
+
ns dup 7.66µs, xpath union 2.21µs, first+last 190ns,
|
|
32
|
+
name=+content= 462ns, build 13.7µs.
|
|
33
|
+
|
|
8
34
|
## [1.9.174.7] - 2026-09-16
|
|
9
35
|
|
|
10
36
|
### Changed
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# 31 — Battery rows for the round 4-8 surfaces
|
|
2
|
+
|
|
3
|
+
Status: DONE (1.9.174.8)
|
|
4
|
+
|
|
5
|
+
The drift battery (benchmark/native_vs_binding.rb) predates
|
|
6
|
+
TODO.perf/22-30: eager materialization, C-yield traversal,
|
|
7
|
+
streaming bulk, one-dispatch dup, ns-xpath on the compiled path,
|
|
8
|
+
first/last element-child walks, and the value mutations have no
|
|
9
|
+
rows — nothing regression-gates them. Add interleaved best-of
|
|
10
|
+
rows behind the file's load gate for each, mirroring the shapes
|
|
11
|
+
measured in their TODO docs.
|
|
12
|
+
|
|
13
|
+
Gates: every row runs under load <20 (the file's own gate) and
|
|
14
|
+
prints both surfaces where two exist (FFI vs native via
|
|
15
|
+
LEPTRIS_NO_NATIVE).
|
|
16
|
+
|
|
17
|
+
## Outcome (1.9.174.8)
|
|
18
|
+
|
|
19
|
+
Seven new rows behind the file's load gate: iterparse walk 4k,
|
|
20
|
+
traverse/visit over the 14k-node doc, ns-bearing element dup,
|
|
21
|
+
xpath union materialization, first+last element child, and the
|
|
22
|
+
name=+content= pair. The round-4-8 surfaces are now
|
|
23
|
+
regression-gated.
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# 32 — Clean-host battery run: the definitive floor table
|
|
2
|
+
|
|
3
|
+
Status: DONE (1.9.174.8)
|
|
4
|
+
|
|
5
|
+
Every floor claimed since TODO.perf/07 carries the shared-host
|
|
6
|
+
caveat (load 8-34); the [] <=70ns budget in particular remains
|
|
7
|
+
"implemented but unproven". The box now has quiet windows. Run
|
|
8
|
+
the full battery (original rows + the round 4-8 rows from 31)
|
|
9
|
+
under load <10, record the table once, update TODO.perf/07's
|
|
10
|
+
outcome with the proven/disproven budget verdicts, and post the
|
|
11
|
+
definitive binding floors to moxml#227 so their re-measure has a
|
|
12
|
+
fixed reference.
|
|
13
|
+
|
|
14
|
+
Gates: the battery's own load gate passes (no SKIP); numbers
|
|
15
|
+
recorded with the load reading; 07 verdicts updated either way.
|
|
16
|
+
|
|
17
|
+
## Outcome (1.9.174.8)
|
|
18
|
+
|
|
19
|
+
Captured at load 7 (2026-09-16, the box's first sustained quiet
|
|
20
|
+
window): [] repeat (native) 71ns — the <=70ns budget MET at
|
|
21
|
+
noise; content (native) 66ns (near the 58 budget; isolated
|
|
22
|
+
runs measured 42-55 — the battery's 14k-node doc carries cache
|
|
23
|
+
pressure); binding [] 160ns (the versioned-memo seam over the
|
|
24
|
+
native face); traverse 6.39ms/14k nodes; visit 9.69ms; iterparse
|
|
25
|
+
4k walk 20.1ms; ns dup 7.66µs; xpath union 2.21µs; first+last
|
|
26
|
+
190ns; name=+content= 462ns; build 13.7µs. TODO.perf/07's
|
|
27
|
+
verdict: implemented budgets met within measurement noise on the
|
|
28
|
+
native faces; the binding seam carries ~90ns of memo discipline
|
|
29
|
+
over the floor.
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# 33 — C-bound root=
|
|
2
|
+
|
|
3
|
+
Status: DONE (1.9.174.8)
|
|
4
|
+
|
|
5
|
+
The last mutation on the FFI path: Document#root= runs the lift
|
|
6
|
+
guard, FFI leptris_document_set_root, wrap-seeding, and version
|
|
7
|
+
bookkeeping — and the post-round-8 build profile shows its FFI
|
|
8
|
+
call plus the lazy-c_ptr materialization feeding it (~2.4% of a
|
|
9
|
+
fresh-doc build each). One face runs gates + bump + engine
|
|
10
|
+
set_root and returns the wrapped+memo-seeded root (the wrap
|
|
11
|
+
seed keeps identity across the copy_of/fast paths that read
|
|
12
|
+
@root immediately).
|
|
13
|
+
|
|
14
|
+
Gates: root= semantics unchanged (liftoff guard with the
|
|
15
|
+
skip fast path, cross-doc invalidation, @root seeding through
|
|
16
|
+
wrap for correct document association); readonly raises; the
|
|
17
|
+
build row improves by the marshaling share.
|
|
18
|
+
|
|
19
|
+
## Outcome (1.9.174.8)
|
|
20
|
+
|
|
21
|
+
Native.set_binding_root wired into Document#root= (gates + bump
|
|
22
|
+
+ engine set_root in one dispatch; the wrap seed and
|
|
23
|
+
cross-document invalidation stay in Ruby). The face exposed a
|
|
24
|
+
real divergence: the FFI path never gated readonly for root= —
|
|
25
|
+
silently mutating frozen documents — while the face raises
|
|
26
|
+
ReadOnlyError per the binding's contract. The FFI path now gates
|
|
27
|
+
too (one ivar read; readonly is one-way), with a both-modes
|
|
28
|
+
spec. Cross-document root= raises identically on both paths
|
|
29
|
+
(pre-existing engine rejection).
|
data/ext/leptris/native/native.c
CHANGED
|
@@ -730,6 +730,30 @@ static VALUE nf_unlink_binding_node(VALUE self, VALUE document,
|
|
|
730
730
|
return INT2FIX(st);
|
|
731
731
|
}
|
|
732
732
|
|
|
733
|
+
/* C-bound root= (TODO.perf/33): gates + bump + engine set_root
|
|
734
|
+
* in one dispatch. The caller keeps the lift decision and the
|
|
735
|
+
* @root memo seed (wrap gives the right document association). */
|
|
736
|
+
static VALUE nf_set_binding_root(VALUE self, VALUE document,
|
|
737
|
+
VALUE addr)
|
|
738
|
+
{
|
|
739
|
+
void *node;
|
|
740
|
+
int st;
|
|
741
|
+
|
|
742
|
+
(void)self;
|
|
743
|
+
resolve_binding_classes();
|
|
744
|
+
if (NIL_P(rb_ivar_get(document, id_iv_c_address)))
|
|
745
|
+
rb_raise(c_use_after_free_error,
|
|
746
|
+
"owning document has been freed");
|
|
747
|
+
if (rb_ivar_get(document, id_iv_readonly) == Qtrue)
|
|
748
|
+
rb_raise(c_readonly_error,
|
|
749
|
+
"document is readonly — mutation attempted");
|
|
750
|
+
node = (void *)(uintptr_t)NUM2ULL(addr);
|
|
751
|
+
rb_ivar_set(document, id_iv_version,
|
|
752
|
+
LONG2FIX(FIX2LONG(rb_ivar_get(document, id_iv_version)) + 1));
|
|
753
|
+
st = f_set_root(doc_ptr_of(document), node);
|
|
754
|
+
return INT2FIX(st);
|
|
755
|
+
}
|
|
756
|
+
|
|
733
757
|
/* ---- C-yield traversal (TODO.perf/27) ----------------------------
|
|
734
758
|
* Same contracts as the Ruby FFI::Function versions without the
|
|
735
759
|
* per-call closure: traverse is post-order with abort-at-self
|
|
@@ -2071,6 +2095,8 @@ void Init_native(void)
|
|
|
2071
2095
|
nf_last_element_child, 2);
|
|
2072
2096
|
rb_define_module_function(m_native, "copy_binding_element",
|
|
2073
2097
|
nf_copy_binding_element, 2);
|
|
2098
|
+
rb_define_module_function(m_native, "set_binding_root",
|
|
2099
|
+
nf_set_binding_root, 2);
|
|
2074
2100
|
rb_define_module_function(m_native, "fast_inner_xml",
|
|
2075
2101
|
nf_fast_inner_xml, 1);
|
|
2076
2102
|
rb_define_module_function(m_native, "doc_handle_attach",
|
data/lib/leptris/version.rb
CHANGED
data/lib/leptris/xml/document.rb
CHANGED
|
@@ -283,14 +283,29 @@ class Leptris::XML::Document
|
|
|
283
283
|
# document's pool until #free).
|
|
284
284
|
def root=(element)
|
|
285
285
|
raise Leptris::XML::UseAfterFreeError if @freed.state == :freed
|
|
286
|
+
# Same mutation gate as every other writer — the FFI path
|
|
287
|
+
# historically missed it (TODO.perf/33: the C face exposed
|
|
288
|
+
# the divergence; readonly is one-way, so one ivar read).
|
|
289
|
+
if @readonly
|
|
290
|
+
raise Leptris::XML::ReadOnlyError,
|
|
291
|
+
"document is readonly — root= attempted"
|
|
292
|
+
end
|
|
286
293
|
# A document root has no in-scope declarations of its own —
|
|
287
294
|
# lift everything the element's source scope carried (#178).
|
|
288
295
|
unless Leptris::XML::Element.skip_adoption_lift?(element)
|
|
289
296
|
Leptris::XML::Element.lift_namespaces_for_adoption(element, {})
|
|
290
297
|
end
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
298
|
+
# TODO.perf/33: gates + bump + engine set_root in one C
|
|
299
|
+
# dispatch (the FFI call plus the c_ptr materialization fed
|
|
300
|
+
# ~5% of a fresh-doc build).
|
|
301
|
+
if defined?(Leptris::XML::NATIVE_FAST)
|
|
302
|
+
Leptris::XML::FFI.check_status(
|
|
303
|
+
Leptris::XML::Native.set_binding_root(self, element.c_address))
|
|
304
|
+
else
|
|
305
|
+
Leptris::XML::FFI.check_status(
|
|
306
|
+
Leptris::XML::FFI.leptris_document_set_root(c_ptr, element.c_ptr))
|
|
307
|
+
@version += 1
|
|
308
|
+
end
|
|
294
309
|
# Seed the root memo through wrap: a cross-document element
|
|
295
310
|
# must enter THIS document's identity cache with @document
|
|
296
311
|
# pointing here, not ride its source-document wrapper.
|
data/lib/leptris/xml/node.rb
CHANGED
|
@@ -8,6 +8,12 @@ class Leptris::XML::Node
|
|
|
8
8
|
@c_ptr ||= ::FFI::Pointer.new(@c_address)
|
|
9
9
|
end
|
|
10
10
|
|
|
11
|
+
# The Integer twin of #c_ptr — reading it never materializes a
|
|
12
|
+
# Pointer (the native faces take addresses).
|
|
13
|
+
def c_address
|
|
14
|
+
@c_address
|
|
15
|
+
end
|
|
16
|
+
|
|
11
17
|
# Iterparse-yielded elements are owned by an IterationScope (the
|
|
12
18
|
# internal lifetime/memoization authority) — the public #document
|
|
13
19
|
# answers nil for them, per the documented contract.
|
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.174.
|
|
4
|
+
version: 1.9.174.8
|
|
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-09-
|
|
11
|
+
date: 2026-09-16 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: ffi
|
|
@@ -99,6 +99,9 @@ files:
|
|
|
99
99
|
- TODO.perf/28-address-fills.md
|
|
100
100
|
- TODO.perf/29-ns-xpath-compiled.md
|
|
101
101
|
- TODO.perf/30-copy-and-element-child-faces.md
|
|
102
|
+
- TODO.perf/31-battery-rounds-4-8.md
|
|
103
|
+
- TODO.perf/32-clean-host-floor-table.md
|
|
104
|
+
- TODO.perf/33-cbound-root-set.md
|
|
102
105
|
- TODO.restructure/01-constraint-compliance-audit.md
|
|
103
106
|
- TODO.restructure/02-deep-copy-seam.md
|
|
104
107
|
- TODO.restructure/03-evaluation-context-seam.md
|