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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7072652eaa9946e9e31e9a3b897057b55899aa823ef5f094d0d4d7e6b3a96ca9
4
- data.tar.gz: a4a4789874227c8a239b0e97c66718f9d166ad0e578dacc69c61b9709f146368
3
+ metadata.gz: d996dcde27b8a92377e9a96fffd0b4887009184bb0b07c0475e12d4f30acb5df
4
+ data.tar.gz: 35426fa785191c3530537a11ec42b600ab447a94d5b7a6146438e6a514e37b84
5
5
  SHA512:
6
- metadata.gz: d4928c23a9d0d0ac644e74b4ec2dbb361407bf365dcd0f0de51578ad789116e725f8b6830d4c47e62c43bd91be5b1cd589f0aac214b528063c5e622229b1801e
7
- data.tar.gz: 4f607dc6e55b470ceb7d7d4262615387386733a434fdb1085d28174ecdf023d82542bba8eaf7f66b1f19dc012cfedd6d2cd4455345e334bedc38abf0fd1a0b0f
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.366"
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
@@ -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 serialize_into_fn f_doc_serialize, f_elem_serialize;
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
- f_doc_serialize = (serialize_into_fn)lib_sym(h, "leptris_document_serialize_into");
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
- !f_doc_serialize || !f_elem_serialize || !f_attr_first ||
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
- return fast_serialize(f_doc_serialize,
2129
- (void *)(uintptr_t)NUM2ULL(addr),
2130
- NUM2INT(indent), RTEST(decl) ? 1 : 0,
2131
- encoding);
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,
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Leptris
4
- VERSION = "1.9.366.1"
4
+ VERSION = "1.9.367.1"
5
5
  end
@@ -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
- def self.flatten_plan(tree, plans)
147
- tree = tree.dup
148
- plans << tree
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
- tree[:children] = (tree[:children] || []).map do |row|
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
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,7 +1,7 @@
1
1
  Sources vendored in this gem (recompile rights; the ruby
2
2
  variant builds them at install via extconf.rb):
3
3
 
4
- - libleptris v1.9.366
4
+ - libleptris v1.9.367
5
5
  - utf8proc v2.11.0
6
6
 
7
7
  Rebuild by hand:
@@ -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
@@ -3,7 +3,7 @@
3
3
  cmake_minimum_required(VERSION 3.20)
4
4
 
5
5
  project(leptris
6
- VERSION 1.9.366
6
+ VERSION 1.9.367
7
7
  DESCRIPTION "Fast XML parser and XPath evaluator in pure C"
8
8
  LANGUAGES C CXX
9
9
  )
@@ -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->prefix = NULL;
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->prefix = NULL;
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->prefix = NULL;
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.366",
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.366.1
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