leptris 1.9.225.0-x86_64-linux → 1.9.226.0-x86_64-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 +4 -4
- data/CHANGELOG.md +12 -0
- data/Rakefile +1 -1
- data/lib/leptris/version.rb +1 -1
- data/lib/libleptris.so +0 -0
- data/vendor-src/README.md +1 -1
- data/vendor-src/libleptris/CHANGELOG.md +16 -0
- data/vendor-src/libleptris/CMakeLists.txt +1 -1
- data/vendor-src/libleptris/src/leptris/html/html_parse.c +37 -2
- data/vendor-src/libleptris/test/CMakeLists.txt +1 -0
- data/vendor-src/libleptris/test/html/test_html_borrowed_names.cpp +143 -0
- data/vendor-src/libleptris/vcpkg.json +1 -1
- metadata +2 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: b58468e929b871a6903114e7caba6a95cdf7132525b9d09ff9d1f8b81073ce5a
|
|
4
|
+
data.tar.gz: 498713bb12c67829f1b3c390a9e5d16e83e5b44f94ec8595c4283cbf948f06f4
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 5caf23541329a1de994066a6a679a07010266d91590cb97f4388292d8584c7fe485d654f84ff73203300dbd5df672783684730099e7e1c339755aa03810e7c37
|
|
7
|
+
data.tar.gz: 47a8dbed7879615c5b7c24e00881c4cf5513df3e5f5ebe91467bf7be8859f4d7763f1b2963cca7cb67f8b30a61d598a21702eb99bdfbab58effbd69ed3ce4d8f
|
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.
|
|
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
|
data/lib/leptris/version.rb
CHANGED
data/lib/libleptris.so
CHANGED
|
Binary file
|
data/vendor-src/README.md
CHANGED
|
@@ -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
|
|
@@ -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
|
-
|
|
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
|
-
:
|
|
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.
|
|
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.
|
|
4
|
+
version: 1.9.226.0
|
|
5
5
|
platform: x86_64-linux
|
|
6
6
|
authors:
|
|
7
7
|
- Ribose Inc.
|
|
@@ -805,6 +805,7 @@ files:
|
|
|
805
805
|
- vendor-src/libleptris/test/html/nokogiri-tree-tests.dat
|
|
806
806
|
- vendor-src/libleptris/test/html/test_html.cpp
|
|
807
807
|
- vendor-src/libleptris/test/html/test_html5lib.cpp
|
|
808
|
+
- vendor-src/libleptris/test/html/test_html_borrowed_names.cpp
|
|
808
809
|
- vendor-src/libleptris/test/html/test_html_builder.cpp
|
|
809
810
|
- vendor-src/libleptris/test/memory/test_arena.cpp
|
|
810
811
|
- vendor-src/libleptris/test/memory/test_doc_block.cpp
|