leptris 1.9.201.4-arm-linux → 1.9.204.0-arm-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.
Files changed (54) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +69 -0
  3. data/Rakefile +8 -2
  4. data/lib/leptris/version.rb +1 -1
  5. data/lib/leptris/xml/document.rb +25 -0
  6. data/lib/leptris/xml/dtd.rb +113 -0
  7. data/lib/leptris/xml/ffi.rb +47 -0
  8. data/lib/leptris/xml.rb +1 -0
  9. data/lib/libleptris.so +0 -0
  10. data/vendor-src/libleptris/CHANGELOG.md +28 -0
  11. data/vendor-src/libleptris/CLAUDE.md +1 -1
  12. data/vendor-src/libleptris/CMakeLists.txt +1 -1
  13. data/vendor-src/libleptris/README.adoc +26 -0
  14. data/vendor-src/libleptris/TODO.max-perf/2-3-oneshot-tokenizer-design.md +98 -0
  15. data/vendor-src/libleptris/cli/commands/validate.c +4 -2
  16. data/vendor-src/libleptris/docs/guide/parsing.md +30 -0
  17. data/vendor-src/libleptris/scripts/check_export_surface.py +20 -0
  18. data/vendor-src/libleptris/src/CMakeLists.txt +44 -1
  19. data/vendor-src/libleptris/src/include/leptris/descriptor.h +7 -1
  20. data/vendor-src/libleptris/src/include/leptris/dom/document.h +7 -1
  21. data/vendor-src/libleptris/src/include/leptris/dom/element.h +7 -1
  22. data/vendor-src/libleptris/src/include/leptris/dom/serialize.h +7 -1
  23. data/vendor-src/libleptris/src/include/leptris/dtd.h +46 -9
  24. data/vendor-src/libleptris/src/include/leptris/error.h +7 -1
  25. data/vendor-src/libleptris/src/include/leptris/html.h +79 -0
  26. data/vendor-src/libleptris/src/include/leptris/sax/sax.h +20 -1
  27. data/vendor-src/libleptris/src/include/leptris/xpath/xpath.h +7 -1
  28. data/vendor-src/libleptris/src/include/leptris.h +59 -37
  29. data/vendor-src/libleptris/src/leptris/core.c +28 -0
  30. data/vendor-src/libleptris/src/leptris/dom/doctype.c +27 -0
  31. data/vendor-src/libleptris/src/leptris/dom/element.c +54 -9
  32. data/vendor-src/libleptris/src/leptris/dom/element.h +5 -0
  33. data/vendor-src/libleptris/src/leptris/dom/element_modify.c +46 -6
  34. data/vendor-src/libleptris/src/leptris/dtd/model.h +6 -0
  35. data/vendor-src/libleptris/src/leptris/dtd/parser.c +49 -12
  36. data/vendor-src/libleptris/src/leptris/html/html_parse.c +31 -3
  37. data/vendor-src/libleptris/src/leptris/leptris.c +4 -2
  38. data/vendor-src/libleptris/src/leptris/sax/pull.c +10 -1
  39. data/vendor-src/libleptris/src/leptris/sax/sax_internal.h +7 -0
  40. data/vendor-src/libleptris/src/leptris/sax/streaming.c +66 -0
  41. data/vendor-src/libleptris/src/leptris/xpath/evaluator.c +19 -2
  42. data/vendor-src/libleptris/src/leptris/xpath/evaluator_operators.c +126 -0
  43. data/vendor-src/libleptris/src/leptris/xpath/functions_ext31.c +653 -151
  44. data/vendor-src/libleptris/src/leptris/xpath/parser.c +4 -2
  45. data/vendor-src/libleptris/src/leptris/xslt/xslt_functions.c +9 -5
  46. data/vendor-src/libleptris/test/dom/test_node_surface_parity.cpp +81 -0
  47. data/vendor-src/libleptris/test/dtd/test_dtd_validate.cpp +113 -0
  48. data/vendor-src/libleptris/test/html/test_html.cpp +51 -0
  49. data/vendor-src/libleptris/test/xquery/qt3/fn/replace.xml +865 -0
  50. data/vendor-src/libleptris/test/xquery/qt3/fn/string-join.xml +449 -0
  51. data/vendor-src/libleptris/test/xquery/qt3/fn/tokenize.xml +639 -0
  52. data/vendor-src/libleptris/test/xquery/test_qt3.cpp +74 -6
  53. data/vendor-src/libleptris/vcpkg.json +1 -1
  54. metadata +7 -1
@@ -156,8 +156,51 @@ if(LEPTRIS_AMALGAMATED)
156
156
 
157
157
  # Append #include for every source. Paths are relative to src/ so the
158
158
  # file's own #include directives resolve identically to the per-file build.
