moxml 0.5.160 → 0.5.161
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/lib/moxml/adapter/leptris.rb +29 -47
- data/lib/moxml/element.rb +17 -0
- data/lib/moxml/version.rb +1 -1
- data/spec/moxml/adapter/columnar_invalidation_spec.rb +56 -0
- metadata +2 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 39e75ce66c66f3b1d5c72e8ba103dd2a4e6a2eb14d3e7fc9b016672e35a8759b
|
|
4
|
+
data.tar.gz: d502f9d3c5c739d6d86c68d79d2486d40b1a85747d3ef5b8503d1f1e9fc0d17c
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 9ae62f4b5b1cef0b4f84c0a12b640292d762d110004dc0fd2bbadf4af5a374ace336d30e9aef7cd9e8e151590de5b67f07d9cf1e31afc7e82b8de1b2f01ebda2
|
|
7
|
+
data.tar.gz: 819a5bade9e79d666087a4a302484e0f88653f3a77db26585c2d370c70cd388e774b685bf5499ee1dbff2cb7307f5476aee3153acbf098db61e6e0c3bb010fcb
|
|
@@ -83,6 +83,15 @@ module Moxml
|
|
|
83
83
|
::Leptris::XML::Native.respond_to?(:xpath_result_rows_bulk) &&
|
|
84
84
|
::Leptris::XML::Document.method_defined?(:xpath_result_rows_bulk)
|
|
85
85
|
|
|
86
|
+
# TODO.sota/04 (build row): ONE-crossing construction —
|
|
87
|
+
# create + attrs + attach (+ optional text) + version bump +
|
|
88
|
+
# consumer mint, all in C off the parent wrapper's struct
|
|
89
|
+
# (leptris-ruby perf/add-element-full). Probed on the class:
|
|
90
|
+
# older exts take the existing two-crossing path.
|
|
91
|
+
ADD_ELEMENT_FULL =
|
|
92
|
+
defined?(::Leptris::XML::NativeNode) &&
|
|
93
|
+
::Leptris::XML::NativeNode.method_defined?(:add_element_full)
|
|
94
|
+
|
|
86
95
|
# leptris-ruby#460: set-text by document+address, kind-dispatched
|
|
87
96
|
# in C — no binding twin mint per write. Face-probed, not
|
|
88
97
|
# version-gated (the floor admits bindings without it).
|
|
@@ -1334,8 +1343,6 @@ module Moxml
|
|
|
1334
1343
|
end
|
|
1335
1344
|
|
|
1336
1345
|
def set_root(doc, element)
|
|
1337
|
-
drop_columnar_children(doc)
|
|
1338
|
-
drop_columnar_children(element)
|
|
1339
1346
|
to_binding(doc).root = to_binding(element)
|
|
1340
1347
|
end
|
|
1341
1348
|
|
|
@@ -1735,7 +1742,6 @@ module Moxml
|
|
|
1735
1742
|
# re-indented). Raw-pointer walk over the root subtree — the
|
|
1736
1743
|
# same batch child-pointer pattern the materializer uses.
|
|
1737
1744
|
def strip_blank_text_nodes!(doc)
|
|
1738
|
-
drop_columnar_children(doc)
|
|
1739
1745
|
binding_ffi = ::Leptris::XML::FFI
|
|
1740
1746
|
root = binding_ffi.leptris_document_root(doc.c_ptr)
|
|
1741
1747
|
return unless root && !root.null?
|
|
@@ -2233,8 +2239,6 @@ module Moxml
|
|
|
2233
2239
|
# source. Answer the installed binding element; callers re-point
|
|
2234
2240
|
# wrappers at it.
|
|
2235
2241
|
def adopt_root(doc, element)
|
|
2236
|
-
drop_columnar_children(doc)
|
|
2237
|
-
drop_columnar_children(element)
|
|
2238
2242
|
return nil unless NATIVE_ROOT_ADOPT
|
|
2239
2243
|
|
|
2240
2244
|
binding_doc = to_binding(doc)
|
|
@@ -2345,7 +2349,10 @@ module Moxml
|
|
|
2345
2349
|
doc = doc_for(node)
|
|
2346
2350
|
return nil unless doc
|
|
2347
2351
|
|
|
2348
|
-
|
|
2352
|
+
pair = attachments.get(doc, COLUMNAR_CHILDREN)
|
|
2353
|
+
return nil unless pair.is_a?(Array) && pair[0] == doc.version
|
|
2354
|
+
|
|
2355
|
+
map = pair[1]
|
|
2349
2356
|
return nil unless map&.key?(node)
|
|
2350
2357
|
|
|
2351
2358
|
map[node].dup
|
|
@@ -2383,36 +2390,14 @@ module Moxml
|
|
|
2383
2390
|
end
|
|
2384
2391
|
mine << handle if depth == child_depth
|
|
2385
2392
|
end
|
|
2386
|
-
|
|
2393
|
+
# Memo-version law: the pair is stamped with the binding
|
|
2394
|
+
# Document#@version the map was primed under; every mutation
|
|
2395
|
+
# face advances that version in C, so a stale map misses the
|
|
2396
|
+
# hit check and re-primes — no write-side drop walk needed.
|
|
2397
|
+
attachments.set(doc, COLUMNAR_CHILDREN, [doc.version, map]) if map
|
|
2387
2398
|
mine
|
|
2388
2399
|
end
|
|
2389
2400
|
|
|
2390
|
-
# Structural seams drop the columnar map wholesale; the next
|
|
2391
|
-
# children read re-primes it (see COLUMNAR_WALK). Cheap no-op
|
|
2392
|
-
# when no map is live: one lock-free attachment probe.
|
|
2393
|
-
def drop_columnar_children(node)
|
|
2394
|
-
return unless COLUMNAR_WALK
|
|
2395
|
-
|
|
2396
|
-
doc =
|
|
2397
|
-
case node
|
|
2398
|
-
when ::Leptris::XML::NativeNode
|
|
2399
|
-
doc_for(node)
|
|
2400
|
-
when ::Leptris::XML::Document
|
|
2401
|
-
node
|
|
2402
|
-
else
|
|
2403
|
-
begin
|
|
2404
|
-
node.document
|
|
2405
|
-
rescue StandardError
|
|
2406
|
-
nil
|
|
2407
|
-
end
|
|
2408
|
-
end
|
|
2409
|
-
return if doc.nil?
|
|
2410
|
-
|
|
2411
|
-
return if attachments.get(doc, COLUMNAR_CHILDREN).nil?
|
|
2412
|
-
|
|
2413
|
-
attachments.delete(doc, COLUMNAR_CHILDREN)
|
|
2414
|
-
end
|
|
2415
|
-
|
|
2416
2401
|
def native_keep_entity_refs?(node)
|
|
2417
2402
|
return false unless NATIVE_READ_LAYER &&
|
|
2418
2403
|
node.is_a?(::Leptris::XML::NativeNode)
|
|
@@ -2625,8 +2610,18 @@ module Moxml
|
|
|
2625
2610
|
# loop is C->C dlsym). Requires parent binding + the face;
|
|
2626
2611
|
# callers fall back to create_element + set_attribute +
|
|
2627
2612
|
# add_child when absent.
|
|
2613
|
+
# TODO.sota/04: the ONE-crossing build face. Returns the
|
|
2614
|
+
# contract-carrying row, or nil when this binding cannot
|
|
2615
|
+
# serve it (caller takes the legacy path).
|
|
2616
|
+
def add_element_full(parent, name, attrs)
|
|
2617
|
+
return nil unless NATIVE_C_MINT && ADD_ELEMENT_FULL &&
|
|
2618
|
+
parent.is_a?(::Leptris::XML::NativeNode)
|
|
2619
|
+
|
|
2620
|
+
validate_element_name(name)
|
|
2621
|
+
parent.add_element_full(name.to_s, attrs.flatten, nil)
|
|
2622
|
+
end
|
|
2623
|
+
|
|
2628
2624
|
def create_element_with_attrs(parent, name, attrs)
|
|
2629
|
-
drop_columnar_children(parent)
|
|
2630
2625
|
validate_element_name(name)
|
|
2631
2626
|
# NativeNode parents need NO FFI twin (TODO.sota/04 bridge
|
|
2632
2627
|
# tax): the bridge existed only to obtain c_address +
|
|
@@ -2964,8 +2959,6 @@ module Moxml
|
|
|
2964
2959
|
# on the source, so a living source can never outlive the
|
|
2965
2960
|
# memory its reads still touch.
|
|
2966
2961
|
def absorb_source_document(dest, owner)
|
|
2967
|
-
drop_columnar_children(dest)
|
|
2968
|
-
drop_columnar_children(owner)
|
|
2969
2962
|
return nil unless NATIVE_DOC_ABSORB
|
|
2970
2963
|
return :direct if attachments.get(dest, :absorbed_sources)&.include?(owner)
|
|
2971
2964
|
|
|
@@ -3011,8 +3004,6 @@ module Moxml
|
|
|
3011
3004
|
end
|
|
3012
3005
|
|
|
3013
3006
|
def add_child(parent, child)
|
|
3014
|
-
drop_columnar_children(parent)
|
|
3015
|
-
drop_columnar_children(child)
|
|
3016
3007
|
result = add_child_impl(parent, child)
|
|
3017
3008
|
# A successful child insertion rebuilds the engine's
|
|
3018
3009
|
# document index; the upstream sibling-insert bug leaves it
|
|
@@ -3134,8 +3125,6 @@ module Moxml
|
|
|
3134
3125
|
end
|
|
3135
3126
|
|
|
3136
3127
|
def add_previous_sibling(node, new_node)
|
|
3137
|
-
drop_columnar_children(node)
|
|
3138
|
-
drop_columnar_children(new_node)
|
|
3139
3128
|
result = add_previous_sibling_impl(node, new_node)
|
|
3140
3129
|
mark_xpath_index_stale(node)
|
|
3141
3130
|
result
|
|
@@ -3188,8 +3177,6 @@ module Moxml
|
|
|
3188
3177
|
end
|
|
3189
3178
|
|
|
3190
3179
|
def add_next_sibling(node, new_node)
|
|
3191
|
-
drop_columnar_children(node)
|
|
3192
|
-
drop_columnar_children(new_node)
|
|
3193
3180
|
result = add_next_sibling_impl(node, new_node)
|
|
3194
3181
|
mark_xpath_index_stale(node)
|
|
3195
3182
|
result
|
|
@@ -3266,7 +3253,6 @@ module Moxml
|
|
|
3266
3253
|
end
|
|
3267
3254
|
|
|
3268
3255
|
def remove(node)
|
|
3269
|
-
drop_columnar_children(node)
|
|
3270
3256
|
node = to_binding(node) if NATIVE_READ_LAYER
|
|
3271
3257
|
case node
|
|
3272
3258
|
when CustomizedLeptris::Declaration
|
|
@@ -3298,8 +3284,6 @@ module Moxml
|
|
|
3298
3284
|
end
|
|
3299
3285
|
|
|
3300
3286
|
def replace(node, new_node)
|
|
3301
|
-
drop_columnar_children(node)
|
|
3302
|
-
drop_columnar_children(new_node)
|
|
3303
3287
|
if NATIVE_READ_LAYER
|
|
3304
3288
|
node = to_binding(node)
|
|
3305
3289
|
new_node = to_binding(new_node)
|
|
@@ -3358,7 +3342,6 @@ module Moxml
|
|
|
3358
3342
|
Gem::Version.new(::Leptris::VERSION) >= Gem::Version.new("1.9.290.0")
|
|
3359
3343
|
|
|
3360
3344
|
def replace_children(node, new_children)
|
|
3361
|
-
drop_columnar_children(node)
|
|
3362
3345
|
# Cross-pool children: the binding's bulk face adopts per
|
|
3363
3346
|
# node and fails on mixed kinds (inner_xml= with
|
|
3364
3347
|
# text+elements died with Memory allocation failed —
|
|
@@ -3448,7 +3431,6 @@ module Moxml
|
|
|
3448
3431
|
::Leptris::XML::NativeNode.method_defined?(:set_text_native)
|
|
3449
3432
|
|
|
3450
3433
|
def set_text_content(node, content)
|
|
3451
|
-
drop_columnar_children(node)
|
|
3452
3434
|
if SET_TEXT_NATIVE && node.is_a?(::Leptris::XML::NativeNode)
|
|
3453
3435
|
begin
|
|
3454
3436
|
node.set_text_native(content.to_s)
|
data/lib/moxml/element.rb
CHANGED
|
@@ -139,10 +139,17 @@ module Moxml
|
|
|
139
139
|
# wrapper. Equivalent to create_element + attribute writes +
|
|
140
140
|
# add_child, minus two crossings and the intermediate churn.
|
|
141
141
|
def add_element(name, attrs = {})
|
|
142
|
+
# TODO.sota/04: one crossing when the binding serves it.
|
|
143
|
+
native_child = adapter.add_element_full(@native, name, attrs)
|
|
144
|
+
if native_child
|
|
145
|
+
attach_built_child(native_child)
|
|
146
|
+
return Moxml::Node.wrap_with(native_child, context, adapter)
|
|
147
|
+
end
|
|
142
148
|
native_child = adapter.create_element_with_attrs(
|
|
143
149
|
@native, name, attrs
|
|
144
150
|
)
|
|
145
151
|
if native_child
|
|
152
|
+
attach_built_child(native_child)
|
|
146
153
|
Moxml::Node.wrap_with(native_child, context, adapter)
|
|
147
154
|
else
|
|
148
155
|
fallback = Moxml::Node.wrap_with(
|
|
@@ -154,6 +161,16 @@ module Moxml
|
|
|
154
161
|
end
|
|
155
162
|
end
|
|
156
163
|
|
|
164
|
+
# The construction fast paths bypass add_child, so they owe the
|
|
165
|
+
# same invalidation contract: the ctx children clock retires every
|
|
166
|
+
# wrapper's generation-checked memo (the receiver's own included —
|
|
167
|
+
# the read re-checks the generation before serving).
|
|
168
|
+
def attach_built_child(native_child)
|
|
169
|
+
context.bump_children_generation
|
|
170
|
+
native_child.parent_node = self
|
|
171
|
+
native_child
|
|
172
|
+
end
|
|
173
|
+
|
|
157
174
|
def attributes
|
|
158
175
|
# Primed like Node.wrap: the adapter and type are known at
|
|
159
176
|
# mint, so the first name/value/attribute? access skips the
|
data/lib/moxml/version.rb
CHANGED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "spec_helper"
|
|
4
|
+
|
|
5
|
+
# The COLUMNAR_WALK children map must never serve rows older than a
|
|
6
|
+
# completed mutation, through EVERY write lane. This is the memo-version
|
|
7
|
+
# law (leptris-ruby#530) applied to the columnar map: the binding
|
|
8
|
+
# Document#@version advances on every mutation, so a map primed under
|
|
9
|
+
# an older version is stale by construction — independent of which
|
|
10
|
+
# adapter seam (or none) happens to drop it.
|
|
11
|
+
RSpec.describe "columnar children map invalidation" do
|
|
12
|
+
let(:ctx) { Moxml.new(:leptris) }
|
|
13
|
+
|
|
14
|
+
def walk_children(node)
|
|
15
|
+
node.children.map { |c| c.is_a?(Moxml::Element) ? c.name : c.text }
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
it "serves fresh rows after content= replaces a child subtree" do
|
|
19
|
+
doc = ctx.parse("<r><a>t1</a><b>t2</b></r>")
|
|
20
|
+
a = doc.root.children.first
|
|
21
|
+
walk_children(a) # prime the columnar map over a's subtree
|
|
22
|
+
a.content = "replaced"
|
|
23
|
+
expect(walk_children(a)).to eq(["replaced"])
|
|
24
|
+
expect(walk_children(doc.root)).to eq(%w[a b])
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
it "serves fresh rows after add_element" do
|
|
28
|
+
doc = ctx.parse("<r><a/></r>")
|
|
29
|
+
walk_children(doc.root)
|
|
30
|
+
doc.root.add_element("b", "k" => "v")
|
|
31
|
+
expect(walk_children(doc.root)).to eq(%w[a b])
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
it "serves fresh rows after add_next_sibling" do
|
|
35
|
+
doc = ctx.parse("<r><a/></r>")
|
|
36
|
+
a = doc.root.children.first
|
|
37
|
+
walk_children(doc.root)
|
|
38
|
+
a.add_next_sibling("<c/>")
|
|
39
|
+
expect(walk_children(doc.root)).to eq(%w[a c])
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
it "serves fresh rows after remove" do
|
|
43
|
+
doc = ctx.parse("<r><a/><b/></r>")
|
|
44
|
+
b = doc.root.children.last
|
|
45
|
+
walk_children(doc.root)
|
|
46
|
+
b.remove
|
|
47
|
+
expect(walk_children(doc.root)).to eq(%w[a])
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
it "keeps the primed rows valid when nothing mutated" do
|
|
51
|
+
doc = ctx.parse("<r><a>t1</a><b>t2</b></r>")
|
|
52
|
+
expected = walk_children(doc.root)
|
|
53
|
+
expect(walk_children(doc.root)).to eq(expected)
|
|
54
|
+
expect(walk_children(doc.root)).to eq(expected)
|
|
55
|
+
end
|
|
56
|
+
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: moxml
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.5.
|
|
4
|
+
version: 0.5.161
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Ribose Inc.
|
|
@@ -477,6 +477,7 @@ files:
|
|
|
477
477
|
- spec/moxml/adapter/.gitkeep
|
|
478
478
|
- spec/moxml/adapter/README.md
|
|
479
479
|
- spec/moxml/adapter/base_spec.rb
|
|
480
|
+
- spec/moxml/adapter/columnar_invalidation_spec.rb
|
|
480
481
|
- spec/moxml/adapter/entity_restoration_spec.rb
|
|
481
482
|
- spec/moxml/adapter/headed_ox_spec.rb
|
|
482
483
|
- spec/moxml/adapter/leptris_adoption_spec.rb
|