leptris 1.9.231.0 → 1.9.232.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.
Files changed (43) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +19 -0
  3. data/Rakefile +1 -1
  4. data/ext/leptris/native/native.c +39 -0
  5. data/lib/leptris/vendor/aarch64-linux/libleptris.so +0 -0
  6. data/lib/leptris/vendor/aarch64-linux-musl/libleptris.so +0 -0
  7. data/lib/leptris/vendor/arm-linux/libleptris.so +0 -0
  8. data/lib/leptris/vendor/arm-linux-musl/libleptris.so +0 -0
  9. data/lib/leptris/vendor/arm64-darwin/libleptris.dylib +0 -0
  10. data/lib/leptris/vendor/ppc64le-linux/libleptris.so +0 -0
  11. data/lib/leptris/vendor/s390x-linux-musl/libleptris.so +0 -0
  12. data/lib/leptris/vendor/x86_64-darwin/libleptris.dylib +0 -0
  13. data/lib/leptris/vendor/x86_64-linux/libleptris.so +0 -0
  14. data/lib/leptris/vendor/x86_64-linux-musl/libleptris.so +0 -0
  15. data/lib/leptris/version.rb +1 -1
  16. data/lib/leptris/xml/ffi.rb +4 -0
  17. data/lib/leptris/xml/node.rb +28 -0
  18. data/vendor-src/README.md +1 -1
  19. data/vendor-src/libleptris/CHANGELOG.md +15 -0
  20. data/vendor-src/libleptris/CMakeLists.txt +1 -1
  21. data/vendor-src/libleptris/src/include/leptris/descriptor.h +5 -1
  22. data/vendor-src/libleptris/src/include/leptris.h +17 -0
  23. data/vendor-src/libleptris/src/leptris/descriptor/plan.c +26 -6
  24. data/vendor-src/libleptris/src/leptris/dom/node.c +24 -7
  25. data/vendor-src/libleptris/src/leptris/xpath/evaluator_operators.c +22 -8
  26. data/vendor-src/libleptris/src/leptris/xpath/functions.c +29 -8
  27. data/vendor-src/libleptris/src/leptris/xpath/functions_ext31.c +30 -1
  28. data/vendor-src/libleptris/src/leptris/xpath/parser.c +106 -6
  29. data/vendor-src/libleptris/test/abi/test_descriptor.cpp +68 -0
  30. data/vendor-src/libleptris/test/dom/test_dom.cpp +56 -0
  31. data/vendor-src/libleptris/test/xquery/qt3/fn/codepoints-to-string.xml +808 -0
  32. data/vendor-src/libleptris/test/xquery/qt3/fn/deep-equal.xml +2709 -0
  33. data/vendor-src/libleptris/test/xquery/qt3/fn/distinct-values.xml +1223 -0
  34. data/vendor-src/libleptris/test/xquery/qt3/fn/index-of.xml +493 -0
  35. data/vendor-src/libleptris/test/xquery/qt3/fn/insert-before.xml +399 -0
  36. data/vendor-src/libleptris/test/xquery/qt3/fn/remove.xml +480 -0
  37. data/vendor-src/libleptris/test/xquery/qt3/fn/reverse.xml +653 -0
  38. data/vendor-src/libleptris/test/xquery/qt3/fn/string-to-codepoints.xml +452 -0
  39. data/vendor-src/libleptris/test/xquery/qt3/fn/subsequence.xml +1041 -0
  40. data/vendor-src/libleptris/test/xquery/test_qt3.cpp +179 -1
  41. data/vendor-src/libleptris/test/xquery/test_xquery.cpp +27 -0
  42. data/vendor-src/libleptris/vcpkg.json +1 -1
  43. metadata +11 -2
@@ -2382,6 +2382,98 @@ static XPathASTNode* parse_switch_expr(XPathParser* parser) {
2382
2382
  }
2383
2383
  }
2384
2384
  }
