@@ -1,5 +1,5 @@
1#include "hexml.h"
1#include "hexml.h"
2−#include <stdlib.h>
2+#include <malloc.h>
3#include <string.h>
3#include <string.h>
4#include <assert.h>
4#include <assert.h>
5#include <ctype.h>
5#include <ctype.h>
@@ -10,18 +10,18 @@ typedef int bool;
10/////////////////////////////////////////////////////////////////////
10/////////////////////////////////////////////////////////////////////
13−static inline int end(str s) { return s.start + s.length; }
13+int static inline end(str s) { return s.start + s.length; }
15−static inline str start_length(int32_t start, int32_t length)
15+str static inline start_length(int32_t start, int32_t length)
17− assert(start >= 0 && length >= 0);
17+ if (start < 0 || length < 0) assert(0);
19 res.start = start;
19 res.start = start;
20 res.length = length;
20 res.length = length;
21 return res;
21 return res;
24−static inline str start_end(int32_t start, int32_t end)
24+str static inline start_end(int32_t start, int32_t end)
26 return start_length(start, end - start);
26 return start_length(start, end - start);
@@ -40,7 +40,7 @@ typedef struct
40 // have a cursor at the end, which is stack scoped, so children write out, then I do
40 // have a cursor at the end, which is stack scoped, so children write out, then I do
41 // when you commit, you copy over from end to front
41 // when you commit, you copy over from end to front
45 int used_front; // front entries, stored for good
45 int used_front; // front entries, stored for good
46 int used_back; // back entries, stack based, copied into front
46 int used_back; // back entries, stack based, copied into front
@@ -51,11 +51,11 @@ typedef struct
52struct document
52struct document
54− const char* body; // pointer to initial argument, not owned by us
54+ char* body; // pointer to initial argument, not owned by us
56 // things only used while parsing
56 // things only used while parsing
57− const char* cursor; // pointer to where we are in body
57+ char* cursor; // pointer to where we are in body
58− const char* end; // pointer to one past the last char
58+ char* end; // pointer to one past the last char
59 // if cursor is > end we have gone past the end
59 // if cursor is > end we have gone past the end
61 char* error_message;
61 char* error_message;
@@ -63,8 +63,8 @@ struct document
63 attr_buffer attrs;
63 attr_buffer attrs;
66−static inline int doc_length(const document* d) { return (int) (d->end - d->body); }
66+int static inline doc_length(document* d) { return d->end - d->body; }
67−static inline int doc_position(const document* d) { return (int) (d->cursor - d->body); }
67+int static inline doc_position(document* d) { return d->cursor - d->body; }
70/////////////////////////////////////////////////////////////////////
70/////////////////////////////////////////////////////////////////////
@@ -72,45 +72,49 @@ static inline int doc_position(const document* d) { return (int) (d->cursor - d-
73typedef struct
73typedef struct
75− const document* d;
75+ document* d;
76 char* buffer;
76 char* buffer;
77 int length;
77 int length;
78 int cursor;
78 int cursor;
81−static inline void render_char(render* r, char c)
81+void static inline render_char(render* r, char c)
83 if (r->cursor < r->length)
83 if (r->cursor < r->length)
84 r->buffer[r->cursor] = c;
84 r->buffer[r->cursor] = c;
85 r->cursor++;
85 r->cursor++;
88−static inline void bound(int idx, int mn, int mx)
88+void static inline bound(char* msg, int index, int mn, int mx)
90+ if (index < mn || index > mx)
92+ //printf("Bounds checking failed %s, got %i, which should be in %i:%i\n", msg, index, mn, mx);
90− assert(idx >= mn && idx <= mx);
93+ assert(0);
93−static inline void bound_str(str s, int mn, int mx)
97+void static inline bound_str(char* msg, str s, int mn, int mx)
95− assert(s.length >= 0);
99+ if (s.length < 0) assert(0);
96− bound(s.start, mn, mx);
100+ bound(msg, s.start, mn, mx);
97− bound(end(s), mn, mx);
101+ bound(msg, end(s), mn, mx);
100−static void render_str(render* r, str s)
104+void render_str(render* r, str s)
102− bound_str(s, 0, doc_length(r->d));
106+ bound_str("render_str", s, 0, doc_length(r->d));
103 for (int i = 0; i < s.length; i++)
107 for (int i = 0; i < s.length; i++)
104 render_char(r, r->d->body[s.start + i]);
108 render_char(r, r->d->body[s.start + i]);
107−static void render_tag(render* r, const node* n);
111+void render_tag(render* r, node* n);
109−static void render_content(render* r, const node* n)
113+void render_content(render* r, node* n)
111− bound_str(n->inner, 0, doc_length(r->d));
115+ bound_str("render_conent inner", n->inner, 0, doc_length(r->d));
112− bound_str(n->nodes, 0, r->d->nodes.used_front);
116+ bound_str("render_conent nodes", n->nodes, 0, r->d->nodes.used_front);
113− bound_str(n->attrs, 0, r->d->attrs.used);
117+ bound_str("render_conent attrs", n->attrs, 0, r->d->attrs.used);
115 int done = n->inner.start;
119 int done = n->inner.start;
116 for (int i = 0; i < n->nodes.length; i++)
120 for (int i = 0; i < n->nodes.length; i++)
@@ -123,7 +127,7 @@ static void render_content(render* r, const node* n)
123 render_str(r, start_end(done, end(n->inner)));
127 render_str(r, start_end(done, end(n->inner)));
126−static void render_tag(render* r, const node* n)
130+void render_tag(render* r, node* n)
128 render_char(r, '<');
132 render_char(r, '<');
129 render_str(r, n->name);
133 render_str(r, n->name);
@@ -145,7 +149,7 @@ static void render_tag(render* r, const node* n)
145 render_char(r, '>');
149 render_char(r, '>');
148−int hexml_node_render(const document* d, const node* n, char* buffer, int length)
152+int node_render(document* d, node* n, char* buffer, int length)
150 render r;
154 render r;
@@ -164,10 +168,10 @@ int hexml_node_render(const document* d, const node* n, char* buffer, int length
164/////////////////////////////////////////////////////////////////////
168/////////////////////////////////////////////////////////////////////
165// NOT THE PARSER
169// NOT THE PARSER
167−char* hexml_document_error(const document* d){return d->error_message;}
171+char* document_error(document* d){return d->error_message;}
168−node* hexml_document_node(const document* d){return &d->nodes.nodes[0];}
172+node* document_node(document* d){return &d->nodes.nodes[0];}
170−void hexml_document_free(document* d)
174+void document_free(document* d)
172 free(d->error_message);
176 free(d->error_message);
173 free(d->nodes.alloc);
177 free(d->nodes.alloc);
@@ -175,24 +179,25 @@ void hexml_document_free(document* d)
178−node* hexml_node_children(const document* d, const node* n, int* res)
182+node* node_children(document* d, node* n, int* res)
180 *res = n->nodes.length;
184 *res = n->nodes.length;
181 return &d->nodes.nodes[n->nodes.start];
185 return &d->nodes.nodes[n->nodes.start];
184−attr* hexml_node_attributes(const document* d, const node* n, int* res)
188+attr* node_attributes(document* d, node* n, int* res)
186 *res = n->attrs.length;
190 *res = n->attrs.length;
187 return &d->attrs.attrs[n->attrs.start];
191 return &d->attrs.attrs[n->attrs.start];
191−attr* hexml_node_attribute(const document* d, const node* n, const char* s, int slen)
195+attr* node_attributeBy(document* d, node* n, char* s, int slen)
193− if (slen == -1) slen = (int) strlen(s);
197+ if (slen == -1) slen = strlen(s);
198+ int i = n->attrs.start;
194− const int limit = end(n->attrs);
199+ int limit = end(n->attrs);
195− for (int i = n->attrs.start; i < limit; i++)
200+ for (i = n->attrs.start; i < limit; i++)
197 attr* r = &d->attrs.attrs[i];
202 attr* r = &d->attrs.attrs[i];
198 if (r->name.length == slen && memcmp(s, &d->body[r->name.start], slen) == 0)
203 if (r->name.length == slen && memcmp(s, &d->body[r->name.start], slen) == 0)
@@ -202,11 +207,11 @@ attr* hexml_node_attribute(const document* d, const node* n, const char* s, int
204// Search for given strings within a node
209// 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)
210+node* node_childBy(document* d, node* parent, node* prev, char* s, int slen)
207− if (slen == -1) slen = (int) strlen(s);
212+ if (slen == -1) slen = strlen(s);
208− int i = prev == NULL ? parent->nodes.start : (int) (prev + 1 - d->nodes.nodes);
213+ int i = prev == NULL ? parent->nodes.start : prev + 1 - d->nodes.nodes;
209− const int limit = end(parent->nodes);
214+ int limit = end(parent->nodes);
210 for (; i < limit; i++)
215 for (; i < limit; i++)
212 node* r = &d->nodes.nodes[i];
217 node* r = &d->nodes.nodes[i];
@@ -220,49 +225,49 @@ node* hexml_node_child(const document* d, const node* parent, const node* prev,
220/////////////////////////////////////////////////////////////////////
225/////////////////////////////////////////////////////////////////////
221// PARSE TABLE
226// PARSE TABLE
223−static const char tag_name1 = 0x1;
228+const char tag_name1 = 0x1;
224−static const char tag_name = 0x2;
229+const char tag_name = 0x2;
225−static const char tag_space = 0x4;
230+const char tag_space = 0x4;
227−static char parse_table[256];
232+char parse_table[256];
229−static void init_parse_table()
234+void init_parse_table()
231 static bool done = 0;
236 static bool done = 0;
232 if (done) return;
237 if (done) return;
233 for (int i = 0; i < 256; i++)
238 for (int i = 0; i < 256; i++)
235 bool name1 = i == ':' || i == '_' || (i >= 'a' && i <= 'z') || (i >= 'A' && i <= 'Z');
240 bool name1 = i == ':' || i == '_' || (i >= 'a' && i <= 'z') || (i >= 'A' && i <= 'Z');
236− bool name = name1 || i == '-' || i == '.' || (i >= '0' && i <= '9');
241+ bool name = name1 || i == '-' || (i >= '0' && i <= '9');
237 bool space = i == ' ' || i == '\t' || i == '\r' || i == '\n';
242 bool space = i == ' ' || i == '\t' || i == '\r' || i == '\n';
238 parse_table[i] = (name1 ? tag_name1 : 0) | (name ? tag_name : 0) | (space ? tag_space : 0);
243 parse_table[i] = (name1 ? tag_name1 : 0) | (name ? tag_name : 0) | (space ? tag_space : 0);
240 done = 1;
245 done = 1;
243−static inline bool is(char c, char tag) { return parse_table[(unsigned char) c] & tag; }
248+bool static inline is(char c, char tag) { return parse_table[c] & tag; }
244−static inline bool is_name1(char c) { return is(c, tag_name1); }
249+bool static inline is_name1(char c) { return is(c, tag_name1); }
245−static inline bool is_name(char c) { return is(c, tag_name); }
250+bool static inline is_name(char c) { return is(c, tag_name); }
246−static inline bool is_space(char c) { return is(c, tag_space); }
251+bool static inline is_space(char c) { return is(c, tag_space); }
249/////////////////////////////////////////////////////////////////////
254/////////////////////////////////////////////////////////////////////
250// PARSER COMBINATORS
255// PARSER COMBINATORS
252−static inline char peek_at(const document* d, int i) { return d->cursor[i]; }
257+char static inline peekAt(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; }
258+void static inline skip(document* d, int i) { d->cursor += i; }
259+char static inline peek(document* d) { return peekAt(d, 0); }
255−static inline char get(document* d) { char c = peek(d); skip(d, 1); return c; }
260+char static inline get(document* d) { char c = peek(d); skip(d, 1); return c; }
257// Remove whitespace characters from the cursor while they are still whitespace
262// Remove whitespace characters from the cursor while they are still whitespace
258−static inline void trim(document* d)
263+void static inline trim(document* d)
260 while (is_space(peek(d)))
265 while (is_space(peek(d)))
261 skip(d, 1);
266 skip(d, 1);
264// Find this character form the cursor onwards, if true adjust the cursor to that char, otherwise leave it at the end
269// 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)
270+bool find(document* d, char c)
267 char* x = memchr(d->cursor, c, d->end - d->cursor);
272 char* x = memchr(d->cursor, c, d->end - d->cursor);
268 if (x == NULL)
273 if (x == NULL)
@@ -281,46 +286,35 @@ static bool find(document* d, char c)
281/////////////////////////////////////////////////////////////////////
286/////////////////////////////////////////////////////////////////////
282// PARSING CODE
287// PARSING CODE
284−static inline void node_alloc(node_buffer* b, int ask)
289+void static inline node_alloc(node_buffer* b, int ask)
286 int space = b->size - b->used_back - b->used_front;
291 int space = b->size - b->used_back - b->used_front;
287 if (space >= ask) return;
292 if (space >= ask) return;
288− int size2 = (b->size + 1000 + ask) * 2;
289− node* buf2 = malloc(size2 * sizeof(node));
290− assert(buf2);
293+ assert(0);
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));
299−static inline void attr_alloc(attr_buffer* b, int ask)
296+void static inline attr_alloc(attr_buffer* b, int ask)
301 int space = b->size - b->used;
298 int space = b->size - b->used;
302 if (space >= ask) return;
299 if (space >= ask) return;
303 int size2 = (b->size + 1000 + ask) * 2;
300 int size2 = (b->size + 1000 + ask) * 2;
304 attr* buf2 = malloc(size2 * sizeof(attr));
301 attr* buf2 = malloc(size2 * sizeof(attr));
306 memcpy(buf2, b->attrs, b->used * sizeof(attr));
302 memcpy(buf2, b->attrs, b->used * sizeof(attr));
307 free(b->alloc);
303 free(b->alloc);
309 b->attrs = buf2;
304 b->attrs = buf2;
310 b->alloc = buf2;
305 b->alloc = buf2;
313−static void set_error(document* d, const char* msg)
308+void set_error(document* d, char* msg)
315 if (d->error_message != NULL) return; // keep the first error message
310 if (d->error_message != NULL) return; // keep the first error message
316 d->error_message = malloc(strlen(msg)+1);
311 d->error_message = malloc(strlen(msg)+1);
317− assert(d->error_message);
318 strcpy(d->error_message, msg);
312 strcpy(d->error_message, msg);
321// you now expect a name, perhaps preceeded by whitespace
315// you now expect a name, perhaps preceeded by whitespace
322// the name may be empty
316// the name may be empty
323−static inline str parse_name(document* d)
317+str static inline parse_name(document* d)
325 int start = doc_position(d);
319 int start = doc_position(d);
326 if (!is_name1(peek(d)))
320 if (!is_name1(peek(d)))
@@ -331,15 +325,14 @@ static inline str parse_name(document* d)
331 return start_end(start, doc_position(d));
325 return start_end(start, doc_position(d));
334−static inline str parse_attrval(document* d)
328+str static inline parse_attrval(document* d)
337− if (peek(d) != '=')
331+ if (get(d) != '=')
339 set_error(d, "Expected = in attribute, but missing");
333 set_error(d, "Expected = in attribute, but missing");
340 return start_length(0, 0);
334 return start_length(0, 0);
344 char c = peek(d);
337 char c = peek(d);
345 if (c == '\"' || c == '\'')
338 if (c == '\"' || c == '\'')
@@ -365,10 +358,11 @@ static inline str parse_attrval(document* d)
366// seen a tag name, now looking for attributes terminated by >
359// seen a tag name, now looking for attributes terminated by >
367// puts the attributes it finds in the attribute buffer
360// puts the attributes it finds in the attribute buffer
368−static inline str parse_attributes(document* d)
361+str static inline parse_attributes(document* d)
370− int start = d->attrs.used;
364+ res.start = d->attrs.used;
371− while (d->error_message == NULL)
365+ for (int i = 0; ; i++)
374 str name = parse_name(d);
368 str name = parse_name(d);
@@ -378,39 +372,34 @@ static inline str parse_attributes(document* d)
378 d->attrs.attrs[d->attrs.used].value = parse_attrval(d);
372 d->attrs.attrs[d->attrs.used].value = parse_attrval(d);
379 d->attrs.used++;
373 d->attrs.used++;
381− return start_end(start, d->attrs.used);
375+ res.length = d->attrs.used - res.start;
384−static str parse_content(document* d);
379+str parse_content(document* d);
387// Add a new entry into tag, am at a '<'
382// Add a new entry into tag, am at a '<'
388−static inline void parse_tag(document* d)
383+void static inline parse_tag(document* d)
390 node_alloc(&d->nodes, 1);
385 node_alloc(&d->nodes, 1);
391 d->nodes.used_back++;
386 d->nodes.used_back++;
392− node* me = &d->nodes.nodes[d->nodes.size - d->nodes.used_back];
387+ int me = d->nodes.size - d->nodes.used_back;
394− me->outer.start = doc_position(d);
389+ d->nodes.nodes[me].outer.start = doc_position(d);
395 char c = get(d);
390 char c = get(d);
396 assert(c == '<');
391 assert(c == '<');
397 if (peek(d) == '?') skip(d, 1);
392 if (peek(d) == '?') skip(d, 1);
398− me->name = parse_name(d);
393+ d->nodes.nodes[me].name = parse_name(d);
399− if (me->name.length == 0)
401− set_error(d, "Missing tag name");
404− me->attrs = parse_attributes(d);
394+ d->nodes.nodes[me].attrs = parse_attributes(d);
405− if (d->error_message != NULL) return;
407− c = peek(d);
396+ c = get(d);
408− if ((c == '/' || c == '?') && peek_at(d, 1) == '>')
397+ if ((c == '/' || c == '?') && peek(d) == '>')
410− skip(d, 2);
399+ skip(d, 1);
411− me->nodes = start_length(0, 0);
400+ d->nodes.nodes[me].nodes = start_length(0, 0);
412− me->outer.length = start_end(me->outer.start, doc_position(d)).length;
401+ d->nodes.nodes[me].outer.length = start_end(d->nodes.nodes[me].outer.start, doc_position(d)).length;
413− me->inner = start_length(doc_position(d), 0);
402+ d->nodes.nodes[me].inner = start_length(doc_position(d), 0);
416 else if (c != '>')
405 else if (c != '>')
@@ -418,28 +407,22 @@ static inline void parse_tag(document* d)
418 set_error(d, "Gunk at the end of the tag");
407 set_error(d, "Gunk at the end of the tag");
423− me->inner.start = doc_position(d);
410+ d->nodes.nodes[me].inner.start = doc_position(d);
424− str content = parse_content(d);
411+ d->nodes.nodes[me].nodes = parse_content(d);
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;
412+ d->nodes.nodes[me].inner.length = start_end(d->nodes.nodes[me].inner.start, doc_position(d)).length;
431 if (d->error_message != NULL) return;
414 if (d->error_message != NULL) return;
432− if (peek(d) == '<' && peek_at(d, 1) == '/')
415+ if (peek(d) == '<' && peekAt(d, 1) == '/')
434 skip(d, 2);
417 skip(d, 2);
435− if (d->end - d->cursor >= me->name.length &&
418+ if (d->end - d->cursor >= d->nodes.nodes[me].name.length &&
436− memcmp(d->cursor, &d->body[me->name.start], me->name.length) == 0)
419+ memcmp(d->cursor, &d->body[d->nodes.nodes[me].name.start], d->nodes.nodes[me].name.length) == 0)
438− skip(d, me->name.length);
421+ skip(d, d->nodes.nodes[me].name.length);
440 if (get(d) == '>')
423 if (get(d) == '>')
442− me->outer.length = start_end(me->outer.start, doc_position(d)).length;
425+ d->nodes.nodes[me].outer.length = start_end(d->nodes.nodes[me].outer.start, doc_position(d)).length;
@@ -451,7 +434,7 @@ static inline void parse_tag(document* d)
452// Parser until </, return the index of your node children
435// Parser until </, return the index of your node children
453// Not inline as it is recursive
436// Not inline as it is recursive
454−static str parse_content(document* d)
437+str parse_content(document* d)
456 int before = d->nodes.used_back;
439 int before = d->nodes.used_back;
457 while (d->error_message == NULL)
440 while (d->error_message == NULL)
@@ -461,33 +444,11 @@ static str parse_content(document* d)
464− else if (peek_at(d, 1) == '/')
447+ if (peekAt(d, 1) == '/')
466 // have found a </
449 // have found a </
469− else if (peek_at(d, 1) == '!' && peek_at(d, 2) == '-' && peek_at(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')
475− set_error(d, "Didn't get a closing comment");
476− return start_end(0, 0);
483− set_error(d, "Didn't get a closing comment");
484− return start_end(0, 0);
487− if (peek_at(d, -3) == '-' && peek_at(d, -2) == '-')
493 parse_tag(d);
454 parse_tag(d);
@@ -503,23 +464,20 @@ static str parse_content(document* d)
503 return res;
464 return res;
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.
508typedef struct
467typedef struct
510 document document;
469 document document;
511 attr attrs[1000];
470 attr attrs[1000];
512− node nodes[500];
471+ node nodes[1000];
515−document* hexml_document_parse(const char* s, int slen)
474+document* document_parse(char* s, int slen)
517− if (slen == -1) slen = (int) strlen(s);
476+ if (slen == -1) slen = strlen(s);
518 assert(s[slen] == 0);
477 assert(s[slen] == 0);
519 init_parse_table();
478 init_parse_table();
521 buffer* buf = malloc(sizeof(buffer));
480 buffer* buf = malloc(sizeof(buffer));
523 document* d = &buf->document;
481 document* d = &buf->document;
524 d->body = s;
482 d->body = s;
525 d->cursor = s;
483 d->cursor = s;
@@ -529,7 +487,7 @@ document* hexml_document_parse(const char* s, int slen)
529 d->attrs.used = 0;
487 d->attrs.used = 0;
530 d->attrs.attrs = buf->attrs;
488 d->attrs.attrs = buf->attrs;
531 d->attrs.alloc = NULL;
489 d->attrs.alloc = NULL;
532− d->nodes.size = 500;
490+ d->nodes.size = 1000;
533 d->nodes.used_back = 0;
491 d->nodes.used_back = 0;
534 d->nodes.used_front = 1;
492 d->nodes.used_front = 1;
535 d->nodes.nodes = buf->nodes;
493 d->nodes.nodes = buf->nodes;
@@ -539,13 +497,11 @@ document* hexml_document_parse(const char* s, int slen)
539 d->nodes.nodes[0].outer = start_length(0, slen);
497 d->nodes.nodes[0].outer = start_length(0, slen);
540 d->nodes.nodes[0].inner = start_length(0, slen);
498 d->nodes.nodes[0].inner = start_length(0, slen);
541 d->nodes.nodes[0].attrs = start_length(0, 0);
499 d->nodes.nodes[0].attrs = start_length(0, 0);
500+ d->nodes.nodes[0].nodes = parse_content(d);
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;
548− if (d->cursor < d->end)
502+ if (d->cursor < d->end && d->error_message == NULL)
549 set_error(d, "Trailing junk at the end of the document");
504 set_error(d, "Trailing junk at the end of the document");
550 return d;
506 return d;