leptris 1.9.304.0-ppc64le-linux → 1.9.306.0-ppc64le-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: 785d9b7e66ab4d337f9002a5d4bc39d67c681a74e1dc04967990279e866e034c
4
- data.tar.gz: e804fd26e2b9ab2d42cbdcd1e0af3972f5d1fc8dda4c6c690c0308bb9b0030ae
3
+ metadata.gz: b52c3a85fab2e17ea780a4a42824f5f5ef0a1e76de6e9785d9f1bf61f0a4e2a7
4
+ data.tar.gz: 8c493949121bf7418a97beabd4fee8329b1f2ee9ee0dc8219b7ec07bc2cf74ef
5
5
  SHA512:
6
- metadata.gz: 36067b9300f15de58055ce88d04ba035410585c79134270ce26fb54ff177f6366e904e248554548c23c7670666f6e90045cf6f756c51a037e5ccddb334770de4
7
- data.tar.gz: 37873702eb98d9aa34dddbd8b876e650a0b1a9abb6a84798922161007a548953df1c3565bf624476862540bd76ff9716b6374ee73a424005182cf71ef79512eb
6
+ metadata.gz: d3aaf0027ab03316068eabb5173873cfb76f7c851f716270f5fb25355a31421309f33b7c9af262fba85f14b25688ac660c93e06b9a56e81c9484c7f16b2a262a
7
+ data.tar.gz: 2ae6b417a8f8a62d820d7af559e3a7f873b65f49da35739daae899187441b6b54e81ea6a0d01a27be5dc6089cb15d11ea5f7dd54ec63e37e3f7f23843dd29767
data/CHANGELOG.md CHANGED
@@ -1,3 +1,12 @@
1
+ ## 1.9.306.0 — 2026-10-05
2
+
3
+ Rides libleptris v1.9.306 (pin 1.9.304 -> 1.9.306): the Lane 18
4
+ mutation-path cycle — the adoption gate's mut-block fast bail
5
+ (#1531), cross-document splice adoption (#1528), and the Lane 18
6
+ gate bench made honest (warm both sides of the append row;
7
+ create/append split, leptris/leptris#1533). No binding-surface
8
+ change; the spec suite runs against the 1.9.306 engine.
9
+
1
10
  ## [1.9.304.0] - 2026-10-05
2
11
 
3
12
  ### Fixed
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.304"
11
+ LIBLEPTRIS_VERSION = "1.9.306"
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.304.0"
4
+ VERSION = "1.9.306.0"
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.304
4
+ - libleptris v1.9.306
5
5
  - utf8proc v2.11.0
6
6
 
7
7
  Rebuild by hand:
@@ -1,5 +1,38 @@
1
1
  ## [Unreleased]
2
2
 
3
+ ## [1.9.306] - 2026-10-05
4
+
5
+ <!-- Edit this section with the actual release notes. -->
6
+ <!-- See https://keepachangelog.com for format guidance. -->
7
+
8
+ ### Changed
9
+
10
+ - (describe changes here)
11
+
12
+
13
+ ## [1.9.305] - 2026-10-05
14
+
15
+ ### Performance
16
+
17
+ - **DOM: the #1528 adoption gate no longer taxes the append hot
18
+ path** — the first cut called `get_document` per splice, whose
19
+ namebp fast-out resolves the name slot before the check: append
20
+ measured 21.5 → 30.3 ns/child (+40%, 1.5x pugixml) and the
21
+ document build/free cycle +51% on the Lane 18 bench. `append_child`
22
+ now proves same-document in two hot loads — a child inside the
23
+ document's CURRENT mut-block extent was carved from this
24
+ document's own malloc by the create call just executed, so
25
+ nothing foreign can alias into it. Rolled-over-block children and
26
+ every other shape take the full adoption check; `prepend`/
27
+ `insert` keep it unconditionally. Measured paired-interleaved on
28
+ fresh Release trees: append 21.5 → 22.4 ns/child median (+0.8 ns,
29
+ +4%, the floor for a sound O(1) check), mutcycle +0.4 ns,
30
+ attr-parse within run noise. `CrossDocumentAdoption` stays green
31
+ on normal and ASAN builds; the binding fast paths all forward to
32
+ these public seams, so every route is covered.
33
+
34
+
35
+
3
36
  ## [1.9.304] - 2026-10-05
4
37
 
5
38
  ### Fixed
@@ -3,7 +3,7 @@
3
3
  cmake_minimum_required(VERSION 3.20)
4
4
 
5
5
  project(leptris
6
- VERSION 1.9.304
6
+ VERSION 1.9.306
7
7
  DESCRIPTION "Fast XML parser and XPath evaluator in pure C"
8
8
  LANGUAGES C CXX
9
9
  )
@@ -210,6 +210,8 @@ endif()
210
210
  # ----------------------------------------------------------------------------
211
211
  leptris_add_benchmark(bench_serialize serialization/bench_leptris_serialize.c)
212
212
  leptris_add_benchmark(bench_modify_leptris modification/bench_leptris_modify.c)
213
+ # Lane 18 (#1179 tail): the create/append split behind the gate row.
214
+ leptris_add_benchmark(bench_append_split modification/bench_append_split.cpp)
213
215
 
214
216
  # Lane 18 (#1179): append + attr-parse vs pugixml (the lane bar is
215
217
  # <=1x on both shapes). Needs C++ (pugixml.hpp) and the pugixml
@@ -19,6 +19,22 @@ int main(void) {
19
19
  const int N_PARSE = 20000;
20
20
 
21
21
  /* ---- append: 200k element children on one root ---- */
22
+ {
23
+ /* Warmup pass, untimed: the leptris row runs first in this
24
+ * bench, and its first-touch page faults (mut blocks + name
25
+ * blocks, ~9 MB at 200k children) landed in the timed row
26
+ * while pugixml's row ran second on a warm allocator — a
27
+ * systematic order bias. Both sides get the same warmup
28
+ * treatment; the timed rows below are steady-state. */
29
+ LeptrisDocument d = leptris_document_create();
30
+ LeptrisElement root = leptris_element_create(d, "root");
31
+ leptris_document_set_root(d, root);
32
+ for (int i = 0; i < N_APPEND; i++) {
33
+ LeptrisElement c = leptris_element_create(d, "c");
34
+ leptris_element_append_child(root, c);
35
+ }
36
+ leptris_document_free(d);
37
+ }
22
38
  {
23
39
  double t0 = now_ns();
24
40
  LeptrisDocument d = leptris_document_create();
@@ -101,6 +117,26 @@ int main(void) {
101
117
  printf("attrparse leptris: %7.1f ns/doc (%zu bytes)\n",
102
118
  (t1 - t0) / N_PARSE, xml.size());
103
119
  }
120
+ {
121
+ /* Door A (leptris#1436, shipped v1.9.272): the opt-in fast
122
+ * path — duplicate detection and source positions off. The
123
+ * default row above carries full conformance work pugixml
124
+ * does none of; this row shows the speed callers can opt
125
+ * into when their pipeline guarantees uniqueness and skips
126
+ * position reporting. */
127
+ LeptrisParseFlags flags = static_cast<LeptrisParseFlags>(
128
+ LEPTRIS_PARSE_SKIP_DUP_DETECTION |
129
+ LEPTRIS_PARSE_SKIP_SOURCE_POSITIONS);
130
+ double t0 = now_ns();
131
+ for (int i = 0; i < N_PARSE; i++) {
132
+ LeptrisDocument d = leptris_parse_string_flags(
133
+ xml.data(), xml.size(), flags, NULL);
134
+ leptris_document_free(d);
135
+ }
136
+ double t1 = now_ns();
137
+ printf("attrparse leptris+flags: %7.1f ns/doc (Door A)\n",
138
+ (t1 - t0) / N_PARSE);
139
+ }
104
140
  {
105
141
  double t0 = now_ns();
106
142
  for (int i = 0; i < N_PARSE; i++) {
@@ -0,0 +1,59 @@
1
+ /* bench_append_split — Lane 18 (#1179 tail): where does the
2
+ * create+append ns/child go? Three rows over the same shape:
3
+ * create only, append only (children pre-created), and the
4
+ * create+append pair the gate bench measures. The split decides
5
+ * which half a lever belongs to. */
6
+ #include "leptris.h"
7
+ #include <chrono>
8
+ #include <cstdio>
9
+
10
+ static double now_ns() {
11
+ return (double)std::chrono::duration_cast<std::chrono::nanoseconds>(
12
+ std::chrono::steady_clock::now().time_since_epoch())
13
+ .count();
14
+ }
15
+
16
+ int main(void) {
17
+ const int N = 200000;
18
+
19
+ {
20
+ LeptrisDocument d = leptris_document_create();
21
+ LeptrisElement root = leptris_element_create(d, "root");
22
+ leptris_document_set_root(d, root);
23
+ double t0 = now_ns();
24
+ for (int i = 0; i < N; i++)
25
+ leptris_element_create(d, "c");
26
+ double t1 = now_ns();
27
+ printf("create only : %6.1f ns/elem\n", (t1 - t0) / N);
28
+ leptris_document_free(d);
29
+ }
30
+ {
31
+ LeptrisDocument d = leptris_document_create();
32
+ LeptrisElement root = leptris_element_create(d, "root");
33
+ leptris_document_set_root(d, root);
34
+ LeptrisElement* kids = new LeptrisElement[N];
35
+ for (int i = 0; i < N; i++)
36
+ kids[i] = leptris_element_create(d, "c");
37
+ double t0 = now_ns();
38
+ for (int i = 0; i < N; i++)
39
+ leptris_element_append_child(root, kids[i]);
40
+ double t1 = now_ns();
41
+ printf("append only : %6.1f ns/elem\n", (t1 - t0) / N);
42
+ delete[] kids;
43
+ leptris_document_free(d);
44
+ }
45
+ {
46
+ LeptrisDocument d = leptris_document_create();
47
+ LeptrisElement root = leptris_element_create(d, "root");
48
+ leptris_document_set_root(d, root);
49
+ double t0 = now_ns();
50
+ for (int i = 0; i < N; i++) {
51
+ LeptrisElement c = leptris_element_create(d, "c");
52
+ leptris_element_append_child(root, c);
53
+ }
54
+ double t1 = now_ns();
55
+ printf("create+app : %6.1f ns/elem\n", (t1 - t0) / N);
56
+ leptris_document_free(d);
57
+ }
58
+ return 0;
59
+ }
@@ -273,6 +273,17 @@ LeptrisElement leptris_element_create_child(LeptrisElement parent, const char* n
273
273
  return elem;
274
274
  }
275
275
 
276
+ /* #1528 hot bail: the child was carved from THIS document's
277
+ * current mut block — the live extent [end - BLOCK_COUNT, cursor)
278
+ * is this doc's own malloc, so nothing foreign can alias into it.
279
+ * Two loads off the doc struct the create call just touched. */
280
+ static int node_in_current_mut_block(struct leptris_document* doc,
281
+ LeptrisElement child) {
282
+ return doc && doc->mut_elem_cursor &&
283
+ child >= doc->mut_elem_end - MUT_ELEM_BLOCK_COUNT &&
284
+ child < doc->mut_elem_cursor;
285
+ }
286
+
276
287
  /* #1528: a node arriving from a DIFFERENT document is adopted by
277
288
  * deep copy (the set_root precedent, #371). Splicing the raw
278
289
  * pointer leaves the tree holding nodes — and text content — from
@@ -285,7 +296,17 @@ LeptrisElement leptris_element_create_child(LeptrisElement parent, const char* n
285
296
  static LeptrisNode* adopt_for_splice(LeptrisNode* node,
286
297
  struct leptris_document* target) {
287
298
  if (!node || !target) return node;
288
- /* Type-safe walk to the chain top: get_document reads
299
+ /* Hot bail (Lane 18 bar): an UNATTACHED namebp element is
300
+ * authoritatively stamped with its create-time document
301
+ * (#1189) — the freshly-created-then-appended shape. Two loads
302
+ * and a pointer compare, no name-slot resolution (get_document
303
+ * computes elem_name before its fast-out, which cost the
304
+ * append bench +40%). */
305
+ if (node->type == LEPTRIS_NODE_TYPE_ELEMENT &&
306
+ !leptris_elem_parent((LeptrisElement)node) &&
307
+ leptris_elem_has_namebp((LeptrisElement)node) &&
308
+ leptris_elem_namebp_doc((LeptrisElement)node) == target)
309
+ return node; /* Type-safe walk to the chain top: get_document reads
289
310
  * element-form fields and misreads non-element structs. */
290
311
  LeptrisNode* top = node;
291
312
  for (;;) {
@@ -311,9 +332,17 @@ LeptrisStatus leptris_element_append_child(LeptrisElement parent, LeptrisElement
311
332
  struct leptris_document* doc = leptris_element_get_document(parent);
312
333
 
313
334
  /* #1528: a foreign document's node is adopted by copy, never
314
- * spliced raw. */
315
- child = (LeptrisElement)adopt_for_splice((LeptrisNode*)child, doc);
316
- if (!child) return LEPTRIS_ERROR_MEMORY;
335
+ * spliced raw. The freshly-created-then-appended shape proves
336
+ * same-doc in two hot loads: a child inside the CURRENT mut
337
+ * block extent [end - BLOCK_COUNT, cursor) was carved from
338
+ * THIS document's block by the create call just executed — no
339
+ * foreign allocation can live inside it. Everything else takes
340
+ * the full check (Lane 18 append bar). */
341
+ if (!(node_in_current_mut_block(doc, child))) {
342
+ child = (LeptrisElement)adopt_for_splice((LeptrisNode*)child,
343
+ doc);
344
+ if (!child) return LEPTRIS_ERROR_MEMORY;
345
+ }
317
346
 
318
347
  /* Call internal void function, assume success */
319
348
  leptris_element_append_child_internal_doc(parent, (LeptrisNode*)child, doc);
@@ -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.304",
4
+ "version": "1.9.306",
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.304.0
4
+ version: 1.9.306.0
5
5
  platform: ppc64le-linux
6
6
  authors:
7
7
  - Ribose Inc.
@@ -425,6 +425,7 @@ files:
425
425
  - vendor-src/libleptris/benchmarks/matrix/bench2html.rb
426
426
  - vendor-src/libleptris/benchmarks/matrix/bench_matrix.cpp
427
427
  - vendor-src/libleptris/benchmarks/modification/bench_append_attr_pugixml.cpp
428
+ - vendor-src/libleptris/benchmarks/modification/bench_append_split.cpp
428
429
  - vendor-src/libleptris/benchmarks/modification/bench_leptris_modify.c
429
430
  - vendor-src/libleptris/benchmarks/modification/bench_node_creation.c
430
431
  - vendor-src/libleptris/benchmarks/parse/bench_parse_ab.cpp