on master help sign in
488 lines 15 kB history raw
2#include "../cbits/hexml.h"
3#include <stdlib.h>
4#include <string.h>
5#include <assert.h>
6#include <stdio.h>
8/////////////////////////////////////////////////////////////////////
9// TYPES
11static inline int end(str s) { return s.start + s.length; }
13static inline str start_length(int32_t start, int32_t length)
15 assert(start >= 0 && length >= 0);
16 str res;
17 res.start = start;
18 res.length = length;
19 return res;
22static inline str start_end(int32_t start, int32_t end)
24 return start_length(start, end - start);
27typedef struct
29 int size;
30 int used;
31 attr* attrs; // dynamically allocated buffer
32 attr* alloc; // what to call free on
33} attr_buffer;
35typedef struct
37 // have a cursor at the front, which is all the stuff I have written out, final
38 // have a cursor at the end, which is stack scoped, so children write out, then I do
39 // when you commit, you copy over from end to front
41 // nodes
42 int size;
43 int used_front; // front entries, stored for good
44 int used_back; // back entries, stack based, copied into front on </close>
45 // back entries which haven't yet been closed have nodes.length == -1
46 node* nodes; // dynamically allocated buffer
47 node* alloc; // what to call free on
48} node_buffer;
51struct document
53 const char* body; // pointer to initial argument, not owned by us
54 int body_len; // length of the body
56 char* error_message;
57 node_buffer nodes;
58 attr_buffer attrs;
59};
62/////////////////////////////////////////////////////////////////////
63// RENDER CODE
65typedef struct
67 const document* d; // Document, used to get at the body
68 char* buffer; // Buffer we are writing too
69 int length; // Length of buffer, do not write past the end.
70 // No character is reserved for a NULL terminator.
71 int cursor; // Cursor of where we are writing now
72} render;
74static inline void render_char(render* r, char c)
76 if (r->cursor < r->length)
77 r->buffer[r->cursor] = c;
78 r->cursor++;
81static inline void bound(int idx, int mn, int mx)
83 assert(idx >= mn && idx <= mx);
86static inline void bound_str(str s, int mn, int mx)
88 assert(s.length >= 0);
89 bound(s.start, mn, mx);
90 bound(end(s), mn, mx);
93static void render_str(render* r, str s)
95 bound_str(s, 0, r->d->body_len);
96 for (int i = 0; i < s.length; i++)
97 render_char(r, r->d->body[s.start + i]);
100static void render_tag(render* r, const node* n);
102static void render_content(render* r, const node* n)
104 bound_str(n->inner, 0, r->d->body_len);
105 bound_str(n->nodes, 0, r->d->nodes.used_front);
106 bound_str(n->attrs, 0, r->d->attrs.used);
108 int done = n->inner.start;
109 for (int i = 0; i < n->nodes.length; i++)
110 {
111 node* x = &r->d->nodes.nodes[n->nodes.start + i];
112 render_str(r, start_end(done, x->outer.start));
113 done = end(x->outer);
114 render_tag(r, x);
115 }
116 render_str(r, start_end(done, end(n->inner)));
119static void render_tag(render* r, const node* n)
121 render_char(r, '<');
122 render_str(r, n->name);
123 for (int i = 0; i < n->attrs.length; i++)
124 {
125 attr* x = &r->d->attrs.attrs[n->attrs.start + i];
126 render_char(r, ' ');
127 render_str(r, x->name);
128 render_char(r, '=');
129 render_char(r, '\"');
130 render_str(r, x->value);
131 render_char(r, '\"');
132 }
133 render_char(r, '>');
134 render_content(r, n);
135 render_char(r, '<');
136 render_char(r, '/');
137 render_str(r, n->name);
138 render_char(r, '>');
141int hexml_node_render(const document* d, const node* n, char* buffer, int length)
143 render r;
144 r.d = d;
145 r.buffer = buffer;
146 r.length = length;
147 r.cursor = 0;
148 // The root node (and only the root node) has an empty length, so just render its innards
149 if (n->name.length == 0)
150 render_content(&r, n);
151 else
152 render_tag(&r, n);
153 return r.cursor;
157/////////////////////////////////////////////////////////////////////
158// NOT THE PARSER
160char* hexml_document_error(const document* d){return d->error_message;}
161node* hexml_document_node(const document* d){return &d->nodes.nodes[0];}
163void hexml_document_free(document* d)
165 free(d->error_message);
166 free(d->nodes.alloc);
167 free(d->attrs.alloc);
168 free(d);
171node* hexml_node_children(const document* d, const node* n, int* res)
173 *res = n->nodes.length;
174 return &d->nodes.nodes[n->nodes.start];
177attr* hexml_node_attributes(const document* d, const node* n, int* res)
179 *res = n->attrs.length;
180 return &d->attrs.attrs[n->attrs.start];
183attr* hexml_node_attribute(const document* d, const node* n, const char* s, int slen)
185 if (slen == -1) slen = (int) strlen(s);
186 const int limit = end(n->attrs);
187 for (int i = n->attrs.start; i < limit; i++)
188 {
189 attr* r = &d->attrs.attrs[i];
190 if (r->name.length == slen && memcmp(s, &d->body[r->name.start], slen) == 0)
191 return r;
192 }
193 return NULL;
196// Search for given strings within a node
197node* hexml_node_child(const document* d, const node* parent, const node* prev, const char* s, int slen)
199 if (slen == -1) slen = (int) strlen(s);
200 int i = prev == NULL ? parent->nodes.start : (int) (prev + 1 - d->nodes.nodes);
201 const int limit = end(parent->nodes);
202 for (; i < limit; i++)
203 {
204 node* r = &d->nodes.nodes[i];
205 if (r->name.length == slen && memcmp(s, &d->body[r->name.start], slen) == 0)
206 return r;
207 }
208 return NULL;
211/////////////////////////////////////////////////////////////////////
212// PARSING CODE
214static inline node* node_alloc(node_buffer* b)
216 int space = b->size - b->used_back - b->used_front;
217 if (space < 1)
218 {
219 int size2 = (b->size + 1000) * 2;
220 node* buf2 = malloc(size2 * sizeof(node));
221 assert(buf2);
222 memcpy(buf2, b->nodes, b->used_front * sizeof(node));
223 memcpy(&buf2[size2 - b->used_back], &b->nodes[b->size - b->used_back], b->used_back * sizeof(node));
224 free(b->alloc);
225 b->size = size2;
226 b->nodes = buf2;
227 b->alloc = buf2;
228 }
229 b->used_back++;
230 return &b->nodes[b->size - b->used_back];
233// buffer[from .. from+n] = reverse (buffer[from .. from+n])
234// The ranges completely overlap
235static inline void reverse(node* buffer, int from, int n)
237 for (int to = from + n - 1; to > from; from++, to--)
238 {
239 node temp = buffer[to];
240 buffer[to] = buffer[from];
241 buffer[from] = temp;
242 }
245// buffer[to .. to+n] = reverse (buffer[from .. from+n])
246// The ranges may not overlap
247static inline void memcpy_reverse(node* buffer, int from, int to, int n)
249 int end = from + n - 1;
250 for (int i = 0; i < n; i++)
251 buffer[to+i] = buffer[end-i];
254// buffer[to .. to+n] = reverse (buffer[from .. from+n])
255// The ranges may overlap
256static inline void memmove_reverse(node* buffer, int from, int to, int n)
258 assert(from >= to); // always the case in my usages
259 if (to + n < from)
260 memcpy_reverse(buffer, from, to, n);
261 else
262 {
263 int overlap = from - to;
264 memcpy_reverse(buffer, from, to + n - overlap, overlap);
265 reverse(buffer, to, n - overlap);
266 }
269static inline str node_commit(node_buffer* b)
271 // first search for an "open" node (nodes.length == -1)
272 int open;
273 for (open = b->size - b->used_back; open < b->size; open++)
274 {
275 if (b->nodes[open].nodes.length == -1)
276 break;
277 }
279 int n = open - (b->size - b->used_back);
280 memmove_reverse(b->nodes, b->size - b->used_back, b->used_front, n);
281 b->used_back -= n;
282 b->used_front += n;
283 return start_length(b->used_front - n, n);
286static inline attr* attr_alloc(attr_buffer* b)
288 // Ensure there is at least one node left
289 int space = b->size - b->used;
290 if (space < 1)
291 {
292 int size2 = (b->size + 1000) * 2;
293 attr* buf2 = malloc(size2 * sizeof(attr));
294 assert(buf2);
295 memcpy(buf2, b->attrs, b->used * sizeof(attr));
296 free(b->alloc);
297 b->size = size2;
298 b->attrs = buf2;
299 b->alloc = buf2;
300 }
301 b->used++;
302 return &b->attrs[b->used - 1];
305static void set_error(document* d, const char* msg)
307 if (d->error_message != NULL) return; // keep the first error message
308 d->error_message = malloc(strlen(msg)+1);
309 assert(d->error_message);
310 strcpy(d->error_message, msg);
313static inline str gap(const char* ref, const char* start, const char* end)
315 return start_end(start - ref, end - ref);
318#define P_Abort(x) return x
319#define P_Tag tag_start = p;
320#define P_NameStart name_start = p
321#define P_NameEnd name_end = p
322#define P_AttribsStart \
323 me = node_alloc(&d->nodes); \
324 me->attrs.start = d->attrs.used; \
325 me->name = gap(d->body, name_start, name_end);
326#define P_AttribsEnd \
327 me->attrs = start_end(me->attrs.start, d->attrs.used);
328#define P_QuoteStart quote_start = p
329#define P_QuoteEnd \
330 attr* attr = attr_alloc(&d->attrs); \
331 attr->name = gap(d->body, name_start, name_end); \
332 attr->value = gap(d->body, quote_start, p);
333#define P_TagComment \
334 // No action required - comments just flow as normal text
335#define P_TagOpen \
336 me->nodes.length = -1; \
337 me->inner = gap(d->body, p, p); \
338 me->outer = gap(d->body, tag_start, tag_start);
339#define P_TagClose \
340 str nodes = node_commit(&d->nodes); \
341 if (d->nodes.used_back == 0) \
342 return "Closing tag with no openning"; \
343 me = &d->nodes.nodes[d->nodes.size - d->nodes.used_back]; \
344 me->nodes = nodes; \
345 if (me->name.length != gap(d->body, name_start, name_end).length || \
346 memcmp(&d->body[me->name.start], name_start, me->name.length) != 0) \
347 return "Mismatch in closing tag"; \
348 me->inner = gap(d->body, &d->body[me->inner.start], tag_start); \
349 me->outer = gap(d->body, &d->body[me->outer.start], p);
350#define P_TagOpenClose \
351 me->nodes = start_end(0, 0); \
352 me->inner = gap(d->body, p, p); \
353 me->outer = gap(d->body, tag_start, p);
355// Given the parsed string, return either NULL (success) or an error message (failure)
356static const char* parser(const char* p, document* d)
358 const char *tag_start; // Where the tag starts [<foo
359 const char *name_start, *name_end; // Where a name is <[foo] or <foo [bar]=
360 const char *quote_start; // Where an attribute quote is <foo bar='[123'>
361 node* me; // The current node I'm working on
362# include "test.h"
363 return NULL;
366// Based on looking at ~50Kb XML documents, they seem to have ~700 attributes
367// and ~300 nodes, so size appropriately to cope with that.
368typedef struct
370 document document;
371 attr attrs[1000];
372 node nodes[500];
373} buffer;
375document* hexml_document_parse(const char* s, int slen)
377 if (slen == -1) slen = (int) strlen(s);
378 assert(s[slen] == 0);
380 buffer* buf = malloc(sizeof(buffer));
381 assert(buf);
382 document* d = &buf->document;
383 d->body = s;
384 d->body_len = slen;
385 d->error_message = NULL;
386 d->attrs.size = 1000;
387 d->attrs.used = 0;
388 d->attrs.attrs = buf->attrs;
389 d->attrs.alloc = NULL;
390 d->nodes.size = 500;
391 d->nodes.used_back = 0;
392 d->nodes.used_front = 1;
393 d->nodes.nodes = buf->nodes;
394 d->nodes.alloc = NULL;
396 d->nodes.nodes[0].name = start_length(0, 0);
397 d->nodes.nodes[0].outer = start_length(0, slen);
398 d->nodes.nodes[0].inner = start_length(0, slen);
399 d->nodes.nodes[0].attrs = start_length(0, 0);
401 // Introduce an intermediate result, otherwise behaviour is undefined
402 // because there is no guaranteed ordering between LHS and RHS evaluation
403 const char* res = parser(s, d);
404 if (res != NULL)
405 set_error(d, res);
406 else
407 {
408 d->nodes.nodes[0].nodes = node_commit(&d->nodes);
409 if (d->nodes.used_back != 0)
410 d->error_message = strdup("used_back failure");
411 }
412 return d;
415/////////////////////////////////////////////////////////////////////
416// TEST HARNESS (temporary)
418#ifdef TEST
420static char* resolve(const document* d, str loc)
422 char* buf = malloc(loc.length + 3);
423 memcpy(&buf[1], &d->body[loc.start], loc.length);
424 buf[0] = '\"';
425 buf[loc.length+1] = '\"';
426 buf[loc.length+2] = 0;
427 return buf;
430static char* disp(str loc)
432 char* buf = malloc(100);
433 if (loc.length == 0)
434 strcpy(buf, "[]");
435 else
436 sprintf(buf, "[%i..%i]", loc.start, end(loc)-1);
437 return buf;
440int main()
442 setbuf(stdout, NULL);
443 const char* example = "<test foo='123 xyz' bar='t'><inner /> value</test>";
444 document* d = hexml_document_parse(example, -1);
445 if (d->error_message != NULL)
446 printf("Parse failure: %s\n", d->error_message);
447 else
448 {
449 char buf[1000];
450 int len = hexml_node_render(d, hexml_document_node(d), buf, sizeof(buf));
451 buf[len] = 0;
452 printf("Result = %s\n", buf);
453 printf("Used nodes = %i, attributes = %i\n", d->nodes.used_front, d->attrs.used);
455 for (int i = 0; i < d->attrs.used; i++)
456 printf("Attr %i: %s=%s\n", i, resolve(d, d->attrs.attrs[i].name), resolve(d, d->attrs.attrs[i].value));
457 for (int i = 0; i < d->nodes.used_front; i++)
458 {
459 printf("Node %i: %s attrs=%s, nodes=%s\n", i, resolve(d, d->nodes.nodes[i].name), disp(d->nodes.nodes[i].attrs), disp(d->nodes.nodes[i].nodes));
460 printf(" inner %s\n outer %s\n", resolve(d, d->nodes.nodes[i].inner), resolve(d, d->nodes.nodes[i].outer));
461 }
462 }
463 hexml_document_free(d);
464 return 0;
467#endif
469/*
470void main()
472 setbuf(stdout, NULL);
473 char* body = "<?xml version=\"1.0\" lang=\"ja\"?><foo><bar baz=\"qux\">quux</bar><bar baz=\"bar\">hoge</bar><piyo>&gt;piyopiyo&lt;</piyo></foo><foo2 />";
474 body = "<test>X</test> ";
475 document* doc = document_parse(body, -1);
476 char* err = document_error(doc);
477 if (err == NULL)
478 {
479 int len = document_render(doc, NULL, 0);
480 char* s = malloc(len + 1);
481 document_render(doc, s, len);
482 s[len] = 0;
483 printf("Parse successful, %s\n", s);
484 }
485 else
486 printf("Parse failed, %s\n", err);
488*/