makiri 0.3.0 → 0.4.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/.github/workflows/conformance.yml +22 -0
- data/.github/workflows/libfuzzer.yml +83 -0
- data/.github/workflows/security.yml +88 -3
- data/.github/workflows/valgrind.yml +135 -0
- data/CHANGELOG.md +60 -2
- data/README.md +81 -77
- data/Rakefile +194 -3
- data/ext/makiri/bridge/ruby_string.c +119 -66
- data/ext/makiri/core/mkr_alloc.c +40 -3
- data/ext/makiri/core/mkr_alloc.h +27 -4
- data/ext/makiri/core/mkr_buf.c +13 -3
- data/ext/makiri/core/mkr_buf.h +80 -5
- data/ext/makiri/core/mkr_core.c +143 -0
- data/ext/makiri/core/mkr_core.h +10 -1
- data/ext/makiri/core/mkr_span.h +186 -0
- data/ext/makiri/core/mkr_utf8.c +101 -0
- data/ext/makiri/core/mkr_utf8.h +88 -0
- data/ext/makiri/extconf.rb +104 -9
- data/ext/makiri/fuzz/Makefile +95 -0
- data/ext/makiri/fuzz/check_fuzzer.cc +4 -0
- data/ext/makiri/fuzz/xml_fuzz.c +24 -0
- data/ext/makiri/fuzz/xpath_fuzz.c +109 -0
- data/ext/makiri/glue/glue.h +8 -0
- data/ext/makiri/glue/ruby_doc.c +20 -24
- data/ext/makiri/glue/ruby_html_css.c +58 -12
- data/ext/makiri/glue/ruby_html_mutate.c +11 -6
- data/ext/makiri/glue/ruby_html_node.c +3 -32
- data/ext/makiri/glue/ruby_node.c +39 -0
- data/ext/makiri/glue/ruby_xml.c +198 -16
- data/ext/makiri/glue/ruby_xml_node.c +46 -59
- data/ext/makiri/glue/ruby_xpath.c +4 -4
- data/ext/makiri/lexbor_compat/source_loc.c +14 -16
- data/ext/makiri/lexbor_compat/utf8_input.c +5 -78
- data/ext/makiri/makiri.c +45 -0
- data/ext/makiri/xml/mkr_xml.h +2 -3
- data/ext/makiri/xml/mkr_xml_chars.c +67 -97
- data/ext/makiri/xml/mkr_xml_index.c +169 -0
- data/ext/makiri/xml/mkr_xml_index.h +48 -0
- data/ext/makiri/xml/mkr_xml_mutate.c +63 -121
- data/ext/makiri/xml/mkr_xml_node.c +147 -15
- data/ext/makiri/xml/mkr_xml_node.h +71 -6
- data/ext/makiri/xml/mkr_xml_tree.c +185 -149
- data/ext/makiri/xpath/mkr_css.c +1023 -0
- data/ext/makiri/xpath/mkr_css.h +65 -0
- data/ext/makiri/xpath/mkr_xpath.c +37 -0
- data/ext/makiri/xpath/mkr_xpath.h +13 -0
- data/ext/makiri/xpath/mkr_xpath_eval_body.h +373 -90
- data/ext/makiri/xpath/mkr_xpath_funcs_body.h +249 -231
- data/ext/makiri/xpath/mkr_xpath_internal.h +89 -9
- data/ext/makiri/xpath/mkr_xpath_lex.c +94 -124
- data/ext/makiri/xpath/mkr_xpath_node_access_xml.h +6 -3
- data/ext/makiri/xpath/mkr_xpath_number.c +109 -0
- data/ext/makiri/xpath/mkr_xpath_parse.c +79 -90
- data/ext/makiri/xpath/mkr_xpath_shared.c +40 -24
- data/ext/makiri/xpath/mkr_xpath_value_body.h +50 -24
- data/lib/makiri/cdata_section.rb +1 -3
- data/lib/makiri/comment.rb +1 -3
- data/lib/makiri/document.rb +8 -0
- data/lib/makiri/element.rb +1 -3
- data/lib/makiri/processing_instruction.rb +1 -3
- data/lib/makiri/text.rb +1 -3
- data/lib/makiri/version.rb +1 -1
- data/lib/makiri/xml/builder.rb +263 -0
- data/lib/makiri/xml/node_methods.rb +47 -0
- data/lib/makiri.rb +1 -0
- data/script/check_alloc_failures.rb +266 -0
- data/script/check_c_safety.rb +45 -2
- data/script/check_c_safety_allowlist.yml +19 -0
- data/script/check_leaks.rb +64 -0
- data/script/leaks_harness.rb +64 -0
- data/vendor/lexbor/CMakeLists.txt +6 -0
- data/vendor/lexbor/README.md +12 -0
- data/vendor/lexbor/config.cmake +1 -1
- data/vendor/lexbor/source/lexbor/core/base.h +1 -1
- data/vendor/lexbor/source/lexbor/core/config.cmake +9 -1
- data/vendor/lexbor/source/lexbor/css/selectors/pseudo_state.c +2 -3
- data/vendor/lexbor/source/lexbor/css/selectors/state.c +3 -0
- data/vendor/lexbor/source/lexbor/dom/interfaces/element.c +21 -0
- data/vendor/lexbor/source/lexbor/dom/interfaces/element.h +5 -0
- data/vendor/lexbor/source/lexbor/encoding/decode.c +33 -4
- data/vendor/lexbor/source/lexbor/html/base.h +1 -1
- data/vendor/lexbor/source/lexbor/html/interfaces/select_element.c +4 -0
- data/vendor/lexbor/source/lexbor/html/serialize.c +545 -41
- data/vendor/lexbor/source/lexbor/html/serialize.h +2 -1
- data/vendor/lexbor/source/lexbor/html/tokenizer.h +2 -2
- data/vendor/lexbor/source/lexbor/html/tree/insertion_mode/in_body.c +1 -1
- data/vendor/lexbor/source/lexbor/html/tree.c +6 -6
- data/vendor/lexbor/source/lexbor/selectors/selectors.c +12 -3
- data/vendor/lexbor/source/lexbor/url/base.h +1 -1
- data/vendor/lexbor/source/lexbor/url/url.c +5 -2
- data/vendor/lexbor/source/lexbor/url/url.h +9 -0
- data/vendor/lexbor/version +1 -1
- metadata +19 -1
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
|
|
14
14
|
#include <stdint.h>
|
|
15
15
|
#include <stddef.h>
|
|
16
|
+
#include "../core/mkr_buf.h" /* mkr_spanbuf_t (the arena hand-fill writer) */
|
|
16
17
|
|
|
17
18
|
/* The XML representation's DOM node-type constants - the counterpart of Lexbor's
|
|
18
19
|
* LXB_DOM_NODE_TYPE_* for HTML. The numeric values mirror the DOM/Lexbor
|
|
@@ -50,7 +51,9 @@ typedef enum {
|
|
|
50
51
|
* lengths stay uint32 (the deliberate per-slice 4 GiB cap; see the enum note).
|
|
51
52
|
* Names/values are arena-owned byte slices (case-preserved). */
|
|
52
53
|
typedef struct mkr_xml_node {
|
|
53
|
-
|
|
54
|
+
mkr_xml_node_type_t type; /* free: the next field is an 8-aligned pointer,
|
|
55
|
+
* so this sits in padding either way (node stays
|
|
56
|
+
* 128 B) - keep the real enum type for safety */
|
|
54
57
|
struct mkr_xml_node *parent, *first_child, *last_child, *prev, *next;
|
|
55
58
|
struct mkr_xml_node *attrs; /* element: head of attribute list (type=ATTRIBUTE) */
|
|
56
59
|
const char *qname; /* element/attr raw "prefix:local" (contiguous; local/prefix slice into it) */
|
|
@@ -62,6 +65,17 @@ typedef struct mkr_xml_node {
|
|
|
62
65
|
uint32_t line, col; /* source position (element only; 0 = none) */
|
|
63
66
|
} mkr_xml_node_t;
|
|
64
67
|
|
|
68
|
+
/* A QName decomposed into its contiguous "prefix:local" parts (the same layout a
|
|
69
|
+
* built node carries): +qname+ is the whole name, +prefix+/+local+ slice into it
|
|
70
|
+
* (prefix points at +qname+; prefix_len 0 = unprefixed). The single value passed
|
|
71
|
+
* through the split/resolve/assign primitives below, so those never take the six
|
|
72
|
+
* fields as loose arguments. */
|
|
73
|
+
typedef struct {
|
|
74
|
+
const char *qname; uint32_t qname_len;
|
|
75
|
+
const char *prefix; uint32_t prefix_len; /* prefix_len 0 = unprefixed */
|
|
76
|
+
const char *local; uint32_t local_len;
|
|
77
|
+
} mkr_xml_qname_t;
|
|
78
|
+
|
|
65
79
|
typedef struct mkr_xml_arena_chunk mkr_xml_arena_chunk_t;
|
|
66
80
|
|
|
67
81
|
/* A document: its append-only arena, the root element (NULL until built), and
|
|
@@ -85,6 +99,14 @@ typedef struct mkr_xml_doc {
|
|
|
85
99
|
* node: local/qname = name, prefix = public
|
|
86
100
|
* (external) id, value = system id; a 0-length
|
|
87
101
|
* prefix/value means that id is absent. */
|
|
102
|
+
void *name_index; /* lazily-built element-name index (mkr_xml_index.c),
|
|
103
|
+
* or NULL; dropped on every structural mutation. */
|
|
104
|
+
int has_encoding_decl; /* the parsed source's <?xml?> declaration carried an
|
|
105
|
+
* `encoding` pseudo-attribute. The serializer emits
|
|
106
|
+
* `encoding="UTF-8"` only when this is set (or an
|
|
107
|
+
* explicit encoding: is requested), so a built or
|
|
108
|
+
* declaration-less document round-trips to a bare
|
|
109
|
+
* `<?xml version="1.0"?>`, like Nokogiri. */
|
|
88
110
|
} mkr_xml_doc_t;
|
|
89
111
|
/* The PER-ELEMENT attribute cap (MKR_XML_MAX_ATTRS) is enforced by the tree
|
|
90
112
|
* builder; the arena counts every node - attributes included - toward max_nodes. */
|
|
@@ -97,12 +119,21 @@ size_t mkr_xml_doc_memsize(const mkr_xml_doc_t *doc);
|
|
|
97
119
|
* The single overflow- and budget-checked alloc choke point is PRIVATE to
|
|
98
120
|
* mkr_xml_node.c. Callers never get a void* to cast: nodes via *_node (zeroed,
|
|
99
121
|
* type-validated), copied name/value bytes via *_bytes (the arena owns them).
|
|
100
|
-
*
|
|
101
|
-
*
|
|
102
|
-
*
|
|
103
|
-
mkr_xml_node_t *mkr_xml_arena_node(mkr_xml_doc_t *doc,
|
|
122
|
+
* A caller that must fill a buffer itself uses mkr_xml_arena_spanbuf (below),
|
|
123
|
+
* never a raw pointer. On any failure doc->oom is set (sticky) and the wrapper
|
|
124
|
+
* returns NULL. */
|
|
125
|
+
mkr_xml_node_t *mkr_xml_arena_node(mkr_xml_doc_t *doc, mkr_xml_node_type_t type);
|
|
104
126
|
const char *mkr_xml_arena_bytes(mkr_xml_doc_t *doc, const char *src, uint32_t len);
|
|
105
|
-
|
|
127
|
+
|
|
128
|
+
/* Carve +cap+ arena bytes and return a bounded writer (core mkr_spanbuf) over
|
|
129
|
+
* them - the ONLY way to hand-fill arena bytes. The writer owns the cursor and
|
|
130
|
+
* the bounds check, so a write past the cut is impossible by construction (it is
|
|
131
|
+
* refused and latches ok=false, never overruns the next cut). Fill it with the
|
|
132
|
+
* core mkr_spanbuf_putc / _write, then mkr_spanbuf_finish; a NULL finish means
|
|
133
|
+
* the backing alloc failed (doc->oom set) OR a write was refused (doc->oom stays
|
|
134
|
+
* OK -> a broken invariant, i.e. our bug). On alloc failure the returned writer
|
|
135
|
+
* is already not-ok (buf == NULL), so every write is a safe no-op. */
|
|
136
|
+
mkr_spanbuf_t mkr_xml_arena_spanbuf(mkr_xml_doc_t *doc, size_t cap);
|
|
106
137
|
|
|
107
138
|
/* If attribute +a+ is an xmlns declaration, set the declared namespace prefix
|
|
108
139
|
* (NULL/plen 0 for the default xmlns) and its URI as arena slices, and return 1;
|
|
@@ -111,6 +142,40 @@ char *mkr_xml_arena_scratch_bytes(mkr_xml_doc_t *doc, size_t len);
|
|
|
111
142
|
int mkr_xml_node_xmlns_decl(const mkr_xml_node_t *a, const char **prefix, uint32_t *plen,
|
|
112
143
|
const char **uri, uint32_t *ulen);
|
|
113
144
|
|
|
145
|
+
/* If the raw name [name,len) is an xmlns declaration ("xmlns" or "xmlns:PREFIX"),
|
|
146
|
+
* set the out-params to the declared prefix ("" / 0 for the default xmlns) and
|
|
147
|
+
* return 1; else return 0. prefix/plen may be NULL when only the boolean is
|
|
148
|
+
* wanted. The single byte-level xmlns detector the parser, the namespace
|
|
149
|
+
* resolver, and mkr_xml_node_xmlns_decl all share (the node version just reads
|
|
150
|
+
* the value too). */
|
|
151
|
+
int mkr_xml_xmlns_prefix(const char *name, uint32_t len, const char **prefix, uint32_t *plen);
|
|
152
|
+
|
|
153
|
+
/* Pre-order (document-order) descendant iteration over +root+'s subtree without
|
|
154
|
+
* recursion: start at +root+, then `cur = mkr_xml_preorder_next(root, cur)` until
|
|
155
|
+
* NULL. Visits +root+ first. The one shared form of the iterative DFS the index
|
|
156
|
+
* build and the mutation namespace resolver both need (no scope/state threaded). */
|
|
157
|
+
mkr_xml_node_t *mkr_xml_preorder_next(const mkr_xml_node_t *root, mkr_xml_node_t *cur);
|
|
158
|
+
|
|
159
|
+
/* Validate [name,len) as a well-formed XML 1.0 QName (full NameStartChar/NameChar
|
|
160
|
+
* scan) and split it into +out+ (prefix_len 0 = unprefixed). 0 on success, -1 if
|
|
161
|
+
* malformed. For the mutation path, whose input is not pre-scanned; the tree
|
|
162
|
+
* builder keeps its own lighter split (the name is already scan_name'd). */
|
|
163
|
+
int mkr_xml_qname_split(const char *name, uint32_t len, mkr_xml_qname_t *out);
|
|
164
|
+
|
|
165
|
+
/* Copy +qn+'s name into +doc+'s arena as one contiguous slice and point
|
|
166
|
+
* node->qname/local/prefix into it (ns_uri is the caller's concern). 0 on
|
|
167
|
+
* success, -1 on arena OOM (node left untouched, so the caller stays fail-closed).
|
|
168
|
+
* The single arena-copy-and-assign both the tree builder and the mutators use. */
|
|
169
|
+
int mkr_xml_qname_assign(mkr_xml_doc_t *doc, mkr_xml_node_t *node, const mkr_xml_qname_t *qn);
|
|
170
|
+
|
|
171
|
+
/* The QName parts a built node already carries, as a mkr_xml_qname_t value. */
|
|
172
|
+
static inline mkr_xml_qname_t
|
|
173
|
+
mkr_xml_qname_of(const mkr_xml_node_t *n)
|
|
174
|
+
{
|
|
175
|
+
mkr_xml_qname_t q = { n->qname, n->qname_len, n->prefix, n->prefix_len, n->local, n->local_len };
|
|
176
|
+
return q;
|
|
177
|
+
}
|
|
178
|
+
|
|
114
179
|
/* Self-test of the arena's secure-by-design properties (overflow, budgets,
|
|
115
180
|
* alignment, zero-init, copy-on-store, whole-free). Returns 0 on success or the
|
|
116
181
|
* 1-based index of the first failing check. Run from Makiri.__c_selftest. */
|