leptris 1.9.241.0 → 1.9.242.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/Rakefile +1 -1
- 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 +45 -0
- data/vendor-src/libleptris/CMakeLists.txt +1 -1
- data/vendor-src/libleptris/src/leptris/flat/direct_parse.c +17 -0
- data/vendor-src/libleptris/src/leptris/leptris_internal.h +6 -0
- data/vendor-src/libleptris/src/leptris/xpath/evaluator.c +6 -0
- data/vendor-src/libleptris/src/leptris/xpath/evaluator_operators.c +121 -7
- data/vendor-src/libleptris/src/leptris/xpath/evaluator_types.c +36 -7
- data/vendor-src/libleptris/src/leptris/xpath/functions.c +106 -6
- data/vendor-src/libleptris/src/leptris/xpath/functions_ext31.c +171 -22
- data/vendor-src/libleptris/src/leptris/xpath/xpath_public.c +19 -20
- data/vendor-src/libleptris/src/leptris/xquery/xquery.c +176 -3
- data/vendor-src/libleptris/test/xquery/test_qt3.cpp +56 -75
- data/vendor-src/libleptris/test/xquery/test_xquery.cpp +147 -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: 4de40885119a37c57132fe2a16bb048198afa93b8e4077f8c3b4d428e176c753
|
|
4
|
+
data.tar.gz: 55353281eb7cd09b749905b35172a40d1be65821758ddffcac687fe77df44e52
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 62f07379ee5b97c1695845e7afd61eb0b1135171eb558feb7f8def8789d86488348c9f14616a67124cfbed8f4608c7f1d0af90793e37d54da755669687777f2e
|
|
7
|
+
data.tar.gz: e086c44b5d76e6e557c19c37cde928c7337e633f20dde340bf46e9ee3c95dd5c2285ea8c51ee5ca9a04a81b04e2f7b2cc605779268bb1c586240c4e58d6524cb
|
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.246"
|
|
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
|
|
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,50 @@
|
|
|
1
1
|
## [Unreleased]
|
|
2
2
|
|
|
3
|
+
## [1.9.246] - 2026-09-25
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
|
|
7
|
+
- exact decimal lexical carrier (xquery)
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
## [1.9.245] - 2026-09-25
|
|
12
|
+
|
|
13
|
+
### Added
|
|
14
|
+
|
|
15
|
+
- array:remove + NaN subsequence 2-arg adoption (xquery)
|
|
16
|
+
- attribute ctor carriers + derived-string deep-equal class (xquery)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
## [1.9.244] - 2026-09-25
|
|
21
|
+
|
|
22
|
+
<!-- Edit this section with the actual release notes. -->
|
|
23
|
+
<!-- See https://keepachangelog.com for format guidance. -->
|
|
24
|
+
|
|
25
|
+
### Changed
|
|
26
|
+
|
|
27
|
+
- (describe changes here)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
## [1.9.243] - 2026-09-25
|
|
31
|
+
|
|
32
|
+
<!-- Edit this section with the actual release notes. -->
|
|
33
|
+
<!-- See https://keepachangelog.com for format guidance. -->
|
|
34
|
+
|
|
35
|
+
### Changed
|
|
36
|
+
|
|
37
|
+
- (describe changes here)
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
## [1.9.242] - 2026-09-25
|
|
41
|
+
|
|
42
|
+
### Added
|
|
43
|
+
|
|
44
|
+
- QT3 maps/arrays slice — empty-aggregate arity guard + canonical map order (xquery)
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
|
|
3
48
|
## [1.9.241] - 2026-09-25
|
|
4
49
|
|
|
5
50
|
### Fixed
|
|
@@ -2290,6 +2290,23 @@ int leptris_il_scan(char* s, size_t len, int drop_ws, IlCtx* c,
|
|
|
2290
2290
|
j = (size_t)(vend - s) + 1;
|
|
2291
2291
|
IlAttr a = { (uint32_t)a0, (uint32_t)al,
|
|
2292
2292
|
(uint32_t)v0, (uint32_t)vl, aws };
|
|
2293
|
+
/* XML 1.0 §3.1: duplicate attributes are a fatal error —
|
|
2294
|
+
* the classic lane reports "Attribute %s redefined" and
|
|
2295
|
+
* the SAX callback path records an ERROR event, but the
|
|
2296
|
+
* record table has no error channel. A dup-containing
|
|
2297
|
+
* document is therefore outside the scannable subset:
|
|
2298
|
+
* bail so callers fall back to the callback path (#281,
|
|
2299
|
+
* leptris-ruby#331 adoption). Same-element prior attrs
|
|
2300
|
+
* are the contiguous range [r.first_attr, nattr). */
|
|
2301
|
+
if (acount > 0) {
|
|
2302
|
+
for (uint32_t k = r.first_attr; k < (uint32_t)c->nattr;
|
|
2303
|
+
k++) {
|
|
2304
|
+
const IlAttr* p = &c->attrs[k];
|
|
2305
|
+
if (p->name_len == (uint32_t)al &&
|
|
2306
|
+
memcmp(s + p->name_off, s + a0, al) == 0)
|
|
2307
|
+
return 0;
|
|
2308
|
+
}
|
|
2309
|
+
}
|
|
2293
2310
|
if (!il_push_attr(c, &a)) return 0;
|
|
2294
2311
|
if (acount == 0) r.first_attr = (uint32_t)(c->nattr - 1);
|
|
2295
2312
|
acount++;
|
|
@@ -659,6 +659,12 @@ struct leptris_xpath_result {
|
|
|
659
659
|
* round-trip E form instead of the libxml2-parity XPath
|
|
660
660
|
* spelling. Cleared in result_new (free-list recycle). */
|
|
661
661
|
int xq_spelling;
|
|
662
|
+
/* Canonical decimal lexical: set by the xs:decimal ctor — the
|
|
663
|
+
* double carrier cannot hold 30 significant digits, so
|
|
664
|
+
* eq-based functions (distinct-values, index-of, deep-equal)
|
|
665
|
+
* key on the canonical form instead (decimal-lex slice). OWNED
|
|
666
|
+
* — freed in result_free, cleared in result_new (recycle). */
|
|
667
|
+
char* decimal_lex;
|
|
662
668
|
};
|
|
663
669
|
|
|
664
670
|
/* Namespace mapping for XPath context (v0.8.0) */
|
|
@@ -744,6 +744,7 @@ struct leptris_xpath_result* xpath_result_new(XPathResultType type) {
|
|
|
744
744
|
result->is_int = 0; /* free-list recycling would leak it */
|
|
745
745
|
result->atomic_type = NULL;
|
|
746
746
|
result->xq_spelling = 0;
|
|
747
|
+
result->decimal_lex = NULL; /* free-list recycling would leak it */
|
|
747
748
|
|
|
748
749
|
/* Initialize union based on type. XPATH_RESULT_CACHED is the
|
|
749
750
|
* free-list sentinel — a fresh result never carries it. */
|
|
@@ -773,6 +774,11 @@ void xpath_result_free(struct leptris_xpath_result* result) {
|
|
|
773
774
|
* no-op, never a second payload release. */
|
|
774
775
|
if (result->type == XPATH_RESULT_CACHED) return;
|
|
775
776
|
|
|
777
|
+
if (result->decimal_lex) {
|
|
778
|
+
LEPTRIS_FREE(result->decimal_lex);
|
|
779
|
+
result->decimal_lex = NULL;
|
|
780
|
+
}
|
|
781
|
+
|
|
776
782
|
/* Release the payload through locals: after this block the
|
|
777
783
|
* union slot is dead, and parking the free-list next-pointer
|
|
778
784
|
* into it cannot alias the memory just released. */
|
|
@@ -418,7 +418,9 @@ struct leptris_xpath_result* evaluate_operator(XPathContext* ctx,
|
|
|
418
418
|
op0 != XPATH_OP_ELEMENT_CTOR &&
|
|
419
419
|
op0 != XPATH_OP_ATTRIBUTE_CTOR &&
|
|
420
420
|
op0 != XPATH_OP_TEXT_CTOR &&
|
|
421
|
-
op0 != XPATH_OP_DOCUMENT_CTOR
|
|
421
|
+
op0 != XPATH_OP_DOCUMENT_CTOR &&
|
|
422
|
+
op0 != XPATH_OP_ARRAY_CONSTRUCTOR &&
|
|
423
|
+
op0 != XPATH_OP_MAP_CONSTRUCTOR)
|
|
422
424
|
return NULL;
|
|
423
425
|
|
|
424
426
|
XPathOperatorType op = (XPathOperatorType)ast->number_value;
|
|
@@ -1597,6 +1599,38 @@ struct leptris_xpath_result* evaluate_operator(XPathContext* ctx,
|
|
|
1597
1599
|
if (v) xpath_result_free(v);
|
|
1598
1600
|
}
|
|
1599
1601
|
if (!s) s = leptris_strdup("");
|
|
1602
|
+
if (op == XPATH_OP_ATTRIBUTE_CTOR) {
|
|
1603
|
+
/* Top-level attribute ctor keeps its name: one
|
|
1604
|
+
* synthetic member carrying "\x03A" name "\x01"
|
|
1605
|
+
* value — deep-equal compares name+value
|
|
1606
|
+
* (K2-SeqDeepEqualFunc-25/31/32), string consumers
|
|
1607
|
+
* strip to the value. \x01 cannot occur in XML
|
|
1608
|
+
* content. */
|
|
1609
|
+
const char* an = ast->value ? ast->value : "";
|
|
1610
|
+
size_t alen = strlen(an), vlen = strlen(s);
|
|
1611
|
+
size_t clen = alen + vlen + 3;
|
|
1612
|
+
char* carrier = (char*)malloc(clen + 1);
|
|
1613
|
+
if (!carrier) { free(s); return NULL; }
|
|
1614
|
+
carrier[0] = '\x03';
|
|
1615
|
+
carrier[1] = 'A';
|
|
1616
|
+
memcpy(carrier + 2, an, alen);
|
|
1617
|
+
carrier[2 + alen] = '\x01';
|
|
1618
|
+
memcpy(carrier + 3 + alen, s, vlen + 1);
|
|
1619
|
+
free(s);
|
|
1620
|
+
XPathNodeSet* ns = xpath_nodeset_new();
|
|
1621
|
+
if (!ns) { free(carrier); return NULL; }
|
|
1622
|
+
ns->owns_synthetic_text = 1;
|
|
1623
|
+
ns->is_sequence = 1;
|
|
1624
|
+
XPathTextNode* tn = synth_text(carrier, clen);
|
|
1625
|
+
free(carrier);
|
|
1626
|
+
if (!tn) { xpath_nodeset_free(ns); return NULL; }
|
|
1627
|
+
xpath_nodeset_add(ns, tn);
|
|
1628
|
+
struct leptris_xpath_result* out =
|
|
1629
|
+
xpath_result_new(XPATH_RESULT_NODESET);
|
|
1630
|
+
if (!out) { xpath_nodeset_free(ns); return NULL; }
|
|
1631
|
+
out->value.nodeset_value = ns;
|
|
1632
|
+
return out;
|
|
1633
|
+
}
|
|
1600
1634
|
struct leptris_xpath_result* out =
|
|
1601
1635
|
xpath_result_new(XPATH_RESULT_STRING);
|
|
1602
1636
|
if (!out) { free(s); return NULL; }
|
|
@@ -1903,6 +1937,81 @@ struct leptris_xpath_result* evaluate_operator(XPathContext* ctx,
|
|
|
1903
1937
|
free(v);
|
|
1904
1938
|
}
|
|
1905
1939
|
buf[len] = '\0';
|
|
1940
|
+
/* Canonical entry order: sort by key so deep-equal and the
|
|
1941
|
+
* serialized carrier are key-order-insensitive (maps-4:
|
|
1942
|
+
* {1:a, 2:b} = {2:b, 1:a}); map iteration order is
|
|
1943
|
+
* implementation-defined in XQuery. */
|
|
1944
|
+
{
|
|
1945
|
+
size_t body_len = len - 4; /* past "\x03MAP" */
|
|
1946
|
+
char* body = buf + 4;
|
|
1947
|
+
size_t count = 0;
|
|
1948
|
+
for (size_t i = 0; i < body_len; i++)
|
|
1949
|
+
if (body[i] == '\x02') count++;
|
|
1950
|
+
if (count > 1) {
|
|
1951
|
+
char** keys = (char**)malloc(count * sizeof(char*));
|
|
1952
|
+
size_t* klens = (size_t*)malloc(count * sizeof(size_t));
|
|
1953
|
+
char** vals = (char**)malloc(count * sizeof(char*));
|
|
1954
|
+
size_t* vlens = (size_t*)malloc(count * sizeof(size_t));
|
|
1955
|
+
if (keys && klens && vals && vlens) {
|
|
1956
|
+
size_t n = 0, p = 0;
|
|
1957
|
+
while (p < body_len) {
|
|
1958
|
+
if (body[p] != '\x02') { p++; continue; }
|
|
1959
|
+
p++;
|
|
1960
|
+
size_t ks = p;
|
|
1961
|
+
while (p < body_len && body[p] != '\x01') p++;
|
|
1962
|
+
keys[n] = body + ks;
|
|
1963
|
+
klens[n] = p - ks;
|
|
1964
|
+
p++;
|
|
1965
|
+
size_t vs = p;
|
|
1966
|
+
while (p < body_len && body[p] != '\x02') p++;
|
|
1967
|
+
vals[n] = body + vs;
|
|
1968
|
+
vlens[n] = p - vs;
|
|
1969
|
+
n++;
|
|
1970
|
+
}
|
|
1971
|
+
for (size_t a = 1; a < n; a++) {
|
|
1972
|
+
char* ek = keys[a]; size_t ekl = klens[a];
|
|
1973
|
+
char* ev = vals[a]; size_t evl = vlens[a];
|
|
1974
|
+
size_t b2 = a;
|
|
1975
|
+
while (b2 > 0) {
|
|
1976
|
+
int c = strncmp(keys[b2 - 1], ek,
|
|
1977
|
+
klens[b2 - 1] < ekl
|
|
1978
|
+
? klens[b2 - 1] : ekl);
|
|
1979
|
+
if (c == 0)
|
|
1980
|
+
c = klens[b2 - 1] < ekl ? -1
|
|
1981
|
+
: klens[b2 - 1] > ekl ? 1 : 0;
|
|
1982
|
+
if (c <= 0) break;
|
|
1983
|
+
keys[b2] = keys[b2 - 1];
|
|
1984
|
+
klens[b2] = klens[b2 - 1];
|
|
1985
|
+
vals[b2] = vals[b2 - 1];
|
|
1986
|
+
vlens[b2] = vlens[b2 - 1];
|
|
1987
|
+
b2--;
|
|
1988
|
+
}
|
|
1989
|
+
keys[b2] = ek; klens[b2] = ekl;
|
|
1990
|
+
vals[b2] = ev; vlens[b2] = evl;
|
|
1991
|
+
}
|
|
1992
|
+
/* Sort-order permutes segments in place: write
|
|
1993
|
+
* through a scratch (a forward write clobbers a
|
|
1994
|
+
* not-yet-copied source segment). */
|
|
1995
|
+
char* scratch = (char*)malloc(body_len + 1);
|
|
1996
|
+
if (scratch) {
|
|
1997
|
+
size_t w2 = 0;
|
|
1998
|
+
for (size_t e = 0; e < n; e++) {
|
|
1999
|
+
scratch[w2++] = '\x02';
|
|
2000
|
+
memcpy(scratch + w2, keys[e], klens[e]);
|
|
2001
|
+
w2 += klens[e];
|
|
2002
|
+
scratch[w2++] = '\x01';
|
|
2003
|
+
memcpy(scratch + w2, vals[e], vlens[e]);
|
|
2004
|
+
w2 += vlens[e];
|
|
2005
|
+
}
|
|
2006
|
+
memcpy(body, scratch, w2);
|
|
2007
|
+
len = 4 + w2;
|
|
2008
|
+
buf[len] = '\0';
|
|
2009
|
+
free(scratch);
|
|
2010
|
+
}
|
|
2011
|
+
}
|
|
2012
|
+
free(keys); free(klens); free(vals); free(vlens);
|
|
2013
|
+
}
|
|
2014
|
+
}
|
|
1906
2015
|
XPathNodeSet* out = xpath_nodeset_new();
|
|
1907
2016
|
if (!out) { free(buf); return NULL; }
|
|
1908
2017
|
out->owns_synthetic_text = 1;
|
|
@@ -2319,12 +2428,17 @@ struct leptris_xpath_result* evaluate_operator(XPathContext* ctx,
|
|
|
2319
2428
|
is->owns_synthetic_text = 0;
|
|
2320
2429
|
} else {
|
|
2321
2430
|
char* piece =
|
|
2322
|
-
|
|
2323
|
-
|
|
2324
|
-
|
|
2325
|
-
|
|
2326
|
-
|
|
2327
|
-
|
|
2431
|
+
item->decimal_lex
|
|
2432
|
+
? leptris_strdup(item->decimal_lex)
|
|
2433
|
+
: (ctx->xquery_spelling &&
|
|
2434
|
+
item->type == XPATH_RESULT_NUMBER &&
|
|
2435
|
+
!item->is_int)
|
|
2436
|
+
? xpath_number_to_string_xq_typed(
|
|
2437
|
+
item->value.number_value,
|
|
2438
|
+
item->atomic_type &&
|
|
2439
|
+
strcmp(item->atomic_type,
|
|
2440
|
+
"xs:float") == 0)
|
|
2441
|
+
: xpath_to_string(item);
|
|
2328
2442
|
if (item->type == XPATH_RESULT_NUMBER) {
|
|
2329
2443
|
/* "\x03N" marks numeric members for per-member
|
|
2330
2444
|
* type checks (instance of); get_node_text
|
|
@@ -276,11 +276,18 @@ char* get_node_text(void* node) {
|
|
|
276
276
|
* per-member type checks (instance of). \x03 is invalid
|
|
277
277
|
* in XML 1.0 text, so it can never collide with real
|
|
278
278
|
* content — strip it for every string consumer. */
|
|
279
|
-
if (c[0] == '\x03'
|
|
280
|
-
(c[1] == '
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
279
|
+
if (c[0] == '\x03') {
|
|
280
|
+
if (c[1] == 'A') {
|
|
281
|
+
/* attribute ctor carrier: string value is the
|
|
282
|
+
* part after the "\x01" separator */
|
|
283
|
+
const char* sep = strchr(c, '\x01');
|
|
284
|
+
return leptris_strdup(sep ? sep + 1 : "");
|
|
285
|
+
}
|
|
286
|
+
if (c[1] == 'N' || c[1] == 'B' || c[1] == 'D' ||
|
|
287
|
+
(c[1] == 'F' &&
|
|
288
|
+
!((c[2] == 'N' && c[3] == '\x02') || c[2] == 'R')))
|
|
289
|
+
c += 2;
|
|
290
|
+
}
|
|
284
291
|
return leptris_strdup(c);
|
|
285
292
|
}
|
|
286
293
|
|
|
@@ -299,8 +306,30 @@ int xpath_to_boolean(struct leptris_xpath_result* result) {
|
|
|
299
306
|
return result->value.number_value != 0.0 && !isnan(result->value.number_value);
|
|
300
307
|
case XPATH_RESULT_STRING:
|
|
301
308
|
return result->value.string_value && result->value.string_value[0] != '\0';
|
|
302
|
-
case XPATH_RESULT_NODESET:
|
|
303
|
-
|
|
309
|
+
case XPATH_RESULT_NODESET: {
|
|
310
|
+
XPathNodeSet* ns = result->value.nodeset_value;
|
|
311
|
+
size_t cnt = ns ? xpath_nodeset_count(ns) : 0;
|
|
312
|
+
/* XQuery sequence EBV: a singleton atomic carries the
|
|
313
|
+
* item's EBV (fn:remove round-trips a false boolean as
|
|
314
|
+
* falsy), while XPath node-existence EBV applies to
|
|
315
|
+
* real nodes and multi-item sequences. */
|
|
316
|
+
if (!ns || !ns->is_sequence) return cnt > 0;
|
|
317
|
+
if (cnt != 1) return cnt > 0;
|
|
318
|
+
void* nd = ns->nodes[0];
|
|
319
|
+
if (!nd || XPATH_NODE_TYPE(nd) != LEPTRIS_NODE_TEXT)
|
|
320
|
+
return 1;
|
|
321
|
+
const char* c = ((XPathTextNode*)nd)->content;
|
|
322
|
+
if (!c) return 1;
|
|
323
|
+
if (c[0] == '\x03') {
|
|
324
|
+
if (c[1] == 'B') return c[2] == 't';
|
|
325
|
+
if (c[1] == 'N' || c[1] == 'F' || c[1] == 'D') {
|
|
326
|
+
double d = strtod(c + 2, NULL);
|
|
327
|
+
return d != 0.0 && !isnan(d);
|
|
328
|
+
}
|
|
329
|
+
return 1;
|
|
330
|
+
}
|
|
331
|
+
return c[0] != '\0';
|
|
332
|
+
}
|
|
304
333
|
default:
|
|
305
334
|
return 0;
|
|
306
335
|
}
|
|
@@ -764,6 +764,71 @@ static int de_node_equal(LeptrisNodeRef a, LeptrisNodeRef b) {
|
|
|
764
764
|
return ca == NULL && cb == NULL;
|
|
765
765
|
}
|
|
766
766
|
|
|
767
|
+
/* Direct-ctor strings compare as trees when the lexical forms
|
|
768
|
+
* differ: attribute order is insignificant to deep-equality
|
|
769
|
+
* (fn-deep-equal-maps-11), and the tree path also inherits the
|
|
770
|
+
* comment/PI-insignificance rule. Unparseable markup compares
|
|
771
|
+
* lexically (both sides then fail identically -> false). */
|
|
772
|
+
static int de_markup_equal_n(const char* a, size_t al,
|
|
773
|
+
const char* b, size_t bl) {
|
|
774
|
+
char* xa = (char*)malloc(al + 1);
|
|
775
|
+
char* xb = (char*)malloc(bl + 1);
|
|
776
|
+
int eq = 0;
|
|
777
|
+
if (xa && xb) {
|
|
778
|
+
memcpy(xa, a, al); xa[al] = 0;
|
|
779
|
+
memcpy(xb, b, bl); xb[bl] = 0;
|
|
780
|
+
LeptrisStatus sa = LEPTRIS_OK, sb = LEPTRIS_OK;
|
|
781
|
+
LeptrisDocument da = leptris_parse_string(xa, al, &sa);
|
|
782
|
+
LeptrisDocument db = leptris_parse_string(xb, bl, &sb);
|
|
783
|
+
if (da && db) {
|
|
784
|
+
LeptrisElement ra = leptris_document_root(da);
|
|
785
|
+
LeptrisElement rb = leptris_document_root(db);
|
|
786
|
+
eq = ra && rb &&
|
|
787
|
+
de_node_equal((LeptrisNodeRef)ra, (LeptrisNodeRef)rb);
|
|
788
|
+
}
|
|
789
|
+
if (da) leptris_document_free(da);
|
|
790
|
+
if (db) leptris_document_free(db);
|
|
791
|
+
}
|
|
792
|
+
free(xa);
|
|
793
|
+
free(xb);
|
|
794
|
+
return eq;
|
|
795
|
+
}
|
|
796
|
+
|
|
797
|
+
/* Map carriers ("\x03MAP" + ("\x02" key "\x01" value)*; entries are
|
|
798
|
+
* canonically key-sorted at construction) compare entry-wise:
|
|
799
|
+
* keys lexically, values with the markup fallback. Segment bytes
|
|
800
|
+
* (\x02/\x01) cannot occur in XML content, so end-scan is safe. */
|
|
801
|
+
static int de_map_equal(const char* a, const char* b) {
|
|
802
|
+
a += 4;
|
|
803
|
+
b += 4;
|
|
804
|
+
while (*a && *b) {
|
|
805
|
+
if (*a != '\x02' || *b != '\x02') return 0;
|
|
806
|
+
a++;
|
|
807
|
+
b++;
|
|
808
|
+
const char* ka = a;
|
|
809
|
+
while (*a && *a != '\x01') a++;
|
|
810
|
+
const char* kb = b;
|
|
811
|
+
while (*b && *b != '\x01') b++;
|
|
812
|
+
if (*a != '\x01' || *b != '\x01') return 0;
|
|
813
|
+
if (a - ka != b - kb || memcmp(ka, kb, (size_t)(a - ka)) != 0)
|
|
814
|
+
return 0;
|
|
815
|
+
a++;
|
|
816
|
+
b++;
|
|
817
|
+
const char* va = a;
|
|
818
|
+
while (*a && *a != '\x02') a++;
|
|
819
|
+
const char* vb = b;
|
|
820
|
+
while (*b && *b != '\x02') b++;
|
|
821
|
+
if (a - va != b - vb ||
|
|
822
|
+
memcmp(va, vb, (size_t)(a - va)) != 0) {
|
|
823
|
+
if (va[0] != '<' || vb[0] != '<') return 0;
|
|
824
|
+
if (!de_markup_equal_n(va, (size_t)(a - va),
|
|
825
|
+
vb, (size_t)(b - vb)))
|
|
826
|
+
return 0;
|
|
827
|
+
}
|
|
828
|
+
}
|
|
829
|
+
return *a == 0 && *b == 0;
|
|
830
|
+
}
|
|
831
|
+
|
|
767
832
|
/* One comparable item: kind 0 = node, 1 = number, 2 = string,
|
|
768
833
|
* 3 = boolean. Synthetic sequence members carry the "\x03N" numeric
|
|
769
834
|
* marker (stripped here); other synthetic text is a string. */
|
|
@@ -789,11 +854,18 @@ static int de_num_rank(const char* t) {
|
|
|
789
854
|
}
|
|
790
855
|
|
|
791
856
|
/* The untyped/string equivalence class: plain ctor/lexical strings,
|
|
792
|
-
* xs:string, xs:anyURI, xs:untypedAtomic
|
|
857
|
+
* xs:string, xs:anyURI, xs:untypedAtomic — and the types DERIVED
|
|
858
|
+
* from xs:string, which all compare by codepoint
|
|
859
|
+
* (K2-SeqDeepEqualFunc-35: "a" = xs:NCName("a")). */
|
|
793
860
|
static int de_str_class(const char* t) {
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
861
|
+
if (!t) return 1;
|
|
862
|
+
static const char* const k_derived[] = {
|
|
863
|
+
"xs:string", "xs:anyURI", "xs:untypedAtomic", "xs:NCName",
|
|
864
|
+
"xs:Name", "xs:token", "xs:normalizedString", "xs:language",
|
|
865
|
+
"xs:NMTOKEN", "xs:ID", "xs:IDREF", "xs:ENTITY"};
|
|
866
|
+
for (size_t i = 0; i < sizeof(k_derived) / sizeof(*k_derived); i++)
|
|
867
|
+
if (strcmp(t, k_derived[i]) == 0) return 1;
|
|
868
|
+
return 0;
|
|
797
869
|
}
|
|
798
870
|
|
|
799
871
|
static void de_item_clear(DeItem* it) {
|
|
@@ -825,13 +897,36 @@ static int de_item_equal(DeItem* a, DeItem* b) {
|
|
|
825
897
|
case 2: {
|
|
826
898
|
const char* as = a->str ? a->str : (a->borrow ? a->borrow : "");
|
|
827
899
|
const char* bs = b->str ? b->str : (b->borrow ? b->borrow : "");
|
|
828
|
-
if (de_str_class(a->type) && de_str_class(b->type))
|
|
829
|
-
|
|
900
|
+
if (de_str_class(a->type) && de_str_class(b->type)) {
|
|
901
|
+
if (strncmp(as, "\x03MAP", 4) == 0 &&
|
|
902
|
+
strncmp(bs, "\x03MAP", 4) == 0)
|
|
903
|
+
return de_map_equal(as, bs);
|
|
904
|
+
if (strcmp(as, bs) == 0) return 1;
|
|
905
|
+
return as[0] == '<' && bs[0] == '<' &&
|
|
906
|
+
de_markup_equal_n(as, strlen(as), bs, strlen(bs));
|
|
907
|
+
}
|
|
830
908
|
if (!de_str_class(a->type) && !de_str_class(b->type) &&
|
|
831
909
|
strcmp(a->type, b->type) == 0)
|
|
832
910
|
return strcmp(as, bs) == 0;
|
|
833
911
|
return 0; /* typed (xs:date...) vs anything else */
|
|
834
912
|
}
|
|
913
|
+
case 4: {
|
|
914
|
+
/* Attribute ctor carriers: name segment (after "\x03A",
|
|
915
|
+
* before "\x01") and value segment must both match. */
|
|
916
|
+
const char* as = a->str ? a->str : (a->borrow ? a->borrow : "");
|
|
917
|
+
const char* bs = b->str ? b->str : (b->borrow ? b->borrow : "");
|
|
918
|
+
const char* ase = strchr(as, '\x01');
|
|
919
|
+
const char* bse = strchr(bs, '\x01');
|
|
920
|
+
if (!ase || !bse) return 0;
|
|
921
|
+
if ((size_t)(ase - as) != (size_t)(bse - bs) ||
|
|
922
|
+
memcmp(as, bs, (size_t)(ase - as)) != 0)
|
|
923
|
+
return 0;
|
|
924
|
+
ase++;
|
|
925
|
+
bse++;
|
|
926
|
+
if (strcmp(ase, bse) == 0) return 1;
|
|
927
|
+
return ase[0] == '<' && bse[0] == '<' &&
|
|
928
|
+
de_markup_equal_n(ase, strlen(ase), bse, strlen(bse));
|
|
929
|
+
}
|
|
835
930
|
default: return a->num == b->num; /* booleans as 0/1 */
|
|
836
931
|
}
|
|
837
932
|
}
|
|
@@ -857,6 +952,11 @@ static size_t de_collect(struct leptris_xpath_result* r, DeItem* out,
|
|
|
857
952
|
if (c[0] == '\x03' && c[1] == 'B') {
|
|
858
953
|
out[i].kind = 3;
|
|
859
954
|
out[i].num = c[2] == 't' ? 1 : 0;
|
|
955
|
+
} else if (c[0] == '\x03' && c[1] == 'A') {
|
|
956
|
+
/* attribute ctor carrier: kind 4 compares
|
|
957
|
+
* name + value (K2-SeqDeepEqualFunc-25/31/32) */
|
|
958
|
+
out[i].kind = 4;
|
|
959
|
+
out[i].borrow = c;
|
|
860
960
|
} else if (c[0] == '\x03' &&
|
|
861
961
|
(c[1] == 'N' || c[1] == 'D' ||
|
|
862
962
|
(c[1] == 'F' &&
|