2385
+
2386
+ /* String literal body -> decoded value (malloc'd). XQuery 3.1
2387
+ * 3.1.1: character references (
, A, 😀) expand to
2388
+ * the referenced codepoint -- EXEMPT from line-end normalization
2389
+ * (QT3 K-CodepointToStringFunc-13) -- and the five predefined
2390
+ * entity refs expand likewise. A '&' that is not a well-formed
2391
+ * reference stays literal (the text-path leniency). Codepoints are
2392
+ * UTF-8 encoded, so astral refs work. */
2393
+ static char* decode_string_literal(const char* p, size_t n) {
2394
+ char* out = (char*)malloc(n + 1);
2395
+ if (!out) return NULL;
2396
+ size_t o = 0;
2397
+ const char* pe = p + n;
2398
+ while (p < pe) {
2399
+ if (*p != '&') {
2400
+ out[o++] = *p++;
2401
+ continue;
2402
+ }
2403
+ const char* semi = memchr(p, ';', (size_t)(pe - p));
2404
+ static const char* const k_predef[] = { "amp", "lt", "gt",
2405
+ "quot", "apos" };
2406
+ static const char k_predef_ch[] = { '&', '<', '>', '"', '\'' };
2407
+ int handled = 0;
2408
+ if (semi && (size_t)(semi - p) <= 12) {
2409
+ size_t rl = (size_t)(semi - p - 1);
2410
+ const char* r = p + 1;
2411
+ if (*r == '#') {
2412
+ r++;
2413
+ int hex = 0;
2414
+ if (r < semi && (*r == 'x' || *r == 'X')) {
2415
+ hex = 1;
2416
+ r++;
2417
+ }
2418
+ unsigned long cp = 0;
2419
+ const char* ds = r;
2420
+ while (r < semi) {
2421
+ char c = *r;
2422
+ int dv = hex ? (c >= '0' && c <= '9')
2423
+ ? c - '0'
2424
+ : (c >= 'a' && c <= 'f')
2425
+ ? c - 'a' + 10
2426
+ : (c >= 'A' && c <= 'F')
2427
+ ? c - 'A' + 10
2428
+ : -1
2429
+ : (c >= '0' && c <= '9') ? c - '0' : -1;
2430
+ if (dv < 0) break;
2431
+ cp = cp * (hex ? 16ul : 10ul) + (unsigned long)dv;
2432
+ if (cp > 0x110000ul) cp = 0x110001ul;
2433
+ r++;
2434
+ }
2435
+ if (r == semi && r > ds &&
2436
+ (cp == 0x9ul || cp == 0xAul || cp == 0xDul ||
2437
+ (cp >= 0x20ul && cp <= 0x10FFFFul)) &&
2438
+ (cp < 0xD800ul || cp > 0xDFFFul)) {
2439
+ if (cp < 0x80ul) {
2440
+ out[o++] = (char)cp;
2441
+ } else if (cp < 0x800ul) {
2442
+ out[o++] = (char)(0xC0 | (cp >> 6));
2443
+ out[o++] = (char)(0x80 | (cp & 0x3F));
2444
+ } else if (cp < 0x10000ul) {
2445
+ out[o++] = (char)(0xE0 | (cp >> 12));
2446
+ out[o++] = (char)(0x80 | ((cp >> 6) & 0x3F));
2447
+ out[o++] = (char)(0x80 | (cp & 0x3F));
2448
+ } else {
2449
+ out[o++] = (char)(0xF0 | (cp >> 18));
2450
+ out[o++] = (char)(0x80 | ((cp >> 12) & 0x3F));
2451
+ out[o++] = (char)(0x80 | ((cp >> 6) & 0x3F));
2452
+ out[o++] = (char)(0x80 | (cp & 0x3F));
2453
+ }
2454
+ handled = 1;
2455
+ }
2456
+ } else {
2457
+ for (int i = 0; i < 5; i++) {
2458
+ if (rl == strlen(k_predef[i]) &&
2459
+ memcmp(r, k_predef[i], rl) == 0) {
2460
+ out[o++] = k_predef_ch[i];
2461
+ handled = 1;
2462
+ break;
2463
+ }
2464
+ }
2465
+ }
2466
+ }
2467
+ if (handled) {
2468
+ p = semi + 1;
2469
+ } else {
2470
+ out[o++] = *p++;
2471
+ }
2472
+ }
2473
+ out[o] = '\0';
2474
+ return out;
2475
+ }
2476
+
2385
2477
  static XPathASTNode* parse_primary_expr(XPathParser* parser) {
2386
2478
  XPathToken* tok = current_token(parser);
2387
2479
  if (!tok) {
@@ -2423,13 +2515,21 @@ static XPathASTNode* parse_primary_expr(XPathParser* parser) {
2423
2515
  XPathASTNode* node = ast_node_new(XPATH_AST_STRING);
2424
2516
  if (!node) return NULL;
2425
2517
 
2426
- /* Remove quotes */
2518
+ /* Remove quotes; expand references when present. The scan
2519
+ * for '&' keeps the plain-literal path a memcpy (the common
2520
+ * case pays nothing). */
2427
2521
  if (tok->value_len >= 2) {
2428
- size_t len = tok->value_len - 2;
2429
- node->value = LEPTRIS_ALLOC_N(char, len + 1);
2430
- if (node->value) {
2431
- memcpy(node->value, tok->value + 1, len);
2432
- node->value[len] = '\0';
2522
+ const char* bp = tok->value + 1;
2523
+ size_t blen = tok->value_len - 2;
2524
+ if (blen && memchr(bp, '&', blen)) {
2525
+ node->value = decode_string_literal(bp, blen);
2526
+ }
2527
+ if (!node->value) {
2528
+ node->value = LEPTRIS_ALLOC_N(char, blen + 1);
2529
+ if (node->value) {
2530
+ memcpy(node->value, bp, blen);
2531
+ node->value[blen] = '\0';
2532
+ }
2433
2533
  }
2434
2534
  }
2435
2535
  advance_token(parser);
@@ -386,6 +386,74 @@ TEST(PlanWalk, NamespaceFormsAndLeniency) {
386
386
  leptris_document_free(doc);
387
387
  }
388
388
 
389
+ /* XSD-002 fallout class: child rows whose wire_name is the PREFIXED
390
+ * form ("w:b") — exactly what the ABI doc promises ("XML element
391
+ * name as it appears on the wire") — must bind namespaced elements.
392
+ * The matcher historically compared the LOCAL name only, so every
393
+ * prefixed row silently matched nothing: hosts hydrated empty
394
+ * subtrees (attributes read NULL, elements fell back to defaults)
395
+ * and re-serialized without them. Local-name rows keep binding. */
396
+ TEST(PlanWalk, PrefixedWireNamesBindNamespacedElements) {
397
+ LeptrisDocument doc = parse(
398
+ "<w:document xmlns:w=\"urn:w\">"
399
+ "<w:r><w:b w:val=\"false\"/></w:r>"
400
+ "</w:document>");
401
+ ASSERT_NE(doc, nullptr);
402
+ LeptrisElement root = leptris_document_root(doc);
403
+
404
+ leptris_attr_plan b_attrs[] = {
405
+ {"w:val", LEPTRIS_PLAN_KIND_SCALAR, 7, 0, 0, nullptr},
406
+ };
407
+ leptris_child_plan b_row = {};
408
+ b_row.wire_name = "w:b";
409
+ b_row.kind = LEPTRIS_PLAN_KIND_NESTED;
410
+ b_row.child_plan_index = 2;
411
+ b_row.ns_form = LEPTRIS_PLAN_NS_ANY;
412
+ leptris_child_plan r_row = {};
413
+ r_row.wire_name = "w:r";
414
+ r_row.kind = LEPTRIS_PLAN_KIND_NESTED;
415
+ r_row.child_plan_index = 1;
416
+ r_row.ns_form = LEPTRIS_PLAN_NS_ANY;
417
+ leptris_element_plan plans[3] = {};
418
+ plans[0].element_name = "document";
419
+ plans[0].ns_form = LEPTRIS_PLAN_NS_ANY;
420
+ plans[0].child_count = 1;
421
+ plans[0].child_plans = &r_row;
422
+ plans[1].element_name = "r";
423
+ plans[1].ns_form = LEPTRIS_PLAN_NS_ANY;
424
+ plans[1].child_count = 1;
425
+ plans[1].child_plans = &b_row;
426
+ plans[2].element_name = "b";
427
+ plans[2].ns_form = LEPTRIS_PLAN_NS_ANY;
428
+ plans[2].attribute_count = 1;
429
+ plans[2].attribute_plans = b_attrs;
430
+
431
+ leptris_plan_spec spec = {};
432
+ spec.abi_version = leptris_plan_abi_version();
433
+ spec.plan_count = 3;
434
+ spec.plans = plans;
435
+ LeptrisStatus st = LEPTRIS_OK;
436
+ LeptrisPlan plan = leptris_plan_build(&spec, &st);
437
+ ASSERT_NE(plan, nullptr);
438
+ LeptrisPlanResult r = leptris_plan_walk(doc, root, plan, &st);
439
+ ASSERT_NE(r, nullptr);
440
+ ASSERT_EQ(leptris_plan_value_count(r), 1u);
441
+ LeptrisPlanResult rcoll = leptris_plan_value_at(r, 0);
442
+ ASSERT_NE(rcoll, nullptr);
443
+ /* the nested w:r row binds its element value */
444
+ ASSERT_EQ(leptris_plan_value_kind(rcoll), LEPTRIS_PLAN_VALUE_ELEMENT);
445
+ ASSERT_EQ(leptris_plan_value_count(rcoll), 1u);
446
+ LeptrisPlanResult bval = leptris_plan_value_at(rcoll, 0);
447
+ ASSERT_NE(bval, nullptr);
448
+ ASSERT_EQ(leptris_plan_value_kind(bval), LEPTRIS_PLAN_VALUE_ELEMENT);
449
+ const char* val = leptris_plan_value_attribute(bval, "w:val");
450
+ ASSERT_NE(val, nullptr);
451
+ EXPECT_STREQ(val, "false");
452
+ leptris_plan_result_free(r);
453
+ leptris_plan_free(plan);
454
+ leptris_document_free(doc);
455
+ }
456
+
389
457
  TEST(PlanBuild, CopiesTheSpecStrings) {
390
458
  LeptrisDocument doc = parse("<doc><t>v</t></doc>");
391
459
  ASSERT_NE(doc, nullptr);
@@ -2234,6 +2234,62 @@ TEST(DomBasics, NodeVisitWalksSubtreeEnterLeave) {
2234
2234
  leptris_document_free(doc);
2235
2235
  }
2236
2236
 
2237
+ /* Issue #1332: entering-only visit mode — one callback per node, on
2238
+ * entering. Halves the C-callback invocations for consumers that
2239
+ * discard the leaving half (family walks bind lazily on enter).
2240
+ * Same walk order and depths as leptris_node_visit. */
2241
+ TEST(DomBasics, NodeVisitEnteringFiresOncePerNode) {
2242
+ const char xml[] = "<r><a>t1</a><b><!--c--></b></r>";
2243
+ LeptrisStatus st = LEPTRIS_OK;
2244
+ LeptrisDocument doc = leptris_parse_string(xml, std::strlen(xml), &st);
2245
+ ASSERT_NE(doc, nullptr);
2246
+ LeptrisElement root = leptris_document_root(doc);
2247
+ ASSERT_NE(root, nullptr);
2248
+
2249
+ VisitLog log;
2250
+ leptris_node_visit_entering((LeptrisNodeRef)root, &VisitLog::visit,
2251
+ &log);
2252
+ /* 5 nodes (r, a, text, b, comment) — the 3 leaving events of
2253
+ * the full mode are gone. The event list has no L* entries. */
2254
+ ASSERT_EQ(log.events.size(), 5u);
2255
+ EXPECT_EQ(log.events[0], "Er:0");
2256
+ EXPECT_EQ(log.events[1], "Ea:1");
2257
+ EXPECT_EQ(log.events[2], "T:2");
2258
+ EXPECT_EQ(log.events[3], "Eb:1");
2259
+ EXPECT_EQ(log.events[4], "O:2");
2260
+
2261
+ /* Document-node walk behaves identically to the full mode. */
2262
+ VisitLog dlog;
2263
+ leptris_node_visit_entering(leptris_document_node(doc),
2264
+ &VisitLog::visit, &dlog);
2265
+ ASSERT_EQ(dlog.events.size(), 5u);
2266
+ EXPECT_EQ(dlog.events[0], "Er:0");
2267
+ leptris_document_free(doc);
2268
+ }
2269
+
2270
+ TEST(DomBasics, NodeVisitEnteringHalvesTheCallbackCount) {
2271
+ /* The moxml-walk contract (#1332): elements visit TWICE in the
2272
+ * full mode, ONCE here. */
2273
+ const char xml[] =
2274
+ "<r><a><b/><c><d/></c></a><e/><f><g><h/></g></f></r>";
2275
+ LeptrisStatus st = LEPTRIS_OK;
2276
+ LeptrisDocument doc = leptris_parse_string(xml, std::strlen(xml), &st);
2277
+ ASSERT_NE(doc, nullptr);
2278
+
2279
+ int full_calls = 0, entering_calls = 0;
2280
+ auto counter = +[](void* ud, LeptrisNodeRef, int, int) {
2281
+ ++*static_cast<int*>(ud);
2282
+ };
2283
+ leptris_node_visit((LeptrisNodeRef)leptris_document_root(doc),
2284
+ counter, &full_calls);
2285
+ leptris_node_visit_entering(
2286
+ (LeptrisNodeRef)leptris_document_root(doc), counter,
2287
+ &entering_calls);
2288
+ EXPECT_EQ(full_calls, 18); /* 9 elements * 2 */
2289
+ EXPECT_EQ(entering_calls, 9); /* 9 elements, once */
2290
+ leptris_document_free(doc);
2291
+ }
2292
+
2237
2293
  TEST(DomBasics, NodeChildrenExCarriesKinds) {
2238
2294
  const char xml[] =
2239
2295
  "<r>text<!--c--><a/><?pi x?></r>";