leptris 1.9.221.1 → 1.9.222.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 +4 -4
- data/CHANGELOG.md +13 -0
- data/Rakefile +1 -1
- data/ext/build_windows_native.rb +20 -5
- data/ext/leptris/native/native.c +72 -5
- data/lib/leptris/vendor/aarch64-linux/libleptris.so +0 -0
- data/lib/leptris/vendor/aarch64-linux-musl/libleptris.so +0 -0
- data/lib/leptris/vendor/arm-linux/libleptris.so +0 -0
- data/lib/leptris/vendor/arm-linux-musl/libleptris.so +0 -0
- data/lib/leptris/vendor/arm64-darwin/libleptris.dylib +0 -0
- data/lib/leptris/vendor/ppc64le-linux/libleptris.so +0 -0
- data/lib/leptris/vendor/s390x-linux-musl/libleptris.so +0 -0
- data/lib/leptris/vendor/x86_64-darwin/libleptris.dylib +0 -0
- data/lib/leptris/vendor/x86_64-linux/libleptris.so +0 -0
- data/lib/leptris/vendor/x86_64-linux-musl/libleptris.so +0 -0
- data/lib/leptris/version.rb +1 -1
- 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/text.h +11 -5
- data/vendor-src/libleptris/src/leptris/flat/direct_parse.c +33 -0
- data/vendor-src/libleptris/test/dom/test_text_borrowed.cpp +70 -11
- data/vendor-src/libleptris/test/parser/test_interleaved.cpp +59 -0
- data/vendor-src/libleptris/vcpkg.json +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 403ad2f7e9b542b03e8f770c1b070e0e972ac0b4aca5ae16e5ed8f675b7599e4
|
|
4
|
+
data.tar.gz: 5e5c49bb4ffdda23e1466b653346e6c20de604212a2941c97a9fa2c6b2297f83
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 751de9f301ea9157169b546b3d81c8c65942e6c13fd8516bbd4e7ec6e940877146e634bccb92ab1b043c63f0b77bca5c3ea7cb83d81d539228a38cba6eaba072
|
|
7
|
+
data.tar.gz: e38a36cfdcf7d4d52d007389dbc6ca5ab4e5f84f8794af81667837005a5928a776dc532b86b50dc8dc32644e8d0cbde979f9edfaa89695564e91e33dd5320d02
|
data/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,19 @@ 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.222.0] - 2026-09-22
|
|
9
|
+
|
|
10
|
+
### Fixed — engine sync (1.9.222): the interleaved lane's namespace regression
|
|
11
|
+
|
|
12
|
+
- Vendored libleptris 1.9.222: the interleaved parse lane
|
|
13
|
+
(`LEPTRIS_INTERLEAVED=1`) dropped the namespace URI of every
|
|
14
|
+
prefixed attribute — `xml:space` lost the XML namespace
|
|
15
|
+
(leptris#1295, Canon-reported). The lane now stamps the #542 ns
|
|
16
|
+
side-cache exactly like the classic parser; regression-tested
|
|
17
|
+
against both lanes (leptris#1300). Also: the borrowed-text
|
|
18
|
+
contract is lane-explicit. No binding-facing surface changes;
|
|
19
|
+
audit 342/342, suite 770/0.
|
|
20
|
+
|
|
8
21
|
## [1.9.221.0] - 2026-09-22
|
|
9
22
|
|
|
10
23
|
### Performance — engine sync (1.9.221)
|
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.222"
|
|
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/build_windows_native.rb
CHANGED
|
@@ -43,12 +43,27 @@ Dir.chdir(ext_dir) do
|
|
|
43
43
|
# string table while exporting only mkmf's Init_native (the arm64
|
|
44
44
|
# toolchain ignores our .def when its own wins the link) — only a
|
|
45
45
|
# real lookup proves native_<minor>.so will load.
|
|
46
|
-
|
|
46
|
+
#
|
|
47
|
+
# Ruby 4.0 moved fiddle out of the default gems (bundled_gems
|
|
48
|
+
# intercepts the require → LoadError when it's not in the
|
|
49
|
+
# Gemfile), so the gate is conditional there: skip loudly — the
|
|
50
|
+
# release workflow's strings gate still hard-verifies the
|
|
51
|
+
# export, and native.c pins the symbols with dllexport at the
|
|
52
|
+
# source level.
|
|
47
53
|
begin
|
|
48
|
-
|
|
49
|
-
rescue
|
|
50
|
-
|
|
51
|
-
|
|
54
|
+
require "fiddle"
|
|
55
|
+
rescue LoadError => e
|
|
56
|
+
warn "fiddle unavailable under #{RUBY_VERSION} (#{e.message}) — " \
|
|
57
|
+
"skipping the functional export gate; the release workflow's " \
|
|
58
|
+
"strings gate still applies"
|
|
59
|
+
end
|
|
60
|
+
if defined?(Fiddle)
|
|
61
|
+
begin
|
|
62
|
+
Fiddle.dlopen(File.expand_path(so))[INIT_PREFIX + minor_us]
|
|
63
|
+
rescue Fiddle::DLError => e
|
|
64
|
+
abort "#{so} does not export #{INIT_PREFIX + minor_us} — refusing to " \
|
|
65
|
+
"install a DLL that would 127 at require (#{e.message})"
|
|
66
|
+
end
|
|
52
67
|
end
|
|
53
68
|
dest = File.join(root, "lib", "leptris", "xml", "native_#{minor_us}.so")
|
|
54
69
|
# The artifact must reference only this minor's Ruby DLL.
|
data/ext/leptris/native/native.c
CHANGED
|
@@ -2027,19 +2027,73 @@ static void plan_walk(void *node, int depth, VALUE spec, VALUE stack,
|
|
|
2027
2027
|
rb_enc_interned_str(an, strlen(an),
|
|
2028
2028
|
rb_utf8_encoding()))
|
|
2029
2029
|
: Qnil;
|
|
2030
|
-
|
|
2031
|
-
|
|
2032
|
-
|
|
2030
|
+
const char *av = f_attr_value(node, a);
|
|
2031
|
+
if (NIL_P(slot)) continue;
|
|
2032
|
+
if (RB_TYPE_P(slot, T_ARRAY) && RARRAY_LEN(slot) == 2) {
|
|
2033
|
+
/* Typed entry [slot, tag]: 1 = integer, 2 = float
|
|
2034
|
+
* (strtod — the engine's float getter is 32-bit
|
|
2035
|
+
* and loses 19.99), 3 = boolean. Parsed C-side
|
|
2036
|
+
* from the fetched bytes: no Ruby String is
|
|
2037
|
+
* materialized for typed slots; unparseable
|
|
2038
|
+
* input degrades to the raw String (#1269a
|
|
2039
|
+
* lenient). */
|
|
2040
|
+
VALUE typed_slot = RARRAY_AREF(slot, 0);
|
|
2041
|
+
int tag = FIX2INT(RARRAY_AREF(slot, 1));
|
|
2042
|
+
char *end = NULL;
|
|
2043
|
+
if (av && tag == 1) {
|
|
2044
|
+
long lv = strtol(av, &end, 10);
|
|
2045
|
+
if (end != av) {
|
|
2046
|
+
rb_struct_aset(value, typed_slot, LONG2NUM(lv));
|
|
2047
|
+
continue;
|
|
2048
|
+
}
|
|
2049
|
+
} else if (av && tag == 2) {
|
|
2050
|
+
double dv = strtod(av, &end);
|
|
2051
|
+
if (end != av) {
|
|
2052
|
+
rb_struct_aset(value, typed_slot, DBL2NUM(dv));
|
|
2053
|
+
continue;
|
|
2054
|
+
}
|
|
2055
|
+
} else if (av && tag == 3) {
|
|
2056
|
+
if (av[0] == 't' || av[0] == '1' ||
|
|
2057
|
+
av[0] == 'y' || av[0] == 'Y') {
|
|
2058
|
+
rb_struct_aset(value, typed_slot, Qtrue);
|
|
2059
|
+
} else {
|
|
2060
|
+
rb_struct_aset(value, typed_slot, Qfalse);
|
|
2061
|
+
}
|
|
2062
|
+
continue;
|
|
2063
|
+
}
|
|
2064
|
+
rb_struct_aset(value, typed_slot,
|
|
2033
2065
|
av ? rb_utf8_str_new_cstr(av) : Qnil);
|
|
2066
|
+
continue;
|
|
2034
2067
|
}
|
|
2068
|
+
rb_struct_aset(value, slot,
|
|
2069
|
+
av ? rb_utf8_str_new_cstr(av) : Qnil);
|
|
2035
2070
|
}
|
|
2036
2071
|
if (!NIL_P(text_slot)) {
|
|
2072
|
+
VALUE t_slot = text_slot;
|
|
2073
|
+
int t_tag = 0;
|
|
2074
|
+
if (RB_TYPE_P(t_slot, T_ARRAY) && RARRAY_LEN(t_slot) == 2) {
|
|
2075
|
+
t_tag = FIX2INT(RARRAY_AREF(t_slot, 1));
|
|
2076
|
+
t_slot = RARRAY_AREF(t_slot, 0);
|
|
2077
|
+
}
|
|
2037
2078
|
void *c;
|
|
2038
2079
|
for (c = f_first_child(node); c; c = f_next_sibling(c)) {
|
|
2039
2080
|
if (f_node_type(c) == WS_NODE_TEXT) {
|
|
2040
2081
|
const char *t = f_text_content(c);
|
|
2041
|
-
|
|
2042
|
-
|
|
2082
|
+
if (!t) {
|
|
2083
|
+
rb_struct_aset(value, t_slot, Qnil);
|
|
2084
|
+
} else if (t_tag == 1) {
|
|
2085
|
+
rb_struct_aset(value, t_slot,
|
|
2086
|
+
LONG2NUM(strtol(t, NULL, 10)));
|
|
2087
|
+
} else if (t_tag == 2) {
|
|
2088
|
+
rb_struct_aset(value, t_slot,
|
|
2089
|
+
DBL2NUM(strtod(t, NULL)));
|
|
2090
|
+
} else if (t_tag == 3) {
|
|
2091
|
+
rb_struct_aset(value, t_slot,
|
|
2092
|
+
(t[0] == 't' || t[0] == '1') ? Qtrue : Qfalse);
|
|
2093
|
+
} else {
|
|
2094
|
+
rb_struct_aset(value, t_slot,
|
|
2095
|
+
rb_utf8_str_new_cstr(t));
|
|
2096
|
+
}
|
|
2043
2097
|
break;
|
|
2044
2098
|
}
|
|
2045
2099
|
}
|
|
@@ -2085,6 +2139,17 @@ static void plan_walk(void *node, int depth, VALUE spec, VALUE stack,
|
|
|
2085
2139
|
}
|
|
2086
2140
|
}
|
|
2087
2141
|
|
|
2142
|
+
/* Capability marker (moxml): the executor understands typed spec
|
|
2143
|
+
* entries ([slot, tag] attr/text slots; 1 = integer, 2 = float via
|
|
2144
|
+
* strtod, 3 = boolean, lenient raw-String fallback). Consumers
|
|
2145
|
+
* probe this instead of version-gating — lockstep releases can
|
|
2146
|
+
* publish without the face. */
|
|
2147
|
+
static VALUE nf_plan_structs_typed_p(VALUE self)
|
|
2148
|
+
{
|
|
2149
|
+
(void)self;
|
|
2150
|
+
return Qtrue;
|
|
2151
|
+
}
|
|
2152
|
+
|
|
2088
2153
|
static VALUE nf_plan_structs(VALUE self, VALUE document, VALUE addr,
|
|
2089
2154
|
VALUE spec)
|
|
2090
2155
|
{
|
|
@@ -2667,6 +2732,8 @@ LEPTRIS_INIT_EXPORT void Init_native(void)
|
|
|
2667
2732
|
nf_attribute_pairs, 2);
|
|
2668
2733
|
rb_define_module_function(m_native, "plan_structs",
|
|
2669
2734
|
nf_plan_structs, 3);
|
|
2735
|
+
rb_define_module_function(m_native, "plan_structs_typed?",
|
|
2736
|
+
nf_plan_structs_typed_p, 0);
|
|
2670
2737
|
rb_define_module_function(m_native, "doc_handle_attach",
|
|
2671
2738
|
nf_doc_handle_attach, 1);
|
|
2672
2739
|
rb_define_module_function(m_native, "doc_handle_release",
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
data/lib/leptris/version.rb
CHANGED
data/vendor-src/README.md
CHANGED
|
@@ -1,5 +1,23 @@
|
|
|
1
1
|
## [Unreleased]
|
|
2
2
|
|
|
3
|
+
## [1.9.222] - 2026-09-22
|
|
4
|
+
|
|
5
|
+
### Fixed
|
|
6
|
+
|
|
7
|
+
- **parse:** the interleaved lane (`LEPTRIS_INTERLEAVED=1`) dropped the
|
|
8
|
+
namespace URI of every prefixed attribute — `xml:space` lost the XML
|
|
9
|
+
namespace, breaking namespace-based whitespace handling (reported by
|
|
10
|
+
Canon, #1295). The lane now stamps the #542 ns side-cache exactly like
|
|
11
|
+
the classic parser. Regression-tested against both lanes (#1300).
|
|
12
|
+
- **dom:** the borrowed-text contract is now lane-explicit: runs
|
|
13
|
+
terminated in place (HTML builder, interleaved lane) are served
|
|
14
|
+
directly by `leptris_text_get_content`; `borrowed` means non-owning,
|
|
15
|
+
not unterminated (#1299). A new CI leg runs the whole suite with the
|
|
16
|
+
lane forced, so env-gated parse paths can no longer ship divergences
|
|
17
|
+
unnoticed (#1301).
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
|
|
3
21
|
## [1.9.221] - 2026-09-22
|
|
4
22
|
|
|
5
23
|
### Performance
|
|
@@ -20,10 +20,16 @@
|
|
|
20
20
|
*
|
|
21
21
|
* TODO 115 Phase B: a "borrowed" text node stores a pointer into a
|
|
22
22
|
* caller-owned buffer (typically the parser's writable input buffer)
|
|
23
|
-
* instead of pool-resident storage.
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
23
|
+
* instead of pool-resident storage. `content_len` is the only
|
|
24
|
+
* authoritative size — the run MAY be terminated in place (the HTML
|
|
25
|
+
* builder and the interleaved XML lane write the terminator into the
|
|
26
|
+
* doc-owned copy) or not (classic XML lane defers the NUL). The
|
|
27
|
+
* `pool` field holds the pool for lazy materialization:
|
|
28
|
+
* leptris_text_get_content serves terminated, entity-free runs in
|
|
29
|
+
* place (no allocation, the node stays borrowed) and materializes a
|
|
30
|
+
* pool-owned NUL-terminated copy otherwise, flipping the node out of
|
|
31
|
+
* borrowed mode. (#1299: serve mode is lane-dependent by design;
|
|
32
|
+
* both uphold the terminated-view contract.)
|
|
27
33
|
*
|
|
28
34
|
* Issue #168: parent_off mirrors next_sibling_off so the parent of a
|
|
29
35
|
* text node can be queried in O(1). */
|
|
@@ -32,7 +38,7 @@ typedef struct leptris_text_node {
|
|
|
32
38
|
char* content; /* Text content - NEVER trim! */
|
|
33
39
|
size_t content_len; /* Byte length of content (excl. NUL) */
|
|
34
40
|
LeptrisMemoryPool* pool; /* Pool for lazy materialization (NULL if content is NUL-term'd) */
|
|
35
|
-
int borrowed; /* 1 =
|
|
41
|
+
int borrowed; /* 1 = non-owning view; 0 = pool-resident (materialized). Not "unterminated": in-place-terminated runs stay borrowed (#1299). */
|
|
36
42
|
/* (#450) int32 sibling edge — was cp16 (±256 KB). Text nodes
|
|
37
43
|
* link to ELEMENT siblings across the parse-time element↔text
|
|
38
44
|
* block gap, which scales with document size and regularly
|
|
@@ -2441,6 +2441,39 @@ static struct leptris_document* dp_il_build(
|
|
|
2441
2441
|
at->name_hash = 0; /* lazy-hash sentinel */
|
|
2442
2442
|
at->ns_cache_off = 0;
|
|
2443
2443
|
at->next_cp = 0;
|
|
2444
|
+
/* #542 ns side-cache stamp, parity with the
|
|
2445
|
+
* classic lane's fused pass: prefixed attrs
|
|
2446
|
+
* carry prefix + owner so the namespace
|
|
2447
|
+
* accessors resolve. Without it xml:space and
|
|
2448
|
+
* every declared prefix read as no-namespace
|
|
2449
|
+
* (#1295). The lane bails on xmlns, so any
|
|
2450
|
+
* colon here is a genuine namespaced attr. */
|
|
2451
|
+
{
|
|
2452
|
+
const char* anm = scratch + a->name_off;
|
|
2453
|
+
const char* acolon =
|
|
2454
|
+
a->name_len >= 3
|
|
2455
|
+
? (const char*)memchr(anm, ':',
|
|
2456
|
+
a->name_len)
|
|
2457
|
+
: NULL;
|
|
2458
|
+
if (acolon) {
|
|
2459
|
+
struct leptris_attr_ns_cache* nc =
|
|
2460
|
+
(struct leptris_attr_ns_cache*)
|
|
2461
|
+
leptris_pool_alloc(pool, sizeof(*nc));
|
|
2462
|
+
if (nc) {
|
|
2463
|
+
memset(nc, 0, sizeof(*nc));
|
|
2464
|
+
size_t pl = (size_t)(acolon - anm);
|
|
2465
|
+
char* pfx = (char*)leptris_pool_alloc(
|
|
2466
|
+
pool, pl + 1);
|
|
2467
|
+
if (pfx) {
|
|
2468
|
+
memcpy(pfx, anm, pl);
|
|
2469
|
+
pfx[pl] = '\0';
|
|
2470
|
+
nc->prefix = pfx;
|
|
2471
|
+
}
|
|
2472
|
+
nc->owner_elem = e;
|
|
2473
|
+
attr_set_ns_cache(at, nc);
|
|
2474
|
+
}
|
|
2475
|
+
}
|
|
2476
|
+
}
|
|
2444
2477
|
/* dup detection — first wins (#1200) */
|
|
2445
2478
|
int dup = 0;
|
|
2446
2479
|
for (struct leptris_attribute* d =
|
|
@@ -2,13 +2,22 @@
|
|
|
2
2
|
//
|
|
3
3
|
// Verifies that the parser hands text nodes a non-owning view into the
|
|
4
4
|
// document's writable input buffer (no per-node pool allocation for
|
|
5
|
-
// content), that consumers see correct content despite
|
|
6
|
-
// NUL terminator, and that
|
|
7
|
-
// NUL-terminated view.
|
|
5
|
+
// content), that consumers see correct content despite a possibly
|
|
6
|
+
// missing NUL terminator, and that leptris_text_get_content produces a
|
|
7
|
+
// stable NUL-terminated view.
|
|
8
|
+
//
|
|
9
|
+
// #1299: the get_content serve mode is lane-dependent. When the parser
|
|
10
|
+
// terminated the run in place (HTML builder, interleaved XML lane),
|
|
11
|
+
// get_content serves the doc-owned pointer directly — no allocation,
|
|
12
|
+
// the borrowed flag stays 1. Otherwise (classic XML lane, deferred
|
|
13
|
+
// NUL) it materializes a pool-owned copy and flips the flag to 0.
|
|
14
|
+
// Both modes must uphold the documented contract: correct bytes,
|
|
15
|
+
// NUL-terminated view, pointer-stable across calls.
|
|
8
16
|
|
|
9
17
|
#include <gtest/gtest.h>
|
|
10
18
|
#include <cstring>
|
|
11
19
|
#include <string>
|
|
20
|
+
#include <cstdlib>
|
|
12
21
|
#include "leptris.h"
|
|
13
22
|
|
|
14
23
|
extern "C" {
|
|
@@ -40,13 +49,15 @@ TEST(TextBorrowed, ParsedTextNodeIsBorrowedFromInputBuffer) {
|
|
|
40
49
|
* itself is internal to the document. */
|
|
41
50
|
EXPECT_NE(text->content, nullptr);
|
|
42
51
|
|
|
43
|
-
/*
|
|
44
|
-
*
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
52
|
+
/* Serve mode is lane-dependent (#1299): a run terminated in place
|
|
53
|
+
* (HTML builder, interleaved lane) is served directly — the
|
|
54
|
+
* returned pointer IS the borrowed pointer and the flag stays 1;
|
|
55
|
+
* the classic lane materializes a fresh pool copy and flips to 0.
|
|
56
|
+
* Either way the view must be correct and pointer-stable. */
|
|
57
|
+
const char* first = leptris_text_get_content(text);
|
|
58
|
+
EXPECT_STREQ(first, "hello world");
|
|
59
|
+
EXPECT_EQ(leptris_text_get_content(text), first)
|
|
60
|
+
<< "served view must be pointer-stable across calls";
|
|
50
61
|
|
|
51
62
|
leptris_document_free(doc);
|
|
52
63
|
}
|
|
@@ -117,10 +128,58 @@ TEST(TextBorrowed, MaterializationIsStableAcrossCalls) {
|
|
|
117
128
|
|
|
118
129
|
LeptrisNodeRef child = leptris_node_first_child(leptris_element_as_node(root));
|
|
119
130
|
LeptrisTextNode* text = (LeptrisTextNode*)child;
|
|
120
|
-
|
|
131
|
+
/* The flip is a materialize-path side effect, not the contract
|
|
132
|
+
* (#1299): in-place serve keeps borrowed at 1 — the mode is
|
|
133
|
+
* owned by ServeContractHoldsInBothParseLanes below. */
|
|
121
134
|
|
|
122
135
|
const char* second = leptris_element_text(root);
|
|
123
136
|
EXPECT_EQ(second, first) << "repeated calls return the same pointer";
|
|
124
137
|
|
|
125
138
|
leptris_document_free(doc);
|
|
126
139
|
}
|
|
140
|
+
|
|
141
|
+
/* #1299: both serve modes uphold the get_content contract for parsed
|
|
142
|
+
* text. The interleaved lane is exercised with the env gate forced
|
|
143
|
+
* in-process (same toggle discipline as test/parser/test_interleaved.cpp;
|
|
144
|
+
* empty-string values count as off for MSVC). */
|
|
145
|
+
#ifdef _WIN32
|
|
146
|
+
static void tb_set_env(const char* k, const char* v) { _putenv_s(k, v); }
|
|
147
|
+
static void tb_unset_env(const char* k) { _putenv_s(k, ""); }
|
|
148
|
+
#else
|
|
149
|
+
static void tb_set_env(const char* k, const char* v) { setenv(k, v, 1); }
|
|
150
|
+
static void tb_unset_env(const char* k) { unsetenv(k); }
|
|
151
|
+
#endif
|
|
152
|
+
|
|
153
|
+
TEST(TextBorrowed, ServeContractHoldsInBothParseLanes) {
|
|
154
|
+
const char* xml = "<r>hello world</r>";
|
|
155
|
+
for (int lane = 0; lane <= 1; lane++) {
|
|
156
|
+
SCOPED_TRACE(lane ? "interleaved" : "classic");
|
|
157
|
+
if (lane) tb_set_env("LEPTRIS_INTERLEAVED", "1");
|
|
158
|
+
else tb_unset_env("LEPTRIS_INTERLEAVED");
|
|
159
|
+
LeptrisStatus st = LEPTRIS_OK;
|
|
160
|
+
LeptrisDocument doc = leptris_parse_string(xml, std::strlen(xml), &st);
|
|
161
|
+
ASSERT_NE(doc, nullptr);
|
|
162
|
+
LeptrisElement root = leptris_document_root(doc);
|
|
163
|
+
ASSERT_NE(root, nullptr);
|
|
164
|
+
LeptrisNodeRef child =
|
|
165
|
+
leptris_node_first_child(leptris_element_as_node(root));
|
|
166
|
+
ASSERT_NE(child, nullptr);
|
|
167
|
+
ASSERT_EQ(child->type, LEPTRIS_NODE_TYPE_TEXT);
|
|
168
|
+
|
|
169
|
+
LeptrisTextNode* text = (LeptrisTextNode*)child;
|
|
170
|
+
/* non-owning in both lanes */
|
|
171
|
+
EXPECT_EQ(text->borrowed, 1);
|
|
172
|
+
EXPECT_EQ(text->content_len, std::strlen("hello world"));
|
|
173
|
+
|
|
174
|
+
const char* a = leptris_text_get_content(text);
|
|
175
|
+
ASSERT_NE(a, nullptr);
|
|
176
|
+
EXPECT_STREQ(a, "hello world");
|
|
177
|
+
EXPECT_EQ(leptris_text_get_content(text), a)
|
|
178
|
+
<< "served view must be pointer-stable across calls";
|
|
179
|
+
/* still correct after the first access */
|
|
180
|
+
EXPECT_STREQ(leptris_element_text(root), "hello world");
|
|
181
|
+
|
|
182
|
+
leptris_document_free(doc);
|
|
183
|
+
}
|
|
184
|
+
tb_unset_env("LEPTRIS_INTERLEAVED");
|
|
185
|
+
}
|
|
@@ -204,6 +204,65 @@ TEST(InterleavedParity, EngagementCanaryFiresOnlyWhenLaneRuns) {
|
|
|
204
204
|
}
|
|
205
205
|
#endif /* !_WIN32: canary capture uses POSIX pipe/dup2 */
|
|
206
206
|
|
|
207
|
+
/* #1295: namespace accessors on prefixed attributes. The il lane
|
|
208
|
+
* built attributes without the #542 ns side-cache stamp, so
|
|
209
|
+
* leptris_attribute_namespace_uri (and prefix) returned NULL for
|
|
210
|
+
* every prefixed attribute — xml: included, which broke Canon's
|
|
211
|
+
* namespace-based xml:space handling. This fixture keeps the il
|
|
212
|
+
* lane engaged: the lane defers xmlns-bearing documents to classic
|
|
213
|
+
* (il_scan bails on declarations), and xml: needs no declaration. */
|
|
214
|
+
TEST(InterleavedParity, XmlAttrNamespaceParity) {
|
|
215
|
+
const char* kXmlNs = "http://www.w3.org/XML/1998/namespace";
|
|
216
|
+
const char* xml = "<root><code xml:space='preserve'>t</code></root>";
|
|
217
|
+
for (int lane = 0; lane <= 1; lane++) {
|
|
218
|
+
SCOPED_TRACE(lane ? "interleaved" : "classic");
|
|
219
|
+
if (lane) set_env("LEPTRIS_INTERLEAVED", "1");
|
|
220
|
+
else unset_env("LEPTRIS_INTERLEAVED");
|
|
221
|
+
LeptrisStatus st = LEPTRIS_OK;
|
|
222
|
+
LeptrisDocument d = leptris_parse_string(xml, strlen(xml), &st);
|
|
223
|
+
ASSERT_NE(d, nullptr);
|
|
224
|
+
LeptrisElement root = leptris_document_root(d);
|
|
225
|
+
ASSERT_NE(root, nullptr);
|
|
226
|
+
LeptrisElement code = leptris_element_first_child(root, NULL);
|
|
227
|
+
ASSERT_NE(code, nullptr);
|
|
228
|
+
LeptrisAttribute a = leptris_element_first_attribute(code);
|
|
229
|
+
ASSERT_NE(a, nullptr);
|
|
230
|
+
EXPECT_STREQ(leptris_attribute_prefix(a), "xml");
|
|
231
|
+
EXPECT_STREQ(leptris_attribute_namespace_uri(a), kXmlNs);
|
|
232
|
+
/* expanded-name lookup rides the element-side resolver and
|
|
233
|
+
* must agree with the attr-side URI */
|
|
234
|
+
EXPECT_STREQ(leptris_element_attribute_ns(code, kXmlNs, "space"),
|
|
235
|
+
"preserve");
|
|
236
|
+
EXPECT_STREQ(leptris_element_attribute(code, "xml:space"),
|
|
237
|
+
"preserve");
|
|
238
|
+
leptris_document_free(d);
|
|
239
|
+
}
|
|
240
|
+
unset_env("LEPTRIS_INTERLEAVED");
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
/* Declared-prefix parity. The il lane bails to classic on xmlns
|
|
244
|
+
* today, so this runs classic twice in practice — but if the
|
|
245
|
+
* declaration bail ever lifts, the ns stamp must come with it. */
|
|
246
|
+
TEST(InterleavedParity, DeclaredPrefixNamespaceParity) {
|
|
247
|
+
const char* xml = "<d xmlns:q='http://example/q' q:k='v'/>";
|
|
248
|
+
for (int lane = 0; lane <= 1; lane++) {
|
|
249
|
+
SCOPED_TRACE(lane ? "interleaved" : "classic");
|
|
250
|
+
if (lane) set_env("LEPTRIS_INTERLEAVED", "1");
|
|
251
|
+
else unset_env("LEPTRIS_INTERLEAVED");
|
|
252
|
+
LeptrisStatus st = LEPTRIS_OK;
|
|
253
|
+
LeptrisDocument d = leptris_parse_string(xml, strlen(xml), &st);
|
|
254
|
+
ASSERT_NE(d, nullptr);
|
|
255
|
+
LeptrisElement root = leptris_document_root(d);
|
|
256
|
+
ASSERT_NE(root, nullptr);
|
|
257
|
+
LeptrisAttribute a = leptris_element_first_attribute(root);
|
|
258
|
+
ASSERT_NE(a, nullptr);
|
|
259
|
+
EXPECT_STREQ(leptris_attribute_prefix(a), "q");
|
|
260
|
+
EXPECT_STREQ(leptris_attribute_namespace_uri(a), "http://example/q");
|
|
261
|
+
leptris_document_free(d);
|
|
262
|
+
}
|
|
263
|
+
unset_env("LEPTRIS_INTERLEAVED");
|
|
264
|
+
}
|
|
265
|
+
|
|
207
266
|
TEST(InterleavedParity, DupAttrDiagnosticParity) {
|
|
208
267
|
const char* xml = "<r a='1' a='2'/>";
|
|
209
268
|
for (int lane = 0; lane <= 1; lane++) {
|
|
@@ -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.222",
|
|
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",
|