159
+ #
160
+ # Static helpers with the same name in different TUs collide once
161
+ # everything is one TU (#1217). The generator renames those tokens
162
+ # per file via #define around the #include — the source files stay
163
+ # untouched. A new collision surfaces as a conflicting-types error
164
+ # in the amalgamated CI job; add a line here.
165
+ set(_amalg_renames
166
+ "leptris/xpath/functions_exslt.c:arg_number=exslt_arg_number"
167
+ "leptris/xpath/functions_exslt.c:arg_string=exslt_arg_string"
168
+ "leptris/common/format_number.c:parse_pattern=fmt_num_parse_pattern"
169
+ "leptris/sax/iterparse.c:set_error=iterparse_set_error"
170
+ "leptris/xpath/vm.c:xpath_context_document=vm_xpath_context_document"
171
+ "leptris/xpath/xpath_public.c:xslt_register_ufn_handlers=xslt_register_ufn_handlers_impl"
172
+ "leptris/xslt/xslt_functions.c:xslt_register_ufn_handlers=xslt_register_ufn_handlers_impl"
173
+ "leptris/xslt/xslt_exec.c:utf8_encode=xslt_utf8_encode"
174
+ "leptris/xslt/xslt_exec.c:ws_only=xslt_ws_only"
175
+ "leptris/rng/validate.c:match_seq=rng_match_seq"
176
+ "leptris/sax/pull.c:cb_start_document=pull_cb_start_document"
177
+ "leptris/sax/pull.c:cb_end_document=pull_cb_end_document"
178
+ "leptris/sax/pull.c:cb_start_element=pull_cb_start_element"
179
+ "leptris/sax/pull.c:cb_end_element=pull_cb_end_element"
180
+ "leptris/sax/pull.c:cb_characters=pull_cb_characters"
181
+ "leptris/sax/pull.c:cb_comment=pull_cb_comment"
182
+ "leptris/sax/pull.c:cb_cdata=pull_cb_cdata"
183
+ "leptris/sax/pull.c:cb_pi=pull_cb_pi"
184
+ "leptris/sax/pull.c:cb_start_prefix=pull_cb_start_prefix"
185
+ "leptris/sax/pull.c:cb_end_prefix=pull_cb_end_prefix"
186
+ "leptris/sax/pull.c:cb_error=pull_cb_error"
187
+ )
159
188
  foreach(_src ${LEPTRIS_SOURCES})
160
- file(APPEND "${LEPTRIS_AMALGAMATED_C}" "#include \"${_src}\"\n")
189
+ set(_amalg_defs "")
190
+ set(_amalg_undefs "")
191
+ foreach(_r ${_amalg_renames})
192
+ string(REPLACE ":" ";" _rr "${_r}")
193
+ string(REPLACE "=" ";" _rr "${_rr}")
194
+ list(GET _rr 0 _rfile)
195
+ list(GET _rr 1 _rold)
196
+ list(GET _rr 2 _rnew)
197
+ if(_rfile STREQUAL _src)
198
+ string(APPEND _amalg_defs "#define ${_rold} ${_rnew}\n")
199
+ string(APPEND _amalg_undefs "#undef ${_rold}\n")
200
+ endif()
201
+ endforeach()
202
+ file(APPEND "${LEPTRIS_AMALGAMATED_C}"
203
+ "${_amalg_defs}#include \"${_src}\"\n${_amalg_undefs}\n")
161
204
  endforeach()
162
205
 
163
206
  # Replace the source list with the amalgamated file so the object
@@ -31,7 +31,13 @@
31
31
  #ifdef _WIN32
32
32
  #define LEPTRIS_API
33
33
  #else
34
- #define LEPTRIS_API __attribute__((visibility("default")))
34
+ /* Mirrors leptris.h (GCC LTO externalization, #1204). */
35
+ #if defined(__GNUC__) && !defined(__clang__)
36
+ #define LEPTRIS_API __attribute__((visibility("default"), used, \
37
+ externally_visible))
38
+ #else
39
+ #define LEPTRIS_API __attribute__((visibility("default"), used))
40
+ #endif
35
41
  #endif
36
42
  #endif
37
43
 
@@ -32,7 +32,13 @@ extern "C" {
32
32
  # define LEPTRIS_API
33
33
  # endif
34
34
  # else
35
- # define LEPTRIS_API __attribute__((visibility("default")))
35
+ /* Mirrors leptris.h (GCC LTO externalization, #1204). */
36
+ # if defined(__GNUC__) && !defined(__clang__)
37
+ # define LEPTRIS_API __attribute__((visibility("default"), used, \
38
+ externally_visible))
39
+ # else
40
+ # define LEPTRIS_API __attribute__((visibility("default"), used))
41
+ # endif
36
42
  # endif
37
43
  #endif
38
44
 
@@ -32,7 +32,13 @@ extern "C" {
32
32
  # define LEPTRIS_API
33
33
  # endif
34
34
  # else
35
- # define LEPTRIS_API __attribute__((visibility("default")))
35
+ /* Mirrors leptris.h (GCC LTO externalization, #1204). */
36
+ # if defined(__GNUC__) && !defined(__clang__)
37
+ # define LEPTRIS_API __attribute__((visibility("default"), used, \
38
+ externally_visible))
39
+ # else
40
+ # define LEPTRIS_API __attribute__((visibility("default"), used))
41
+ # endif
36
42
  # endif
