leptris 1.9.364.2-aarch64-linux → 1.9.366.1-aarch64-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: d3d3bd0e468a92817427100b26e5ca0bcb2fc9f4a588881df21ca73baf345203
4
- data.tar.gz: da6fe398fa6311cbe26803546a60598ab84c90c41ed3ca4e3ac2c36c2a4fbf8e
3
+ metadata.gz: d847172247048f7d9acb3898a3a63e61bd4744632e7b5213ca7c13694fee85e9
4
+ data.tar.gz: 48c6b128d630e278ca1a747509b9403bb8c335fbf91a01764869c0298fb1c719
5
5
  SHA512:
6
- metadata.gz: 9fbb91daefb87daab4c8c6c4c4234d53ff5e175cc09efbb90441cfb3d5bc4fd03802040da5ade5809214501c5dc6cb6b8565a2d5928a74d687d51125bc8d40d2
7
- data.tar.gz: 1807c7274c5cce84427991747c41c061da829082816f69d26276805f16e429386a8c0fb456fbb8673be28a6fd6b60ccf4387a6b79dfd21392663af41d7de0f3f
6
+ metadata.gz: 3f0ace5e75d874349929f02f71f3dc685d34e4a42966c024112e7480a031a5cb77b93ab6dca92616ae78bc4dbf9c02a8dab9ce1def4321230867be0ecd631f1c
7
+ data.tar.gz: f372629ba2782ee782c405bddb40766bef226eab32cb2f763ffd681b0f31766d21a32e61e5dc1db28759096a3942baae380d7ab923013e954a2e3e8c2ead336b
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.364"
11
+ LIBLEPTRIS_VERSION = "1.9.366"
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.364.2"
4
+ VERSION = "1.9.366.1"
5
5
  end
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.364
4
+ - libleptris v1.9.366
5
5
  - utf8proc v2.11.0
6
6
 
7
7
  Rebuild by hand:
@@ -1,5 +1,45 @@
1
1
  ## [Unreleased]
2
2
 
