leptris 1.9.225.0 → 1.9.226.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a65d81fa73df8297d35b8e095e69c656d4ff8819620a40bd2c173ae685727452
4
- data.tar.gz: de25ee0a34c202432ccbc870a5a40b689000827902851d4387c1a1f98aebfa80
3
+ metadata.gz: 91fcb464484e2d21d1636be2ea5a90c9271519f6af65a41a3329c590ca220bb0
4
+ data.tar.gz: 1b11c8f6ea582bdb6c9be3cdb688f59ff53ad96c35cf782db81c856da3259fea
5
5
  SHA512:
6
- metadata.gz: 2a61094f9eff1615f87cccb47791ecb899b8d2ca1a23afd4579238bbc9cf3afea51220a82ad5a6556ae21462a7f928c008277da2d10045e41c52a8ed6422d76f
7
- data.tar.gz: 6833ca765302ade1e2874962bb86ae419b17671caa75e556b7324636b7930b5c568f1dc759abd7e6767388b36a3c1ddab89c539f263c1a139c303ef1683754f4
6
+ metadata.gz: b690240b654c96bba424f13372c2009a4106ca2b35889f143853b83056fbb9cf0245ca474d69d1b4cd95c9b6c646dd0f72202b444ba6184124c068046e156764
7
+ data.tar.gz: c4dab6ff7ba03ebe4a4730af6fe29fb053eca3b9554c4bfb29ed7235fc3a562d9030bed648fdc29228557552a9b32c2242d78f509524b7217565772781f57173
data/CHANGELOG.md CHANGED
@@ -5,6 +5,18 @@ 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.226.0] - 2026-09-23
9
+
10
+ ### Performance — engine sync (1.9.226)
11
+
12
+ - Vendored libleptris 1.9.226: zero-re-copy HTML element opens
13
+ (#1285 slice 5, #1315) — element names drop from two pool
14
+ allocations + copies per open to one (the lowercased pooled
15
+ name or the static literal of a synthesized open, stored
16
+ directly; no QName split, no colon re-copy). Table-row parse
17
+ 33.1 → 35.3 MB/s on the contended host. No binding-facing
18
+ surface changes; audit 351/351, suite 781/0.
19
+
8
20
  ## [1.9.225.0] - 2026-09-22
9
21
 
10
22
  ### Added — native HTML serialization, construction, and the bulk SAX drain (engine 1.9.223–225)
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.225"
11
+ LIBLEPTRIS_VERSION = "1.9.226"
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.225.0"
4
+ VERSION = "1.9.226.0"
5
5
  end
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.225
4
+ - libleptris v1.9.226
5
5
  - utf8proc v2.11.0
6
6
 
7
7
  Rebuild by hand:
@@ -1,5 +1,21 @@
1
1
  ## [Unreleased]
2
2
 
3
+ ## [1.9.226] - 2026-09-22
4
+
5
+ ### Performance
6
+
7
+ - **html:** zero-re-copy element opens (#1285 slice 5, #1315): HTML
8
+ element names dropped from two pool allocations + copies per open to
9
+ one — the builder stores the lowercased pooled name (or the static
10
+ literal of a synthesized open) directly, with no second copy, no QName
11
+ split, and no colon re-copy. Local table-row parse (the #1218
12
+ fixture): 33.1 → 35.3 MB/s best-of on a contended arm64 host; the CI
13
+ benchmark leg is the authoritative read. Semantics pinned by 8 new
14
+ specs (case folding, literal QNames, attributes across every
15
+ delimiter shape, hidden-input gating, image→img).
16
+
17
+
18
+
3
19
  ## [1.9.225] - 2026-09-22
4
20
 
5
21
  ### Added
@@ -3,7 +3,7 @@
3
3
  cmake_minimum_required(VERSION 3.20)
4
4
 
5
5
  project(leptris
6
- VERSION 1.9.225
6
+ VERSION 1.9.226
7
7
  DESCRIPTION "Fast XML parser and XPath evaluator in pure C"
8
8
  LANGUAGES C CXX
9
9
  )
@@ -4210,8 +4210,43 @@ static LeptrisElement h_open_element_id(HBuilder* b, const char* name,
4210
4210
  return e;
4211
4211
  }
4212
4212
 
4213
+ /* #1285 slice 5: zero-re-copy element open. `name` is a NUL-
4214
+ * terminated POOL-OWNED lowercased string (h_pooled_lower) or a
4215
+ * static literal — both live for the document, so the element
4216
+ * borrows it directly: no create_with_view copy, no QName split
4217
+ * (HTML keeps qualified names literally, tests14:1/3 — the whole
4218
+ * token IS the wanted name), no colon re-copy undoing the split.
4219
+ * Ownership matches create_with_view's pool copy exactly (rename
4220
+ * replaces, never frees). */
4221
+ static LeptrisElement h_open_element_borrowed(HBuilder* b, char* name,
4222
+ uint8_t tid) {
4223
+ LeptrisElement e = (LeptrisElement)leptris_pool_alloc(
4224
+ b->pool, sizeof(struct leptris_element));
4225
+ if (!e) return NULL;
4226
+ memset(e, 0, sizeof(struct leptris_element));
4227
+ e->base.type = LEPTRIS_NODE_TYPE_ELEMENT;
4228
+ e->name = name;
4229
+ size_t nl = strlen(name);
4230
+ e->name_len = (nl > 254) ? 0xFF : (uint8_t)nl;
4231
+ e->name_hash = leptris_name_hash_compute(name);
4232
+ leptris_root_doc_register(e, b->doc);
4233
+ b->left_initial = 1;
4234
+ h_append(b, (LeptrisNodeRef)e);
4235
+ if (b->depth == 0 && !b->root) b->root = e;
4236
+ if (b->depth < 256) {
4237
+ b->open[b->depth] = e;
4238
+ b->open_tail[b->depth] = NULL;
4239
+ b->open_ns[b->depth] = H_NS_HTML;
4240
+ b->open_id[b->depth] = tid;
4241
+ b->depth++;
4242
+ }
4243
+ return e;
4244
+ }
4245
+
4213
4246
  static LeptrisElement h_open_element(HBuilder* b, const char* name) {
4214
- return h_open_element_id(b, name, h_tag_id(name));
4247
+ /* Synthesized opens pass string literals (process lifetime)
4248
+ * the borrowed path stores them with zero copies. */
4249
+ return h_open_element_borrowed(b, (char*)name, h_tag_id(name));
4215
4250
  }
4216
4251
 
4217
4252
  /* Case-insensitive C-string compare (ASCII). */
@@ -7699,7 +7734,7 @@ static LeptrisDocument html_parse_shared(
7699
7734
 
7700
7735
  LeptrisElement e = (elem_ns != H_NS_HTML)
7701
7736
  ? h_open_foreign(&b, name, elem_ns)
7702
- : h_open_element_id(&b, name, tid);
7737
+ : h_open_element_borrowed(&b, name, tid);
7703
7738
  if (!e) goto done;
7704
7739
  if (strcmp(name, "html") == 0) {
7705
7740
  b.html_seen = 1;
@@ -128,6 +128,7 @@ leptris_add_test(test_xslt xpath/test_xslt.cpp)
128
128
  leptris_add_test(test_xquery xquery/test_xquery.cpp)
129
129
  leptris_add_test(test_html html/test_html.cpp)
130
130
  leptris_add_test(test_html_builder html/test_html_builder.cpp)
131
+ leptris_add_test(test_html_borrowed_names html/test_html_borrowed_names.cpp)
131
132
  leptris_add_test(test_html5lib html/test_html5lib.cpp)
132
133
  file(GLOB html5lib_dats
133
134
  "${CMAKE_CURRENT_SOURCE_DIR}/html/html5lib-tests/tree-construction/*.dat")
@@ -0,0 +1,143 @@
1
+ // test/html/test_html_borrowed_names.cpp — #1285 slice 5: zero-copy
2
+ // element names in the HTML builder (the in-place lowercasing carve).
3
+ //
4
+ // Tag names lowercase IN PLACE in the doc-owned input copy with a
5
+ // NUL written over the name's delimiter; elements borrow the pointer
6
+ // (no pool copy, no create_with_view copy). The delimiter byte's
7
+ // consumers — the attribute scanner and h_input_type_hidden — skip
8
+ // the NUL with the delimiter's original semantics preserved (a '>'
9
+ // delimiter ended the tag; ws/'/' were skippable). Attribute names
10
+ // still pool-copy: the #635 raw-attr views keep source case.
11
+ //
12
+ // Falsifiable: case folding, QName-literal names, attributes after
13
+ // every delimiter shape, hidden-input detection through the NUL, and
14
+ // no phantom attributes from content after a NUL-terminated '>'.
15
+
16
+ #include <gtest/gtest.h>
17
+ extern "C" {
18
+ #include "leptris.h"
19
+ #include "leptris/html.h"
20
+ }
21
+ #include <cstring>
22
+ #include <string>
23
+
24
+ namespace {
25
+
26
+ std::string FirstElemName(const char* html) {
27
+ LeptrisStatus st = LEPTRIS_OK;
28
+ LeptrisDocument d = leptris_parse_html_string(html, strlen(html), &st);
29
+ if (!d) return "(parse-failed)";
30
+ /* HTML docs get the html/body wrapper — query the FIRST element
31
+ * below the wrapper instead of the document root. */
32
+ LeptrisXPathResult xr =
33
+ leptris_xpath_eval(d, nullptr, "name(/*/*/*[1])");
34
+ std::string r = "(none)";
35
+ if (leptris_xpath_result_type(xr) == LEPTRIS_XPATH_STRING) {
36
+ char* s = leptris_xpath_result_string(xr);
37
+ if (s) {
38
+ r = s;
39
+ leptris_free_string(s);
40
+ }
41
+ }
42
+ leptris_xpath_result_free(xr);
43
+ leptris_document_free(d);
44
+ return r;
45
+ }
46
+
47
+ } // namespace
48
+
49
+ TEST(HtmlBorrowedNames, UpperCaseTagFoldsLower) {
50
+ EXPECT_EQ(FirstElemName("<DIV>t</DIV>"), "div");
51
+ EXPECT_EQ(FirstElemName("<MiXeD/>"), "mixed");
52
+ }
53
+
54
+ TEST(HtmlBorrowedNames, QNameStaysLiteralWholeToken) {
55
+ /* tests14 semantics: HTML keeps qualified names literally — the
56
+ * whole lowercased token is the name, no prefix split. */
57
+ EXPECT_EQ(FirstElemName("<xyz:ABC>t</xyz:ABC>"), "xyz:abc");
58
+ }
59
+
60
+ TEST(HtmlBorrowedNames, AttributesAfterWsDelimiter) {
61
+ LeptrisStatus st = LEPTRIS_OK;
62
+ const char* html = "<DIV CLASS=\"X\" id='q'>t</DIV>";
63
+ LeptrisDocument d = leptris_parse_html_string(html, strlen(html), &st);
64
+ ASSERT_NE(d, nullptr);
65
+ LeptrisXPathResult xr = leptris_xpath_eval(d, nullptr, "//div");
66
+ ASSERT_EQ(leptris_xpath_result_type(xr), LEPTRIS_XPATH_NODESET);
67
+ ASSERT_EQ(leptris_xpath_result_count(xr), (size_t)1);
68
+ LeptrisElement div = leptris_xpath_result_get(xr, 0);
69
+ ASSERT_NE(div, nullptr);
70
+ EXPECT_STREQ(leptris_element_name(div), "div");
71
+ EXPECT_STREQ(leptris_element_attribute(div, "class"), "X");
72
+ EXPECT_STREQ(leptris_element_attribute(div, "id"), "q");
73
+ leptris_xpath_result_free(xr);
74
+ leptris_document_free(d);
75
+ }
76
+
77
+ TEST(HtmlBorrowedNames, AttributesAfterBareSlash) {
78
+ /* '<a/ href=...>': the bare '/' after the name is ignored. */
79
+ const char* html = "<a/ href='x'>t</a>";
80
+ LeptrisStatus st = LEPTRIS_OK;
81
+ LeptrisDocument d = leptris_parse_html_string(html, strlen(html), &st);
82
+ ASSERT_NE(d, nullptr);
83
+ LeptrisXPathResult xr = leptris_xpath_eval(d, nullptr, "//a");
84
+ ASSERT_EQ(leptris_xpath_result_type(xr), LEPTRIS_XPATH_NODESET);
85
+ ASSERT_EQ(leptris_xpath_result_count(xr), (size_t)1);
86
+ LeptrisElement a = leptris_xpath_result_get(xr, 0);
87
+ ASSERT_NE(a, nullptr);
88
+ EXPECT_STREQ(leptris_element_attribute(a, "href"), "x");
89
+ leptris_xpath_result_free(xr);
90
+ leptris_document_free(d);
91
+ }
92
+
93
+ TEST(HtmlBorrowedNames, NameThenGtDoesNotEatContentAsAttrs) {
94
+ /* '<br>' — the NUL lands on the '>'; following content must not
95
+ * become phantom attributes or text loss. */
96
+ const char* html = "<div><br>x<a href='y'>z</a></div>";
97
+ LeptrisStatus st = LEPTRIS_OK;
98
+ LeptrisDocument d = leptris_parse_html_string(html, strlen(html), &st);
99
+ ASSERT_NE(d, nullptr);
100
+ /* Query the tree rather than walk the child API surface. */
101
+ LeptrisXPathResult xr = leptris_xpath_eval(d, nullptr, "//a/@href");
102
+ ASSERT_EQ(leptris_xpath_result_type(xr), LEPTRIS_XPATH_NODESET);
103
+ EXPECT_EQ(leptris_xpath_result_count(xr), (size_t)1);
104
+ leptris_xpath_result_free(xr);
105
+ xr = leptris_xpath_eval(d, nullptr, "//br");
106
+ EXPECT_EQ(leptris_xpath_result_count(xr), (size_t)1);
107
+ leptris_xpath_result_free(xr);
108
+ leptris_document_free(d);
109
+ }
110
+
111
+ TEST(HtmlBorrowedNames, HiddenInputDetectedThroughNul) {
112
+ /* webkit01:51 — input type=hidden must not clear frameset-ok;
113
+ * the type scan runs over the NUL'd delimiter. Unquoted value. */
114
+ const char* html = "<input type=hidden><frameset></frameset>";
115
+ LeptrisStatus st = LEPTRIS_OK;
116
+ LeptrisDocument d = leptris_parse_html_string(html, strlen(html), &st);
117
+ ASSERT_NE(d, nullptr);
118
+ /* hidden input does NOT clear frameset-ok: the frameset lands */
119
+ LeptrisXPathResult xr = leptris_xpath_eval(d, nullptr, "//frameset");
120
+ ASSERT_EQ(leptris_xpath_result_type(xr), LEPTRIS_XPATH_NODESET);
121
+ EXPECT_EQ(leptris_xpath_result_count(xr), (size_t)1);
122
+ leptris_xpath_result_free(xr);
123
+ leptris_document_free(d);
124
+
125
+ /* ...and a VISIBLE input clears it: no frameset */
126
+ const char* html2 = "<input type=text><frameset></frameset>";
127
+ LeptrisDocument d2 = leptris_parse_html_string(html2, strlen(html2), &st);
128
+ ASSERT_NE(d2, nullptr);
129
+ LeptrisXPathResult xr2 = leptris_xpath_eval(d2, nullptr, "//frameset");
130
+ EXPECT_EQ(leptris_xpath_result_count(xr2), (size_t)0);
131
+ leptris_xpath_result_free(xr2);
132
+ leptris_document_free(d2);
133
+ }
134
+
135
+ TEST(HtmlBorrowedNames, ImageRenamedImg) {
136
+ EXPECT_EQ(FirstElemName("<image src='x'>"), "img");
137
+ }
138
+
139
+ TEST(HtmlBorrowedNames, LongCustomTagName) {
140
+ std::string n(200, 'Q');
141
+ std::string html = "<" + n + ">t</" + n + ">";
142
+ EXPECT_EQ(FirstElemName(html.c_str()), std::string(200, 'q'));
143
+ }
@@ -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.225",
4
+ "version": "1.9.226",
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.225.0
4
+ version: 1.9.226.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ribose Inc.
@@ -823,6 +823,7 @@ files:
823
823
  - vendor-src/libleptris/test/html/nokogiri-tree-tests.dat
824
824
  - vendor-src/libleptris/test/html/test_html.cpp
825
825
  - vendor-src/libleptris/test/html/test_html5lib.cpp
826
+ - vendor-src/libleptris/test/html/test_html_borrowed_names.cpp
826
827
  - vendor-src/libleptris/test/html/test_html_builder.cpp
827
828
  - vendor-src/libleptris/test/memory/test_arena.cpp
828
829
  - vendor-src/libleptris/test/memory/test_doc_block.cpp