108 lines
2.2 KiB
C
108 lines
2.2 KiB
C
#pragma once
|
|
|
|
#include<stdbool.h>
|
|
#include<stdint.h>
|
|
#include<stddef.h>
|
|
#include<ctype.h>
|
|
#include<stdlib.h>
|
|
#include<stdio.h>
|
|
#include<string.h>
|
|
#include<stdatomic.h>
|
|
#include<assert.h>
|
|
|
|
#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);
|
|
}
|