#include #include #include #include #include #include #include #include "utils/utils.h" #include "testutils.h" #define NODE_REF_CHUNK 8192 static uint16_t *node_ref; static uintptr_t node_ref_alloc; static uintptr_t node_counter; #define GROW_REF \ if (node_counter >= node_ref_alloc) { \ uint16_t *temp = realloc(node_ref, \ (node_ref_alloc + NODE_REF_CHUNK) * \ sizeof(uint16_t)); \ if (temp == NULL) { \ printf("FAIL - no memory\n"); \ exit(1); \ } \ node_ref = temp; \ node_ref_alloc += NODE_REF_CHUNK; \ } static hubbub_error create_comment(void *ctx, const hubbub_string *data, void **result); static hubbub_error create_doctype(void *ctx, const hubbub_doctype *doctype, void **result); static hubbub_error create_element(void *ctx, const hubbub_tag *tag, void **result); static hubbub_error create_text(void *ctx, const hubbub_string *data, void **result); static hubbub_error ref_node(void *ctx, void *node); static hubbub_error unref_node(void *ctx, void *node); static hubbub_error append_child(void *ctx, void *parent, void *child, void **result); static hubbub_error insert_before(void *ctx, void *parent, void *child, void *ref_child, void **result); static hubbub_error remove_child(void *ctx, void *parent, void *child, void **result); static hubbub_error clone_node(void *ctx, void *node, bool deep, void **result); static hubbub_error reparent_children(void *ctx, void *node, void *new_parent); static hubbub_error get_parent(void *ctx, void *node, bool element_only, void **result); static hubbub_error has_children(void *ctx, void *node, bool *result); static hubbub_error form_associate(void *ctx, void *form, void *node); static hubbub_error add_attributes(void *ctx, void *node, const hubbub_attribute *attributes, uint32_t n_attributes); static hubbub_error set_quirks_mode(void *ctx, hubbub_quirks_mode mode); static hubbub_tree_handler tree_handler = { create_comment, create_doctype, create_element, create_text, ref_node, unref_node, append_child, insert_before, remove_child, clone_node, reparent_children, get_parent, has_children, form_associate, add_attributes, set_quirks_mode, NULL, NULL }; static void *myrealloc(void *ptr, size_t len, void *pw) { UNUSED(pw); return realloc(ptr, len); } static int run_test(int argc, char **argv, unsigned int CHUNK_SIZE) { hubbub_parser *parser; hubbub_parser_optparams params; FILE *fp; size_t len, origlen; uint8_t *buf = alloca(CHUNK_SIZE); const char *charset; hubbub_charset_source cssource; bool passed = true; uintptr_t n; UNUSED(argc); node_ref = calloc(NODE_REF_CHUNK, sizeof(uint16_t)); if (node_ref == NULL) { printf("Failed allocating node_ref\n"); return 1; } node_ref_alloc = NODE_REF_CHUNK; assert(hubbub_parser_create("UTF-8", false, myrealloc, NULL, &parser) == HUBBUB_OK); params.tree_handler = &tree_handler; assert(hubbub_parser_setopt(parser, HUBBUB_PARSER_TREE_HANDLER, ¶ms) == HUBBUB_OK); params.document_node = (void *) ++node_counter; ref_node(NULL, (void *) node_counter); assert(hubbub_parser_setopt(parser, HUBBUB_PARSER_DOCUMENT_NODE, ¶ms) == HUBBUB_OK); fp = fopen(argv[1], "rb"); if (fp == NULL) { printf("Failed opening %s\n", argv[1]); return 1; } fseek(fp, 0, SEEK_END); origlen = len = ftell(fp); fseek(fp, 0, SEEK_SET); while (len > 0) { ssize_t bytes_read = fread(buf, 1, CHUNK_SIZE, fp); if (bytes_read < 1) break; assert(hubbub_parser_parse_chunk(parser, buf, bytes_read) == HUBBUB_OK); len -= bytes_read; } assert(len == 0); fclose(fp); charset = hubbub_parser_read_charset(parser, &cssource); printf("Charset: %s (from %d)\n", charset, cssource); hubbub_parser_destroy(parser); /* Ensure that all nodes have been released by the treebuilder */ for (n = 1; n <= node_counter; n++) { if (node_ref[n] != 0) { printf("%" PRIuPTR " still referenced (=%u)\n", n, node_ref[n]); passed = false; } } free(node_ref); node_counter = 0; printf("%s\n", passed ? "PASS" : "FAIL"); return passed ? 0 : 1; } int main(int argc, char **argv) { int ret; int shift; int offset; if (argc != 2) { printf("Usage: %s \n", argv[0]); return 1; } #define DO_TEST(n) if ((ret = run_test(argc, argv, (n))) != 0) return ret for (shift = 0; (1 << shift) != 16384; shift++) for (offset = 0; offset < 10; offset += 3) DO_TEST((1 << shift) + offset); return 0; #undef DO_TEST } hubbub_error create_comment(void *ctx, const hubbub_string *data, void **result) { printf("Creating (%" PRIuPTR ") [comment '%.*s']\n", ++node_counter, (int) data->len, data->ptr); assert(memchr(data->ptr, 0xff, data->len) == NULL); GROW_REF node_ref[node_counter] = 0; ref_node(ctx, (void *) node_counter); *result = (void *) node_counter; return HUBBUB_OK; } hubbub_error create_doctype(void *ctx, const hubbub_doctype *doctype, void **result) { printf("Creating (%" PRIuPTR ") [doctype '%.*s']\n", ++node_counter, (int) doctype->name.len, doctype->name.ptr); assert(memchr(doctype->name.ptr, 0xff, doctype->name.len) == NULL); if (doctype->public_missing == false) { assert(memchr(doctype->public_id.ptr, 0xff, doctype->public_id.len) == NULL); } if (doctype->system_missing == false) { assert(memchr(doctype->system_id.ptr, 0xff, doctype->system_id.len) == NULL); } GROW_REF node_ref[node_counter] = 0; ref_node(ctx, (void *) node_counter); *result = (void *) node_counter; return HUBBUB_OK; } hubbub_error create_element(void *ctx, const hubbub_tag *tag, void **result) { uint32_t i; printf("Creating (%" PRIuPTR ") [element '%.*s']\n", ++node_counter, (int) tag->name.len, tag->name.ptr); assert(memchr(tag->name.ptr, 0xff, tag->name.len) == NULL); for (i = 0; i < tag->n_attributes; i++) { hubbub_attribute *attr = &tag->attributes[i]; assert(memchr(attr->name.ptr, 0xff, attr->name.len) == NULL); assert(memchr(attr->value.ptr, 0xff, attr->value.len) == NULL); } GROW_REF node_ref[node_counter] = 0; ref_node(ctx, (void *) node_counter); *result = (void *) node_counter; return HUBBUB_OK; } hubbub_error create_text(void *ctx, const hubbub_string *data, void **result) { printf("Creating (%" PRIuPTR ") [text '%.*s']\n", ++node_counter, (int) data->len, data->ptr); assert(memchr(data->ptr, 0xff, data->len) == NULL); GROW_REF node_ref[node_counter] = 0; ref_node(ctx, (void *) node_counter); *result = (void *) node_counter; return HUBBUB_OK; } hubbub_error ref_node(void *ctx, void *node) { UNUSED(ctx); printf("Referencing %" PRIuPTR " (=%u)\n", (uintptr_t) node, ++node_ref[(uintptr_t) node]); return HUBBUB_OK; } hubbub_error unref_node(void *ctx, void *node) { UNUSED(ctx); printf("Unreferencing %" PRIuPTR " (=%u)\n", (uintptr_t) node, --node_ref[(uintptr_t) node]); return HUBBUB_OK; } hubbub_error append_child(void *ctx, void *parent, void *child, void **result) { printf("Appending %" PRIuPTR " to %" PRIuPTR "\n", (uintptr_t) child, (uintptr_t) parent); ref_node(ctx, child); *result = (void *) child; return HUBBUB_OK; } hubbub_error insert_before(void *ctx, void *parent, void *child, void *ref_child, void **result) { printf("Inserting %" PRIuPTR " in %" PRIuPTR " before %" PRIuPTR "\n", (uintptr_t) child, (uintptr_t) parent, (uintptr_t) ref_child); ref_node(ctx, child); *result = (void *) child; return HUBBUB_OK; } hubbub_error remove_child(void *ctx, void *parent, void *child, void **result) { printf("Removing %" PRIuPTR " from %" PRIuPTR "\n", (uintptr_t) child, (uintptr_t) parent); ref_node(ctx, child); *result = (void *) child; return HUBBUB_OK; } hubbub_error clone_node(void *ctx, void *node, bool deep, void **result) { printf("%sCloning %" PRIuPTR " -> %" PRIuPTR "\n", deep ? "Deep-" : "", (uintptr_t) node, ++node_counter); GROW_REF node_ref[node_counter] = 0; ref_node(ctx, (void *) node_counter); *result = (void *) node_counter; return HUBBUB_OK; } hubbub_error reparent_children(void *ctx, void *node, void *new_parent) { UNUSED(ctx); printf("Reparenting children of %" PRIuPTR " to %" PRIuPTR "\n", (uintptr_t) node, (uintptr_t) new_parent); return HUBBUB_OK; } hubbub_error get_parent(void *ctx, void *node, bool element_only, void **result) { printf("Retrieving parent of %" PRIuPTR " (%s)\n", (uintptr_t) node, element_only ? "element only" : ""); ref_node(ctx, (void *) 1); *result = (void *) 1; return HUBBUB_OK; } hubbub_error has_children(void *ctx, void *node, bool *result) { UNUSED(ctx); printf("Want children for %" PRIuPTR "\n", (uintptr_t) node); *result = false; return HUBBUB_OK; } hubbub_error form_associate(void *ctx, void *form, void *node) { UNUSED(ctx); printf("Associating %" PRIuPTR " with form %" PRIuPTR "\n", (uintptr_t) node, (uintptr_t) form); return HUBBUB_OK; } hubbub_error add_attributes(void *ctx, void *node, const hubbub_attribute *attributes, uint32_t n_attributes) { uint32_t i; UNUSED(ctx); UNUSED(attributes); UNUSED(n_attributes); printf("Adding attributes to %" PRIuPTR "\n", (uintptr_t) node); for (i = 0; i < n_attributes; i++) { const hubbub_attribute *attr = &attributes[i]; assert(memchr(attr->name.ptr, 0xff, attr->name.len) == NULL); assert(memchr(attr->value.ptr, 0xff, attr->value.len) == NULL); } return HUBBUB_OK; } hubbub_error set_quirks_mode(void *ctx, hubbub_quirks_mode mode) { UNUSED(ctx); printf("Quirks mode = %u\n", mode); return HUBBUB_OK; }