packages feed

hexml 0.1 → 0.2

raw patch · 5 files changed

+79/−73 lines, 5 filesPVP ok

version bump matches the API change (PVP)

API changes (from Hackage documentation)

Files

CHANGES.txt view
@@ -1,4 +1,7 @@ Changelog for Hexml +0.2+    #6, remove buffer overruns+    #5, add lower bounds 0.1     Initial version
README.md view
@@ -8,4 +8,4 @@  The name "hexml" is a combination of "Hex" (a curse) and "XML". The "X" should not be capitalised because the parser is more curse and less XML. -Hexml may be suitable if you want to quickly parse XML, from known sources, and a full XML parser has been shown to be a bottleneck. As an alternative to hexml, which supports things like entities but is still pretty fast, see Pugixml.+Hexml may be suitable if you want to quickly parse XML, from known sources, and a full XML parser has been shown to be a bottleneck. As an alternative to hexml, which supports things like entities but is still pretty fast, see [Pugixml](http://pugixml.org/) (with a [Haskell binding](https://hackage.haskell.org/package/pugixml)).
cbits/hexml.c view
@@ -10,18 +10,18 @@ ///////////////////////////////////////////////////////////////////// // TYPES -int static inline end(str s) { return s.start + s.length; }+static inline int end(str s) { return s.start + s.length; } -str static inline start_length(int32_t start, int32_t length)+static inline str start_length(int32_t start, int32_t length) {-    if (start < 0 || length < 0) assert(0);+    assert(start >= 0 && length >= 0);     str res;     res.start = start;     res.length = length;     return res; } -str static inline start_end(int32_t start, int32_t end)+static inline str start_end(int32_t start, int32_t end) {     return start_length(start, end - start); }@@ -51,11 +51,11 @@  struct document {-    char* body; // pointer to initial argument, not owned by us+    const char* body; // pointer to initial argument, not owned by us      // things only used while parsing-    char* cursor; // pointer to where we are in body-    char* end; // pointer to one past the last char+    const char* cursor; // pointer to where we are in body+    const char* end; // pointer to one past the last char     // if cursor is > end we have gone past the end      char* error_message;@@ -63,8 +63,8 @@     attr_buffer attrs; }; -int static inline doc_length(document* d) { return (int) (d->end - d->body); }-int static inline doc_position(document* d) { return (int) (d->cursor - d->body); }+static inline int doc_length(const document* d) { return (int) (d->end - d->body); }+static inline int doc_position(const document* d) { return (int) (d->cursor - d->body); }   /////////////////////////////////////////////////////////////////////@@ -72,49 +72,45 @@  typedef struct {-    document* d;+    const document* d;     char* buffer;     int length;     int cursor; } render; -void static inline render_char(render* r, char c)+static inline void render_char(render* r, char c) {     if (r->cursor < r->length)         r->buffer[r->cursor] = c;     r->cursor++; } -void static inline bound(char* msg, int index, int mn, int mx)+static inline void bound(int idx, int mn, int mx) {-    if (index < mn || index > mx)-    {-        //printf("Bounds checking failed %s, got %i, which should be in %i:%i\n", msg, index, mn, mx);-        assert(0);-    }+    assert(idx >= mn && idx <= mx); } -void static inline bound_str(char* msg, str s, int mn, int mx)+static inline void bound_str(str s, int mn, int mx) {-    if (s.length < 0) assert(0);-    bound(msg, s.start, mn, mx);-    bound(msg, end(s), mn, mx);+    assert(s.length >= 0);+    bound(s.start, mn, mx);+    bound(end(s), mn, mx); }  void render_str(render* r, str s) {-    bound_str("render_str", s, 0, doc_length(r->d));+    bound_str(s, 0, doc_length(r->d));     for (int i = 0; i < s.length; i++)         render_char(r, r->d->body[s.start + i]); } -void render_tag(render* r, node* n);+void render_tag(render* r, const node* n); -void render_content(render* r, node* n)+void render_content(render* r, const node* n) {-    bound_str("render_conent inner", n->inner, 0, doc_length(r->d));-    bound_str("render_conent nodes", n->nodes, 0, r->d->nodes.used_front);-    bound_str("render_conent attrs", n->attrs, 0, r->d->attrs.used);+    bound_str(n->inner, 0, doc_length(r->d));+    bound_str(n->nodes, 0, r->d->nodes.used_front);+    bound_str(n->attrs, 0, r->d->attrs.used);      int done = n->inner.start;     for (int i = 0; i < n->nodes.length; i++)@@ -127,7 +123,7 @@     render_str(r, start_end(done, end(n->inner))); } -void render_tag(render* r, node* n)+void render_tag(render* r, const node* n) {     render_char(r, '<');     render_str(r, n->name);@@ -149,7 +145,7 @@     render_char(r, '>'); } -int node_render(document* d, node* n, char* buffer, int length)+int node_render(const document* d, const node* n, char* buffer, int length) {     render r;     r.d = d;@@ -168,8 +164,8 @@ ///////////////////////////////////////////////////////////////////// // NOT THE PARSER -char* document_error(document* d){return d->error_message;}-node* document_node(document* d){return &d->nodes.nodes[0];}+char* document_error(const document* d){return d->error_message;}+node* document_node(const document* d){return &d->nodes.nodes[0];}  void document_free(document* d) {@@ -179,20 +175,20 @@     free(d); } -node* node_children(document* d, node* n, int* res)+node* node_children(const document* d, const node* n, int* res) {     *res = n->nodes.length;     return &d->nodes.nodes[n->nodes.start]; } -attr* node_attributes(document* d, node* n, int* res)+attr* node_attributes(const document* d, const node* n, int* res) {     *res = n->attrs.length;     return &d->attrs.attrs[n->attrs.start]; }  -attr* node_attributeBy(document* d, node* n, char* s, int slen)+attr* node_attributeBy(const document* d, const node* n, const char* s, int slen) {     if (slen == -1) slen = (int) strlen(s);     const int limit = end(n->attrs);@@ -206,7 +202,7 @@ }  // Search for given strings within a node-node* node_childBy(document* d, node* parent, node* prev, char* s, int slen)+node* node_childBy(const document* d, const node* parent, const node* prev, const char* s, int slen) {     if (slen == -1) slen = (int) strlen(s);     int i = prev == NULL ? parent->nodes.start : (int) (prev + 1 - d->nodes.nodes);@@ -244,22 +240,22 @@     done = 1; } -bool static inline is(char c, char tag) { return parse_table[c] & tag; }-bool static inline is_name1(char c) { return is(c, tag_name1); }-bool static inline is_name(char c) { return is(c, tag_name); }-bool static inline is_space(char c) { return is(c, tag_space); }+static inline bool is(char c, char tag) { return parse_table[c] & tag; }+static inline bool is_name1(char c) { return is(c, tag_name1); }+static inline bool is_name(char c) { return is(c, tag_name); }+static inline bool is_space(char c) { return is(c, tag_space); }   ///////////////////////////////////////////////////////////////////// // PARSER COMBINATORS -char static inline peekAt(document* d, int i) { return d->cursor[i]; }-void static inline skip(document* d, int i) { d->cursor += i; }-char static inline peek(document* d) { return peekAt(d, 0); }-char static inline get(document* d) { char c = peek(d); skip(d, 1); return c; }+static inline char peekAt(const document* d, int i) { return d->cursor[i]; }+static inline char peek(const document* d) { return peekAt(d, 0); }+static inline void skip(document* d, int i) { d->cursor += i; }+static inline char get(document* d) { char c = peek(d); skip(d, 1); return c; }  // Remove whitespace characters from the cursor while they are still whitespace-void static inline trim(document* d)+static inline void trim(document* d) {     while (is_space(peek(d)))         skip(d, 1);@@ -285,7 +281,7 @@ ///////////////////////////////////////////////////////////////////// // PARSING CODE -void static inline node_alloc(node_buffer* b, int ask)+static inline void node_alloc(node_buffer* b, int ask) {     int space = b->size - b->used_back - b->used_front;     if (space >= ask) return;@@ -300,7 +296,7 @@     b->alloc = buf2; } -void static inline attr_alloc(attr_buffer* b, int ask)+static inline void attr_alloc(attr_buffer* b, int ask) {     int space = b->size - b->used;     if (space >= ask) return;@@ -314,7 +310,7 @@     b->alloc = buf2; } -void set_error(document* d, char* msg)+void set_error(document* d, const char* msg) {     if (d->error_message != NULL) return; // keep the first error message     d->error_message = malloc(strlen(msg)+1);@@ -324,7 +320,7 @@  // you now expect a name, perhaps preceeded by whitespace // the name may be empty-str static inline parse_name(document* d)+static inline str parse_name(document* d) {     int start = doc_position(d);     if (!is_name1(peek(d)))@@ -335,14 +331,15 @@     return start_end(start, doc_position(d)); } -str static inline parse_attrval(document* d)+static inline str parse_attrval(document* d) {     trim(d);-    if (get(d) != '=')+    if (peek(d) != '=')     {         set_error(d, "Expected = in attribute, but missing");         return start_length(0, 0);     }+    skip(d, 1);     trim(d);     char c = peek(d);     if (c == '\"' || c == '\'')@@ -368,10 +365,10 @@  // seen a tag name, now looking for attributes terminated by > // puts the attributes it finds in the attribute buffer-str static inline parse_attributes(document* d)+static inline str parse_attributes(document* d) {     int start = d->attrs.used;-    for (int i = 0; ; i++)+    while (d->error_message == NULL)     {         trim(d);         str name = parse_name(d);@@ -388,7 +385,7 @@   // Add a new entry into tag, am at a '<'-void static inline parse_tag(document* d)+static inline void parse_tag(document* d) {     node_alloc(&d->nodes, 1);     d->nodes.used_back++;@@ -399,12 +396,18 @@     assert(c == '<');     if (peek(d) == '?') skip(d, 1);     me->name = parse_name(d);+    if (me->name.length == 0)+    {+        set_error(d, "Missing tag name");+        return;+    }     me->attrs = parse_attributes(d);+    if (d->error_message != NULL) return; -    c = get(d);-    if ((c == '/' || c == '?') && peek(d) == '>')+    c = peek(d);+    if ((c == '/' || c == '?') && peekAt(d, 1) == '>')     {-        skip(d, 1);+        skip(d, 2);         me->nodes = start_length(0, 0);         me->outer.length = start_end(me->outer.start, doc_position(d)).length;         me->inner = start_length(doc_position(d), 0);@@ -415,6 +418,8 @@         set_error(d, "Gunk at the end of the tag");         return;     }+    else+        skip(d, 1);     me->inner.start = doc_position(d);     str content = parse_content(d); @@ -471,7 +476,7 @@                 return start_end(0, 0);             }             skip(d, 2);-            while (1)+            for (;;)             {                 if (!find(d, '>'))                 {@@ -507,7 +512,7 @@     node nodes[500]; } buffer; -document* document_parse(char* s, int slen)+document* document_parse(const char* s, int slen) {     if (slen == -1) slen = (int) strlen(s);     assert(s[slen] == 0);@@ -540,9 +545,7 @@     str content = parse_content(d);     d->nodes.nodes[0].nodes = content; -    if (d->cursor < d->end && d->error_message == NULL)-    {+    if (d->cursor < d->end)         set_error(d, "Trailing junk at the end of the document");-    }     return d; }
cbits/hexml.h view
@@ -30,7 +30,7 @@ // Parse a document, returns a potentially invalid document - use document_error to check // Must use document_free to release the memory (even if invalid). // The string must be nul terminated, e.g. slen != -1 ==> slen[0]-document* document_parse(char* s, int slen);+document* document_parse(const char* s, int slen);  // Free the memory returned from document_parse. void document_free(document* d);@@ -38,19 +38,19 @@ // Generate a fresh string with the same semantics as the node. // Requires an input buffer, returns the size of the rendered document. // Requires the string passed to document_parse to be valid.-int node_render(document* d, node* n, char* buffer, int length);+int node_render(const document* d, const node* n, char* buffer, int length);  // Return either NULL (successful parse) or the error message.-char* document_error(document* d);+char* document_error(const document* d);  // Return the root node of the document - imagine the document is wrapped in <>$DOC</> tags.-node* document_node(document* d);+node* document_node(const document* d);  // List all items within a node-node* node_children(document* d, node* n, int* res);-attr* node_attributes(document* d, node* n, int* res);+node* node_children(const document* d, const node* n, int* res);+attr* node_attributes(const document* d, const node* n, int* res);  // Search for given strings within a node, note that prev may be NULL // Requires the string passed to document_parse to be valid.-node* node_childBy(document* d, node* parent, node* prev, char* s, int slen);-attr* node_attributeBy(document* d, node* n, char* s, int slen);+node* node_childBy(const document* d, const node* parent, const node* prev, const char* s, int slen);+attr* node_attributeBy(const document* d, const node* n, const char* s, int slen);
hexml.cabal view
@@ -1,7 +1,7 @@ cabal-version:      >= 1.18 build-type:         Simple name:               hexml-version:            0.1+version:            0.2 license:            BSD3 license-file:       LICENSE category:           Development@@ -30,9 +30,9 @@     default-language:   Haskell2010      build-depends:-        base > 4 && < 5,+        base >= 4.5 && < 5,         bytestring,-        extra+        extra >= 0.3      c-sources:        cbits/hexml.c     include-dirs:     cbits