37
43
  #endif
38
44
 
@@ -31,7 +31,13 @@ extern "C" {
31
31
  # define LEPTRIS_API
32
32
  # endif
33
33
  # else
34
- # define LEPTRIS_API __attribute__((visibility("default")))
34
+ /* Mirrors leptris.h (GCC LTO externalization, #1204). */
35
+ # if defined(__GNUC__) && !defined(__clang__)
36
+ # define LEPTRIS_API __attribute__((visibility("default"), used, \
37
+ externally_visible))
38
+ # else
39
+ # define LEPTRIS_API __attribute__((visibility("default"), used))
40
+ # endif
35
41
  # endif
36
42
  #endif
37
43
 
@@ -10,6 +10,39 @@
10
10
 
11
11
  #include <stddef.h>
12
12
 
13
+ /* Export macro: mirrors leptris.h/sax.h so dtd.h is includable
14
+ * standalone (bindings include sub-headers directly). See TODO 80. */
15
+ /* Export macro. Defined fully in leptris.h; sax.h allows standalone
16
+ * inclusion (leptris.h is not required) so we mirror the same logic.
17
+ * See TODO 80 (visibility preset) and TODO 122 (SAX surface). */
18
+ #ifndef LEPTRIS_API
19
+ # ifdef LEPTRIS_FOR_BINDGEN
20
+ # define LEPTRIS_API
21
+ # elif defined(_WIN32)
22
+ # ifdef LEPTRIS_BUILD_SHARED
23
+ # define LEPTRIS_API __declspec(dllexport)
24
+ # elif defined(LEPTRIS_BUILDING_DLL)
25
+ /* Mirrors leptris.h (issue #278): CMake defines
26
+ * LEPTRIS_BUILDING_DLL on the objects that build the DLL. */
27
+ # define LEPTRIS_API __declspec(dllexport)
28
+ # elif defined(LEPTRIS_USE_SHARED)
29
+ # define LEPTRIS_API __declspec(dllimport)
30
+ # else
31
+ # define LEPTRIS_API
32
+ # endif
33
+ # else
34
+ /* Mirrors leptris.h: GCC LTO needs externally_visible or it
35
+ * internalizes public symbols with no in-library caller
36
+ * (#1154/#1196/#1204 class). */
37
+ # if defined(__GNUC__) && !defined(__clang__)
38
+ # define LEPTRIS_API __attribute__((visibility("default"), used, \
39
+ externally_visible))
40
+ # else
41
+ # define LEPTRIS_API __attribute__((visibility("default"), used))
42
+ # endif
43
+ # endif
44
+ #endif
45
+
13
46
  #ifdef __cplusplus
