leptris 1.9.366.1-ppc64le-linux → 1.9.367.1-ppc64le-linux
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/Rakefile +1 -1
- data/ext/leptris/native/native.c +47 -7
- data/lib/leptris/version.rb +1 -1
- data/lib/leptris/xml/descriptor.rb +25 -6
- data/lib/leptris/xml/native.so +0 -0
- data/lib/leptris/xml/native_3_3.so +0 -0
- data/lib/leptris/xml/serialization.rb +16 -0
- data/lib/libleptris.so +0 -0
- data/vendor-src/README.md +1 -1
- data/vendor-src/libleptris/CHANGELOG.md +18 -0
- data/vendor-src/libleptris/CMakeLists.txt +1 -1
- data/vendor-src/libleptris/src/leptris/dom/element.h +3 -10
- data/vendor-src/libleptris/test/CMakeLists.txt +1 -0
- data/vendor-src/libleptris/test/abi/test_descriptor.cpp +126 -0
- data/vendor-src/libleptris/test/dom/test_namespace_cache.cpp +89 -0
- data/vendor-src/libleptris/vcpkg.json +1 -1
- 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: d996dcde27b8a92377e9a96fffd0b4887009184bb0b07c0475e12d4f30acb5df
|
|
4
|
+
data.tar.gz: 35426fa785191c3530537a11ec42b600ab447a94d5b7a6146438e6a514e37b84
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 49c83209ba13e90f95df084cf4a47bbfc66c4e7c6b376d35a5351066aa265d0c9911dd2c447b675e99bded7a788e0398890cd02d92edc69f4834a5f29be525a1
|
|
7
|
+
data.tar.gz: 7470cc1306c12dd6058aefefc61d6ae83bd296eea613eff614a5e6c6bf272444424a4fd51fed900cd9c17f29c6c70fc16a73e208d32777c508c6730d9f233c84
|
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.367"
|
|
12
12
|
# Vendored alongside libleptris for fn:normalize-unicode (TODO
|
|
13
13
|
# .restructure/20): built per platform with a RELOCATABLE @rpath
|
|
14
14
|
# install name, loaded by ffi.rb before libleptris so the
|
data/ext/leptris/native/native.c
CHANGED
|
@@ -39,6 +39,7 @@ typedef int (*xp_node_kind_fn)(void *, int);
|
|
|
39
39
|
typedef const char *(*xp_node_name_fn)(void *, int);
|
|
40
40
|
typedef const char *(*xp_node_value_fn)(void *, int);
|
|
41
41
|
typedef size_t (*serialize_into_fn)(void *, char *, size_t, void *, void *);
|
|
42
|
+
typedef char *(*serialize_ext_sized_fn)(void *, const void *, const void *, size_t);
|
|
42
43
|
typedef int (*ns_count_fn)(void *);
|
|
43
44
|
typedef void *(*attr_first_fn)(void *);
|
|
44
45
|
typedef void *(*attr_next_fn)(void *);
|
|
@@ -75,7 +76,8 @@ static xp_nodes_ex_fn f_xp_nodes_ex;
|
|
|
75
76
|
static xp_node_kind_fn f_xp_node_kind;
|
|
76
77
|
static xp_node_name_fn f_xp_node_name;
|
|
77
78
|
static xp_node_value_fn f_xp_node_value;
|
|
78
|
-
static
|
|
79
|
+
static serialize_ext_sized_fn f_doc_serialize_xml;
|
|
80
|
+
static serialize_into_fn f_elem_serialize;
|
|
79
81
|
static ns_count_fn f_elem_ns_count;
|
|
80
82
|
static attr_first_fn f_attr_first;
|
|
81
83
|
static attr_next_fn f_attr_next;
|
|
@@ -272,7 +274,8 @@ static void resolve_symbols(const char *lib_path)
|
|
|
272
274
|
f_xp_node_kind = (xp_node_kind_fn)lib_sym(h, "leptris_xpath_result_node_kind");
|
|
273
275
|
f_xp_node_name = (xp_node_name_fn)lib_sym(h, "leptris_xpath_result_node_name");
|
|
274
276
|
f_xp_node_value = (xp_node_value_fn)lib_sym(h, "leptris_xpath_result_node_value");
|
|
275
|
-
|
|
277
|
+
f_doc_serialize_xml = (serialize_ext_sized_fn)lib_sym(
|
|
278
|
+
h, "leptris_document_serialize_ext_sized");
|
|
276
279
|
f_elem_serialize = (serialize_into_fn)lib_sym(h, "leptris_element_serialize_into");
|
|
277
280
|
f_attr_first = (attr_first_fn)lib_sym(h, "leptris_element_first_attribute");
|
|
278
281
|
f_attr_next = (attr_next_fn)lib_sym(h, "leptris_attribute_next");
|
|
@@ -352,7 +355,7 @@ static void resolve_symbols(const char *lib_path)
|
|
|
352
355
|
!f_sax_pfree || !f_sax_set_flags ||
|
|
353
356
|
!f_elem_prefix || !f_xp_count || !f_xp_nodes_ex ||
|
|
354
357
|
!f_xp_node_kind || !f_xp_node_name || !f_xp_node_value ||
|
|
355
|
-
!
|
|
358
|
+
!f_doc_serialize_xml || !f_elem_serialize || !f_attr_first ||
|
|
356
359
|
!f_elem_ns_count || !f_elem_ns ||
|
|
357
360
|
!f_attr_next || !f_attr_name || !f_attr_value ||
|
|
358
361
|
!f_node_line || !f_node_offset)
|
|
@@ -2120,15 +2123,52 @@ static VALUE fast_serialize(serialize_into_fn fn, void *node,
|
|
|
2120
2123
|
: rb_utf8_str_new_cstr("");
|
|
2121
2124
|
}
|
|
2122
2125
|
|
|
2126
|
+
/* rb_utf8_str_new_cstr may raise; the engine allocation remains owned
|
|
2127
|
+
* until the ensure callback runs on either return or exception. */
|
|
2128
|
+
struct document_xml_result {
|
|
2129
|
+
char *xml;
|
|
2130
|
+
};
|
|
2131
|
+
|
|
2132
|
+
static VALUE document_xml_string(VALUE arg)
|
|
2133
|
+
{
|
|
2134
|
+
struct document_xml_result *result = (void *)(uintptr_t)arg;
|
|
2135
|
+
return rb_utf8_str_new_cstr(result->xml);
|
|
2136
|
+
}
|
|
2137
|
+
|
|
2138
|
+
static VALUE document_xml_free(VALUE arg)
|
|
2139
|
+
{
|
|
2140
|
+
struct document_xml_result *result = (void *)(uintptr_t)arg;
|
|
2141
|
+
f_free_str(result->xml);
|
|
2142
|
+
return Qnil;
|
|
2143
|
+
}
|
|
2144
|
+
|
|
2123
2145
|
static VALUE nf_fast_document_xml(VALUE self, VALUE addr,
|
|
2124
2146
|
VALUE indent, VALUE decl,
|
|
2125
2147
|
VALUE encoding)
|
|
2126
2148
|
{
|
|
2149
|
+
/* Prefix of LeptrisSerializeExtOptions through html_method.
|
|
2150
|
+
* Pass its size so later engine fields remain at their defaults. */
|
|
2151
|
+
struct {
|
|
2152
|
+
int indent_text;
|
|
2153
|
+
const char *indent_unit;
|
|
2154
|
+
int expand_empty;
|
|
2155
|
+
int html_method;
|
|
2156
|
+
} ext = {0, NULL, 0, -1};
|
|
2157
|
+
struct serialize_opts opts;
|
|
2158
|
+
struct document_xml_result result;
|
|
2159
|
+
|
|
2127
2160
|
(void)self;
|
|
2128
|
-
|
|
2129
|
-
|
|
2130
|
-
|
|
2131
|
-
|
|
2161
|
+
opts.indent = NUM2INT(indent);
|
|
2162
|
+
opts.xml_declaration = RTEST(decl) ? 1 : 0;
|
|
2163
|
+
opts.encoding = NIL_P(encoding) ? NULL : StringValueCStr(encoding);
|
|
2164
|
+
/* HTML documents default to HTML output in the generic entry.
|
|
2165
|
+
* This XML face forces XML without changing the document's mode. */
|
|
2166
|
+
result.xml = f_doc_serialize_xml((void *)(uintptr_t)NUM2ULL(addr),
|
|
2167
|
+
&opts, &ext, sizeof(ext));
|
|
2168
|
+
if (!result.xml)
|
|
2169
|
+
return rb_utf8_str_new_cstr("");
|
|
2170
|
+
return rb_ensure(document_xml_string, (VALUE)(uintptr_t)&result,
|
|
2171
|
+
document_xml_free, (VALUE)(uintptr_t)&result);
|
|
2132
2172
|
}
|
|
2133
2173
|
|
|
2134
2174
|
static VALUE nf_fast_element_xml(VALUE self, VALUE addr,
|
data/lib/leptris/version.rb
CHANGED
|
@@ -126,7 +126,7 @@ class Leptris::XML::Descriptor
|
|
|
126
126
|
"descriptor ABI v#{abi} != binding v#{Leptris::XML::FFI::PLAN_ABI_VERSION}"
|
|
127
127
|
end
|
|
128
128
|
plans = []
|
|
129
|
-
root_index = flatten_plan(tree, plans)
|
|
129
|
+
root_index = flatten_plan(tree, plans, {})
|
|
130
130
|
spec, plans_pointer, anchors = pack_plans(plans)
|
|
131
131
|
spec[:abi_version] = abi
|
|
132
132
|
spec[:plan_count] = plans.size
|
|
@@ -143,13 +143,32 @@ class Leptris::XML::Descriptor
|
|
|
143
143
|
# Flattens the nested hash tree into the engine's flat plans
|
|
144
144
|
# array (child rows reference plans by index). Returns the root
|
|
145
145
|
# plan's index — always 0 for a single root.
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
146
|
+
#
|
|
147
|
+
# Identity-memoized (leptris#1743): lutaml-model's PlanCompiler
|
|
148
|
+
# (0.8.107+) feeds recursive content models as a DAG — the SAME
|
|
149
|
+
# plan Hash reachable from many parents. Without the memo each
|
|
150
|
+
# shared subtree re-flattened once per path (2^depth visits: the
|
|
151
|
+
# STS runaway — 28+ CPU-min, 14 GB RSS) and the duplicated index
|
|
152
|
+
# graph even mis-walked. Memoize on the ARGUMENT's identity
|
|
153
|
+
# (object_id, read before the dup) so each unique plan object is
|
|
154
|
+
# flattened once and shared rows reference one index.
|
|
155
|
+
def self.flatten_plan(tree, plans, memo = {})
|
|
156
|
+
key = tree.object_id
|
|
157
|
+
hit = memo[key]
|
|
158
|
+
return hit if hit
|
|
159
|
+
|
|
160
|
+
raise Leptris::XML::Error,
|
|
161
|
+
"plan tree contains a cycle at #{tree[:name].inspect} — " "the plan graph must be a DAG" if hit == false
|
|
162
|
+
|
|
163
|
+
memo[key] = false # in-progress; a finished entry is an Integer index
|
|
164
|
+
duped = tree.dup
|
|
165
|
+
plans << duped
|
|
149
166
|
tree_index = plans.size - 1
|
|
150
|
-
|
|
167
|
+
memo[key] = tree_index
|
|
168
|
+
duped[:children] = (duped[:children] || []).map do |row|
|
|
151
169
|
if row[:kind] == :nested
|
|
152
|
-
row.merge(child_plan_index: flatten_plan(row.fetch(:plan), plans
|
|
170
|
+
row.merge(child_plan_index: flatten_plan(row.fetch(:plan), plans,
|
|
171
|
+
memo))
|
|
153
172
|
else
|
|
154
173
|
row
|
|
155
174
|
end
|
data/lib/leptris/xml/native.so
CHANGED
|
Binary file
|
|
Binary file
|
|
@@ -28,6 +28,15 @@ module Leptris::XML::Serialization
|
|
|
28
28
|
# Public within the library: Element#to_xml and Document#to_xml
|
|
29
29
|
# pass the hoisted bound methods straight through.
|
|
30
30
|
|
|
31
|
+
# Document XML output must override an HTML document's default
|
|
32
|
+
# serialization method. Element serialization already defaults to XML.
|
|
33
|
+
XML_METHOD_EXT = begin
|
|
34
|
+
ext = Leptris::XML::FFI::SerializeExtStruct.new
|
|
35
|
+
ext[:html_method] = -1
|
|
36
|
+
ext
|
|
37
|
+
end
|
|
38
|
+
private_constant :XML_METHOD_EXT
|
|
39
|
+
|
|
31
40
|
# +ffi_function+ is a bound _serialize_into function. The buffer
|
|
32
41
|
# cycle lives at the FFI seam (FFI.serialize_into_string); this
|
|
33
42
|
# module owns only options selection and construction.
|
|
@@ -52,6 +61,11 @@ module Leptris::XML::Serialization
|
|
|
52
61
|
indent: indent, no_decl: no_decl, encoding: encoding)
|
|
53
62
|
opts
|
|
54
63
|
end
|
|
64
|
+
if ffi_function == DOCUMENT_SERIALIZE_INTO
|
|
65
|
+
return Leptris::XML::FFI.read_owned_string(
|
|
66
|
+
Leptris::XML::FFI.leptris_document_serialize_ext_sized(
|
|
67
|
+
c_ptr, opts.pointer, XML_METHOD_EXT.pointer, XML_METHOD_EXT.size))
|
|
68
|
+
end
|
|
55
69
|
Leptris::XML::FFI.serialize_into_string(ffi_function, c_ptr, opts.pointer)
|
|
56
70
|
end
|
|
57
71
|
|
|
@@ -140,6 +154,7 @@ module Leptris::XML::Serialization
|
|
|
140
154
|
INDENT_TEXT_EXT = begin
|
|
141
155
|
ext = Leptris::XML::FFI::SerializeExtStruct.new
|
|
142
156
|
ext[:indent_text] = 1
|
|
157
|
+
ext[:html_method] = -1
|
|
143
158
|
ext
|
|
144
159
|
end
|
|
145
160
|
private_constant :INDENT_TEXT_EXT
|
|
@@ -167,6 +182,7 @@ module Leptris::XML::Serialization
|
|
|
167
182
|
unit_anchor = ::FFI::MemoryPointer.from_string(
|
|
168
183
|
indent.positive? ? unit.to_s * indent : unit.to_s)
|
|
169
184
|
ext[:indent_unit] = unit_anchor
|
|
185
|
+
ext[:html_method] = -1
|
|
170
186
|
str_ptr = Leptris::XML::FFI.leptris_document_serialize_ext_sized(
|
|
171
187
|
c_ptr, opts.pointer, ext.pointer, ext.size)
|
|
172
188
|
Leptris::XML::FFI.read_owned_string(str_ptr)
|
data/lib/libleptris.so
CHANGED
|
Binary file
|
data/vendor-src/README.md
CHANGED
|
@@ -1,5 +1,23 @@
|
|
|
1
1
|
## [Unreleased]
|
|
2
2
|
|
|
3
|
+
## [1.9.367] - 2026-10-17
|
|
4
|
+
|
|
5
|
+
### Fixed
|
|
6
|
+
|
|
7
|
+
- **dom: zero-initialize the namespace cache at every allocation site (#1742, PR #1742).**
|
|
8
|
+
The three cache-creating sites partially initialized the new
|
|
9
|
+
`leptris_ns_cache` — prefix/URI/declarations (and `raw_attrs` at only one of
|
|
10
|
+
the three) — leaving `doc_next`, the heap-ownership flags, and the
|
|
11
|
+
child-iteration cache (generations, index, node pointer) on **arena
|
|
12
|
+
garbage**. Any read before its explicit set was an uninitialized read: the
|
|
13
|
+
document-free walk chain and the child-cache generation compare could act on
|
|
14
|
+
poisoned bytes. All three sites now zero the whole struct. New
|
|
15
|
+
poisoned-arena regressions fill the remaining arena with `0xa5` and assert
|
|
16
|
+
every field comes up clean through the prefix setter, the URI setter, and
|
|
17
|
+
`cache_ensure` (red on main's header, green on the fix). Engine suite
|
|
18
|
+
2040/2040 on the PR, 2048/2048 on merged main.
|
|
19
|
+
|
|
20
|
+
|
|
3
21
|
## [1.9.366] - 2026-10-17
|
|
4
22
|
|
|
5
23
|
### Fixed
|
|
@@ -576,9 +576,7 @@ static inline void leptris_elem_set_prefix(LeptrisElement e, char* prefix,
|
|
|
576
576
|
nc = (struct leptris_ns_cache*)
|
|
577
577
|
leptris_pool_alloc(pool, sizeof(struct leptris_ns_cache));
|
|
578
578
|
if (!nc) return;
|
|
579
|
-
nc
|
|
580
|
-
nc->namespace_uri = NULL;
|
|
581
|
-
nc->declarations = NULL;
|
|
579
|
+
memset(nc, 0, sizeof(*nc));
|
|
582
580
|
elem_set_ns_cache(e, nc);
|
|
583
581
|
}
|
|
584
582
|
nc->prefix = prefix;
|
|
@@ -594,9 +592,7 @@ static inline void leptris_elem_set_ns_uri(LeptrisElement e, char* uri,
|
|
|
594
592
|
nc = (struct leptris_ns_cache*)
|
|
595
593
|
leptris_pool_alloc(pool, sizeof(struct leptris_ns_cache));
|
|
596
594
|
if (!nc) return;
|
|
597
|
-
nc
|
|
598
|
-
nc->namespace_uri = NULL;
|
|
599
|
-
nc->declarations = NULL;
|
|
595
|
+
memset(nc, 0, sizeof(*nc));
|
|
600
596
|
elem_set_ns_cache(e, nc);
|
|
601
597
|
}
|
|
602
598
|
nc->namespace_uri = uri;
|
|
@@ -694,10 +690,7 @@ static inline struct leptris_ns_cache* leptris_elem_cache_ensure(
|
|
|
694
690
|
nc = (struct leptris_ns_cache*)
|
|
695
691
|
leptris_pool_alloc(pool, sizeof(struct leptris_ns_cache));
|
|
696
692
|
if (!nc) return NULL;
|
|
697
|
-
nc
|
|
698
|
-
nc->namespace_uri = NULL;
|
|
699
|
-
nc->declarations = NULL;
|
|
700
|
-
nc->raw_attrs = NULL;
|
|
693
|
+
memset(nc, 0, sizeof(*nc));
|
|
701
694
|
elem_set_ns_cache(e, nc);
|
|
702
695
|
return nc;
|
|
703
696
|
}
|
|
@@ -105,6 +105,7 @@ leptris_add_test(test_large_docs parser/test_large_documents.cpp)
|
|
|
105
105
|
leptris_add_test(test_encoding parser/test_encoding.cpp)
|
|
106
106
|
leptris_add_test(test_dom dom/test_dom.cpp)
|
|
107
107
|
leptris_add_test(test_element_children dom/test_element_children.cpp)
|
|
108
|
+
leptris_add_test(test_namespace_cache dom/test_namespace_cache.cpp)
|
|
108
109
|
leptris_add_test(test_map_lifecycle dom/test_map_lifecycle.cpp)
|
|
109
110
|
leptris_add_test(test_simd_count parser/test_simd_count.cpp)
|
|
110
111
|
leptris_add_test(test_ws_shape parser/test_whitespace_shape.cpp)
|
|
@@ -407,6 +407,132 @@ TEST(PlanWalk, NestedScalarsAttributesCollections) {
|
|
|
407
407
|
leptris_plan_free(plan);
|
|
408
408
|
}
|
|
409
409
|
|
|
410
|
+
/* leptris#1743: diamond-shared plan graphs — two sibling rows
|
|
411
|
+
* referencing the SAME plan index (lutaml-model's PlanCompiler
|
|
412
|
+
* emits recursive content models as a DAG; the binding flattens it
|
|
413
|
+
* identity-memoized). Each parent must walk the shared plan
|
|
414
|
+
* independently: b's <leaf> must materialize even though plan 1
|
|
415
|
+
* was already walked under a. */
|
|
416
|
+
TEST(PlanWalk, SharedChildPlanIndexWalksPerParent) {
|
|
417
|
+
LeptrisDocument doc = parse(
|
|
418
|
+
"<n><a><leaf>x</leaf></a><b><leaf>y</leaf></b></n>");
|
|
419
|
+
ASSERT_NE(doc, nullptr);
|
|
420
|
+
LeptrisElement root = leptris_document_root(doc);
|
|
421
|
+
|
|
422
|
+
leptris_child_plan n_kids[2] = {
|
|
423
|
+
{"a", LEPTRIS_PLAN_KIND_NESTED, 1, 1},
|
|
424
|
+
{"b", LEPTRIS_PLAN_KIND_NESTED, 1, 1},
|
|
425
|
+
};
|
|
426
|
+
leptris_child_plan shared_kids[1] = {
|
|
427
|
+
{"leaf", LEPTRIS_PLAN_KIND_NESTED, 2, 2}};
|
|
428
|
+
leptris_element_plan plans[3] = {};
|
|
429
|
+
plans[0].element_name = "n";
|
|
430
|
+
plans[0].child_count = 2;
|
|
431
|
+
plans[0].child_plans = n_kids;
|
|
432
|
+
plans[1].element_name = "a";
|
|
433
|
+
plans[1].child_count = 1;
|
|
434
|
+
plans[1].child_plans = shared_kids;
|
|
435
|
+
plans[2].element_name = "leaf";
|
|
436
|
+
|
|
437
|
+
leptris_plan_spec spec = {};
|
|
438
|
+
spec.abi_version = leptris_plan_abi_version();
|
|
439
|
+
spec.plan_count = 3;
|
|
440
|
+
spec.plans = plans;
|
|
441
|
+
|
|
442
|
+
LeptrisStatus st = LEPTRIS_OK;
|
|
443
|
+
LeptrisPlan plan = leptris_plan_build(&spec, &st);
|
|
444
|
+
ASSERT_NE(plan, nullptr);
|
|
445
|
+
EXPECT_EQ(st, LEPTRIS_OK);
|
|
446
|
+
|
|
447
|
+
LeptrisPlanResult r = leptris_plan_walk(doc, root, plan, &st);
|
|
448
|
+
ASSERT_NE(r, nullptr);
|
|
449
|
+
ASSERT_EQ(leptris_plan_value_count(r), 2u);
|
|
450
|
+
|
|
451
|
+
LeptrisPlanResult a = leptris_plan_value_at(r, 0);
|
|
452
|
+
ASSERT_NE(a, nullptr);
|
|
453
|
+
EXPECT_STREQ(leptris_plan_value_name(a), "a");
|
|
454
|
+
ASSERT_EQ(leptris_plan_value_count(a), 1u);
|
|
455
|
+
LeptrisPlanResult al = leptris_plan_value_at(a, 0);
|
|
456
|
+
EXPECT_STREQ(leptris_plan_value_name(al), "leaf");
|
|
457
|
+
EXPECT_EQ(leptris_plan_value_kind(al), LEPTRIS_PLAN_VALUE_ELEMENT);
|
|
458
|
+
|
|
459
|
+
LeptrisPlanResult b = leptris_plan_value_at(r, 1);
|
|
460
|
+
ASSERT_NE(b, nullptr);
|
|
461
|
+
EXPECT_STREQ(leptris_plan_value_name(b), "b");
|
|
462
|
+
ASSERT_EQ(leptris_plan_value_count(b), 1u);
|
|
463
|
+
LeptrisPlanResult bl = leptris_plan_value_at(b, 0);
|
|
464
|
+
EXPECT_STREQ(leptris_plan_value_name(bl), "leaf");
|
|
465
|
+
EXPECT_EQ(leptris_plan_value_kind(bl), LEPTRIS_PLAN_VALUE_ELEMENT);
|
|
466
|
+
|
|
467
|
+
leptris_plan_result_free(r);
|
|
468
|
+
leptris_plan_free(plan);
|
|
469
|
+
leptris_document_free(doc);
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
/* leptris#1743: RECURSIVE plan graphs — lutaml-model's
|
|
473
|
+
* PlanCompiler emits recursive content models as a plan CYCLE
|
|
474
|
+
* (entry -> sub -> entry); a NESTED row's target plan describes
|
|
475
|
+
* the element the row matches, so the cycle rides an
|
|
476
|
+
* intermediate plan. The binding flattens these identity-memoized
|
|
477
|
+
* (the flatten is where the STS runaway lived). The walk follows
|
|
478
|
+
* the DOCUMENT tree, so a finite document must materialize
|
|
479
|
+
* through every re-entry of the shared plans. */
|
|
480
|
+
TEST(PlanWalk, RecursivePlanCycleWalksFiniteDocument) {
|
|
481
|
+
LeptrisDocument doc = parse(
|
|
482
|
+
"<entry><t>1</t><sub><entry><t>2</t></entry></sub></entry>");
|
|
483
|
+
ASSERT_NE(doc, nullptr);
|
|
484
|
+
LeptrisElement root = leptris_document_root(doc);
|
|
485
|
+
|
|
486
|
+
leptris_child_plan entry_kids[2] = {
|
|
487
|
+
{"t", LEPTRIS_PLAN_KIND_SCALAR, 1, -1},
|
|
488
|
+
{"sub", LEPTRIS_PLAN_KIND_NESTED, 2, 1},
|
|
489
|
+
};
|
|
490
|
+
leptris_child_plan sub_kids[1] = {
|
|
491
|
+
{"entry", LEPTRIS_PLAN_KIND_NESTED, 3, 0}};
|
|
492
|
+
leptris_element_plan plans[2] = {};
|
|
493
|
+
plans[0].element_name = "entry";
|
|
494
|
+
plans[0].child_count = 2;
|
|
495
|
+
plans[0].child_plans = entry_kids;
|
|
496
|
+
plans[1].element_name = "sub";
|
|
497
|
+
plans[1].child_count = 1;
|
|
498
|
+
plans[1].child_plans = sub_kids;
|
|
499
|
+
|
|
500
|
+
leptris_plan_spec spec = {};
|
|
501
|
+
spec.abi_version = leptris_plan_abi_version();
|
|
502
|
+
spec.plan_count = 2;
|
|
503
|
+
spec.plans = plans;
|
|
504
|
+
|
|
505
|
+
LeptrisStatus st = LEPTRIS_OK;
|
|
506
|
+
LeptrisPlan plan = leptris_plan_build(&spec, &st);
|
|
507
|
+
ASSERT_NE(plan, nullptr);
|
|
508
|
+
EXPECT_EQ(st, LEPTRIS_OK);
|
|
509
|
+
|
|
510
|
+
LeptrisPlanResult r = leptris_plan_walk(doc, root, plan, &st);
|
|
511
|
+
ASSERT_NE(r, nullptr);
|
|
512
|
+
ASSERT_EQ(leptris_plan_value_count(r), 2u);
|
|
513
|
+
|
|
514
|
+
LeptrisPlanResult t1 = leptris_plan_value_at(r, 0);
|
|
515
|
+
ASSERT_NE(t1, nullptr);
|
|
516
|
+
EXPECT_STREQ(leptris_plan_value_string(t1), "1");
|
|
517
|
+
|
|
518
|
+
LeptrisPlanResult sub = leptris_plan_value_at(r, 1);
|
|
519
|
+
ASSERT_NE(sub, nullptr);
|
|
520
|
+
EXPECT_STREQ(leptris_plan_value_name(sub), "sub");
|
|
521
|
+
/* the cycle re-enters plan 0 for the entry inside sub */
|
|
522
|
+
ASSERT_EQ(leptris_plan_value_count(sub), 1u);
|
|
523
|
+
LeptrisPlanResult inner = leptris_plan_value_at(sub, 0);
|
|
524
|
+
ASSERT_NE(inner, nullptr);
|
|
525
|
+
EXPECT_STREQ(leptris_plan_value_name(inner), "entry");
|
|
526
|
+
ASSERT_EQ(leptris_plan_value_count(inner), 1u);
|
|
527
|
+
LeptrisPlanResult t2 = leptris_plan_value_at(inner, 0);
|
|
528
|
+
ASSERT_NE(t2, nullptr);
|
|
529
|
+
EXPECT_STREQ(leptris_plan_value_string(t2), "2");
|
|
530
|
+
|
|
531
|
+
leptris_plan_result_free(r);
|
|
532
|
+
leptris_plan_free(plan);
|
|
533
|
+
leptris_document_free(doc);
|
|
534
|
+
}
|
|
535
|
+
|
|
410
536
|
TEST(PlanWalk, RawCallbackAndMixedContent) {
|
|
411
537
|
LeptrisDocument doc = parse(
|
|
412
538
|
"<p>intro <b>bold</b> mid"
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
#include <gtest/gtest.h>
|
|
2
|
+
#include <cstring>
|
|
3
|
+
|
|
4
|
+
#include "leptris.h"
|
|
5
|
+
extern "C" {
|
|
6
|
+
#include "element.h"
|
|
7
|
+
#include "pool.h"
|
|
8
|
+
#include "arena.h"
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
namespace {
|
|
12
|
+
|
|
13
|
+
class NamespaceCacheInitialization : public ::testing::Test {
|
|
14
|
+
protected:
|
|
15
|
+
LeptrisMemoryPool* pool = nullptr;
|
|
16
|
+
LeptrisElement element = nullptr;
|
|
17
|
+
|
|
18
|
+
void SetUp() override {
|
|
19
|
+
LeptrisArena* arena = leptris_arena_create(4096);
|
|
20
|
+
ASSERT_NE(arena, nullptr);
|
|
21
|
+
pool = leptris_pool_create_arena_backed(arena, 1);
|
|
22
|
+
if (!pool) leptris_arena_destroy(arena);
|
|
23
|
+
ASSERT_NE(pool, nullptr);
|
|
24
|
+
// Pool allocations are deliberately not zeroed. Poison the remaining
|
|
25
|
+
// arena so a new cache cannot pass by inheriting fresh OS zero pages.
|
|
26
|
+
std::memset(arena->base + arena->used, 0xa5, arena->size - arena->used);
|
|
27
|
+
element = static_cast<LeptrisElement>(
|
|
28
|
+
leptris_pool_alloc(pool, sizeof(*element)));
|
|
29
|
+
ASSERT_NE(element, nullptr);
|
|
30
|
+
std::memset(element, 0, sizeof(*element));
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
void TearDown() override {
|
|
34
|
+
leptris_pool_destroy(pool);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
void ExpectEmptyAuxiliaryState(const leptris_ns_cache* cache) {
|
|
38
|
+
ASSERT_NE(cache, nullptr);
|
|
39
|
+
EXPECT_EQ(cache->declarations, nullptr);
|
|
40
|
+
EXPECT_EQ(cache->raw_attrs, nullptr);
|
|
41
|
+
EXPECT_EQ(cache->doc_next, nullptr);
|
|
42
|
+
EXPECT_EQ(cache->prefix_heap, 0);
|
|
43
|
+
EXPECT_EQ(cache->uri_heap, 0);
|
|
44
|
+
EXPECT_EQ(cache->child_gen, 0u);
|
|
45
|
+
EXPECT_EQ(cache->child_cached_gen, 0u);
|
|
46
|
+
EXPECT_EQ(cache->child_idx, 0u);
|
|
47
|
+
EXPECT_EQ(cache->child_node, nullptr);
|
|
48
|
+
// The public raw-attribute read must see no phantom journal entries.
|
|
49
|
+
// Avoid following the poisoned pointer if the assertion above failed.
|
|
50
|
+
if (!cache->raw_attrs) {
|
|
51
|
+
EXPECT_EQ(leptris_element_attributes_raw(element, nullptr, nullptr, 0), 0u);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
TEST_F(NamespaceCacheInitialization, PrefixSetterStartsWithEmptyAuxiliaryState) {
|
|
57
|
+
char prefix[] = "p";
|
|
58
|
+
leptris_elem_set_prefix(element, prefix, pool);
|
|
59
|
+
const auto* cache = elem_get_ns_cache(element);
|
|
60
|
+
ASSERT_NE(cache, nullptr);
|
|
61
|
+
EXPECT_EQ(cache->prefix, prefix);
|
|
62
|
+
EXPECT_EQ(cache->namespace_uri, nullptr);
|
|
63
|
+
ExpectEmptyAuxiliaryState(cache);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
TEST_F(NamespaceCacheInitialization, NamespaceSetterStartsWithEmptyAuxiliaryState) {
|
|
67
|
+
char uri[] = "urn:example";
|
|
68
|
+
leptris_elem_set_ns_uri(element, uri, pool);
|
|
69
|
+
const auto* cache = elem_get_ns_cache(element);
|
|
70
|
+
ASSERT_NE(cache, nullptr);
|
|
71
|
+
EXPECT_EQ(cache->prefix, nullptr);
|
|
72
|
+
EXPECT_EQ(cache->namespace_uri, uri);
|
|
73
|
+
ExpectEmptyAuxiliaryState(cache);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
TEST_F(NamespaceCacheInitialization, EnsureStartsEmptyAndPreservesExistingCache) {
|
|
77
|
+
auto* cache = leptris_elem_cache_ensure(element, pool);
|
|
78
|
+
ASSERT_NE(cache, nullptr);
|
|
79
|
+
EXPECT_EQ(cache->prefix, nullptr);
|
|
80
|
+
EXPECT_EQ(cache->namespace_uri, nullptr);
|
|
81
|
+
ExpectEmptyAuxiliaryState(cache);
|
|
82
|
+
|
|
83
|
+
// Reusing a cache must preserve the state its consumers already stored.
|
|
84
|
+
cache->child_gen = 7;
|
|
85
|
+
EXPECT_EQ(leptris_elem_cache_ensure(element, pool), cache);
|
|
86
|
+
EXPECT_EQ(cache->child_gen, 7u);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
} // namespace
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://raw.githubusercontent.com/microsoft/vcpkg/master/scripts/vcpkg.schema.json",
|
|
3
3
|
"name": "leptris",
|
|
4
|
-
"version": "1.9.
|
|
4
|
+
"version": "1.9.367",
|
|
5
5
|
"description": "High-performance XML parser and XPath 1.0 engine in C (libleptris)",
|
|
6
6
|
"homepage": "https://github.com/leptris/leptris",
|
|
7
7
|
"license": "MIT",
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
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.367.1
|
|
5
5
|
platform: ppc64le-linux
|
|
6
6
|
authors:
|
|
7
7
|
- Ribose Inc.
|
|
@@ -745,6 +745,7 @@ files:
|
|
|
745
745
|
- vendor-src/libleptris/test/dom/test_dom.cpp
|
|
746
746
|
- vendor-src/libleptris/test/dom/test_element_children.cpp
|
|
747
747
|
- vendor-src/libleptris/test/dom/test_map_lifecycle.cpp
|
|
748
|
+
- vendor-src/libleptris/test/dom/test_namespace_cache.cpp
|
|
748
749
|
- vendor-src/libleptris/test/dom/test_node_surface_parity.cpp
|
|
749
750
|
- vendor-src/libleptris/test/dom/test_text_borrowed.cpp
|
|
750
751
|
- vendor-src/libleptris/test/dom/test_vtable.cpp
|