#pragma once #include #include #include #include #include #include #include #include #include #include"value.h" typedef struct LTableEntry { LValue key; LValue val; } LTableEntry; typedef struct LTableBuckets { size_t capacity; LTableEntry data[]; } LTableBuckets; typedef struct LTable { bool ref; LTableBuckets *buckets; } LTable; static inline LTable *ltable_new(size_t capacity) { assert(capacity >= 8 && (capacity & (capacity - 1)) == 0); LTable *tbl = calloc(1, sizeof(*tbl)); tbl->ref = false; tbl->buckets = calloc(1, sizeof(LTableBuckets) + sizeof(LTableEntry) * capacity); tbl->buckets->capacity = capacity; for(int i = 0; i < tbl->buckets->capacity; i++) { tbl->buckets->data[i] = (LTableEntry) { .key = {.u = LTAG_NIL}, .val = {.u = LTAG_NIL}, }; } return tbl; } static inline bool ltablebuckets_set(LTableBuckets *self, LValue key, LValue val) { size_t idx = lvalue_hash(key); int probe_limit = __builtin_ctz(self->capacity); probe_limit += probe_limit >> 1; LTableEntry *current = self->data; while(1) { idx &= self->capacity - 1; LValue prevKey = {.u = LTAG_NIL}; atomic_compare_exchange_strong(¤t[idx].key.u, &prevKey.u, key.u); if(prevKey.u == LTAG_NIL || prevKey.u == key.u) { atomic_store(¤t[idx].val.u, val.u); break; } idx++; probe_limit--; if(probe_limit == 0) { return false; } } return true; } static inline void ltable_set(LTable *self, LValue key, LValue val) { if(!ltablebuckets_set(self->buckets, key, val)) { assert(0 && "No table resizing"); } } static inline LValue ltablebuckets_get(LTableBuckets *self, LValue key) { size_t idx = lvalue_hash(key); size_t tries = self->capacity; while(1) { idx &= self->capacity - 1; LValue foundKey; foundKey.u = atomic_load(&self->data[idx].key.u); if(lvalue_eq(foundKey, key)) { return (LValue) {.u = atomic_load(&self->data[idx].val.u)}; } else if(--tries == 0) { return (LValue) {.u = LTAG_NIL}; } idx++; } } static inline LValue ltable_get(LTable *self, LValue key) { return ltablebuckets_get(self->buckets, key); }