14
47
  extern "C" {
15
48
  #endif
@@ -57,7 +90,7 @@ typedef struct {
57
90
  *
58
91
  * Memory: Caller must free with leptris_dtd_free()
59
92
  */
60
- LeptrisDTD* leptris_dtd_parse(const char* dtd_content, size_t len);
93
+ LEPTRIS_API LeptrisDTD* leptris_dtd_parse(const char* dtd_content, size_t len);
61
94
 
62
95
  /**
63
96
  * Get the document's DTD (the declarations parsed from the DOCTYPE
@@ -101,7 +134,7 @@ LEPTRIS_API LeptrisDTD* leptris_document_get_dtd(LeptrisDocument doc);
101
134
  * leptris_dtd_parse_external_subset(dtd, buffer, len);
102
135
  * int valid = leptris_dtd_validate(doc, dtd, &error);
103
136
  */
104
- int leptris_dtd_parse_external_subset(LeptrisDTD* dtd, const char* content,
137
+ LEPTRIS_API int leptris_dtd_parse_external_subset(LeptrisDTD* dtd, const char* content,
105
138
  size_t len);
106
139
 
107
140
  /**
@@ -112,9 +145,12 @@ int leptris_dtd_parse_external_subset(LeptrisDTD* dtd, const char* content,
112
145
  * this loader for the resource named by the system id. Same pattern
113
146
  * as leptris_dtd_parse_external_subset: the library never does I/O.
114
147
  *
115
- * The loader returns a malloc'd (or calloc'd) buffer in *out_len
116
- * bytes, which the parser consumes and frees; return NULL for
117
- * "unavailable" (the reference is then skipped, leniently).
148
+ * The loader returns a buffer in *out_len bytes, which the parser
149
+ * consumes and frees through the engine's allocator; return NULL
150
+ * for "unavailable" (the reference is then skipped, leniently).
151
+ * FFI loaders MUST allocate the buffer with leptris_alloc_buffer
152
+ * (#1219) so allocator/CRT mismatches cannot corrupt the heap —
153
+ * in-process C loaders may keep using plain malloc.
118
154
  *
119
155
  * The loader is consulted when DTD CONTENT IS PARSED — register it
120
156
  * before leptris_dtd_parse_external_subset (or before parsing a
@@ -126,7 +162,7 @@ int leptris_dtd_parse_external_subset(LeptrisDTD* dtd, const char* content,
126
162
  * @param loader Callback (NULL clears it)
127
163
  * @param user_data Opaque pointer handed to the loader
128
164
  */
129
- void leptris_dtd_set_pe_loader(LeptrisDTD* dtd,
165
+ LEPTRIS_API void leptris_dtd_set_pe_loader(LeptrisDTD* dtd,
130
166
  char* (*loader)(void* user_data,
131
167
  const char* system_id,
132
168
  size_t* out_len),
@@ -153,14 +189,15 @@ void leptris_dtd_set_pe_loader(LeptrisDTD* dtd,
153
189
  * leptris_dtd_error_free(&error);
154
190
  * }
155
191
  */
156
- int leptris_dtd_validate(LeptrisDocument doc, LeptrisDTD* dtd, LeptrisDTDError* error);
192
+ LEPTRIS_API int leptris_dtd_validate(LeptrisDocument doc, LeptrisDTD* dtd,
193
+ LeptrisDTDError* error);
157
194
 
158
195
  /**
159
196
  * Free DTD object
160
197
  *
161
198
  * @param dtd DTD to free (can be NULL)
162
199
  */
163
- void leptris_dtd_free(LeptrisDTD* dtd);
200
+ LEPTRIS_API void leptris_dtd_free(LeptrisDTD* dtd);
164
201
 
165
202
  /**
166
203
  * Free DTD error
@@ -169,7 +206,7 @@ void leptris_dtd_free(LeptrisDTD* dtd);
169
206
  *
170
207
  * @param error Error to free (can be NULL)
171
208
  */
172
- void leptris_dtd_error_free(LeptrisDTDError* error);
209
+ LEPTRIS_API void leptris_dtd_error_free(LeptrisDTDError* error);
173
210
 
174
211
  #ifdef __cplusplus
175
212
  }
@@ -19,7 +19,13 @@
19
19
  # define LEPTRIS_API __declspec(dllimport)
20
20
  # endif
21
21
  # else
22
- # define LEPTRIS_API __attribute__((visibility("default")))
22
+ /* Mirrors leptris.h (GCC LTO externalization, #1204). */
23
+ # if defined(__GNUC__) && !defined(__clang__)
24
+ # define LEPTRIS_API __attribute__((visibility("default"), used, \
25
+ externally_visible))
26
+ # else
27
+ # define LEPTRIS_API __attribute__((visibility("default"), used))
28
+ # endif
23
29
  # endif
24
30
  #endif
25
31
 
@@ -0,0 +1,79 @@
1
+ #ifndef LEPTRIS_HTML_H
2
+ #define LEPTRIS_HTML_H
3
+
4
+ /* ============================================================================
5
+ * HTML parsing (issue #659) — the WHATWG-conformant engine plus the
6
+ * libxml2/Nokogiri-compatibility mode. Split out of leptris.h so the
7
+ * HTML surface is discoverable as a subsystem (TODO 99 convention:
8
+ * headers split by subsystem; bindings include sub-headers directly).
9
+ * ============================================================================ */
10
+
11
+ #include <stddef.h>
12
+ #include "types.h"
13
+
14
+ /* Export macro: mirrors leptris.h/sax.h so html.h is includable
15
+ * standalone. See TODO 80 (visibility preset). */
16
+ #ifndef LEPTRIS_API
17
+ # ifdef LEPTRIS_FOR_BINDGEN
18
+ # define LEPTRIS_API
19
+ # elif defined(_WIN32)
20
+ # ifdef LEPTRIS_BUILD_SHARED
21
+ # define LEPTRIS_API __declspec(dllexport)
22
+ # elif defined(LEPTRIS_BUILDING_DLL)
23
+ # define LEPTRIS_API __declspec(dllexport)
24
+ # elif defined(LEPTRIS_USE_SHARED)
25
+ # define LEPTRIS_API __declspec(dllimport)
26
+ # else
27
+ # define LEPTRIS_API
28
+ # endif
29
+ # else
30
+ # define LEPTRIS_API __attribute__((visibility("default")))
31
+ # endif
32
+ #endif
33
+
34
+ #ifdef __cplusplus
35
+ extern "C" {
36
+ #endif
37
+
38
+ /**
39
+ * Parse an HTML string into a document (issue #659)
40
+ *
41
+ * Tolerant HTML4/5 parse into the standard DOM: implied end tags
42
+ * (p/li/td/tr/th/dt/dd/option/...), void elements, raw-text
43
+ * <script>/<style>, minimized + unquoted attribute values,
44
+ * case-insensitive tag/attribute names, and the HTML named-entity
45
+ * table. Behaviors follow libxml2's HTMLparser as exposed by
46
+ * Nokogiri (fragment shape: no synthesized <html>/<head>/<body>,
47
+ * no implied <tbody>). Malformed input never fails the parse — it
48
+ * degrades to text; a document with no nodes at all is an error.
49
+ * The result serializes and queries (XPath/XSLT) like any
50
+ * libleptris document.
51
+ *
52
+ * @param html HTML input (must be valid UTF-8)
53
+ * @param length Length of input in bytes
54
+ * @param status Output status code (can be NULL)
55
+ * @return Document handle or NULL on error
56
+ *
57
+ * Memory: Caller must call leptris_document_free() when done
58
+ * Thread safety: Not thread-safe. One document per thread.
59
+ */
60
+ LEPTRIS_API LeptrisDocument leptris_parse_html_string(const char* html,
61
+ size_t length,
62
+ LeptrisStatus* status);
63
+
64
+ /* HTML4/libxml2-compatibility mode (#659): identical tolerant
65
+ * tokenizer and Nokogiri document shape, except leading
66
+ * script/style content stays in <body> (libxml2's shape —
67
+ * title/meta/link/base still lift into the implied head). Bindings
68
+ * that must match Nokogiri byte-for-byte pin this entry;
69
+ * leptris_parse_html_string is the WHATWG-conformant engine.
70
+ * Memory contract identical to leptris_parse_html_string. */
71
+ LEPTRIS_API LeptrisDocument leptris_parse_html4_string(const char* html,
72
+ size_t length,
73
+ LeptrisStatus* status);
74
+
75
+ #ifdef __cplusplus
76
+ }
77
+ #endif
78
+
79
+ #endif /* LEPTRIS_HTML_H */
@@ -31,7 +31,15 @@
31
31
  # define LEPTRIS_API
32
32
  # endif
33
33
  # else
34
- # define LEPTRIS_API __attribute__((visibility("default")))
34
+ /* Mirrors leptris.h: GCC LTO needs externally_visible or it
35
+ * internalizes public symbols with no in-library caller
36
+ * (#1154/#1196/#1204 class). */
37
+ # if defined(__GNUC__) && !defined(__clang__)
38
+ # define LEPTRIS_API __attribute__((visibility("default"), used, \
39
+ externally_visible))
40
+ # else
41
+ # define LEPTRIS_API __attribute__((visibility("default"), used))
42
+ # endif
35
43
  # endif
