diff options
author | kartofen <mladenovnasko0@gmail.com> | 2024-09-08 18:36:07 +0300 |
---|---|---|
committer | kartofen <mladenovnasko0@gmail.com> | 2024-09-08 18:36:07 +0300 |
commit | 4308cd4abe5a75fb8410df929eac687cbd04032b (patch) | |
tree | 4a5871db2168cb96deab29be7aed36262511c0c1 | |
parent | db32849ce314a93db01f877a057a91022fec7c8b (diff) |
update mempool and add implement hash array mapped trie (not integrated)
-rw-r--r-- | README.md | 3 | ||||
-rw-r--r-- | files/test-lambda.l | 7 | ||||
-rw-r--r-- | src/env.c | 5 | ||||
-rw-r--r-- | src/hamt.c | 337 | ||||
-rw-r--r-- | src/hamt.h | 45 | ||||
-rw-r--r-- | src/hashtable.c | 9 | ||||
-rw-r--r-- | src/main.c | 18 | ||||
-rw-r--r-- | src/memdebug.h | 35 | ||||
-rw-r--r-- | src/mempool.h | 22 | ||||
-rw-r--r-- | src/value.c | 5 |
10 files changed, 438 insertions, 48 deletions
@@ -4,6 +4,9 @@ A simple lisp/scheme interpreter #### TODO +* totally opaque value_t (maybe add add something with tagged pointers) +* hamt for allocations + * reduce allocations * overhaul environement/closures * improve errors diff --git a/files/test-lambda.l b/files/test-lambda.l index 29e1f52..d58169a 100644 --- a/files/test-lambda.l +++ b/files/test-lambda.l @@ -77,6 +77,9 @@ (defmacro let* (vars body) (foldr (lambda (var rest) `(let (,var) ,rest)) body vars)) +(defmacro cond (vars) + (foldr (lambda (clause rest) `(if ,(car clause) ,(cdar clause) ,rest)) 1 vars)) + (let ((a 9) (b 13)) (+ a b)) @@ -85,3 +88,7 @@ (b (- a 3)) (c (+ a b))) (- c 3)) + +(cond (((= 1 2) 3) + ((= 1 (+ 1 1)) (+ 6 2)) + (1 (quote test)))) @@ -6,10 +6,7 @@ #include "value.h" #include "mempool.h" - -#define MEMPOOL_OBJ_TYPE struct symbol_table -#define MEMPOOL_CAP 64 -MEMPOOL_GENERATE(env) +MEMPOOL_GENERATE(env, struct symbol_table, 64) #define ENV_TABLE_CAP (1 << 3) diff --git a/src/hamt.c b/src/hamt.c new file mode 100644 index 0000000..a6be3ba --- /dev/null +++ b/src/hamt.c @@ -0,0 +1,337 @@ +#include <stdio.h> +#include <stdlib.h> +#include <stdint.h> +#include <string.h> +#include <assert.h> +#include "hamt.h" + +#include "mempool.h" +MEMPOOL_GENERATE(hamt, struct hamt, 16) +MEMPOOL_GENERATE(hl, struct hamt_nodelist, 64) +MEMPOOL_GENERATE(hi, struct hamt_item, 32) + +#define tag_ptr(ptr, tag) ((uintptr_t)(ptr) | (tag)) +#define untag_ptr(ptr, tag) ((uintptr_t)(ptr) & ~(tag)) +#define is_tagged(ptr, tag) ((uintptr_t)(ptr) & (tag)) + +#define HAMT_ITEM_TAG 0x1 +#define TAG_NODELIST(ptr) \ + (hamtptr_t)untag_ptr(ptr, HAMT_ITEM_TAG) +#define TAG_ITEM(ptr) \ + (hamtptr_t)tag_ptr(ptr, HAMT_ITEM_TAG) +#define AS_NODELIST(ptr) \ + ((struct hamt_nodelist *)untag_ptr(ptr, HAMT_ITEM_TAG)) +#define AS_ITEM(ptr) \ + ((struct hamt_item *)untag_ptr(ptr, HAMT_ITEM_TAG)) +#define AS_VOIDPTR(ptr) \ + ((void *)untag_ptr(ptr, HAMT_ITEM_TAG) +#define IS_NODELIST(hamtptr) \ + !is_tagged(hamtptr, HAMT_ITEM_TAG) +#define IS_ITEM(hamtptr) \ + is_tagged(hamtptr, HAMT_ITEM_TAG) + +#define for_each_item(item, head) \ + for(struct hamt_item *item = (head), *next = NULL; \ + item && (next = item->next, 1); item = next) + +#define popcount(i) __builtin_popcount(i) + +#define BITS 6 +#define BITS_MASK 0x3f + +static inline struct hamt_nodelist *hamt_nodelist_alloc(void); +static inline struct hamt_item *hamt_item_alloc(void); + +static void hamtptr_destroy(hamtptr_t hamtptr); +static inline void hamtptr_add_ref(hamtptr_t hamtptr); + +// static void hamt_print_hamtptr(hamtptr_t hamtptr, int depth); + +hamt_t hamt_create(hamt_equal_fn equal_fn, hamt_hash_fn hash_fn) +{ + // hamt_t hamt = malloc(sizeof(*hamt)); + hamt_t hamt = hamt_mempool_allocate(); + hamt->equal_fn = equal_fn; + hamt->hash_fn = hash_fn; + hamt->root = TAG_NODELIST(hamt_nodelist_alloc()); + + return hamt; +} + +void hamt_destroy(hamt_t hamt) +{ + if(!hamt) return; + + hamtptr_destroy(hamt->root); + // free(hamt); + hamt_mempool_free(hamt); +} + +hamt_t hamt_clone(hamt_t src) +{ + // hamt_t hamt = malloc(sizeof(*hamt)); + hamt_t hamt = hamt_mempool_allocate(); + + if(!src) return hamt; + + hamt->equal_fn = src->equal_fn; + hamt->hash_fn = src->hash_fn; + hamt->root = src->root; + hamtptr_add_ref(hamt->root); + + return hamt; +} + +static int hamt_find_hamtptr(hamtptr_t root, hamtptr_t *ret, uint32_t *hash) +{ + *ret = root; + + for(size_t i = 0; i < sizeof(*hash)*8/BITS; i++) { + assert(IS_NODELIST(*ret)); + struct hamt_nodelist *nodelist = AS_NODELIST(*ret); + + size_t rawidx = *hash & BITS_MASK; + if(!(nodelist->bitmask & (1 << rawidx))) { + return sizeof(*hash)*8/BITS - i; + } + + *hash >>= BITS; + + size_t idx = popcount(nodelist->bitmask & ((1 << rawidx) - 1)); + *ret = nodelist->list[idx]; + } + + return 0; +} + +int hamt_get(hamt_t hamt, void *key, void **data) +{ + hamtptr_t hamtptr; + uint32_t hash = hamt->hash_fn(key); + + if(hamt_find_hamtptr(hamt->root, &hamtptr, &hash) != 0) + return 1; + + assert(IS_ITEM(hamtptr)); + struct hamt_item *item = AS_ITEM(hamtptr); + + while(!hamt->equal_fn(key, item->key)) { + if(item->next == NULL) return 1; + item = item->next; + } + + *data = item->data; + return 0; +} + +static hamtptr_t hamt_build(uint32_t hash, size_t iter, struct hamt_item **item) +{ + if(iter == 1) { + *item = hamt_item_alloc(); + return TAG_ITEM(*item); + } + + hash >>= BITS; + size_t next_idx = hash & BITS_MASK; + + hamtptr_t next = hamt_build(hash, iter-1, item); + + struct hamt_nodelist *nodelist = hamt_nodelist_alloc(); + + // nodelist->list = &next; + nodelist->list = calloc(1, sizeof(*nodelist->list)); + nodelist->list[0] = next; + + nodelist->bitmask = 1 << next_idx; + + return TAG_NODELIST(nodelist); +} + +static hamtptr_t hamt_insert_hamtptr(hamtptr_t root, hamtptr_t hamtptr, uint32_t hash, hamt_equal_fn equal_fn) +{ + if(IS_ITEM(root)) { + struct hamt_item *item = AS_ITEM(root); + + if(equal_fn(item->key, AS_ITEM(hamtptr)->key)) { + item->refs--; + AS_ITEM(hamtptr)->next = item->next; + return hamtptr; + } + + if(item->next) { + hamtptr = hamt_insert_hamtptr(TAG_ITEM(item->next), hamtptr, hash, equal_fn); + } + + struct hamt_item *new; + + if(item->refs == 1) { + new = item; + } else { + item->refs--; + new = hamt_item_alloc(); + new->key = item->key; + new->data = item->data; + } + + new->next = AS_ITEM(hamtptr); + return TAG_ITEM(new); + } + + struct hamt_nodelist *nodelist = AS_NODELIST(root); + + size_t rawidx = hash & BITS_MASK; + size_t idx = popcount(nodelist->bitmask & ((1 << rawidx) - 1)); + + size_t list_len = popcount(nodelist->bitmask); + size_t newlist_len = list_len; + + if(nodelist->bitmask & (1 << rawidx)) { + hamtptr = hamt_insert_hamtptr(nodelist->list[idx], hamtptr, hash >> BITS, equal_fn); + + if(nodelist->refs == 1) { + nodelist->list[idx] = hamtptr; + return TAG_NODELIST(nodelist); + } + } else { + newlist_len++; + } + + hamtptr_t *newlist = calloc(newlist_len, sizeof(*newlist)); + newlist[idx] = hamtptr; + + for(size_t i = 0; i < idx; i++) + newlist[i] = nodelist->list[i]; + + if(list_len == newlist_len) + for(size_t i = idx+1; i < newlist_len; i++) + newlist[i] = nodelist->list[i]; + else + for(size_t i = idx+1; i < newlist_len; i++) + newlist[i] = nodelist->list[i-1]; + + struct hamt_nodelist *new; + + if(nodelist->refs == 1) { + new = nodelist; + free(nodelist->list); + } else { + nodelist->refs--; + new = hamt_nodelist_alloc(); + } + + new->list = newlist; + new->bitmask = nodelist->bitmask | (1 << rawidx); + return TAG_NODELIST(new); +} + +int hamt_set(hamt_t hamt, void *key, void *data, void **keyptr, void **prevdata) +{ + hamtptr_t hamtptr; + uint32_t hash = hamt->hash_fn(key); + uint32_t hash_cpy = hash; + + size_t iter; + if((iter = hamt_find_hamtptr(hamt->root, &hamtptr, &hash_cpy)) != 0) { + struct hamt_item *item; + hamtptr_t new = hamt_build(hash_cpy, iter, &item); + + hamt->root = hamt_insert_hamtptr(hamt->root, new, hash, hamt->equal_fn); + + item->key = key; + item->data = data; + return 0; + } + + assert(IS_ITEM(hamtptr)); + + for_each_item(item, AS_ITEM(hamtptr)) { + if(hamt->equal_fn(item->key, key)) { + if(keyptr) *keyptr = item->key; + if(prevdata) *prevdata = item->data; + if(item->refs == 1) { + item->data = data; + return 0; + } + + key = item->key; + break; + } + } + + struct hamt_item *new = hamt_item_alloc(); + new->key = key; + new->data = data; + + hamt->root = hamt_insert_hamtptr(hamt->root, TAG_ITEM(new), hash, hamt->equal_fn); + return 0; +} + +static inline struct hamt_nodelist *hamt_nodelist_alloc(void) +{ + // struct hamt_nodelist *nodelist = malloc(sizeof(*nodelist)); + struct hamt_nodelist *nodelist = hl_mempool_allocate(); + nodelist->refs = 1; + nodelist->list = 0; + nodelist->bitmask = 0; + return nodelist; +} + +static inline struct hamt_item *hamt_item_alloc(void) +{ + // struct hamt_item *item = malloc(sizeof(*item)); + struct hamt_item *item = hi_mempool_allocate(); + item->refs = 1; + item->key = NULL; + item->data = NULL; + item->next = NULL; + return item; +} + +static inline void hamtptr_destroy(hamtptr_t hamtptr) +{ + if(IS_NODELIST(hamtptr)) { + struct hamt_nodelist *nodelist = AS_NODELIST(hamtptr); + + for(size_t i = 0; i < popcount(nodelist->bitmask); i++) + hamtptr_destroy(nodelist->list[i]); + + if(--nodelist->refs == 0) { + free(nodelist->list); + // free(nodelist); + hl_mempool_free(nodelist); + } + } else { + for_each_item(item, AS_ITEM(hamtptr)) + if(--item->refs == 0) + hi_mempool_free(item); + // free(item); + } +} + +static inline void hamtptr_add_ref(hamtptr_t hamtptr) +{ + if(IS_NODELIST(hamtptr)) { + for(size_t i = 0; i < popcount(AS_NODELIST(hamtptr)->bitmask); i++) + hamtptr_add_ref(AS_NODELIST(hamtptr)->list[i]); + AS_NODELIST(hamtptr)->refs++; + } else { + for_each_item(item, AS_ITEM(hamtptr)) + item->refs++; + } +} + +// static void hamt_print_hamtptr(hamtptr_t hamtptr, int depth) +// { +// for(int i = 0; i < depth; i++) printf(" "); + +// if(IS_NODELIST(hamtptr)) { +// printf("%d, MASK %b\n", AS_NODELIST(hamtptr)->refs, AS_NODELIST(hamtptr)->bitmask); +// for(size_t i = 0; i < popcount(AS_NODELIST(hamtptr)->bitmask); i++) { +// hamt_print_hamtptr(AS_NODELIST(hamtptr)->list[i], depth+1); +// } +// } else { +// printf("%d, %s: %s\n", AS_ITEM(hamtptr)->refs, (char *)AS_ITEM(hamtptr)->key, (char *)AS_ITEM(hamtptr)->data); +// if(AS_ITEM(hamtptr)->next) +// hamt_print_hamtptr(TAG_ITEM(AS_ITEM(hamtptr)->next), depth+1); +// } +// } diff --git a/src/hamt.h b/src/hamt.h new file mode 100644 index 0000000..6ef27ba --- /dev/null +++ b/src/hamt.h @@ -0,0 +1,45 @@ +#ifndef HAMT_H +#define HAMT_H + +#include <stdint.h> + +typedef struct hamt *hamt_t; + +// tagged pointer to either +// hamt_item or hamt_nodelist +typedef uintptr_t hamtptr_t; + +struct hamt_item { + uint32_t refs; + + void *key; + void *data; + struct hamt_item *next; +}; + +struct hamt_nodelist { + uint32_t refs; + + uint64_t bitmask; + hamtptr_t *list; +}; + +typedef int (*hamt_equal_fn)(void *key1, void *key2); +typedef uint32_t (*hamt_hash_fn)(void *key); + +struct hamt { + hamt_equal_fn equal_fn; + hamt_hash_fn hash_fn; + + hamtptr_t root; +}; + +hamt_t hamt_create(hamt_equal_fn equal_fn, hamt_hash_fn hash_fn); +void hamt_destroy(hamt_t hamt); + +hamt_t hamt_clone(hamt_t src); + +int hamt_get(hamt_t hamt, void *key, void **data); +int hamt_set(hamt_t hamt, void *key, void *data, void **keyptr, void **prevdata); + +#endif diff --git a/src/hashtable.c b/src/hashtable.c index 955a3bf..e9845f2 100644 --- a/src/hashtable.c +++ b/src/hashtable.c @@ -5,13 +5,8 @@ #include "hashtable.h" #include "mempool.h" - -#define MEMPOOL_CAP 32 -#define MEMPOOL_OBJ_TYPE struct hashtable -MEMPOOL_GENERATE(ht) -#define MEMPOOL_CAP 128 -#define MEMPOOL_OBJ_TYPE struct hashtable_item -MEMPOOL_GENERATE(hi) +MEMPOOL_GENERATE(ht, struct hashtable, 32) +MEMPOOL_GENERATE(hi, struct hashtable_item, 128) // TODO: // - insertion options @@ -811,7 +811,10 @@ static int toklist_expr(struct tctx *tctx, struct toklist **toklist) if(TOKEN(tctx)->type == TOKEN_LP) depth++; else if(TOKEN(tctx)->type == TOKEN_RP) depth--; - + + // printf("%zu\n", depth); + // print_token(TOKEN(tctx)); + if(toklist) token_clone(&tokens[tokens_len++], TOKEN(tctx)); @@ -823,10 +826,6 @@ static int toklist_expr(struct tctx *tctx, struct toklist **toklist) } return 0; - -// fail: - // for(size_t i = 0; i < tokens_len; i++) token_dealloc(&tokens[i]); - // return 1; } #define SET_TOKEN_TYPE(t, ttype) (t)->type = (ttype) @@ -842,6 +841,15 @@ static struct toklist *value_to_toklist(value_t value) if(strcmp(value->value.atom, "lambda") == 0) { SET_TOKEN_TYPE(&token, TOKEN_LAMBDA); break; + } else if(strcmp(value->value.atom, "if") == 0) { + SET_TOKEN_TYPE(&token, TOKEN_IF); + break; + } else if(strcmp(value->value.atom, "quote") == 0) { + SET_TOKEN_TYPE(&token, TOKEN_QUOTE_FORM); + break; + } else if(strcmp(value->value.atom, "'") == 0) { + SET_TOKEN_TYPE(&token, TOKEN_QUOTE); + break; } SET_TOKEN_TYPE(&token, TOKEN_ID); diff --git a/src/memdebug.h b/src/memdebug.h index 358203a..7d72a10 100644 --- a/src/memdebug.h +++ b/src/memdebug.h @@ -64,6 +64,7 @@ typedef long long int memdebug_suffix; #include <stdio.h> #include <stdlib.h> +#include <stdint.h> #include <string.h> #include <errno.h> #include <time.h> @@ -80,17 +81,19 @@ static FILE *_memdebug_fp = NULL; errno, strerror(errno)); \ } while(0) -#define MEMDEBUG_OUT_OF_BOUNDS_CHECK(addr, size) \ - do { \ - if(addr != NULL) { \ - *(size_t*)addr = size; \ - addr += sizeof(size_t); \ - \ - memdebug_suffix suffix = MEMDEBUG_MAGIC_SUFFIX; \ - memcpy(addr + size, &suffix, sizeof(suffix)); \ - } \ - } while(0); +static inline void *memdebug_add_out_of_bounds_check(void *addr, size_t size) +{ + uintptr_t size_addr = (uintptr_t)addr; + uintptr_t suffix_addr = size_addr + + sizeof(size) + size; + + memcpy((void *)size_addr, &size, sizeof(size)); + memdebug_suffix suffix = MEMDEBUG_MAGIC_SUFFIX; + memcpy((void *)suffix_addr, &suffix, sizeof(suffix)); + + return (void *)((uintptr_t)addr + sizeof(size)); +} void *__memdebug_malloc(size_t size, char *file, int line) { @@ -99,7 +102,7 @@ void *__memdebug_malloc(size_t size, char *file, int line) #else void *addr = MEMDEBUG_MALLOC_SYMBOL( size + MEMDEBUG_OUT_OF_BOUNDS_EXTRA_SIZE); - MEMDEBUG_OUT_OF_BOUNDS_CHECK(addr, size); + if(addr) addr = memdebug_add_out_of_bounds_check(addr, size); #endif MEMDEBUG_LOG_FUNC(malloc, addr, file, line); @@ -118,7 +121,7 @@ void *__memdebug_realloc(void *ptr, size_t size, char *file, int line) #else void *addr = MEMDEBUG_REALLOC_SYMBOL( ptr, size + MEMDEBUG_OUT_OF_BOUNDS_EXTRA_SIZE); - MEMDEBUG_OUT_OF_BOUNDS_CHECK(addr, size); + if(addr) addr = memdebug_add_out_of_bounds_check(addr, size); #endif MEMDEBUG_LOG_FUNC(realloc, addr, file, line); @@ -137,7 +140,7 @@ void *__memdebug_calloc(size_t nmemb, size_t size, char *file, int line) #else void *addr = MEMDEBUG_MALLOC_SYMBOL( nmemb * size + MEMDEBUG_OUT_OF_BOUNDS_EXTRA_SIZE); - MEMDEBUG_OUT_OF_BOUNDS_CHECK(addr, nmemb * size); + if(addr) addr = memdebug_add_out_of_bounds_check(addr, nmemb * size); memset(addr, 0, nmemb * size); #endif @@ -158,9 +161,9 @@ void __memdebug_free(void *ptr, char *file, int line) #ifdef MEMDEBUG_OUT_OF_BOUNDS if(ptr != NULL) { - size_t size = *(size_t *)(ptr - sizeof(size_t)); + size_t size = *(size_t *)((uintptr_t)ptr - sizeof(size_t)); memdebug_suffix suffix = 0; - memcpy(&suffix, ptr + size, sizeof(suffix)); + memcpy(&suffix, (void *)((uintptr_t)ptr + size), sizeof(suffix)); MEMDEBUG_LOG(", "); MEMDEBUG_LOG("out-of-bounds-check: "); @@ -169,7 +172,7 @@ void __memdebug_free(void *ptr, char *file, int line) MEMDEBUG_LOG("SUCCESS"); else MEMDEBUG_LOG("FAILED"); - ptr -= sizeof(size_t); + ptr = (void *)((uintptr_t)ptr + sizeof(size_t)); } #endif diff --git a/src/mempool.h b/src/mempool.h index 6c25ac9..7d065f5 100644 --- a/src/mempool.h +++ b/src/mempool.h @@ -1,13 +1,11 @@ #ifndef MEMPOOL_H #define MEMPOOL_H -#ifndef MEMPOOL_OBJ_TYPE -#define MEMPOOL_OBJ_TYPE int -#endif - -#ifndef MEMPOOL_CAP -#define MEMPOOL_CAP (1 << 16) -#endif +/* To generate use MEMPOOL_GENERATE(id, type, cap), where functions + * _mempool_allocate() and _mempool_free(type *obj) will be prefixed with id + * and can be used to allocate objects of type <type> with <cap> objects in + * each block + */ #define for_each_block(id, b, head) \ for(struct id##_block *b = (head), *next = NULL; \ @@ -15,13 +13,13 @@ #define get_last_block(b) \ while((b)->next != NULL) (b) = (b)->next -#define MEMPOOL_GENERATE(id) \ +#define MEMPOOL_GENERATE(id, type, cap) \ \ struct id##_block { \ union id##_chunk { \ - MEMPOOL_OBJ_TYPE obj; \ + type obj; \ union id##_chunk *next; \ - } chunks[MEMPOOL_CAP]; \ + } chunks[cap]; \ \ struct id##_block *next; \ }; \ @@ -33,9 +31,9 @@ static inline void *_##id##_mempool_init_block(struct id##_block *b) \ { \ b->next = NULL; \ - for(size_t i = 0; i < MEMPOOL_CAP; i++) \ + for(size_t i = 0; i < cap; i++) \ b->chunks[i].next = &b->chunks[i+1]; \ - b->chunks[MEMPOOL_CAP-1].next = NULL; \ + b->chunks[cap-1].next = NULL; \ \ return b->chunks; \ } \ diff --git a/src/value.c b/src/value.c index 0d263d7..c72c4a2 100644 --- a/src/value.c +++ b/src/value.c @@ -6,10 +6,7 @@ #include "lexer.h" #include "mempool.h" - -#define MEMPOOL_OBJ_TYPE struct value -#define MEMPOOL_CAP 64 -MEMPOOL_GENERATE(value) +MEMPOOL_GENERATE(value, struct value, 64) #define NOT_IMPLEMENTED() die("Not Implemented. ABORTING") |