3
+ ## [1.9.366] - 2026-10-17
4
+
5
+ ### Fixed
6
+
7
+ - **dom: the foreign leaf adopt copies exactly the argument node (#1755, PR #1757).**
8
+ `adopt_for_splice`'s non-element branch ran a wholesale sibling-chain copy
9
+ (`copy_leaf_chain`) whose switch had no ELEMENT case: any mixed-content
10
+ chain — a text node whose next sibling is the element that follows it, the
11
+ standard fragment shape lutaml/moxml produce — hit `default:` and the whole
12
+ append failed with `LEPTRIS_ERROR_MEMORY` ("Memory allocation failed": a
13
+ lie; nothing was out of memory). The run-copy was also wrong for every
14
+ caller that reaches this path — the binding's fragment loop and moxml's
15
+ NodeSet iteration append fragment children individually, so a chain copy
16
+ duplicated every later sibling even where it succeeded. The foreign leaf
17
+ adopt now copies exactly the argument (doc-level leaves were already
18
+ single-node for the same reason, #1539); the single-caller chain copier is
19
+ deleted. This is the deterministic face of the Ruby 4.0 render crash
20
+ (leptris-ruby#489): the GC-stressed metanorma collection render goes
21
+ 2 failures -> 0. Engine suite 2048/2048; binding suite 1094/1094.
22
+
23
+
24
+
25
+ ## [1.9.365] - 2026-10-16
26
+
27
+ ### Fixed
28
+
29
+ - **html4: skip the XML-declaration construct in HTML input (#1748, PR #1754).**
30
+ The html4 lane parsed `<?xml ...?>` as a PI node (target `xml`) whose data
31
+ carried the terminator `?`, so serialization emitted
32
+ `<?xml version="1.0" encoding="UTF-8"??>` — and with the serializer's
33
+ `xml_declaration` riding along (the default `to_xml`), the output carried
34
+ two declarations, which canon's XHTML pretty-printer degenerated to
35
+ declaration-only output downstream. libxml2's HTML parser skips the
36
+ XML-declaration construct entirely; the html4 lane now does the same (the
37
+ WHATWG lane keeps its specified bogus-comment shape). Red-first gtest pins
38
+ the contract: `xml_declaration=0` emits no declaration, `1` emits exactly
39
+ the serializer's own — never the input's remnant, never `??`. Engine suite
40
+ 2047/2047; canon's battery 9/9.
41
+
42
+
3
43
  ## [1.9.364] - 2026-10-16
4
44
 
5
45
  ### Added
@@ -3,7 +3,7 @@
3
3
  cmake_minimum_required(VERSION 3.20)
4
4
 
5
5
  project(leptris
6
- VERSION 1.9.364
6
+ VERSION 1.9.366
7
7
  DESCRIPTION "Fast XML parser and XPath evaluator in pure C"
8
8
  LANGUAGES C CXX
9
9
  )
@@ -359,74 +359,6 @@ static LeptrisNode* copy_leaf_one(LeptrisNode* c,
359
359
  }
360
360
  }
361
361
 
362
- /* #1534: copy a non-element node (plus its linked tail — the #540
363
- * chain shape) into the target document's pool. Same per-kind
364
- * creation helpers the subtree copier uses. */
365
- static LeptrisNode* copy_leaf_chain(LeptrisNode* head,
366
- struct leptris_document* target) {
367
- LeptrisNode* copy_head = NULL;
368
- LeptrisNode* copy_tail = NULL;
369
- for (LeptrisNode* c = head; c;
370
- c = leptris_node_get_next_sibling(c)) {
371
- LeptrisNode* cc = NULL;
372
- switch (c->type) {
373
- case LEPTRIS_NODE_TYPE_TEXT: {
374
- LeptrisTextNode* t = (LeptrisTextNode*)c;
375
- const char* content = leptris_textnode_content(t);
376
- cc = (LeptrisNode*)leptris_text_create(
377
- content, t->content_len, target->pool, target);
378
- break;
379
- }
380
- case LEPTRIS_NODE_TYPE_CDATA: {
381
- LeptrisCDATANode* cd = (LeptrisCDATANode*)c;
382
- cc = (LeptrisNode*)leptris_cdata_create(
383
- cd->content, cd->content ? strlen(cd->content) : 0,
384
- target->pool);
385
- break;
386
- }
387
- case LEPTRIS_NODE_TYPE_COMMENT: {
388
- LeptrisCommentNode* cm = (LeptrisCommentNode*)c;
389
- cc = (LeptrisNode*)leptris_comment_create(
390
- cm->content,
391
- cm->content ? strlen(cm->content) : 0,
392
- target->pool);
393
- break;
394
- }
395
- case LEPTRIS_NODE_TYPE_PI: {
396
- LeptrisPINode* pi = (LeptrisPINode*)c;
397
- cc = (LeptrisNode*)leptris_pi_create(
398
- pi->target, pi->target ? strlen(pi->target) : 0,
399
- pi->data ? pi->data : "",
400
- pi->data ? strlen(pi->data) : 0, target->pool);
401
- break;
402
- }
403
- case LEPTRIS_NODE_TYPE_ENTITY_REF: {
404
- LeptrisEntityRefNode* er = (LeptrisEntityRefNode*)c;
405
- cc = (LeptrisNode*)leptris_entity_ref_create(
406
- er->name, er->name ? strlen(er->name) : 0,
407
- target->pool);
408
- break;
409
- }
410
- default:
411
- cc = NULL;
412
- }
413
- if (!cc) {
414
- /* OOM mid-chain: copies already made live only in the
415
- * target pool — no caller-visible handle exists, and
416
- * document free reclaims them (the same conservatism
417
- * copy_subtree_detached uses on !cc). */
418
- return NULL;
419
- }
420
- if (!copy_head) {
421
- copy_head = cc;
422
- } else {
423
- leptris_node_set_next_sibling(copy_tail, cc);
424
- }
425
- copy_tail = cc;
426
- }
427
- return copy_head;
428
- }
429
-
430
362
  static LeptrisNode* adopt_for_splice(LeptrisNode* node,
431
363
  struct leptris_document* target) {
432
364
  if (!node || !target) return node;
@@ -465,13 +397,15 @@ static LeptrisNode* adopt_for_splice(LeptrisNode* node,
465
397
  * the target — its pool dies with the target, so the node
466
398
  * moves by reference. */
467
399
  if (src->absorbed_by == target) return node;
468
- /* top == node: a DOC-LEVEL leaf (prolog/epilog, #580) —
469
- * its sibling chain spans unrelated nodes (including the
470
- * root element), so copy exactly the argument (#1539).
471
- * An in-element leaf keeps the #540 chain shape. */
472
- if (top == node) return copy_leaf_one(node, target);
473
- LeptrisNode* copy = copy_leaf_chain(node, target);
474
- return copy;
400
+ /* Copy exactly the argument (#1755): the callers of this
401
+ * face append fragment children INDIVIDUALLY (the binding's
402
+ * fragment loop, moxml's NodeSet iteration), so a wholesale
403
+ * run copy duplicated every later sibling — and its missing
404
+ * ELEMENT case failed the whole append with
405
+ * LEPTRIS_ERROR_MEMORY on the first mixed-content chain.
406
+ * Doc-level leaves (prolog/epilog, #580) were already
407
+ * single-node for the same reason (#1539). */
408
+ return copy_leaf_one(node, target);
475
409
  }
476
410
  /* Type-safe walk to the chain top: get_document reads
477
411
  * element-form fields and misreads non-element structs. */
@@ -6685,6 +6685,19 @@ static LeptrisDocument html_parse_shared(
6685
6685
  size_t dlen = (size_t)(de > ds ? de - ds : 0);
6686
6686
  char* tgt = (char*)leptris_pool_alloc(b.pool, tlen + 1);
6687
6687
  char* dat = (char*)leptris_pool_alloc(b.pool, dlen + 1);
6688
+ /* libxml2 HTML parity (#1748): the XML-declaration
6689
+ * construct ('<?xml ...?>') is an XML-ism — the HTML
6690
+ * lane skips it entirely. Retaining it as a PI made the
6691
+ * data carry the terminator '?' (serialized
6692
+ * '<?xml ...??>'), and the declaration metadata + PI
6693
+ * doubled when the serializer's xml_declaration rode
6694
+ * along. */
6695
+ if (tlen == 3 && (ts[0] | 0x20) == 'x' &&
6696
+ (ts[1] | 0x20) == 'm' && (ts[2] | 0x20) == 'l') {
6697
+ p = (de < end) ? de + 1 : end;
6698
+ text = p;
6699
+ continue;
6700
+ }
6688
6701
  if (tgt && dat) {
6689
6702
  memcpy(tgt, ts, tlen);
6690
6703
  tgt[tlen] = 0;
@@ -3019,6 +3019,58 @@ TEST(DomAttachInstalled, ForeignAttachReportsCopyAddress) {
3019
3019
  leptris_document_free(target);
3020
3020
  }
3021
3021
 
3022
+ /* leptris#1755: a foreign MIXED-CONTENT fragment hands the
3023
+ * engine a text node whose sibling chain carries the following
3024
+ * element. The leaf adopt ran a WHOLESALE chain copy that had no
3025
+ * ELEMENT case — the element hit default: and the whole append
3026
+ * failed with LEPTRIS_ERROR_MEMORY ("Memory allocation failed" —
3027
+ * a lie; nothing was out of memory). Every caller of this face
3028
+ * appends fragment children INDIVIDUALLY (the binding's fragment
3029
+ * loop, moxml's NodeSet iteration), so the foreign leaf adopt
3030
+ * copies exactly the argument node — the sibling run stays in the
3031
+ * source for its own appends. */
3032
+ TEST(DomAttachInstalled, ForeignMixedChainTextBeforeElement) {
3033
+ LeptrisStatus st = LEPTRIS_OK;
3034
+ const char* src = "<f>lead<item>t</item>tail</f>";
3035
+ LeptrisDocument frag = leptris_parse_string(src, strlen(src), &st);
3036
+ ASSERT_NE(frag, nullptr);
3037
+ LeptrisDocument target = leptris_parse_string("<doc/>", 6, &st);
3038
+ ASSERT_NE(target, nullptr);
3039
+ LeptrisElement troot = leptris_document_root(target);
3040
+ LeptrisElement froot = leptris_document_root(frag);
3041
+ /* node-level first child: the TEXT "lead" (the element-level
3042
+ * face would skip it and return <item>). */
3043
+ LeptrisNodeRef text = leptris_node_first_child((LeptrisNodeRef)froot);
3044
+ ASSERT_NE(text, nullptr);
3045
+ ASSERT_EQ(leptris_node_get_type(text), LEPTRIS_NODE_TYPE_TEXT);
3046
+
3047
+ EXPECT_EQ(leptris_element_add_child_addr(
3048
+ (uintptr_t)troot, (uintptr_t)text),
3049
+ LEPTRIS_OK);
3050
+ /* exactly the argument: no sibling run rides along (serialize
3051
+ * pins the child set; child_count is element-oriented) */
3052
+ char* out = leptris_element_serialize(troot, nullptr);
3053
+ ASSERT_NE(out, nullptr);
3054
+ EXPECT_STREQ(out, "<doc>lead</doc>");
3055
+ leptris_free_string(out);
3056
+
3057
+ /* the binding's loop continues: the element sibling appends on
3058
+ * its own, and the trailing text with it — no duplication of
3059
+ * the element the first adopt must NOT have carried */
3060
+ LeptrisElement item = leptris_element_first_child_any(froot);
3061
+ ASSERT_NE(item, nullptr);
3062
+ EXPECT_EQ(leptris_element_add_child_addr(
3063
+ (uintptr_t)troot, (uintptr_t)item),
3064
+ LEPTRIS_OK);
3065
+ /* the source dies; the installed copies must survive */
3066
+ leptris_document_free(frag);
3067
+ out = leptris_element_serialize(troot, nullptr);
3068
+ ASSERT_NE(out, nullptr);
3069
+ EXPECT_STREQ(out, "<doc>lead<item>t</item></doc>");
3070
+ leptris_free_string(out);
3071
+ leptris_document_free(target);
3072
+ }
3073
+
3022
3074
  TEST(DomAttachInstalled, SameDocumentReturnsArgument) {
3023
3075
  LeptrisStatus st = LEPTRIS_OK;
3024
3076
  LeptrisDocument d = leptris_parse_string("<r/>", 4, &st);
@@ -188,3 +188,43 @@ TEST(HtmlBuilder, RawTextElementNotEscaped) {
188
188
  EXPECT_EQ(out.find("&lt;"), std::string::npos) << out;
189
189
  leptris_document_free(d);
190
190
  }
191
+
192
+ /* leptris#1748: the XML-declaration construct ('<?xml ...?>') in
193
+ * HTML input is an XML-ism — libxml2's HTML parser skips it, and
194
+ * retaining it as a PI (data carrying the terminator '?') made
195
+ * serialization emit '<?xml ...??>' and double the declaration when
196
+ * the serializer's own xml_declaration rode along. */
197
+ TEST(HtmlBuilder, Html4DropsXmlDeclarationProlog) {
198
+ const char* html =
199
+ "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
200
+ "<html xmlns=\"http://www.w3.org/1999/xhtml\">"
201
+ "<head><title>t</title></head><body><p>x</p></body></html>";
202
+ LeptrisStatus st = LEPTRIS_OK;
203
+ LeptrisDocument d =
204
+ leptris_parse_html4_string(html, strlen(html), &st);
205
+ ASSERT_EQ(st, LEPTRIS_OK);
206
+ ASSERT_NE(d, nullptr);
207
+ for (int decl = 0; decl < 2; decl++) {
208
+ LeptrisSerializeOptions opts;
209
+ memset(&opts, 0, sizeof(opts));
210
+ opts.xml_declaration = decl;
211
+ opts.encoding = "UTF-8";
212
+ char* s = leptris_document_serialize(d, &opts);
213
+ ASSERT_NE(s, nullptr);
214
+ std::string out(s);
215
+ leptris_free_string(s);
216
+ EXPECT_EQ(out.find("??"), std::string::npos)
217
+ << "decl=" << decl << " got: " << out.substr(0, 90);
218
+ /* decl=0: the prolog never leaks. decl=1: exactly the
219
+ * serializer's own declaration — never two, never the
220
+ * input's remnant. */
221
+ size_t decls = 0;
222
+ for (size_t at = out.find("<?xml"); at != std::string::npos;
223
+ at = out.find("<?xml", at + 1))
224
+ decls++;
225
+ EXPECT_EQ(decls, (size_t)decl)
226
+ << "decl=" << decl << " got: " << out.substr(0, 90);
227
+ EXPECT_NE(out.find("<html"), std::string::npos) << out;
228
+ }
229
+ leptris_document_free(d);
230
+ }
@@ -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.364",
4
+ "version": "1.9.366",
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,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: leptris
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.9.364.2
4
+ version: 1.9.366.1
5
5
  platform: aarch64-linux
6
6
  authors:
7
7
  - Ribose Inc.
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2026-10-16 00:00:00.000000000 Z
11
+ date: 2026-10-17 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: ffi