36
44
  #endif
37
45
 
@@ -293,6 +301,17 @@ LEPTRIS_API void leptris_sax_parser_free(LeptrisSAXParser* parser);
293
301
  */
294
302
  LEPTRIS_API int leptris_sax_parser_set_streaming(LeptrisSAXParser* parser, int streaming);
295
303
 
304
+ /**
305
+ * Mark the input as one-shot: the whole document arrives in a
306
+ * single feed() and stays alive for the parser's lifetime. The
307
+ * tokenizer then uses in-place scans with single-copy name storage
308
+ * instead of the chunk-safe carry paths. No effect on correctness
309
+ * for any conforming caller; only enable when the input truly is
310
+ * one-shot (leptris_pull_new does this for its memory source).
311
+ */
312
+ LEPTRIS_API void leptris_sax_parser_set_one_shot(LeptrisSAXParser* parser,
313
+ int one_shot);
314
+
296
315
  /* ============================================================================
297
316
  * Pull (StAX-style) API — TODO.bindings/04, issue #510 Tier 2
298
317
  * ============================================================================ */
@@ -31,7 +31,13 @@ extern "C" {
31
31
  # define LEPTRIS_API
32
32
  # endif
33
33
  # else
34
- # define LEPTRIS_API __attribute__((visibility("default")))
34
+ /* Mirrors leptris.h (GCC LTO externalization, #1204). */
35
+ # if defined(__GNUC__) && !defined(__clang__)
36
+ # define LEPTRIS_API __attribute__((visibility("default"), used, \
37
+ externally_visible))
38
+ # else
39
+ # define LEPTRIS_API __attribute__((visibility("default"), used))
40
+ # endif
35
41
  # endif
36
42
  #endif
37
43
 
@@ -44,7 +44,17 @@ extern "C" {
44
44
  # define LEPTRIS_API
45
45
  # endif
46
46
  # else
47
- # define LEPTRIS_API __attribute__((visibility("default"), used))
47
+ /* GCC LTO internalizes even visibility("default") symbols with
48
+ * no in-library caller (#1154/#1196/#1204 class: the CLI under
49
+ * -flto, ppc64le shared builds). `externally_visible` is the
50
+ * GCC-side partner of `used`; clang accepts and ignores it, so
51
+ * it is scoped to GCC to keep -Wall clean elsewhere. */
52
+ # if defined(__GNUC__) && !defined(__clang__)
53
+ # define LEPTRIS_API __attribute__((visibility("default"), used, \
54
+ externally_visible))
55
+ # else
56
+ # define LEPTRIS_API __attribute__((visibility("default"), used))
57
+ # endif
48
58
  # endif
49
59
  #endif
50
60
  #endif /* LEPTRIS_FOR_BINDGEN */
