diff --git a/common.mk b/common.mk index 57e80ddce26ef183d36664f3618f8bc0f9f92ce0..de00ecbf0e2f0f58ff033d547f85ce200b67883e 100644 --- a/common.mk +++ b/common.mk @@ -3,7 +3,7 @@ LDFLAGS := $(shell pkg-config --libs glib-2.0) CC := gcc # Set V=1 for verbose mode... V := 0 -CFLAGS += -DINCLUDE_TESTS +CFLAGS += -DINCLUDE_TESTS $(EXTRA_CFLAGS) HUSH = $(TOPLEVEL)/lib/hush # Check to make sure variables are properly set diff --git a/src/allocator.c b/src/allocator.c index 77f14ea347d7a8f6c0f7da11a517e7b78ff25dab..c45affd45e7a9d7c507e6824968985306e88115c 100644 --- a/src/allocator.c +++ b/src/allocator.c @@ -20,17 +20,22 @@ struct arena_link { struct arena { struct arena_link *head; size_t block_size; + size_t used; + size_t wasted; }; arena_t new_arena(size_t block_size) { + if (block_size == 0) + block_size = 4096; struct arena *ret = g_new(struct arena, 1); struct arena_link *link = (struct arena_link*)g_malloc0(sizeof(struct arena_link) + block_size); link->free = block_size; link->used = 0; link->next = NULL; ret->head = link; - ret->block_size = 0; - + ret->block_size = block_size; + ret->used = 0; + ret->wasted = sizeof(struct arena_link) + sizeof(struct arena) + block_size; return ret; } @@ -38,12 +43,16 @@ void* arena_malloc(arena_t arena, size_t size) { if (size <= arena->head->free) { // fast path.. void* ret = arena->head->rest + arena->head->used; + arena->used += size; + arena->wasted -= size; arena->head->used += size; arena->head->free -= size; return ret; } else if (size > arena->block_size) { // We need a new, dedicated block for it, because it won't fit in a standard sized one. // This involves some annoying casting... + arena->used += size; + arena->wasted += sizeof(struct arena_link*); void* link = g_malloc(size + sizeof(struct arena_link*)); *(struct arena_link**)link = arena->head->next; arena->head->next = (struct arena_link*)link; @@ -55,6 +64,8 @@ void* arena_malloc(arena_t arena, size_t size) { link->used = size; link->next = arena->head; arena->head = link; + arena->used += size; + arena->wasted += sizeof(struct arena_link) + arena->block_size - size; return link->rest; } } @@ -71,3 +82,8 @@ void delete_arena(arena_t arena) { } g_free(arena); } + +void allocator_stats(arena_t arena, arena_stats_t *stats) { + stats->used = arena->used; + stats->wasted = arena->wasted; +} diff --git a/src/allocator.h b/src/allocator.h index 3bc7cedc44739424dd9ad7a9f9f7db6b5b0d51ad..4904b290b14c76bef9323e1ddde064019be3fea2 100644 --- a/src/allocator.h +++ b/src/allocator.h @@ -8,5 +8,12 @@ arena_t new_arena(size_t block_size); // pass 0 for default... void* arena_malloc(arena_t arena, size_t count) __attribute__(( malloc, alloc_size(2) )); void delete_arena(arena_t arena); +typedef struct { + size_t used; + size_t wasted; +} arena_stats_t; + +void allocator_stats(arena_t arena, arena_stats_t *stats); + #endif // #ifndef LIB_ALLOCATOR__H__ diff --git a/src/hammer.c b/src/hammer.c index 0e48b85cafd27a3d345dd62c064ac74f2f5f3980..ced1a23db2bb4aff1fdec64cd0dbb098520f0260 100644 --- a/src/hammer.c +++ b/src/hammer.c @@ -37,10 +37,9 @@ guint djbhash(const uint8_t *buf, size_t len) { parse_result_t* do_parse(const parser_t* parser, parse_state_t *state) { // TODO(thequux): add caching here. - parser_cache_key_t key = { - .input_pos = state->input_stream, - .parser = parser - }; + parser_cache_key_t *key = a_new(parser_cache_key_t, 1); + key->input_pos = state->input_stream; + key->parser = parser; // check to see if there is already a result for this object... if (g_hash_table_contains(state->cache, &key)) { @@ -50,9 +49,11 @@ parse_result_t* do_parse(const parser_t* parser, parse_state_t *state) { } else { // It doesn't exist... run the parse_result_t *res; - if (parser) + if (parser) { res = parser->fn(parser->env, state); - else + if (res) + res->arena = state->arena; + } else res = NULL; if (state->input_stream.overrun) res = NULL; // overrun is always failure. @@ -766,7 +767,10 @@ static void test_and(void) { static void test_not(void) { const parser_t *not_1 = sequence(ch('a'), choice(ch('+'), token((const uint8_t*)"++", 2), NULL), ch('b'), NULL); - const parser_t *not_2 = sequence(ch('a'), choice(sequence(ch('+'), not(ch('+')), NULL), token((const uint8_t*)"", 2), NULL), ch('b'), NULL); + const parser_t *not_2 = sequence(ch('a'), + choice(sequence(ch('+'), not(ch('+')), NULL), + token((const uint8_t*)"++", 2), + NULL), ch('b'), NULL); g_check_parse_ok(not_1, "a+b", 3, "(s0x61 s0x2B s0x62)"); g_check_parse_failed(not_1, "a++b", 4); diff --git a/src/test_suite.h b/src/test_suite.h index 47c5ac401f4a368b7ceba0642030d9f3aaaa0457..a67fa1f6cfe5b86babcd775d5c70d91755163543 100644 --- a/src/test_suite.h +++ b/src/test_suite.h @@ -54,11 +54,17 @@ #define g_check_parse_ok(parser, input, inp_len, result) { \ parse_result_t *res = parse(parser, (const uint8_t*)input, inp_len); \ if (!res) { \ - g_test_message("Parse failed on line %d", __LINE__); \ + g_test_message("Parse failed on line %d", __LINE__); \ g_test_fail(); \ } else { \ char* cres = write_result_unamb(res->ast); \ g_check_string(cres, ==, result); \ + arena_stats_t stats; \ + allocator_stats(res->arena, &stats); \ + g_test_message("Parse used %zd bytes, wasted %zd bytes. " \ + "Inefficiency: %5f%%", \ + stats.used, stats.wasted, \ + stats.wasted * 100. / (stats.used+stats.wasted)); \ } \ }