@@ -1,5 +1,5 @@
11#include "hexml.h"
2−#include <stdlib.h>
2+#include <malloc.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−static inline int end(str s) { return s.start + s.length; }
13+int static inline end(str s) { return s.start + s.length; }
1414
15−static inline str start_length(int32_t start, int32_t length)
15+str static inline start_length(int32_t start, int32_t length)
1616{
17− assert(start >= 0 && length >= 0);
17+ if (start < 0 || length < 0) assert(0);
1818 str res;
1919 res.start = start;
2020 res.length = length;
2121 return res;
2222}
2323
24−static inline str start_end(int32_t start, int32_t end)
24+str static inline start_end(int32_t start, int32_t end)
2525{
2626 return start_length(start, end - start);
2727}
@@ -34,13 +34,23 @@ 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+
3747typedef struct
3848{
3949 // have a cursor at the front, which is all the stuff I have written out, final
4050 // have a cursor at the end, which is stack scoped, so children write out, then I do
4151 // when you commit, you copy over from end to front
4252
43− // nodes
53+ // nodes
4454 int size;
4555 int used_front; // front entries, stored for good
4656 int used_back; // back entries, stack based, copied into front
@@ -51,11 +61,11 @@ typedef struct
5161
5262struct document
5363{
54− const char* body; // pointer to initial argument, not owned by us
64+ char* body; // pointer to initial argument, not owned by us
5565
5666 // things only used while parsing
57− const char* cursor; // pointer to where we are in body
58− const char* end; // pointer to one past the last char
67+ char* cursor; // pointer to where we are in body
68+ char* end; // pointer to one past the last char
5969 // if cursor is > end we have gone past the end
6070
6171 char* error_message;
@@ -63,8 +73,8 @@ struct document
6373 attr_buffer attrs;
6474};
6575
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); }
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; }
6878
6979
7080/////////////////////////////////////////////////////////////////////
@@ -72,45 +82,51 @@ static inline int doc_position(const document* d) { return (int) (d->cursor - d-
7282
7383typedef struct
7484{
75− const document* d;
85+ document* d;
7686 char* buffer;
7787 int length;
7888 int cursor;
7989} render;
8090
81−static inline void render_char(render* r, char c)
91+void static inline render_char(render* r, char c)
8292{
8393 if (r->cursor < r->length)
8494 r->buffer[r->cursor] = c;
8595 r->cursor++;
8696}
8797
88−static inline void bound(int idx, int mn, int mx)
98+void static inline bound(char* msg, int index, int mn, int mx)
8999{
90− assert(idx >= mn && idx <= mx);
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+ }
91105}
92106
93−static inline void bound_str(str s, int mn, int mx)
107+void static inline bound_str(char* msg, str s, int mn, int mx)
94108{
95− assert(s.length >= 0);
96− bound(s.start, mn, mx);
97− bound(end(s), mn, mx);
109+ if (s.length < 0) assert(0);
110+ bound(msg, s.start, mn, mx);
111+ bound(msg, end(s), mn, mx);
98112}
99113
100−static void render_str(render* r, str s)
114+void render_str(render* r, str s)
101115{
102− bound_str(s, 0, doc_length(r->d));
116+ bound_str("render_str", s, 0, doc_length(r->d));
117+ render_char(r, '[');
103118 for (int i = 0; i < s.length; i++)
104119 render_char(r, r->d->body[s.start + i]);
120+ render_char(r, ']');
105121}
106122
107−static void render_tag(render* r, const node* n);
123+void render_tag(render* r, node* n);
108124
109−static void render_content(render* r, const node* n)
125+void render_content(render* r, node* n)
110126{
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);
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);
114130
115131 int done = n->inner.start;
116132 for (int i = 0; i < n->nodes.length; i++)
@@ -123,7 +139,7 @@ static void render_content(render* r, const node* n)
123139 render_str(r, start_end(done, end(n->inner)));
124140}
125141
126−static void render_tag(render* r, const node* n)
142+void render_tag(render* r, node* n)
127143{
128144 render_char(r, '<');
129145 render_str(r, n->name);
@@ -145,18 +161,14 @@ static void render_tag(render* r, const node* n)
145161 render_char(r, '>');
146162}
147163
148−int hexml_node_render(const document* d, const node* n, char* buffer, int length)
164+int document_render(document* d, char* buffer, int length)
149165{
150166 render r;
151167 r.d = d;
152168 r.buffer = buffer;
153169 r.length = length;
154170 r.cursor = 0;
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);
171+ render_content(&r, document_node(d));
160172 return r.cursor;
161173}
162174
@@ -164,10 +176,12 @@ int hexml_node_render(const document* d, const node* n, char* buffer, int length
164176/////////////////////////////////////////////////////////////////////
165177// NOT THE PARSER
166178
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];}
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;}
169183
170−void hexml_document_free(document* d)
184+void document_free(document* d)
171185{
172186 free(d->error_message);
173187 free(d->nodes.alloc);
@@ -175,94 +189,93 @@ void hexml_document_free(document* d)
175189 free(d);
176190}
177191
178−node* hexml_node_children(const document* d, const node* n, int* res)
192+node* node_children(document* d, node* n, int* res)
179193{
180194 *res = n->nodes.length;
181195 return &d->nodes.nodes[n->nodes.start];
182196}
183197
184−attr* hexml_node_attributes(const document* d, const node* n, int* res)
198+attr* node_attributes(document* d, node* n, int* res)
185199{
186200 *res = n->attrs.length;
187201 return &d->attrs.attrs[n->attrs.start];
188202}
189203
190204
191−attr* hexml_node_attribute(const document* d, const node* n, const char* s, int slen)
205+attr* node_attributeBy(document* d, node* n, char* s, int slen)
192206{
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− }
207+ assert(0);
201208 return NULL;
202209}
203210
204211// Search for given strings within a node
205−node* hexml_node_child(const document* d, const node* parent, const node* prev, const char* s, int slen)
212+node* node_firstChildBy(document* d, node* n, char* s, int slen)
206213{
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);
214+ if (slen == -1) slen = strlen(s);
215+ int i = n->nodes.start;
216+ int limit = i + n->nodes.length;
210217 for (; i < limit; i++)
211218 {
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;
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];
215222 }
216223 return NULL;
217224}
218225
226+node* node_nextChildBy(document* d, node* parent, node* prev, char* s, int slen)
227+{
228+ assert(0);
229+ return NULL;
230+}
231+
219232
220233/////////////////////////////////////////////////////////////////////
221234// PARSE TABLE
222235
223−static const char tag_name1 = 0x1;
224−static const char tag_name = 0x2;
225−static const char tag_space = 0x4;
236+const char tag_name1 = 0x1;
237+const char tag_name = 0x2;
238+const char tag_space = 0x4;
226239
227−static char parse_table[256];
240+char parse_table[256];
228241
229−static void init_parse_table()
242+void init_parse_table()
230243{
231244 static bool done = 0;
232245 if (done) return;
233246 for (int i = 0; i < 256; i++)
234247 {
235248 bool name1 = i == ':' || i == '_' || (i >= 'a' && i <= 'z') || (i >= 'A' && i <= 'Z');
236− bool name = name1 || i == '-' || i == '.' || (i >= '0' && i <= '9');
249+ bool name = name1 || i == '-' || (i >= '0' && i <= '9');
237250 bool space = i == ' ' || i == '\t' || i == '\r' || i == '\n';
238251 parse_table[i] = (name1 ? tag_name1 : 0) | (name ? tag_name : 0) | (space ? tag_space : 0);
239252 }
240253 done = 1;
241254}
242255
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); }
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); }
247260
248261
249262/////////////////////////////////////////////////////////////////////
250263// PARSER COMBINATORS
251264
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; }
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; }
256269
257270// Remove whitespace characters from the cursor while they are still whitespace
258−static inline void trim(document* d)
271+void static inline trim(document* d)
259272{
260273 while (is_space(peek(d)))
261274 skip(d, 1);
262275}
263276
264277// Find this character form the cursor onwards, if true adjust the cursor to that char, otherwise leave it at the end
265−static bool find(document* d, char c)
278+bool find(document* d, char c)
266279{
267280 char* x = memchr(d->cursor, c, d->end - d->cursor);
268281 if (x == NULL)
@@ -281,47 +294,30 @@ static bool find(document* d, char c)
281294/////////////////////////////////////////////////////////////////////
282295// PARSING CODE
283296
284−static inline void node_alloc(node_buffer* b, int ask)
297+void static inline node_alloc(node_buffer* b, int ask)
285298{
286299 int space = b->size - b->used_back - b->used_front;
287300 if (space >= ask) return;
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;
301+ assert(0);
297302}
298303
299−static inline void attr_alloc(attr_buffer* b, int ask)
304+void static inline attr_alloc(attr_buffer* b, int ask)
300305{
301306 int space = b->size - b->used;
302307 if (space >= ask) return;
303308 int size2 = (b->size + 1000 + ask) * 2;
304309 attr* buf2 = malloc(size2 * sizeof(attr));
305− assert(buf2);
306310 memcpy(buf2, b->attrs, b->used * sizeof(attr));
307311 free(b->alloc);
308− b->size = size2;
309312 b->attrs = buf2;
310313 b->alloc = buf2;
311314}
312315
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−
321316// you now expect a name, perhaps preceeded by whitespace
322317// the name may be empty
323−static inline str parse_name(document* d)
318+str static inline parse_name(document* d)
324319{
320+ trim(d);
325321 int start = doc_position(d);
326322 if (!is_name1(peek(d)))
327323 return start_length(start, 0);
@@ -331,14 +327,10 @@ static inline str parse_name(document* d)
331327 return start_end(start, doc_position(d));
332328}
333329
334−static inline str parse_attrval(document* d)
330+str static inline parse_attrval(document* d)
335331{
336332 trim(d);
337− if (peek(d) != '=')
338− {
339− set_error(d, "Expected = in attribute, but missing");
340− return start_length(0, 0);
341− }
333+ if (peek(d) != '=') return start_length(0,0);
342334 skip(d, 1);
343335 trim(d);
344336 char c = peek(d);
@@ -348,29 +340,26 @@ static inline str parse_attrval(document* d)
348340 int start = doc_position(d);
349341 if (!find(d, c))
350342 {
351− set_error(d, "Couldn't find closing attribute bit");
343+ d->error_message = _strdup("Couldn't find closing attribute bit");
352344 return start_length(0, 0);
353345 }
354346 skip(d, 1);
355347 return start_end(start, doc_position(d) - 1);
356348 }
357349 else
358− {
359− set_error(d, "Invalid attribute");
360− return start_length(0, 0);
361− }
350+ return parse_name(d);
362351}
363352
364353
365354
366355// seen a tag name, now looking for attributes terminated by >
367356// puts the attributes it finds in the attribute buffer
368−static inline str parse_attributes(document* d)
357+str static inline parse_attributes(document* d)
369358{
370− int start = d->attrs.used;
371− while (d->error_message == NULL)
359+ str res;
360+ res.start = d->attrs.used;
361+ for (int i = 0; ; i++)
372362 {
373− trim(d);
374363 str name = parse_name(d);
375364 if (name.length == 0) break;
376365 attr_alloc(&d->attrs, 1);
@@ -378,80 +367,70 @@ static inline str parse_attributes(document* d)
378367 d->attrs.attrs[d->attrs.used].value = parse_attrval(d);
379368 d->attrs.used++;
380369 }
381− return start_end(start, d->attrs.used);
370+ res.length = d->attrs.used - res.start;
371+ return res;
382372}
383373
384−static str parse_content(document* d);
374+str parse_content(document* d);
385375
386376
387377// Add a new entry into tag, am at a '<'
388−static inline void parse_tag(document* d)
378+void static inline parse_tag(document* d)
389379{
390380 node_alloc(&d->nodes, 1);
391381 d->nodes.used_back++;
392− node* me = &d->nodes.nodes[d->nodes.size - d->nodes.used_back];
382+ int me = d->nodes.size - d->nodes.used_back;
393383
394− me->outer.start = doc_position(d);
384+ d->nodes.nodes[me].outer.start = doc_position(d);
395385 char c = get(d);
396386 assert(c == '<');
397387 if (peek(d) == '?') skip(d, 1);
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;
388+ d->nodes.nodes[me].name = parse_name(d);
389+ d->nodes.nodes[me].attrs = parse_attributes(d);
406390
407− c = peek(d);
408− if ((c == '/' || c == '?') && peek_at(d, 1) == '>')
391+ c = get(d);
392+ if ((c == '/' || c == '?') && peek(d) == '>')
409393 {
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);
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);
414398 return;
415399 }
416400 else if (c != '>')
417401 {
418− set_error(d, "Gunk at the end of the tag");
402+ d->error_message = _strdup("Gunk at the end of the tag");
419403 return;
420404 }
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;
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;
430408
431409 if (d->error_message != NULL) return;
432− if (peek(d) == '<' && peek_at(d, 1) == '/')
410+ if (peek(d) == '<' && peekAt(d, 1) == '/')
433411 {
434412 skip(d, 2);
435− if (d->end - d->cursor >= me->name.length &&
436− memcmp(d->cursor, &d->body[me->name.start], me->name.length) == 0)
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)
437416 {
438− skip(d, me->name.length);
417+ skip(d, d->nodes.nodes[me].name.length);
439418 trim(d);
440419 if (get(d) == '>')
441420 {
442− me->outer.length = start_end(me->outer.start, doc_position(d)).length;
421+ d->nodes.nodes[me].outer.length = start_end(d->nodes.nodes[me].outer.start, doc_position(d)).length;
443422 return;
444423 }
445424 }
446− set_error(d, "Mismatch in closing tags");
425+ d->error_message = _strdup("Mismatch in closing tags");
447426 return;
448427 }
449− set_error(d, "Weirdness when trying to close tags");
428+ d->error_message = _strdup("Weirdness when trying to close tags");
450429}
451430
452431// Parser until </, return the index of your node children
453432// Not inline as it is recursive
454−static str parse_content(document* d)
433+str parse_content(document* d)
455434{
456435 int before = d->nodes.used_back;
457436 while (d->error_message == NULL)
@@ -461,33 +440,11 @@ static str parse_content(document* d)
461440 {
462441 break;
463442 }
464− else if (peek_at(d, 1) == '/')
443+ if (peekAt(d, 1) == '/')
465444 {
466445 // have found a </
467446 break;
468447 }
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− }
491448 else
492449 {
493450 parse_tag(d);
@@ -503,23 +460,20 @@ static str parse_content(document* d)
503460 return res;
504461}
505462
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.
508463typedef struct
509464{
510465 document document;
511466 attr attrs[1000];
512− node nodes[500];
467+ node nodes[1000];
513468} buffer;
514469
515−document* hexml_document_parse(const char* s, int slen)
470+document* document_parse(char* s, int slen)
516471{
517− if (slen == -1) slen = (int) strlen(s);
472+ if (slen == -1) slen = strlen(s);
518473 assert(s[slen] == 0);
519474 init_parse_table();
520475
521476 buffer* buf = malloc(sizeof(buffer));
522− assert(buf);
523477 document* d = &buf->document;
524478 d->body = s;
525479 d->cursor = s;
@@ -529,7 +483,7 @@ document* hexml_document_parse(const char* s, int slen)
529483 d->attrs.used = 0;
530484 d->attrs.attrs = buf->attrs;
531485 d->attrs.alloc = NULL;
532− d->nodes.size = 500;
486+ d->nodes.size = 1000;
533487 d->nodes.used_back = 0;
534488 d->nodes.used_front = 1;
535489 d->nodes.nodes = buf->nodes;
@@ -539,13 +493,11 @@ document* hexml_document_parse(const char* s, int slen)
539493 d->nodes.nodes[0].outer = start_length(0, slen);
540494 d->nodes.nodes[0].inner = start_length(0, slen);
541495 d->nodes.nodes[0].attrs = start_length(0, 0);
496+ d->nodes.nodes[0].nodes = parse_content(d);
542497
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");
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+ }
550502 return d;
551503}