leptris 1.9.319.0 → 1.9.320.0

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: 2249f8242e8a5d15ce4f661147bd4c6aecc64a0a8926e9601b0797299b1ea2c2
4
- data.tar.gz: 4db6afad4fcb92cbb8dca44b44db3703fa7831b0a5bcbe6b545f1c7321da5375
3
+ metadata.gz: 7e309fd6444a641db0b0f24f92aef96097860678c006a217a783724c499b7f50
4
+ data.tar.gz: d041122c9d4d1b366549b4dd2d81c8e6c8ee226cc8ddee62d30a6f9a7a582458
5
5
  SHA512:
6
- metadata.gz: 23e33b8150701da1f5535e9df6fe4d59a15f1255f3c8bfe4183e5391b6e717dfc6ee6946852fd19247f988ba2d255f86530bc8b020cd56befdb18e1174ffbdf1
7
- data.tar.gz: 55899c894639282f6ce290e940f61daf8b1416bd7fee6cc24b2d758df21f0b8fa80246b830309a4b6a684dfe34ebfe3af887d8b462f738a13ef0936436c28b85
6
+ metadata.gz: ae5b672a25949b41161419054c7ba635e2d234a5ea635a195ad5002d2aeaff4a4c18c3b0e98af5c23cb09c1fc7cec6967e4e866ea4b96c44031e0422fe1545fd
7
+ data.tar.gz: 6f454b0ad8bc92f31196fca9eb28369118cfe9ed81a7ccff3563a9dd903345ff145cdcb9549bcb6efc7d087718f143e1338704f4d6fdd63face059b74369f97f
data/CHANGELOG.md CHANGED
@@ -1,3 +1,21 @@
1
+ ## [1.9.320.0] - 2026-10-08
2
+
3
+ ### Fixed — engine sync (libleptris 1.9.320)
4
+
5
+ - **Exact-URI child rows survive build-anchor GC (#1585)**: the
6
+ engine's plan build let child-row `ns_uri` ride a pointer into
7
+ the caller's build buffers instead of deep-copying — bindings
8
+ GC their anchors after build, so exact-URI child rows
9
+ deterministically stopped matching (uniword's
10
+ dc:title/dc:creator/dc:description hydrated nil). The engine
11
+ retains the URI; pinned with build + GC + walk.
12
+ - **Plain attribute rows match any qualification (#1586)**:
13
+ ns-unset attr rows try the exact wire spelling first, then any
14
+ qualification by local name (#758 parity) — plain rows bind
15
+ `w:name`/`w:val` exactly as the interpretive path does, top
16
+ level and nested; exact-URI rows keep their #1486 precedence.
17
+ Pinned at both depths.
18
+
1
19
  ## [1.9.319.0] - 2026-10-08
2
20
 
3
21
  ### Added — engine sync (libleptris 1.9.319)
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.319"
11
+ LIBLEPTRIS_VERSION = "1.9.320"
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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Leptris
4
- VERSION = "1.9.319.0"
4
+ VERSION = "1.9.320.0"
5
5
  end
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.319
4
+ - libleptris v1.9.320
5
5
  - utf8proc v2.11.0
6
6
 
7
7
  Rebuild by hand:
@@ -1,5 +1,28 @@
1
1
  ## [Unreleased]
2
2
 
3
+ ## [1.9.320] - 2026-10-08
4
+
5
+ ### Fixed
6
+
7
+ - **Descriptor: child-row exact-URI `ns_uri` retained at build
8
+ (#1585, PR #1587).** The build deep-copied child `ns_prefix` but
9
+ let `ns_uri` ride the struct copy — a pointer into the caller's
10
+ build buffers. Bindings GC their anchors (a warm walk plus one GC
11
+ makes it deterministic), after which exact-URI child rows
12
+ silently stopped matching and hydration answered nil (uniword's
13
+ `dc:title`/`dc:creator`/`dc:description`). The spec trashes the
14
+ caller's buffer after build and asserts the row still binds.
15
+ - **Descriptor: plain attribute rows match any qualification
16
+ (#1586, PR #1587).** `dp_attr_value`'s ns-unset path now mirrors
17
+ #758 — the exact wire spelling first, then any qualification by
18
+ local name — so plain rows bind `w:name`/`w:val` exactly as the
19
+ interpretive path does, top level and nested. Exact-URI rows keep
20
+ their #1486 precedence. A NESTED row's binding also consults the
21
+ TARGET plan's `ns_lenient` now (the #1115-consistent authority)
22
+ instead of the outer plan's.
23
+
24
+
25
+
3
26
  ## [1.9.319] - 2026-10-08
4
27
 
5
28
  ### Added
@@ -3,7 +3,7 @@
3
3
  cmake_minimum_required(VERSION 3.20)
4
4
 
5
5
  project(leptris
6
- VERSION 1.9.319
6
+ VERSION 1.9.320
7
7
  DESCRIPTION "Fast XML parser and XPath evaluator in pure C"
8
8
  LANGUAGES C CXX
9
9
  )
@@ -285,6 +285,7 @@ static void dp_plan_free(LeptrisPlan p) {
285
285
  for (uint32_t c = 0; c < d->child_count; c++) {
286
286
  free((char *)d->child_plans[c].wire_name);
287
287
  free((char *)d->child_plans[c].ns_prefix);
288
+ free((char *)d->child_plans[c].ns_uri);
288
289
  for (uint16_t pi = 0;
289
290
  pi < d->child_plans[c].predicate_count; pi++) {
290
291
  free((char*)d->child_plans[c]
@@ -481,6 +482,15 @@ LEPTRIS_API LeptrisPlan leptris_plan_build(const leptris_plan_spec* spec,
481
482
  sc->ns_prefix ? dp_strdup(sc->ns_prefix) : NULL;
482
483
  if (sc->ns_prefix && !d->child_plans[c].ns_prefix)
483
484
  goto oom;
485
+ /* #1585: ns_uri rides the struct copy otherwise —
486
+ * a dangling pointer into the caller's build
487
+ * buffers once they are reclaimed (bindings GC
488
+ * their anchors; the descriptor must outlive
489
+ * them). Same retention as attr rows (#1486). */
490
+ d->child_plans[c].ns_uri =
491
+ sc->ns_uri ? dp_strdup(sc->ns_uri) : NULL;
492
+ if (sc->ns_uri && !d->child_plans[c].ns_uri)
493
+ goto oom;
484
494
  uint16_t pc = sc->predicate_count;
485
495
  if (pc) {
486
496
  if (!sc->predicates) goto oom;
@@ -598,8 +608,25 @@ static int dp_walk_children(LeptrisElement elem, const dp_plan* plan,
598
608
  * namespace (XML namespaces §5.2) — EXACT never binds them. */
599
609
  static const char* dp_attr_value(LeptrisElement elem,
600
610
  const leptris_attr_plan* ap) {
601
- if (!ap->ns_form)
602
- return leptris_element_attribute(elem, ap->wire_name);
611
+ if (!ap->ns_form) {
612
+ /* #1586, the plan-path mirror of #758: the exact wire
613
+ * spelling first, then any qualification by local name —
614
+ * plain rows are lenient exactly like element rows under
615
+ * ns_lenient. Exact-URI rows keep their #1486 semantics. */
616
+ const char* exact =
617
+ leptris_element_attribute(elem, ap->wire_name);
618
+ if (exact) return exact;
619
+ for (LeptrisAttribute at = leptris_element_first_attribute(elem);
620
+ at; at = leptris_attribute_next(at)) {
621
+ const char* qn = leptris_attribute_get_name(at);
622
+ if (!qn) continue;
623
+ const char* colon = strchr(qn, ':');
624
+ const char* local = colon ? colon + 1 : qn;
625
+ if (strcmp(local, ap->wire_name) == 0)
626
+ return leptris_attribute_get_value(elem, at);
627
+ }
628
+ return NULL;
629
+ }
603
630
  for (LeptrisAttribute at = leptris_element_first_attribute(elem);
604
631
  at; at = leptris_attribute_next(at)) {
605
632
  const char* qn = leptris_attribute_get_name(at);
@@ -832,8 +859,13 @@ static int dp_walk_children(LeptrisElement elem, const dp_plan* plan,
832
859
  LeptrisElement child = (LeptrisElement)n;
833
860
  if (!dp_wire_name_matches(child, row->wire_name))
834
861
  continue;
862
+ /* The TARGET plan's form governs (#1115) — so its
863
+ * lenient flag governs the leniency, not the outer
864
+ * plan's (#1586: a namespaced child under a plan
865
+ * that declares ns_lenient binds by local name). */
835
866
  if (!dp_ns_binds(child, target->ns_form, target->ns_uri,
836
- lenient))
867
+ (target->flags &
868
+ LEPTRIS_PLAN_FLAG_NS_LENIENT) != 0))
837
869
  continue;
838
870
  /* Claim the element against later same-wire rows
839
871
  * (predicate partitioning). */
@@ -1963,3 +1963,171 @@ TEST(Plan1563, PlainAttrRowAgreesNestedVsRoot) {
1963
1963
  leptris_plan_free(root_plan);
1964
1964
  leptris_plan_free(nested_plan);
1965
1965
  }
1966
+
1967
+ // ---- #1585: child-row exact-URI ns_uri retention -------------------
1968
+ // The build copied child ns_prefix but left ns_uri pointing at the
1969
+ // CALLER's buffer — once the binding's build anchors are GC'd the
1970
+ // row dangles and silently stops matching. Retention is observable
1971
+ // without a GC: trash the caller's buffer after build; the row
1972
+ // must still bind.
1973
+ TEST(Plan1585, ChildRowNsUriIsRetained) {
1974
+ LeptrisStatus st = LEPTRIS_OK;
1975
+ char ns_uri_buf[64];
1976
+ strcpy(ns_uri_buf, "http://purl.org/dc/elements/1.1/");
1977
+
1978
+ leptris_child_plan kids[2] = {};
1979
+ kids[0].wire_name = "c";
1980
+ kids[0].kind = LEPTRIS_PLAN_KIND_SCALAR;
1981
+ kids[0].type_tag = 1;
1982
+ kids[0].child_plan_index = -1;
1983
+ kids[0].ns_form = LEPTRIS_PLAN_NS_EXACT;
1984
+ kids[0].ns_uri = ns_uri_buf;
1985
+ kids[0].ns_prefix = "d";
1986
+ kids[1].wire_name = "p";
1987
+ kids[1].kind = LEPTRIS_PLAN_KIND_SCALAR;
1988
+ kids[1].type_tag = 2;
1989
+ kids[1].child_plan_index = -1;
1990
+ leptris_element_plan plans[1] = {};
1991
+ plans[0].element_name = "r";
1992
+ plans[0].ns_form = LEPTRIS_PLAN_NS_ANY;
1993
+ plans[0].child_count = 2;
1994
+ plans[0].child_plans = kids;
1995
+ leptris_plan_spec spec = {};
1996
+ spec.abi_version = leptris_plan_abi_version();
1997
+ spec.plan_count = 1;
1998
+ spec.plans = plans;
1999
+ LeptrisPlan plan = leptris_plan_build(&spec, &st);
2000
+ ASSERT_NE(plan, nullptr);
2001
+
2002
+ /* Simulate the GC reclaiming the build anchor: the spec buffer
2003
+ * is now garbage. */
2004
+ memset(ns_uri_buf, 'X', sizeof(ns_uri_buf) - 1);
2005
+
2006
+ LeptrisDocument doc = parse(
2007
+ "<r xmlns:d=\"http://purl.org/dc/elements/1.1/\">"
2008
+ "<d:c>x</d:c><p>y</p></r>");
2009
+ ASSERT_NE(doc, nullptr);
2010
+ LeptrisPlanResult r =
2011
+ leptris_plan_walk(doc, leptris_document_root(doc), plan, &st);
2012
+ ASSERT_NE(r, nullptr);
2013
+ ASSERT_EQ(leptris_plan_value_count(r), 2u);
2014
+ EXPECT_STREQ(leptris_plan_value_string(leptris_plan_value_at(r, 0)),
2015
+ "x")
2016
+ << "exact-URI child row lost its ns_uri";
2017
+ EXPECT_STREQ(leptris_plan_value_string(leptris_plan_value_at(r, 1)),
2018
+ "y");
2019
+ leptris_plan_result_free(r);
2020
+ leptris_plan_free(plan);
2021
+ leptris_document_free(doc);
2022
+ }
2023
+
2024
+ // ---- #1586: plain attribute rows match any qualification -----------
2025
+ // The plan-path mirror of #758 (exact wire spelling first, then any
2026
+ // qualification by local name) — a plain attr row must bind w:name
2027
+ // the same way the interpretive path does, at top level and nested.
2028
+ TEST(Plan1586, PlainAttrRowLenientSpelling) {
2029
+ LeptrisStatus st = LEPTRIS_OK;
2030
+ const char* xml = "<r xmlns:w=\"http://w/1\" w:top=\"T\"/>";
2031
+
2032
+ leptris_attr_plan attrs[1] = {};
2033
+ attrs[0].wire_name = "top";
2034
+ attrs[0].kind = LEPTRIS_PLAN_KIND_SCALAR;
2035
+ attrs[0].type_tag = 1;
2036
+ leptris_element_plan plans[1] = {};
2037
+ plans[0].element_name = "r";
2038
+ plans[0].attribute_count = 1;
2039
+ plans[0].attribute_plans = attrs;
2040
+ plans[0].flags = LEPTRIS_PLAN_FLAG_NS_LENIENT;
2041
+ leptris_plan_spec spec = {};
2042
+ spec.abi_version = leptris_plan_abi_version();
2043
+ spec.plan_count = 1;
2044
+ spec.plans = plans;
2045
+ LeptrisPlan plan = leptris_plan_build(&spec, &st);
2046
+ ASSERT_NE(plan, nullptr);
2047
+
2048
+ LeptrisDocument doc = parse(xml);
2049
+ ASSERT_NE(doc, nullptr);
2050
+ LeptrisPlanResult r =
2051
+ leptris_plan_walk(doc, leptris_document_root(doc), plan, &st);
2052
+ ASSERT_NE(r, nullptr);
2053
+ EXPECT_STREQ(leptris_plan_value_attribute(r, "top"), "T");
2054
+ leptris_plan_result_free(r);
2055
+ leptris_document_free(doc);
2056
+ leptris_plan_free(plan);
2057
+ }
2058
+
2059
+ TEST(Plan1586, PlainAttrRowLenientUnderNestedRow) {
2060
+ LeptrisStatus st = LEPTRIS_OK;
2061
+ const char* xml = "<r xmlns:w=\"http://w/1\"><w:c w:n=\"N\"/></r>";
2062
+
2063
+ leptris_attr_plan c_attrs[1] = {};
2064
+ c_attrs[0].wire_name = "n";
2065
+ c_attrs[0].kind = LEPTRIS_PLAN_KIND_SCALAR;
2066
+ c_attrs[0].type_tag = 1;
2067
+ leptris_child_plan c_row = {};
2068
+ c_row.wire_name = "c";
2069
+ c_row.kind = LEPTRIS_PLAN_KIND_NESTED;
2070
+ c_row.child_plan_index = 1;
2071
+ c_row.ns_form = LEPTRIS_PLAN_NS_ANY;
2072
+ leptris_element_plan plans[2] = {};
2073
+ plans[0].element_name = "r";
2074
+ plans[0].child_count = 1;
2075
+ plans[0].child_plans = &c_row;
2076
+ plans[1].element_name = "c";
2077
+ plans[1].attribute_count = 1;
2078
+ plans[1].attribute_plans = c_attrs;
2079
+ plans[1].flags = LEPTRIS_PLAN_FLAG_NS_LENIENT;
2080
+ leptris_plan_spec spec = {};
2081
+ spec.abi_version = leptris_plan_abi_version();
2082
+ spec.plan_count = 2;
2083
+ spec.plans = plans;
2084
+ LeptrisPlan plan = leptris_plan_build(&spec, &st);
2085
+ ASSERT_NE(plan, nullptr);
2086
+
2087
+ LeptrisDocument doc = parse(xml);
2088
+ ASSERT_NE(doc, nullptr);
2089
+ LeptrisPlanResult r =
2090
+ leptris_plan_walk(doc, leptris_document_root(doc), plan, &st);
2091
+ ASSERT_NE(r, nullptr);
2092
+ ASSERT_EQ(leptris_plan_value_count(r), 1u);
2093
+ LeptrisPlanResult child = leptris_plan_value_at(r, 0);
2094
+ ASSERT_NE(child, nullptr);
2095
+ EXPECT_STREQ(leptris_plan_value_attribute(child, "n"), "N");
2096
+ leptris_plan_result_free(r);
2097
+ leptris_document_free(doc);
2098
+ leptris_plan_free(plan);
2099
+ }
2100
+
2101
+ TEST(Plan1586, ExactUriRowStillWinsAndUnqualifiedStillBinds) {
2102
+ LeptrisStatus st = LEPTRIS_OK;
2103
+ /* exact-URI row keeps its #1486 semantics; unqualified wire
2104
+ * attr binds a plain row by the literal spelling. */
2105
+ const char* xml = "<r xmlns:w=\"http://w/1\" w:top=\"T\" top=\"U\"/>";
2106
+ leptris_attr_plan exact[1] = {};
2107
+ exact[0].wire_name = "top";
2108
+ exact[0].kind = LEPTRIS_PLAN_KIND_SCALAR;
2109
+ exact[0].type_tag = 1;
2110
+ exact[0].ns_form = LEPTRIS_PLAN_NS_EXACT;
2111
+ exact[0].ns_uri = "http://w/1";
2112
+ leptris_element_plan plans[1] = {};
2113
+ plans[0].element_name = "r";
2114
+ plans[0].attribute_count = 1;
2115
+ plans[0].attribute_plans = exact;
2116
+ leptris_plan_spec spec = {};
2117
+ spec.abi_version = leptris_plan_abi_version();
2118
+ spec.plan_count = 1;
2119
+ spec.plans = plans;
2120
+ LeptrisPlan plan = leptris_plan_build(&spec, &st);
2121
+ ASSERT_NE(plan, nullptr);
2122
+ LeptrisDocument doc = parse(xml);
2123
+ ASSERT_NE(doc, nullptr);
2124
+ LeptrisPlanResult r =
2125
+ leptris_plan_walk(doc, leptris_document_root(doc), plan, &st);
2126
+ ASSERT_NE(r, nullptr);
2127
+ /* The qualified spelling carries "T"; the exact row must take
2128
+ * it, not the unqualified "U". */
2129
+ EXPECT_STREQ(leptris_plan_value_attribute(r, "top"), "T");
2130
+ leptris_plan_result_free(r);
2131
+ leptris_document_free(doc);
2132
+ leptris_plan_free(plan);
2133
+ }
@@ -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.319",
4
+ "version": "1.9.320",
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.319.0
4
+ version: 1.9.320.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ribose Inc.