@@ -793,42 +803,7 @@ LEPTRIS_API LeptrisStatus leptris_document_set_root(LeptrisDocument doc,
793
803
  */
794
804
  LEPTRIS_API LeptrisDocument leptris_parse_string(const char* xml, size_t length, LeptrisStatus* status);
795
805
 
796
- /**
797
- * Parse an HTML string into a document (issue #659)
798
- *
799
- * Tolerant HTML4/5 parse into the standard DOM: implied end tags
800
- * (p/li/td/tr/th/dt/dd/option/...), void elements, raw-text
801
- * <script>/<style>, minimized + unquoted attribute values,
802
- * case-insensitive tag/attribute names, and the HTML named-entity
803
- * table. Behaviors follow libxml2's HTMLparser as exposed by
804
- * Nokogiri (fragment shape: no synthesized <html>/<head>/<body>,
805
- * no implied <tbody>). Malformed input never fails the parse — it
806
- * degrades to text; a document with no nodes at all is an error.
807
- * The result serializes and queries (XPath/XSLT) like any
808
- * libleptris document.
809
- *
810
- * @param html HTML input (must be valid UTF-8)
811
- * @param length Length of input in bytes
812
- * @param status Output status code (can be NULL)
813
- * @return Document handle or NULL on error
814
- *
815
- * Memory: Caller must call leptris_document_free() when done
816
- * Thread safety: Not thread-safe. One document per thread.
817
- */
818
- LEPTRIS_API LeptrisDocument leptris_parse_html_string(const char* html,
819
- size_t length,
820
- LeptrisStatus* status);
821
-
822
- /* HTML4/libxml2-compatibility mode (#659): identical tolerant
823
- * tokenizer and Nokogiri document shape, except leading
824
- * script/style content stays in <body> (libxml2's shape —
825
- * title/meta/link/base still lift into the implied head). Bindings
826
- * that must match Nokogiri byte-for-byte pin this entry;
827
- * leptris_parse_html_string is the WHATWG-conformant engine.
828
- * Memory contract identical to leptris_parse_html_string. */
829
- LEPTRIS_API LeptrisDocument leptris_parse_html4_string(const char* html,
830
- size_t length,
831
- LeptrisStatus* status);
806
+ #include "leptris/html.h"
832
807
 
833
808
  /* ---- RELAX NG (#878) ------------------------------------------------
834
809
  * Phase 1: parse a RELAX NG XML-syntax schema into the compiled
@@ -1092,6 +1067,18 @@ LEPTRIS_API LeptrisStatus leptris_document_set_encoding(
1092
1067
  LEPTRIS_API LeptrisStatus leptris_document_set_standalone(
1093
1068
  LeptrisDocument doc, int standalone);
1094
1069
 
1070
+ /**
1071
+ * Un-set the XML declaration (#1229): the document stops being
1072
+ * declaration-bearing and serialization emits none — exactly as if
1073
+ * the input had none. Clears the version/encoding strings and the
1074
+ * standalone marker. Idempotent.
1075
+ *
1076
+ * @param doc Document handle
1077
+ * @return LEPTRIS_OK, or LEPTRIS_ERROR_NULL_ARG
1078
+ */
1079
+ LEPTRIS_API LeptrisStatus leptris_document_clear_declaration(
1080
+ LeptrisDocument doc);
1081
+
1095
1082
  /**
1096
1083
  * Programmatic DOCTYPE (#1094): create and set the document's
1097
1084
  * DOCTYPE (root name + optional PUBLIC/SYSTEM external
@@ -1105,6 +1092,19 @@ LEPTRIS_API LeptrisDoctype leptris_document_set_doctype(
1105
1092
  LeptrisDocument doc, const char* name,
1106
1093
  const char* public_id, const char* system_id);
1107
1094
 
1095
+ /**
1096
+ * Un-set the document's DOCTYPE (#1229). The LeptrisDoctype node
1097
+ * stays pool-owned and readable until leptris_document_free; it
1098
+ * simply leaves the document children chain and the serializer's
1099
+ * view. leptris_document_internal_subset returns NULL afterwards.
1100
+ *
1101
+ * @param doc Document handle
1102
+ * @return LEPTRIS_OK; LEPTRIS_ERROR_NOT_FOUND when no DOCTYPE is
1103
+ * set; LEPTRIS_ERROR_NULL_ARG for a NULL document.
1104
+ */
1105
+ LEPTRIS_API LeptrisStatus leptris_document_remove_doctype(
1106
+ LeptrisDocument doc);
1107
+
1108
1108
  /**
1109
1109
  * Get the document's internal DTD subset — the DOCTYPE declaration
1110
1110
  * (TODO 148 Phase 2).
@@ -3537,6 +3537,28 @@ LEPTRIS_API leptris_allocation_function leptris_get_memory_allocation_function(v
3537
3537
  */
3538
3538
  LEPTRIS_API leptris_deallocation_function leptris_get_memory_deallocation_function(void);
3539
3539
 
3540
+ /**
3541
+ * Allocate a buffer from the engine's heap (#1219).
3542
+ *
3543
+ * FFI callers that must hand the engine a buffer it later frees —
3544
+ * notably DTD parameter-entity loaders (leptris_dtd_set_pe_loader)
3545
+ * — allocate through this entry so allocator/CRT mismatches cannot
3546
+ * corrupt the heap (the engine DLL may link a different CRT than
3547
+ * the host language runtime). Routes through the custom allocator
3548
+ * when one is installed (leptris_set_memory_management_functions),
3549
+ * malloc otherwise; the engine releases loader buffers through the
3550
+ * matching deallocation path.
3551
+ *
3552
+ * @param len Bytes to allocate
3553
+ * @return Buffer from the engine's heap (release with
3554
+ * `leptris_free_string`), or NULL when len is 0 or the
3555
+ * allocation fails
3556
+ *
3557
+ * Memory: Caller-owned unless handed to the engine (PE-loader
3558
+ * buffers are consumed and freed by the parser).
3559
+ */
3560
+ LEPTRIS_API char* leptris_alloc_buffer(size_t len);
3561
+
3540
3562
  /* ============================================================================
3541
3563
  * Per-Document Allocator Hooks (TODO 74)
3542
3564
  *
@@ -96,6 +96,14 @@ LEPTRIS_API leptris_deallocation_function leptris_get_memory_deallocation_functi
96
96
  return g_leptris_dealloc_function;
97
97
  }
98
98
 
99
+ /* #1219: engine-heap buffer for FFI callers whose buffers the
100
+ * engine later frees (DTD PE loaders). The hook pair keeps the
101
+ * custom-allocator configuration symmetric. */
102
+ LEPTRIS_API char* leptris_alloc_buffer(size_t len) {
103
+ if (len == 0) return NULL;
104
+ return (char*)leptris_alloc_hook(len);
105
+ }
106
+
99
107
  LEPTRIS_API LeptrisStatus leptris_document_set_allocators(
100
108
  LeptrisDocument doc,
101
109
  leptris_allocation_function alloc,
@@ -199,3 +207,23 @@ LEPTRIS_API LeptrisStatus leptris_document_set_standalone(
199
207
  doc->had_declaration = 1;
200
208
  return LEPTRIS_OK;
201
209
  }
210
+
211
+ /* #1229: the remove-half — back to as-if-the-input-had-none. The
212
+ * setters' malloc'd strings are released here (document_free checks
213
+ * for NULL), and the standalone marker returns to unset. */
214
+ LEPTRIS_API LeptrisStatus leptris_document_clear_declaration(
215
+ struct leptris_document* doc) {
216
+ if (!doc) return LEPTRIS_ERROR_NULL_ARG;
217
+ if (doc->xml_version) {
218
+ leptris_free(doc->xml_version);
219
+ doc->xml_version = NULL;
220
+ }
221
+ if (doc->encoding) {
222
+ leptris_free(doc->encoding);
223
+ doc->encoding = NULL;
224
+ }
225
+ doc->standalone = -1;
226
+ doc->decl_encoding_verbatim = 0;
227
+ doc->had_declaration = 0;
228
+ return LEPTRIS_OK;
229
+ }
@@ -74,6 +74,33 @@ LEPTRIS_API LeptrisDoctype leptris_document_set_doctype(
74
74
  return (LeptrisDoctype)dt;
75
75
  }
76
76
 
77
+ /* #1229: the remove-half of set_doctype. The node is pool-owned, so
78
+ * it is unlinked (document children chain, when linked) — never
79
+ * freed; it stays readable until leptris_document_free. */
80
+ LEPTRIS_API LeptrisStatus leptris_document_remove_doctype(
81
+ LeptrisDocument doc) {
82
+ if (!doc) return LEPTRIS_ERROR_NULL_ARG;
83
+ LeptrisDoctypeNode* dt = (LeptrisDoctypeNode*)doc->doctype;
84
+ if (!dt) return LEPTRIS_ERROR_NOT_FOUND;
85
+ LeptrisNode* prev = NULL;
86
+ for (LeptrisNode* c = (LeptrisNode*)doc->doc_children_head; c; ) {
87
+ LeptrisNode* next = leptris_node_get_next_sibling(c);
88
+ if (c == (LeptrisNode*)dt) {
89
+ if (prev)
90
+ leptris_node_set_next_sibling(prev, next);
91
+ else
92
+ doc->doc_children_head = next;
93
+ if (doc->doc_children_tail == c)
94
+ doc->doc_children_tail = prev;
95
+ break;
96
+ }
97
+ prev = c;
98
+ c = next;
99
+ }
100
+ doc->doctype = NULL;
101
+ return LEPTRIS_OK;
102
+ }
103
+
77
104
  LEPTRIS_API const char* leptris_doctype_get_name(LeptrisDoctypeNode* doctype) {
78
105
  return doctype ? doctype->name : NULL;
79
106
  }
@@ -687,19 +687,50 @@ const char* leptris_element_lookup_namespace(LeptrisElement elem,
687
687
  }
688
688
 
689
689
  /* Children manipulation */
690
- void leptris_element_append_child_internal_doc(LeptrisElement elem, LeptrisNode* child,
691
- struct leptris_document* doc);
690
+ void leptris_element_append_child_hint_doc(LeptrisElement elem,
691
+ LeptrisNode* child,
692
+ struct leptris_document* doc,
693
+ LeptrisNode* hint);
692
694
  void leptris_element_append_child_internal(LeptrisElement elem, LeptrisNode* child) {
693
695
  if (!elem || !child) return;
694
- leptris_element_append_child_internal_doc(elem, child,
695
- leptris_element_get_document(elem));
696
+ leptris_element_append_child_hint_doc(elem, child,
697
+ leptris_element_get_document(elem),
698
+ NULL);
699
+ }
700
+
701
+ /* #1225: caller-supplied tail hint. The HTML builder tracks each
702
+ * open element's last child itself — the shared direct-mapped
703
+ * tail cache loses a persistent parent to an address collision
704
+ * every ~1KB of fresh pool allocations, and each eviction costs
705
+ * an O(children) walk (quadratic on table/text-heavy pages). The
706
+ * hint is validated (parent back-pointer + no next sibling) and
707
+ * silently ignored when stale — callers stay correct through the
708
+ * cache/walk fallback. */
709
+ void leptris_element_append_child_tail(LeptrisElement elem,
710
+ LeptrisNode* child,
711
+ struct leptris_document* doc,
712
+ LeptrisNode* hint) {
713
+ if (!elem || !child) return;
714
+ if (hint &&
715
+ (leptris_node_parent_inline(hint) != (LeptrisElement)elem ||
716
+ leptris_node_get_next_sibling(hint) != NULL))
717
+ hint = NULL;
718
+ leptris_element_append_child_hint_doc(elem, child, doc, hint);
696
719
  }
697
720
 
698
- /* doc is the caller-resolved document (NULL for detached trees) —
699
- * the public mutation path resolves it once instead of paying the
700
- * root walk + map lookup on every append (TODO 195c). */
701
721
  void leptris_element_append_child_internal_doc(LeptrisElement elem, LeptrisNode* child,
702
722
  struct leptris_document* doc) {
723
+ leptris_element_append_child_hint_doc(elem, child, doc, NULL);
724
+ }
725
+
726
+ /* doc is the caller-resolved document (NULL for detached trees) —
727
+ * the public mutation path resolves it once instead of paying the
728
+ * root walk + map lookup on every append (TODO 195c). hint is a
729
+ * pre-validated last-child hint (NULL = none). */
730
+ void leptris_element_append_child_hint_doc(LeptrisElement elem,
731
+ LeptrisNode* child,
732
+ struct leptris_document* doc,
733
+ LeptrisNode* hint) {
703
734
  if (!elem || !child) return;
704
735
 
705
736
  /* SAFETY: Verify node type field is valid before accessing it
@@ -746,6 +777,12 @@ void leptris_element_append_child_internal_doc(LeptrisElement elem, LeptrisNode*
746
777
  mut_tail = s->child;
747
778
  }
748
779
 
780
+ /* An empty child list must win over any cached tail: a NULL
781
+ * first_child with a non-NULL tail is a stale cache (or stale
782
+ * tail pointer), and appending through it splices the child
783
+ * onto a detached node while first_child stays NULL (#1220). */
784
+ int list_empty = (leptris_elem_first_child(elem) == NULL);
785
+
749
786
  /* For element children, set up linked list structure */
750
787
  if (child->type == LEPTRIS_NODE_TYPE_ELEMENT) {
751
788
  LeptrisElement child_elem = (LeptrisElement)child;
@@ -757,7 +794,11 @@ void leptris_element_append_child_internal_doc(LeptrisElement elem, LeptrisNode*
757
794
  /* Append to end of children list.
758
795
  * last_child may point to any node type (text/comment/etc.) so
759
796
  * we set its next_sibling via the type-dispatching setter. */
760
- LeptrisNode* last_node = mut_tail ? mut_tail : leptris_elem_last_child(elem);
797
+ LeptrisNode* last_node =
798
+ list_empty ? NULL
799
+ : (hint ? hint
800
+ : (mut_tail ? mut_tail
801
+ : leptris_elem_last_child(elem)));
761
802
  if (last_node) {
762
803
  /* Lane 18 P3: sequential appends' tail is an element —
763
804
  * the offset store directly; the type-dispatching node
@@ -776,7 +817,11 @@ void leptris_element_append_child_internal_doc(LeptrisElement elem, LeptrisNode*
776
817
  elem->child_count++;
777
818
  } else {
778
819
  /* For non-element children (text, cdata, comment, pi), append to linked list */
779
- LeptrisNode* last = mut_tail ? mut_tail : leptris_elem_last_child(elem);
820
+ LeptrisNode* last =
821
+ list_empty ? NULL
822
+ : (hint ? hint
823
+ : (mut_tail ? mut_tail
824
+ : leptris_elem_last_child(elem)));
780
825
  if (last) {
781
826
  leptris_node_set_next_sibling(last, (LeptrisNode*)child);
782
827
  leptris_elem_set_last_child(elem, (LeptrisNode*)child);