leptris 1.9.222.3 → 1.9.225.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 +4 -4
- data/CHANGELOG.md +27 -0
- data/Rakefile +1 -1
- data/lib/leptris/vendor/aarch64-linux/libleptris.so +0 -0
- data/lib/leptris/vendor/aarch64-linux-musl/libleptris.so +0 -0
- data/lib/leptris/vendor/arm-linux/libleptris.so +0 -0
- data/lib/leptris/vendor/arm-linux-musl/libleptris.so +0 -0
- data/lib/leptris/vendor/arm64-darwin/libleptris.dylib +0 -0
- data/lib/leptris/vendor/ppc64le-linux/libleptris.so +0 -0
- data/lib/leptris/vendor/s390x-linux-musl/libleptris.so +0 -0
- data/lib/leptris/vendor/x86_64-darwin/libleptris.dylib +0 -0
- data/lib/leptris/vendor/x86_64-linux/libleptris.so +0 -0
- data/lib/leptris/vendor/x86_64-linux-musl/libleptris.so +0 -0
- data/lib/leptris/version.rb +1 -1
- data/lib/leptris/xml/document.rb +12 -4
- data/lib/leptris/xml/element.rb +1 -1
- data/lib/leptris/xml/ffi.rb +27 -1
- data/lib/leptris/xml/serialization.rb +27 -0
- data/lib/leptris/xml.rb +0 -1
- data/vendor-src/README.md +1 -1
- data/vendor-src/libleptris/CHANGELOG.md +73 -0
- data/vendor-src/libleptris/CMakeLists.txt +1 -1
- data/vendor-src/libleptris/src/CMakeLists.txt +2 -0
- data/vendor-src/libleptris/src/include/leptris/sax/sax.h +87 -0
- data/vendor-src/libleptris/src/include/leptris/types.h +7 -1
- data/vendor-src/libleptris/src/include/leptris.h +49 -2
- data/vendor-src/libleptris/src/leptris/dom/digest.c +11 -0
- data/vendor-src/libleptris/src/leptris/dom/elem_pos.c +88 -0
- data/vendor-src/libleptris/src/leptris/dom/element.c +2 -1
- data/vendor-src/libleptris/src/leptris/dom/element.h +89 -56
- data/vendor-src/libleptris/src/leptris/dom/element_query.c +6 -5
- data/vendor-src/libleptris/src/leptris/dom/node_public.c +10 -23
- data/vendor-src/libleptris/src/leptris/dom/text.c +14 -79
- data/vendor-src/libleptris/src/leptris/dom/text.h +21 -33
- data/vendor-src/libleptris/src/leptris/flat/direct_parse.c +150 -58
- data/vendor-src/libleptris/src/leptris/flat/il_scan.h +69 -0
- data/vendor-src/libleptris/src/leptris/html/html_parse.c +3 -0
- data/vendor-src/libleptris/src/leptris/leptris.c +10 -0
- data/vendor-src/libleptris/src/leptris/leptris_internal.h +9 -0
- data/vendor-src/libleptris/src/leptris/sax/records.c +135 -0
- data/vendor-src/libleptris/src/leptris/serialize/serialize.c +61 -47
- data/vendor-src/libleptris/src/leptris/xslt/xslt_exec.c +3 -8
- data/vendor-src/libleptris/test/CMakeLists.txt +2 -0
- data/vendor-src/libleptris/test/dom/test_digest.cpp +49 -0
- data/vendor-src/libleptris/test/dom/test_text_borrowed.cpp +51 -40
- data/vendor-src/libleptris/test/html/test_html.cpp +23 -9
- data/vendor-src/libleptris/test/html/test_html_builder.cpp +190 -0
- data/vendor-src/libleptris/test/perf/test_perf.cpp +28 -17
- data/vendor-src/libleptris/test/sax/test_records.cpp +196 -0
- data/vendor-src/libleptris/vcpkg.json +1 -1
- metadata +6 -2
- data/lib/leptris/xml/html_serialize.rb +0 -97
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: a65d81fa73df8297d35b8e095e69c656d4ff8819620a40bd2c173ae685727452
|
|
4
|
+
data.tar.gz: de25ee0a34c202432ccbc870a5a40b689000827902851d4387c1a1f98aebfa80
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 2a61094f9eff1615f87cccb47791ecb899b8d2ca1a23afd4579238bbc9cf3afea51220a82ad5a6556ae21462a7f928c008277da2d10045e41c52a8ed6422d76f
|
|
7
|
+
data.tar.gz: 6833ca765302ade1e2874962bb86ae419b17671caa75e556b7324636b7930b5c568f1dc759abd7e6767388b36a3c1ddab89c539f263c1a139c303ef1683754f4
|
data/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,33 @@ 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.225.0] - 2026-09-22
|
|
9
|
+
|
|
10
|
+
### Added — native HTML serialization, construction, and the bulk SAX drain (engine 1.9.223–225)
|
|
11
|
+
|
|
12
|
+
- **`to_html` re-pointed at the engine's native HTML mode**
|
|
13
|
+
(1.9.225, upstream #1309/#1311 — our filing answered in one
|
|
14
|
+
release): `Document#to_html` → `leptris_document_serialize_html`,
|
|
15
|
+
`Element#to_html` → the ext serializer with `html_method: 1`.
|
|
16
|
+
Byte-parity with the interim Ruby walk is pinned by the #309
|
|
17
|
+
spec battery; the native path also fixes the walk's PI branch
|
|
18
|
+
(the Ruby walk crashed on processing instructions — target/data
|
|
19
|
+
readers don't exist on the wrapper). The Ruby walk is removed.
|
|
20
|
+
- **`Document#save(path, mode: :html)`** → `leptris_document_save_html`.
|
|
21
|
+
- **`leptris_document_create_html` attached** (native HTML-mode
|
|
22
|
+
documents — serialize AS HTML by default, Nokogiri behavior);
|
|
23
|
+
the facade's `create` stays on the XML base + doctype preset so
|
|
24
|
+
`to_xml` output on facade docs is unchanged. `html_method`
|
|
25
|
+
(tri-state) rides `SerializeExtStruct` — `to_xml` on an HTML
|
|
26
|
+
document can force XML with `-1` when the face needs it.
|
|
27
|
+
- **Bulk SAX record drain attached** (1.9.223, upstream #1298,
|
|
28
|
+
Canon-reported): `leptris_sax_records_*` — one call yields the
|
|
29
|
+
whole document as a flat record table (tree edges, source
|
|
30
|
+
offsets, flat attribute table with §3.3.3 normalization flags).
|
|
31
|
+
Attach-only until the SAX face wants the bulk lane.
|
|
32
|
+
- 1.9.224 rides along (LeakSanitizer fix on the direct-parse
|
|
33
|
+
failure path). Audit 351/351, suite 780/0.
|
|
34
|
+
|
|
8
35
|
## [1.9.222.0] - 2026-09-22
|
|
9
36
|
|
|
10
37
|
### Fixed — engine sync (1.9.222): the interleaved lane's namespace regression
|
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.225"
|
|
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
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
data/lib/leptris/version.rb
CHANGED
data/lib/leptris/xml/document.rb
CHANGED
|
@@ -481,16 +481,24 @@ class Leptris::XML::Document
|
|
|
481
481
|
# the perf headroom this face gates on.
|
|
482
482
|
def to_html
|
|
483
483
|
raise Leptris::XML::UseAfterFreeError if @freed.state == :freed
|
|
484
|
-
Leptris::XML::
|
|
484
|
+
Leptris::XML::Serialization.document_html(c_ptr)
|
|
485
485
|
end
|
|
486
486
|
|
|
487
|
-
def save(path, **opts)
|
|
487
|
+
def save(path, mode: :xml, **opts)
|
|
488
488
|
opts_struct, _encoding_anchor = Leptris::XML::Serialization.build_options(
|
|
489
489
|
indent: opts.fetch(:indent, 0),
|
|
490
490
|
no_decl: opts.fetch(:no_decl, false),
|
|
491
491
|
encoding: opts[:encoding])
|
|
492
|
-
status =
|
|
493
|
-
|
|
492
|
+
status =
|
|
493
|
+
if mode == :html
|
|
494
|
+
Leptris::XML::FFI.leptris_document_save_html(
|
|
495
|
+
c_ptr, path, opts_struct.pointer)
|
|
496
|
+
elsif mode != :xml
|
|
497
|
+
raise ArgumentError, "mode must be :xml or :html, got #{mode.inspect}"
|
|
498
|
+
else
|
|
499
|
+
Leptris::XML::FFI.leptris_document_save_file(
|
|
500
|
+
c_ptr, path, opts_struct.pointer)
|
|
501
|
+
end
|
|
494
502
|
Leptris::XML::FFI.check_status(status)
|
|
495
503
|
self
|
|
496
504
|
end
|
data/lib/leptris/xml/element.rb
CHANGED
|
@@ -784,7 +784,7 @@ class Leptris::XML::Element < Leptris::XML::Node
|
|
|
784
784
|
# Document#to_html for the semantics).
|
|
785
785
|
def to_html
|
|
786
786
|
ensure_alive!
|
|
787
|
-
Leptris::XML::
|
|
787
|
+
Leptris::XML::Serialization.element_html(c_ptr)
|
|
788
788
|
end
|
|
789
789
|
|
|
790
790
|
def canonicalize(version = Leptris::XML::FFI::C14N_1_0,
|
data/lib/leptris/xml/ffi.rb
CHANGED
|
@@ -169,7 +169,11 @@ module Leptris
|
|
|
169
169
|
class SerializeExtStruct < ::FFI::Struct
|
|
170
170
|
layout :indent_text, :int,
|
|
171
171
|
:indent_unit, :pointer,
|
|
172
|
-
:expand_empty, :int
|
|
172
|
+
:expand_empty, :int,
|
|
173
|
+
# libleptris 1.9.225 (#1311): tri-state — 0 = the
|
|
174
|
+
# document's default method, 1 = force HTML, -1 =
|
|
175
|
+
# force XML (to_xml on an HTML document).
|
|
176
|
+
:html_method, :int
|
|
173
177
|
end
|
|
174
178
|
|
|
175
179
|
# Mirrors LeptrisParseOptions (libleptris >= 1.9.0 carries the
|
|
@@ -992,6 +996,28 @@ attach_function :leptris_parse_string,
|
|
|
992
996
|
[:leptris_element, :pointer, :size_t, :pointer, :pointer], :size_t
|
|
993
997
|
attach_function :leptris_document_save_file,
|
|
994
998
|
[:leptris_document, :string, :pointer], :leptris_status
|
|
999
|
+
# HTML construction + serialization (libleptris 1.9.225,
|
|
1000
|
+
# upstream #1309/#1311 — the engine side of #309): native HTML
|
|
1001
|
+
# documents carry the html serialization mode (voids unclosed,
|
|
1002
|
+
# raw-text script/style, explicit closes, no declaration).
|
|
1003
|
+
attach_function :leptris_document_create_html, [], :leptris_document
|
|
1004
|
+
attach_function :leptris_document_serialize_html,
|
|
1005
|
+
[:leptris_document, :pointer], :pointer
|
|
1006
|
+
attach_function :leptris_document_save_html,
|
|
1007
|
+
[:leptris_document, :string, :pointer], :leptris_status
|
|
1008
|
+
# Bulk SAX record drain (libleptris 1.9.223, upstream #1298):
|
|
1009
|
+
# ONE call parses the document into a flat record table —
|
|
1010
|
+
# tree edges, source offsets, and a flat attribute table, all
|
|
1011
|
+
# offset-views into a parser-owned buffer. Attach-only until
|
|
1012
|
+
# the SAX face wants the bulk lane.
|
|
1013
|
+
attach_function :leptris_sax_records_parse,
|
|
1014
|
+
[:string, :size_t, :uint, :pointer], :leptris_status
|
|
1015
|
+
attach_function :leptris_sax_records_count, [:pointer], :size_t
|
|
1016
|
+
attach_function :leptris_sax_records_data, [:pointer], :pointer
|
|
1017
|
+
attach_function :leptris_sax_records_attrs,
|
|
1018
|
+
[:pointer, :pointer], :pointer
|
|
1019
|
+
attach_function :leptris_sax_records_buffer, [:pointer], :string
|
|
1020
|
+
attach_function :leptris_sax_records_free, [:pointer], :void
|
|
995
1021
|
# XSLT 1.0 engine (libleptris 1.9.1): compile once, apply many.
|
|
996
1022
|
# XQuery 1.0 core (libleptris 1.9.64-1.9.66): orchestration
|
|
997
1023
|
# over the XPath engine — prolog (declare variable/namespace/
|
|
@@ -58,6 +58,33 @@ module Leptris::XML::Serialization
|
|
|
58
58
|
# Default-options element serialization for inner_html's per-child
|
|
59
59
|
# loop: no kwargs, no options rebuild, no method allocation —
|
|
60
60
|
# one FFI fast-path dispatch per child.
|
|
61
|
+
# Document HTML serialization (libleptris 1.9.225, #1311): the
|
|
62
|
+
# native HTML mode — voids unclosed, raw-text script/style,
|
|
63
|
+
# explicit closes, no declaration, DOCTYPE first.
|
|
64
|
+
def self.document_html(c_ptr)
|
|
65
|
+
Leptris::XML::FFI.read_owned_string(
|
|
66
|
+
Leptris::XML::FFI.leptris_document_serialize_html(c_ptr, nil))
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
# Element-level HTML mode through the ext serializer (the
|
|
70
|
+
# html_method tri-state forces HTML regardless of the document
|
|
71
|
+
# flavor).
|
|
72
|
+
HTML_METHOD_EXT = begin
|
|
73
|
+
ext = Leptris::XML::FFI::SerializeExtStruct.new
|
|
74
|
+
ext[:indent_text] = 0
|
|
75
|
+
ext[:indent_unit] = nil
|
|
76
|
+
ext[:expand_empty] = 0
|
|
77
|
+
ext[:html_method] = 1
|
|
78
|
+
ext
|
|
79
|
+
end
|
|
80
|
+
private_constant :HTML_METHOD_EXT
|
|
81
|
+
|
|
82
|
+
def self.element_html(c_ptr)
|
|
83
|
+
Leptris::XML::FFI.read_owned_string(
|
|
84
|
+
Leptris::XML::FFI.leptris_element_serialize_ext(
|
|
85
|
+
c_ptr, nil, HTML_METHOD_EXT.pointer))
|
|
86
|
+
end
|
|
87
|
+
|
|
61
88
|
def self.element_xml_default(c_ptr)
|
|
62
89
|
return Leptris::XML::Native.fast_element_xml(c_ptr.address, 0, true,
|
|
63
90
|
nil) if
|
data/lib/leptris/xml.rb
CHANGED
|
@@ -21,7 +21,6 @@ module Leptris
|
|
|
21
21
|
autoload :CssToXPath, "leptris/xml/css_to_xpath"
|
|
22
22
|
autoload :CStringArray, "leptris/xml/c_string_array"
|
|
23
23
|
autoload :Serialization, "leptris/xml/serialization"
|
|
24
|
-
autoload :HTMLSerialize, "leptris/xml/html_serialize"
|
|
25
24
|
autoload :SAX, "leptris/xml/sax"
|
|
26
25
|
autoload :XPath, "leptris/xml/xpath"
|
|
27
26
|
autoload :EvaluationContext, "leptris/xml/evaluation_context"
|
data/vendor-src/README.md
CHANGED
|
@@ -1,5 +1,78 @@
|
|
|
1
1
|
## [Unreleased]
|
|
2
2
|
|
|
3
|
+
## [1.9.225] - 2026-09-22
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
|
|
7
|
+
- **html: public HTML construction + serialization surface (#1309,
|
|
8
|
+
#1311).** HTML documents now serialize AS HTML by default
|
|
9
|
+
(libxml2/Nokogiri behavior): void elements unclosed (`<br>`,
|
|
10
|
+
`<meta charset="utf-8">`), raw-text script/style verbatim,
|
|
11
|
+
childless non-voids explicitly closed, no XML declaration. New:
|
|
12
|
+
`leptris_document_create_html()`, `leptris_document_serialize_html()`,
|
|
13
|
+
`leptris_document_save_html()`, and `LeptrisSerializeExtOptions.html_method`
|
|
14
|
+
(tri-state: 0 = document default, 1 = force HTML, -1 = force XML —
|
|
15
|
+
to_xml on an HTML doc). Element-level `html_method` maps through the
|
|
16
|
+
ext serializer too. HTML-parsed documents (whatwg + html4 modes)
|
|
17
|
+
carry the mode automatically; XSLT `method="html"` output unchanged
|
|
18
|
+
(#1312).
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
## [1.9.224] - 2026-09-22
|
|
22
|
+
|
|
23
|
+
### Fixed
|
|
24
|
+
|
|
25
|
+
- **mem:** the element source-offset side table (new in this
|
|
26
|
+
release) is now also freed on the direct-parse failure path,
|
|
27
|
+
which bypasses leptris_document_free — caught by CI LeakSanitizer.
|
|
28
|
+
- **test:** the runner-contention perf gate
|
|
29
|
+
(SmallDocumentParseIsFast) retries across settling windows instead
|
|
30
|
+
of failing on a single contended measurement (three CI flakes in
|
|
31
|
+
one day, budget unchanged).
|
|
32
|
+
|
|
33
|
+
### Performance
|
|
34
|
+
|
|
35
|
+
- **dom:** elements shrink **72 -> 64 bytes** (ILP32 56 -> 48;
|
|
36
|
+
#1285 slice 3a): the namespace side-cache pointer became a
|
|
37
|
+
self-relative int32 offset (the attrs' round-19 pattern), and the
|
|
38
|
+
#1124 source offsets moved to a document-owned side table — cold
|
|
39
|
+
diagnostics-only data no longer costs hot-path cache lines.
|
|
40
|
+
Position accessors are unchanged (#1124 specs green). Part of the
|
|
41
|
+
pugi-style node-storage lane (#1222/#1238) (#1308).
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
## [1.9.223] - 2026-09-22
|
|
46
|
+
|
|
47
|
+
### Added
|
|
48
|
+
|
|
49
|
+
- **sax:** `leptris_sax_records_parse` — a bulk record drain for hot SAX
|
|
50
|
+
loops (#1298, reported by Canon): ONE call returns the whole document
|
|
51
|
+
as a flat record table (element tree edges, start/end offsets, flat
|
|
52
|
+
attribute table with XML 1.0 §3.3.3 normalization flags), all
|
|
53
|
+
NUL-terminated views into a parser-owned buffer. Unsupported
|
|
54
|
+
constructs (comments/PIs/CDATA/DOCTYPE/entities/xmlns) return
|
|
55
|
+
`LEPTRIS_ERROR_NOT_SUPPORTED` — fall back to the pull API. New
|
|
56
|
+
additive status code (#1305).
|
|
57
|
+
|
|
58
|
+
### Fixed
|
|
59
|
+
|
|
60
|
+
- **dom:** `LEPTRIS_DIGEST_ATTR_ORDER` is now actually honored — the
|
|
61
|
+
1.9.205 flag shipped only as a C enum with no env reader and no
|
|
62
|
+
binding carrier, so `strings` never found it in shipped bundles
|
|
63
|
+
(#1297, reported by Canon). `leptris_node_digest` reads the env
|
|
64
|
+
per call and ORs it into the passed flags (#1304).
|
|
65
|
+
|
|
66
|
+
### Performance
|
|
67
|
+
|
|
68
|
+
- **dom:** text nodes shrink **64 → 48 bytes** (#1285 slice 4): the
|
|
69
|
+
`pool`/`borrowed` fields are gone — every parse lane terminates
|
|
70
|
+
text in place and entities decode eagerly at parse time, so
|
|
71
|
+
`leptris_text_get_content` is a plain field read. Content is
|
|
72
|
+
always NUL-terminated; `content_len` is authoritative (#1303).
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
|
|
3
76
|
## [1.9.222] - 2026-09-22
|
|
4
77
|
|
|
5
78
|
### Fixed
|
|
@@ -35,6 +35,7 @@ set(LEPTRIS_SOURCES
|
|
|
35
35
|
leptris/dom/node_public.c
|
|
36
36
|
leptris/descriptor/plan.c
|
|
37
37
|
leptris/dom/element.c
|
|
38
|
+
leptris/dom/elem_pos.c
|
|
38
39
|
leptris/dom/element_query.c
|
|
39
40
|
leptris/dom/element_modify.c
|
|
40
41
|
leptris/dom/element_index.c
|
|
@@ -112,6 +113,7 @@ set(LEPTRIS_SOURCES
|
|
|
112
113
|
leptris/sax/iterparse.c
|
|
113
114
|
leptris/sax/streaming.c
|
|
114
115
|
leptris/sax/recorder.c
|
|
116
|
+
leptris/sax/records.c
|
|
115
117
|
|
|
116
118
|
# DTD
|
|
117
119
|
leptris/dtd/model.c
|
|
@@ -596,6 +596,93 @@ LEPTRIS_API const char* leptris_iterparse_error(LeptrisIterparse it);
|
|
|
596
596
|
|
|
597
597
|
LEPTRIS_API void leptris_iterparse_free(LeptrisIterparse it);
|
|
598
598
|
|
|
599
|
+
|
|
600
|
+
/* ============================================================================
|
|
601
|
+
* Bulk SAX records (issue #1298) — one-crossing whole-document drain
|
|
602
|
+
* ============================================================================ */
|
|
603
|
+
|
|
604
|
+
/** Record kinds (LeptrisSaxRecord.kind). */
|
|
605
|
+
enum {
|
|
606
|
+
LEPTRIS_SAX_REC_ELEMENT = 0, /**< start+end collapsed; tree via parent/next_sib */
|
|
607
|
+
LEPTRIS_SAX_REC_TEXT = 1 /**< character data run (entity-free docs only) */
|
|
608
|
+
};
|
|
609
|
+
|
|
610
|
+
/** One element or text record. Offsets are byte positions into the
|
|
611
|
+
* buffer returned by leptris_sax_records_buffer; every view is
|
|
612
|
+
* NUL-terminated at off+len (names and text both). */
|
|
613
|
+
typedef struct LeptrisSaxRecord {
|
|
614
|
+
uint32_t kind; /**< LEPTRIS_SAX_REC_* */
|
|
615
|
+
uint32_t parent; /**< record index, 0xFFFFFFFF for the root */
|
|
616
|
+
uint32_t next_sib; /**< record index, 0xFFFFFFFF = last sibling */
|
|
617
|
+
uint32_t off; /**< element: name; text: content */
|
|
618
|
+
uint32_t len; /**< byte length at off (excl. NUL) */
|
|
619
|
+
uint32_t line; /**< byte offset of '<' (or text start) + 1 (#1124 dp semantics) */
|
|
620
|
+
uint32_t start_tag_end; /**< byte offset after the open '>' (elements; 0 when unknown) */
|
|
621
|
+
uint32_t elem_end; /**< byte offset after the close '>' (self-close == start) */
|
|
622
|
+
uint32_t attr_first; /**< index into the flat attr table, 0xFFFFFFFF = none */
|
|
623
|
+
uint32_t attr_count; /**< number of attributes */
|
|
624
|
+
uint8_t self_closing; /**< element written <a/> */
|
|
625
|
+
} LeptrisSaxRecord;
|
|
626
|
+
|
|
627
|
+
/** One attribute: flat (off,len) views + 3.3.3 normalization need. */
|
|
628
|
+
typedef struct LeptrisSaxAttr {
|
|
629
|
+
uint32_t name_off, name_len; /**< attribute name (qname, NUL-terminated at [name_len]) */
|
|
630
|
+
uint32_t value_off, value_len; /**< raw value (NUL-terminated at [value_len]) */
|
|
631
|
+
uint8_t value_has_ws; /**< literal \t/\n/\r present — apply XML 1.0 3.3.3 normalization */
|
|
632
|
+
} LeptrisSaxAttr;
|
|
633
|
+
|
|
634
|
+
/** Opaque whole-document record table. */
|
|
635
|
+
typedef struct LeptrisSaxRecords LeptrisSaxRecords;
|
|
636
|
+
|
|
637
|
+
/**
|
|
638
|
+
* Parse a whole document into a flat record table (ONE call)
|
|
639
|
+
*
|
|
640
|
+
* The engine-side surface for #1298: hosts with hot SAX loops drain
|
|
641
|
+
* the entire document with a single crossing and then walk plain C
|
|
642
|
+
* structs — no per-event callback dispatch, no per-event string
|
|
643
|
+
* minting C-side. Records are (off,len) views into the parser's
|
|
644
|
+
* private copy of the input; every view is NUL-terminated in place.
|
|
645
|
+
*
|
|
646
|
+
* Coverage: the interleaved lane's scannable subset — elements,
|
|
647
|
+
* text, attributes; well-formed entity-free documents without
|
|
648
|
+
* comments/PIs/CDATA/DOCTYPE/xmlns declarations. Anything outside
|
|
649
|
+
* that subset returns LEPTRIS_ERROR_NOT_SUPPORTED (zero allocation);
|
|
650
|
+
* hosts fall back to the pull API (leptris_pull_new) for those
|
|
651
|
+
* documents. Malformed input returns LEPTRIS_ERROR_PARSE.
|
|
652
|
+
*
|
|
653
|
+
* @param xml Input (copied; the caller's buffer is untouched)
|
|
654
|
+
* @param len Input length in bytes (< 2 GiB)
|
|
655
|
+
* @param flags Reserved; pass 0
|
|
656
|
+
* @param out Receives the table handle
|
|
657
|
+
* @return LEPTRIS_OK, LEPTRIS_ERROR_NULL_ARG, LEPTRIS_ERROR_NOT_SUPPORTED,
|
|
658
|
+
* LEPTRIS_ERROR_PARSE, or LEPTRIS_ERROR_MEMORY
|
|
659
|
+
*
|
|
660
|
+
* Memory: free with leptris_sax_records_free; all views die with it.
|
|
661
|
+
*/
|
|
662
|
+
LEPTRIS_API LeptrisStatus leptris_sax_records_parse(const char* xml,
|
|
663
|
+
size_t len,
|
|
664
|
+
unsigned flags,
|
|
665
|
+
LeptrisSaxRecords** out);
|
|
666
|
+
|
|
667
|
+
/** Number of records. */
|
|
668
|
+
LEPTRIS_API size_t leptris_sax_records_count(const LeptrisSaxRecords* recs);
|
|
669
|
+
|
|
670
|
+
/** Record array (count entries; valid until free). */
|
|
671
|
+
LEPTRIS_API const LeptrisSaxRecord* leptris_sax_records_data(
|
|
672
|
+
const LeptrisSaxRecords* recs);
|
|
673
|
+
|
|
674
|
+
/** Flat attribute table + its length (valid until free). */
|
|
675
|
+
LEPTRIS_API const LeptrisSaxAttr* leptris_sax_records_attrs(
|
|
676
|
+
const LeptrisSaxRecords* recs, size_t* out_attr_count);
|
|
677
|
+
|
|
678
|
+
/** The buffer all record/attr offsets index into (NUL-padded past
|
|
679
|
+
* len). */
|
|
680
|
+
LEPTRIS_API const char* leptris_sax_records_buffer(
|
|
681
|
+
const LeptrisSaxRecords* recs);
|
|
682
|
+
|
|
683
|
+
/** Free the table (invalidates every view). */
|
|
684
|
+
LEPTRIS_API void leptris_sax_records_free(LeptrisSaxRecords* recs);
|
|
685
|
+
|
|
599
686
|
#ifdef __cplusplus
|
|
600
687
|
}
|
|
601
688
|
#endif
|
|
@@ -49,7 +49,8 @@ typedef enum {
|
|
|
49
49
|
LEPTRIS_ERROR_INVALID_ARG = -5, /* Invalid argument */
|
|
50
50
|
LEPTRIS_ERROR_NOT_FOUND = -6, /* Resource not found */
|
|
51
51
|
LEPTRIS_ERROR_IO = -7, /* I/O error (file not found, etc.) */
|
|
52
|
-
LEPTRIS_ERROR_NOT_IMPLEMENTED = -8 /* Feature not yet implemented */
|
|
52
|
+
LEPTRIS_ERROR_NOT_IMPLEMENTED = -8, /* Feature not yet implemented */
|
|
53
|
+
LEPTRIS_ERROR_NOT_SUPPORTED = -9 /* Construct outside this API's subset (additive, #1298) */
|
|
53
54
|
} LeptrisStatus;
|
|
54
55
|
|
|
55
56
|
/* ============================================================================
|
|
@@ -342,6 +343,11 @@ typedef struct {
|
|
|
342
343
|
* Applies to the XML method only — HTML void elements keep
|
|
343
344
|
* their HTML shapes. */
|
|
344
345
|
int expand_empty;
|
|
346
|
+
/* §16.2 HTML method control (#1309): 0 = document default
|
|
347
|
+
* (HTML-parsed and create_html documents serialize as HTML),
|
|
348
|
+
* 1 = force HTML on any document, -1 = force XML (to_xml on an
|
|
349
|
+
* HTML document). XSLT method="html" maps to 1. */
|
|
350
|
+
int html_method;
|
|
345
351
|
} LeptrisSerializeExtOptions;
|
|
346
352
|
|
|
347
353
|
/* ============================================================================
|
|
@@ -191,9 +191,14 @@ LEPTRIS_API size_t leptris_node_child_count(LeptrisNodeRef node);
|
|
|
191
191
|
*
|
|
192
192
|
* @param node Subtree root (any node type; NULL hashes to 0)
|
|
193
193
|
* @param flags LEPTRIS_DIGEST_DROP_WS_TEXT skips whitespace-only
|
|
194
|
-
* text nodes
|
|
194
|
+
* text nodes; LEPTRIS_DIGEST_ATTR_ORDER makes attribute
|
|
195
|
+
* ORDER identity (no sort)
|
|
195
196
|
* @return 64-bit content digest
|
|
196
|
-
|
|
197
|
+
*
|
|
198
|
+
* Environment opt-in (#1297): setting LEPTRIS_DIGEST_ATTR_ORDER=1 in
|
|
199
|
+
* the process environment ORs LEPTRIS_DIGEST_ATTR_ORDER into every
|
|
200
|
+
* call — the carrier for hosts whose binding exposes no flags
|
|
201
|
+
* parameter. "0" or an empty value leaves the passed flags alone. */
|
|
197
202
|
LEPTRIS_API uint64_t leptris_node_digest(LeptrisNodeRef node,
|
|
198
203
|
LeptrisDigestFlags flags);
|
|
199
204
|
|
|
@@ -773,6 +778,19 @@ LEPTRIS_API const char* leptris_pi_node_get_data(LeptrisNodeRef node);
|
|
|
773
778
|
*/
|
|
774
779
|
LEPTRIS_API LeptrisDocument leptris_document_create(void);
|
|
775
780
|
|
|
781
|
+
/**
|
|
782
|
+
* Create an empty HTML-mode document (#1309)
|
|
783
|
+
*
|
|
784
|
+
* Identical to leptris_document_create except the document carries
|
|
785
|
+
* html_mode: the default serializer emits §16.2 HTML output (void
|
|
786
|
+
* elements unclosed, raw-text script/style, HTML attribute
|
|
787
|
+
* escaping). Build with the regular element APIs.
|
|
788
|
+
*
|
|
789
|
+
* @return New empty HTML document, or NULL on failure
|
|
790
|
+
* Memory: free with leptris_document_free.
|
|
791
|
+
*/
|
|
792
|
+
LEPTRIS_API LeptrisDocument leptris_document_create_html(void);
|
|
793
|
+
|
|
776
794
|
/**
|
|
777
795
|
* Attach an element as the document's root element
|
|
778
796
|
*
|
|
@@ -2451,6 +2469,35 @@ LEPTRIS_API char* leptris_document_serialize_ext_sized(LeptrisDocument doc,
|
|
|
2451
2469
|
const LeptrisSerializeExtOptions* ext,
|
|
2452
2470
|
size_t ext_size);
|
|
2453
2471
|
|
|
2472
|
+
/**
|
|
2473
|
+
* Serialize a document with the §16.2 HTML method (#1309)
|
|
2474
|
+
*
|
|
2475
|
+
* Forces the HTML serialization rules (void elements unclosed,
|
|
2476
|
+
* raw-text script/style, HTML attribute escaping) regardless of the
|
|
2477
|
+
* document's mode — the XSLT method="html" semantic, public.
|
|
2478
|
+
* HTML-parsed and create_html documents get this behavior by
|
|
2479
|
+
* default; this entry forces it on any document.
|
|
2480
|
+
*
|
|
2481
|
+
* @param doc Document
|
|
2482
|
+
* @param options Frozen serialize options (NULL = defaults)
|
|
2483
|
+
* @return Serialized HTML string
|
|
2484
|
+
* Memory: Caller must call leptris_free_string() when done.
|
|
2485
|
+
*/
|
|
2486
|
+
LEPTRIS_API char* leptris_document_serialize_html(
|
|
2487
|
+
LeptrisDocument doc, const LeptrisSerializeOptions* options);
|
|
2488
|
+
|
|
2489
|
+
/**
|
|
2490
|
+
* Save a document as HTML to a file (#1309)
|
|
2491
|
+
*
|
|
2492
|
+
* @param doc Document
|
|
2493
|
+
* @param filepath Destination path
|
|
2494
|
+
* @param options Frozen serialize options (NULL = defaults)
|
|
2495
|
+
* @return LEPTRIS_OK (0), or a negative LeptrisStatus on failure
|
|
2496
|
+
*/
|
|
2497
|
+
LEPTRIS_API int leptris_document_save_html(LeptrisDocument doc,
|
|
2498
|
+
const char* filepath,
|
|
2499
|
+
const LeptrisSerializeOptions* options);
|
|
2500
|
+
|
|
2454
2501
|
/**
|
|
2455
2502
|
* Extended element serialization (issue #882): the same option
|
|
2456
2503
|
* surface as leptris_document_serialize_ext — indent_text,
|
|
@@ -224,6 +224,17 @@ static uint64_t digest_element(LeptrisElement e, LeptrisDigestFlags flags) {
|
|
|
224
224
|
LEPTRIS_API uint64_t leptris_node_digest(LeptrisNodeRef node,
|
|
225
225
|
LeptrisDigestFlags flags) {
|
|
226
226
|
if (!node) return 0;
|
|
227
|
+
/* #1297: process-global opt-in for ATTR_ORDER (#1200 part 1) —
|
|
228
|
+
* hosts whose binding has no flags carrier (moxml's Node#digest
|
|
229
|
+
* takes no flags parameter) set LEPTRIS_DIGEST_ATTR_ORDER. Read
|
|
230
|
+
* per call, same discipline as the LEPTRIS_INTERLEAVED lane
|
|
231
|
+
* gate; empty string counts as unset (MSVC _putenv_s(k, "")),
|
|
232
|
+
* "0" disables. ORs with explicitly passed flags. */
|
|
233
|
+
{
|
|
234
|
+
const char* env = getenv("LEPTRIS_DIGEST_ATTR_ORDER");
|
|
235
|
+
if (env && *env && *env != '0')
|
|
236
|
+
flags = (LeptrisDigestFlags)(flags | LEPTRIS_DIGEST_ATTR_ORDER);
|
|
237
|
+
}
|
|
227
238
|
LeptrisNode* n = (LeptrisNode*)node;
|
|
228
239
|
switch (n->type) {
|
|
229
240
|
case LEPTRIS_NODE_TYPE_ELEMENT:
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/* lib/src/dom/elem_pos.c — element source-offset side table.
|
|
2
|
+
*
|
|
3
|
+
* #1285 slice 3a: start_tag_end_off/element_end_off (#1124) left the
|
|
4
|
+
* element struct (cold diagnostics-only data was costing hot-path
|
|
5
|
+
* cache lines). Parse lanes record here instead; the single reader
|
|
6
|
+
* (node_public.c position accessor) probes. Open addressing,
|
|
7
|
+
* Fibonacci hash, insert-or-update, power-of-two capacity, freed
|
|
8
|
+
* with the document. */
|
|
9
|
+
#include "../leptris_internal.h"
|
|
10
|
+
#include "element.h"
|
|
11
|
+
#include <stdlib.h>
|
|
12
|
+
#include <stdint.h>
|
|
13
|
+
#include <string.h>
|
|
14
|
+
|
|
15
|
+
struct leptris_elem_pos_entry {
|
|
16
|
+
struct leptris_element* elem;
|
|
17
|
+
uint32_t start_tag_end;
|
|
18
|
+
uint32_t element_end;
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
static size_t elem_pos_hash(const struct leptris_element* e, size_t cap) {
|
|
22
|
+
uintptr_t k = (uintptr_t)e;
|
|
23
|
+
return ((size_t)((k * 0x9E3779B97F4A7C15ULL) >> 32)) & (cap - 1);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
static void elem_pos_insert_raw(struct leptris_elem_pos_entry* tab,
|
|
27
|
+
size_t cap, struct leptris_element* elem,
|
|
28
|
+
uint32_t ste, uint32_t ee) {
|
|
29
|
+
size_t i = elem_pos_hash(elem, cap);
|
|
30
|
+
while (tab[i].elem && tab[i].elem != elem)
|
|
31
|
+
i = (i + 1) & (cap - 1);
|
|
32
|
+
tab[i].elem = elem;
|
|
33
|
+
tab[i].start_tag_end = ste;
|
|
34
|
+
tab[i].element_end = ee;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
static int elem_pos_grow(struct leptris_document* doc) {
|
|
38
|
+
size_t ncap = doc->elem_pos_cap ? doc->elem_pos_cap * 2 : 64;
|
|
39
|
+
struct leptris_elem_pos_entry* nt =
|
|
40
|
+
(struct leptris_elem_pos_entry*)calloc(ncap, sizeof(*nt));
|
|
41
|
+
if (!nt) return -1;
|
|
42
|
+
for (size_t i = 0; i < doc->elem_pos_cap; i++) {
|
|
43
|
+
struct leptris_elem_pos_entry* e = &doc->elem_pos[i];
|
|
44
|
+
if (e->elem)
|
|
45
|
+
elem_pos_insert_raw(nt, ncap, e->elem, e->start_tag_end,
|
|
46
|
+
e->element_end);
|
|
47
|
+
}
|
|
48
|
+
free(doc->elem_pos);
|
|
49
|
+
doc->elem_pos = nt;
|
|
50
|
+
doc->elem_pos_cap = ncap;
|
|
51
|
+
return 0;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
void leptris_elem_pos_record(struct leptris_document* doc,
|
|
55
|
+
struct leptris_element* elem,
|
|
56
|
+
uint32_t start_tag_end, uint32_t element_end) {
|
|
57
|
+
if (!doc || !elem) return;
|
|
58
|
+
if (doc->elem_pos_count * 2 + 2 >= doc->elem_pos_cap) {
|
|
59
|
+
if (elem_pos_grow(doc) != 0) return; /* cold data: skip on OOM */
|
|
60
|
+
}
|
|
61
|
+
struct leptris_elem_pos_entry* tab = doc->elem_pos;
|
|
62
|
+
size_t cap = doc->elem_pos_cap;
|
|
63
|
+
size_t i = elem_pos_hash(elem, cap);
|
|
64
|
+
while (tab[i].elem && tab[i].elem != elem)
|
|
65
|
+
i = (i + 1) & (cap - 1);
|
|
66
|
+
if (!tab[i].elem) {
|
|
67
|
+
tab[i].elem = elem;
|
|
68
|
+
doc->elem_pos_count++;
|
|
69
|
+
}
|
|
70
|
+
tab[i].start_tag_end = start_tag_end;
|
|
71
|
+
tab[i].element_end = element_end;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
int leptris_elem_pos_lookup(const struct leptris_document* doc,
|
|
75
|
+
const struct leptris_element* elem,
|
|
76
|
+
uint32_t* start_tag_end, uint32_t* element_end) {
|
|
77
|
+
if (!doc || !elem || !doc->elem_pos_cap) return 0;
|
|
78
|
+
size_t i = elem_pos_hash(elem, doc->elem_pos_cap);
|
|
79
|
+
while (doc->elem_pos[i].elem) {
|
|
80
|
+
if (doc->elem_pos[i].elem == elem) {
|
|
81
|
+
if (start_tag_end) *start_tag_end = doc->elem_pos[i].start_tag_end;
|
|
82
|
+
if (element_end) *element_end = doc->elem_pos[i].element_end;
|
|
83
|
+
return 1;
|
|
84
|
+
}
|
|
85
|
+
i = (i + 1) & (doc->elem_pos_cap - 1);
|
|
86
|
+
}
|
|
87
|
+
return 0;
|
|
88
|
+
}
|
|
@@ -425,7 +425,8 @@ const char* leptris_element_get_name(LeptrisElement elem) {
|
|
|
425
425
|
void leptris_element_set_namespace_uri_view(LeptrisElement elem, LeptrisStringView uri_view) {
|
|
426
426
|
if (!elem) return;
|
|
427
427
|
if (leptris_sv_is_empty(&uri_view)) {
|
|
428
|
-
|
|
428
|
+
struct leptris_ns_cache* nsc = elem_get_ns_cache(elem);
|
|
429
|
+
if (nsc) nsc->namespace_uri = NULL;
|
|
429
430
|
return;
|
|
430
431
|
}
|
|
431
432
|
/* Issue #525: a namespace now exists — unprefixed XPath name
|