@@ -1,5 +1,5 @@
11#include "hexml.h"
2−#include <malloc.h>
2+#include <stdlib.h>
33#include <string.h>
44#include <assert.h>
55#include <ctype.h>
@@ -10,18 +10,18 @@ typedef int bool;
1010/////////////////////////////////////////////////////////////////////
1111// TYPES
1212
13−int static inline end(str s) { return s.start + s.length; }
13+static inline int end(str s) { return s.start + s.length; }
1414
15−str static inline start_length(int32_t start, int32_t length)
15+static inline str start_length(int32_t start, int32_t length)
1616{
17− if (start < 0 || length < 0) assert(0);
17+ assert(start >= 0 && length >= 0);
1818 str res;
1919 res.start = start;
2020 res.length = length;
2121 return res;
2222}
2323
24−str static inline start_end(int32_t start, int32_t end)
24+static inline str start_end(int32_t start, int32_t end)
2525{
2626 return start_length(start, end - start);
2727}
@@ -34,23 +34,13 @@ typedef struct
3434 attr* alloc; // what to call free on
3535} attr_buffer;
3636
37−
38−struct node
39−{
40− str name; // tag name, e.g. <[foo]>
41− str outer; // outer text, [<foo>bar</foo>]
42− str inner; // inner text, <foo>[bar]</foo>
43− str attrs; // all the attributes, in the attribute buffer
44− str nodes; // all the nodes, in the node buffer
45−};
46−
4737typedef struct
4838{
4939 // have a cursor at the front, which is all the stuff I have written out, final
5040 // have a cursor at the end, which is stack scoped, so children write out, then I do
5141 // when you commit, you copy over from end to front
5242
53− // nodes
43+ // nodes
5444 int size;
5545 int used_front; // front entries, stored for good
5646 int used_back; // back entries, stack based, copied into front
@@ -61,11 +51,11 @@ typedef struct
6151
6252struct document
6353{
64− char* body; // pointer to initial argument, not owned by us
54+ const char* body; // pointer to initial argument, not owned by us
6555
6656 // things only used while parsing
67− char* cursor; // pointer to where we are in body
68− char* end; // pointer to one past the last char
57+ const char* cursor; // pointer to where we are in body
58+ const char* end; // pointer to one past the last char
6959 // if cursor is > end we have gone past the end
7060
7161 char* error_message;
@@ -73,8 +63,8 @@ struct document
7363 attr_buffer attrs;
7464};
7565
76−int static inline doc_length(document* d) { return d->end - d->body; }
77−int static inline doc_position(document* d) { return d->cursor - d->body; }
66+static inline int doc_length(const document* d) { return (int) (d->end - d->body); }
67+static inline int doc_position(const document* d) { return (int) (d->cursor - d->body); }
7868
7969
8070/////////////////////////////////////////////////////////////////////
@@ -82,51 +72,45 @@ int static inline doc_position(document* d) { return d->cursor - d->body; }
8272
8373typedef struct
8474{
85− document* d;
75+ const document* d;
8676 char* buffer;
8777 int length;
8878 int cursor;
8979} render;
9080
91−void static inline render_char(render* r, char c)
81+static inline void render_char(render* r, char c)
9282{
9383 if (r->cursor < r->length)
9484 r->buffer[r->cursor] = c;
9585 r->cursor++;
9686}
9787
98−void static inline bound(char* msg, int index, int mn, int mx)
88+static inline void bound(int idx, int mn, int mx)
9989{
100− if (index < mn || index > mx)
101− {
102− printf("Bounds checking failed %s, got %i, which should be in %i:%i\n", msg, index, mn, mx);
103− assert(0);
104− }
90+ assert(idx >= mn && idx <= mx);
10591}
10692
107−void static inline bound_str(char* msg, str s, int mn, int mx)
93+static inline void bound_str(str s, int mn, int mx)
10894{
109− if (s.length < 0) assert(0);
110− bound(msg, s.start, mn, mx);
111− bound(msg, end(s), mn, mx);
95+ assert(s.length >= 0);
96+ bound(s.start, mn, mx);
97+ bound(end(s), mn, mx);
11298}
11399
114−void render_str(render* r, str s)
100+static void render_str(render* r, str s)
115101{
116− bound_str("render_str", s, 0, doc_length(r->d));
117− render_char(r, '[');
102+ bound_str(s, 0, doc_length(r->d));
118103 for (int i = 0; i < s.length; i++)
119104 render_char(r, r->d->body[s.start + i]);
120− render_char(r, ']');
121105}
122106
123−void render_tag(render* r, node* n);
107+static void render_tag(render* r, const node* n);
124108
125−void render_content(render* r, node* n)
109+static void render_content(render* r, const node* n)
126110{
127− bound_str("render_conent inner", n->inner, 0, doc_length(r->d));
128− bound_str("render_conent nodes", n->nodes, 0, r->d->nodes.used_front);
129− bound_str("render_conent attrs", n->attrs, 0, r->d->attrs.used);
111+ bound_str(n->inner, 0, doc_length(r->d));
112+ bound_str(n->nodes, 0, r->d->nodes.used_front);
113+ bound_str(n->attrs, 0, r->d->attrs.used);
130114
131115 int done = n->inner.start;
132116 for (int i = 0; i < n->nodes.length; i++)
@@ -139,7 +123,7 @@ void render_content(render* r, node* n)
139123 render_str(r, start_end(done, end(n->inner)));
140124}
141125
142−void render_tag(render* r, node* n)
126+static void render_tag(render* r, const node* n)
143127{
144128 render_char(r, '<');
145129 render_str(r, n->name);
@@ -161,14 +145,18 @@ void render_tag(render* r, node* n)
161145 render_char(r, '>');
162146}
163147
164−int document_render(document* d, char* buffer, int length)
148+int hexml_node_render(const document* d, const node* n, char* buffer, int length)
165149{
166150 render r;
167151 r.d = d;
168152 r.buffer = buffer;
169153 r.length = length;
170154 r.cursor = 0;
171− render_content(&r, document_node(d));
155+ // The root node (and only the root node) has an empty length, so just render its innards
156+ if (n->name.length == 0)
157+ render_content(&r, n);
158+ else
159+ render_tag(&r, n);
172160 return r.cursor;
173161}
174162
@@ -176,12 +164,10 @@ int document_render(document* d, char* buffer, int length)
176164/////////////////////////////////////////////////////////////////////
177165// NOT THE PARSER
178166
179−char* document_error(document* d){return d->error_message;}
180−node* document_node(document* d){return &d->nodes.nodes[0];}
181−str node_inner(node* n){return n->inner;}
182−str node_outer(node* n){return n->outer;}
167+char* hexml_document_error(const document* d){return d->error_message;}
168+node* hexml_document_node(const document* d){return &d->nodes.nodes[0];}
183169
184−void document_free(document* d)
170+void hexml_document_free(document* d)
185171{
186172 free(d->error_message);
187173 free(d->nodes.alloc);
@@ -189,93 +175,94 @@ void document_free(document* d)
189175 free(d);
190176}
191177
192−node* node_children(document* d, node* n, int* res)
178+node* hexml_node_children(const document* d, const node* n, int* res)
193179{
194180 *res = n->nodes.length;
195181 return &d->nodes.nodes[n->nodes.start];
196182}
197183
198−attr* node_attributes(document* d, node* n, int* res)
184+attr* hexml_node_attributes(const document* d, const node* n, int* res)
199185{
200186 *res = n->attrs.length;
201187 return &d->attrs.attrs[n->attrs.start];
202188}
203189
204190
205−attr* node_attributeBy(document* d, node* n, char* s, int slen)
191+attr* hexml_node_attribute(const document* d, const node* n, const char* s, int slen)
206192{
207− assert(0);
193+ if (slen == -1) slen = (int) strlen(s);
194+ const int limit = end(n->attrs);
195+ for (int i = n->attrs.start; i < limit; i++)
196+ {
197+ attr* r = &d->attrs.attrs[i];
198+ if (r->name.length == slen && memcmp(s, &d->body[r->name.start], slen) == 0)
199+ return r;
200+ }
208201 return NULL;
209202}
210203
211204// Search for given strings within a node
212−node* node_firstChildBy(document* d, node* n, char* s, int slen)
205+node* hexml_node_child(const document* d, const node* parent, const node* prev, const char* s, int slen)
213206{
214− if (slen == -1) slen = strlen(s);
215− int i = n->nodes.start;
216− int limit = i + n->nodes.length;
207+ if (slen == -1) slen = (int) strlen(s);
208+ int i = prev == NULL ? parent->nodes.start : (int) (prev + 1 - d->nodes.nodes);
209+ const int limit = end(parent->nodes);
217210 for (; i < limit; i++)
218211 {
219− if (d->nodes.nodes[i].name.length == slen &&
220− memcmp(s, &d->body[d->nodes.nodes[i].name.start], slen) == 0)
221− return &d->nodes.nodes[i];
212+ node* r = &d->nodes.nodes[i];
213+ if (r->name.length == slen && memcmp(s, &d->body[r->name.start], slen) == 0)
214+ return r;
222215 }
223216 return NULL;
224217}
225218
226−node* node_nextChildBy(document* d, node* parent, node* prev, char* s, int slen)
227−{
228− assert(0);
229− return NULL;
230−}
231−
232219
233220/////////////////////////////////////////////////////////////////////
234221// PARSE TABLE
235222
236−const char tag_name1 = 0x1;
237−const char tag_name = 0x2;
238−const char tag_space = 0x4;
223+static const char tag_name1 = 0x1;
224+static const char tag_name = 0x2;
225+static const char tag_space = 0x4;
239226
240−char parse_table[256];
227+static char parse_table[256];
241228
242−void init_parse_table()
229+static void init_parse_table()
243230{
244231 static bool done = 0;
245232 if (done) return;
246233 for (int i = 0; i < 256; i++)
247234 {
248235 bool name1 = i == ':' || i == '_' || (i >= 'a' && i <= 'z') || (i >= 'A' && i <= 'Z');
249− bool name = name1 || i == '-' || (i >= '0' && i <= '9');
236+ bool name = name1 || i == '-' || i == '.' || (i >= '0' && i <= '9');
250237 bool space = i == ' ' || i == '\t' || i == '\r' || i == '\n';
251238 parse_table[i] = (name1 ? tag_name1 : 0) | (name ? tag_name : 0) | (space ? tag_space : 0);
252239 }
253240 done = 1;
254241}
255242
256−bool static inline is(char c, char tag) { return parse_table[c] & tag; }
257−bool static inline is_name1(char c) { return is(c, tag_name1); }
258−bool static inline is_name(char c) { return is(c, tag_name); }
259−bool static inline is_space(char c) { return is(c, tag_space); }
243+static inline bool is(char c, char tag) { return parse_table[(unsigned char) c] & tag; }
244+static inline bool is_name1(char c) { return is(c, tag_name1); }
245+static inline bool is_name(char c) { return is(c, tag_name); }
246+static inline bool is_space(char c) { return is(c, tag_space); }
260247
261248
262249/////////////////////////////////////////////////////////////////////
263250// PARSER COMBINATORS
264251
265−char static inline peekAt(document* d, int i) { return d->cursor[i]; }
266−void static inline skip(document* d, int i) { d->cursor += i; }
267−char static inline peek(document* d) { return peekAt(d, 0); }
268−char static inline get(document* d) { char c = peek(d); skip(d, 1); return c; }
252+static inline char peek_at(const document* d, int i) { return d->cursor[i]; }
253+static inline char peek(const document* d) { return peek_at(d, 0); }
254+static inline void skip(document* d, int i) { d->cursor += i; }
255+static inline char get(document* d) { char c = peek(d); skip(d, 1); return c; }
269256
270257// Remove whitespace characters from the cursor while they are still whitespace
271−void static inline trim(document* d)
258+static inline void trim(document* d)
272259{
273260 while (is_space(peek(d)))
274261 skip(d, 1);
275262}
276263
277264// Find this character form the cursor onwards, if true adjust the cursor to that char, otherwise leave it at the end
278−bool find(document* d, char c)
265+static bool find(document* d, char c)
279266{
280267 char* x = memchr(d->cursor, c, d->end - d->cursor);
281268 if (x == NULL)
@@ -294,30 +281,47 @@ bool find(document* d, char c)
294281/////////////////////////////////////////////////////////////////////
295282// PARSING CODE
296283
297−void static inline node_alloc(node_buffer* b, int ask)
284+static inline void node_alloc(node_buffer* b, int ask)
298285{
299286 int space = b->size - b->used_back - b->used_front;
300287 if (space >= ask) return;
301− assert(0);
288+ int size2 = (b->size + 1000 + ask) * 2;
289+ node* buf2 = malloc(size2 * sizeof(node));
290+ assert(buf2);
291+ memcpy(buf2, b->nodes, b->used_front * sizeof(node));
292+ memcpy(&buf2[size2 - b->used_back], &b->nodes[b->size - b->used_back], b->used_back * sizeof(node));
293+ free(b->alloc);
294+ b->size = size2;
295+ b->nodes = buf2;
296+ b->alloc = buf2;
302297}
303298
304−void static inline attr_alloc(attr_buffer* b, int ask)
299+static inline void attr_alloc(attr_buffer* b, int ask)
305300{
306301 int space = b->size - b->used;
307302 if (space >= ask) return;
308303 int size2 = (b->size + 1000 + ask) * 2;
309304 attr* buf2 = malloc(size2 * sizeof(attr));
305+ assert(buf2);
310306 memcpy(buf2, b->attrs, b->used * sizeof(attr));
311307 free(b->alloc);
308+ b->size = size2;
312309 b->attrs = buf2;
313310 b->alloc = buf2;
314311}
315312
313+static void set_error(document* d, const char* msg)
314+{
315+ if (d->error_message != NULL) return; // keep the first error message
316+ d->error_message = malloc(strlen(msg)+1);
317+ assert(d->error_message);
318+ strcpy(d->error_message, msg);
319+}
320+
316321// you now expect a name, perhaps preceeded by whitespace
317322// the name may be empty
318−str static inline parse_name(document* d)
323+static inline str parse_name(document* d)
319324{
320− trim(d);
321325 int start = doc_position(d);
322326 if (!is_name1(peek(d)))
323327 return start_length(start, 0);
@@ -327,10 +331,14 @@ str static inline parse_name(document* d)
327331 return start_end(start, doc_position(d));
328332}
329333
330−str static inline parse_attrval(document* d)
334+static inline str parse_attrval(document* d)
331335{
332336 trim(d);
333− if (peek(d) != '=') return start_length(0,0);
337+ if (peek(d) != '=')
338+ {
339+ set_error(d, "Expected = in attribute, but missing");
340+ return start_length(0, 0);
341+ }
334342 skip(d, 1);
335343 trim(d);
336344 char c = peek(d);
@@ -340,26 +348,29 @@ str static inline parse_attrval(document* d)
340348 int start = doc_position(d);
341349 if (!find(d, c))
342350 {
343− d->error_message = _strdup("Couldn't find closing attribute bit");
351+ set_error(d, "Couldn't find closing attribute bit");
344352 return start_length(0, 0);
345353 }
346354 skip(d, 1);
347355 return start_end(start, doc_position(d) - 1);
348356 }
349357 else
350− return parse_name(d);
358+ {
359+ set_error(d, "Invalid attribute");
360+ return start_length(0, 0);
361+ }
351362}
352363
353364
354365
355366// seen a tag name, now looking for attributes terminated by >
356367// puts the attributes it finds in the attribute buffer
357−str static inline parse_attributes(document* d)
368+static inline str parse_attributes(document* d)
358369{
359− str res;
360− res.start = d->attrs.used;
361− for (int i = 0; ; i++)
370+ int start = d->attrs.used;
371+ while (d->error_message == NULL)
362372 {
373+ trim(d);
363374 str name = parse_name(d);
364375 if (name.length == 0) break;
365376 attr_alloc(&d->attrs, 1);
@@ -367,70 +378,80 @@ str static inline parse_attributes(document* d)
367378 d->attrs.attrs[d->attrs.used].value = parse_attrval(d);
368379 d->attrs.used++;
369380 }
370− res.length = d->attrs.used - res.start;
371− return res;
381+ return start_end(start, d->attrs.used);
372382}
373383
374−str parse_content(document* d);
384+static str parse_content(document* d);
375385
376386
377387// Add a new entry into tag, am at a '<'
378−void static inline parse_tag(document* d)
388+static inline void parse_tag(document* d)
379389{
380390 node_alloc(&d->nodes, 1);
381391 d->nodes.used_back++;
382− int me = d->nodes.size - d->nodes.used_back;
392+ node* me = &d->nodes.nodes[d->nodes.size - d->nodes.used_back];
383393
384− d->nodes.nodes[me].outer.start = doc_position(d);
394+ me->outer.start = doc_position(d);
385395 char c = get(d);
386396 assert(c == '<');
387397 if (peek(d) == '?') skip(d, 1);
388− d->nodes.nodes[me].name = parse_name(d);
389− d->nodes.nodes[me].attrs = parse_attributes(d);
398+ me->name = parse_name(d);
399+ if (me->name.length == 0)
400+ {
401+ set_error(d, "Missing tag name");
402+ return;
403+ }
404+ me->attrs = parse_attributes(d);
405+ if (d->error_message != NULL) return;
390406
391− c = get(d);
392− if ((c == '/' || c == '?') && peek(d) == '>')
407+ c = peek(d);
408+ if ((c == '/' || c == '?') && peek_at(d, 1) == '>')
393409 {
394− skip(d, 1);
395− d->nodes.nodes[me].nodes = start_length(0, 0);
396− d->nodes.nodes[me].outer.length = start_end(d->nodes.nodes[me].outer.start, doc_position(d)).length;
397− d->nodes.nodes[me].inner = start_length(doc_position(d), 0);
410+ skip(d, 2);
411+ me->nodes = start_length(0, 0);
412+ me->outer.length = start_end(me->outer.start, doc_position(d)).length;
413+ me->inner = start_length(doc_position(d), 0);
398414 return;
399415 }
400416 else if (c != '>')
401417 {
402− d->error_message = _strdup("Gunk at the end of the tag");
418+ set_error(d, "Gunk at the end of the tag");
403419 return;
404420 }
405− d->nodes.nodes[me].inner.start = doc_position(d);
406− d->nodes.nodes[me].nodes = parse_content(d);
407− d->nodes.nodes[me].inner.length = start_end(d->nodes.nodes[me].inner.start, doc_position(d)).length;
421+ else
422+ skip(d, 1);
423+ me->inner.start = doc_position(d);
424+ str content = parse_content(d);
425+
426+ // parse_content may have allocated more nodes, so recompute me
427+ me = &d->nodes.nodes[d->nodes.size - d->nodes.used_back];
428+ me->nodes = content;
429+ me->inner.length = start_end(me->inner.start, doc_position(d)).length;
408430
409431 if (d->error_message != NULL) return;
410− if (peek(d) == '<' && peekAt(d, 1) == '/')
432+ if (peek(d) == '<' && peek_at(d, 1) == '/')
411433 {
412434 skip(d, 2);
413− trim(d);
414− if (d->end - d->cursor >= d->nodes.nodes[me].name.length &&
415− memcmp(d->cursor, &d->body[d->nodes.nodes[me].name.start], d->nodes.nodes[me].name.length) == 0)
435+ if (d->end - d->cursor >= me->name.length &&
436+ memcmp(d->cursor, &d->body[me->name.start], me->name.length) == 0)
416437 {
417− skip(d, d->nodes.nodes[me].name.length);
438+ skip(d, me->name.length);
418439 trim(d);
419440 if (get(d) == '>')
420441 {
421− d->nodes.nodes[me].outer.length = start_end(d->nodes.nodes[me].outer.start, doc_position(d)).length;
442+ me->outer.length = start_end(me->outer.start, doc_position(d)).length;
422443 return;
423444 }
424445 }
425− d->error_message = _strdup("Mismatch in closing tags");
446+ set_error(d, "Mismatch in closing tags");
426447 return;
427448 }
428− d->error_message = _strdup("Weirdness when trying to close tags");
449+ set_error(d, "Weirdness when trying to close tags");
429450}
430451
431452// Parser until </, return the index of your node children
432453// Not inline as it is recursive
433−str parse_content(document* d)
454+static str parse_content(document* d)
434455{
435456 int before = d->nodes.used_back;
436457 while (d->error_message == NULL)
@@ -440,11 +461,33 @@ str parse_content(document* d)
440461 {
441462 break;
442463 }
443− if (peekAt(d, 1) == '/')
464+ else if (peek_at(d, 1) == '/')
444465 {
445466 // have found a </
446467 break;
447468 }
469+ else if (peek_at(d, 1) == '!' && peek_at(d, 2) == '-' && peek_at(d, 3) == '-')
470+ {
471+ skip(d, 3);
472+ // you can't reuse the two '-' characters for the closing as well
473+ if (peek_at(d, 0) == '\0' || peek_at(d, 1) == '\0')
474+ {
475+ set_error(d, "Didn't get a closing comment");
476+ return start_end(0, 0);
477+ }
478+ skip(d, 2);
479+ for (;;)
480+ {
481+ if (!find(d, '>'))
482+ {
483+ set_error(d, "Didn't get a closing comment");
484+ return start_end(0, 0);
485+ }
486+ skip(d, 1);
487+ if (peek_at(d, -3) == '-' && peek_at(d, -2) == '-')
488+ break;
489+ }
490+ }
448491 else
449492 {
450493 parse_tag(d);
@@ -460,20 +503,23 @@ str parse_content(document* d)
460503 return res;
461504}
462505
506+// Based on looking at ~50Kb XML documents, they seem to have ~700 attributes
507+// and ~300 nodes, so size appropriately to cope with that.
463508typedef struct
464509{
465510 document document;
466511 attr attrs[1000];
467− node nodes[1000];
512+ node nodes[500];
468513} buffer;
469514
470−document* document_parse(char* s, int slen)
515+document* hexml_document_parse(const char* s, int slen)
471516{
472− if (slen == -1) slen = strlen(s);
517+ if (slen == -1) slen = (int) strlen(s);
473518 assert(s[slen] == 0);
474519 init_parse_table();
475520
476521 buffer* buf = malloc(sizeof(buffer));
522+ assert(buf);
477523 document* d = &buf->document;
478524 d->body = s;
479525 d->cursor = s;
@@ -483,7 +529,7 @@ document* document_parse(char* s, int slen)
483529 d->attrs.used = 0;
484530 d->attrs.attrs = buf->attrs;
485531 d->attrs.alloc = NULL;
486− d->nodes.size = 1000;
532+ d->nodes.size = 500;
487533 d->nodes.used_back = 0;
488534 d->nodes.used_front = 1;
489535 d->nodes.nodes = buf->nodes;
@@ -493,11 +539,13 @@ document* document_parse(char* s, int slen)
493539 d->nodes.nodes[0].outer = start_length(0, slen);
494540 d->nodes.nodes[0].inner = start_length(0, slen);
495541 d->nodes.nodes[0].attrs = start_length(0, 0);
496− d->nodes.nodes[0].nodes = parse_content(d);
497542
498− if (d->cursor < d->end && d->error_message == NULL)
499− {
500− d->error_message = _strdup("Trailing junk at the end of the document");
501− }
543+ // Introduce an intermediate result, otherwise behaviour is undefined
544+ // because there is no guaranteed ordering between LHS and RHS evaluation
545+ str content = parse_content(d);
546+ d->nodes.nodes[0].nodes = content;
547+
548+ if (d->cursor < d->end)
549+ set_error(d, "Trailing junk at the end of the document");
502550 return d;
503551}