});
}
+static sbIrExpr *expr_hash(hIrChunk ck, sbIrExpr *value) {
+ return new_expr(ck, &(sbIrExpr) {
+ .type = IR_E_HASH,
+ .list.this = value,
+ });
+}
+
static void put_ir_stmt(hIrChunk ck, sbIrStmt *stmt) {
sbIrStmt *where_to_put = sbArena_alloc(&ck->program->arena, sizeof(sbIrStmt));
memcpy(where_to_put, stmt, sizeof(sbIrStmt));
return list;
}
+static sbIrExpr *compile_ast_hash(hIrChunk ck, sbAst node) {
+ sbAst considering = node;
+ sbIrExpr *list = NULL;
+ sbIrExpr **place_here = &list;
+ while (considering != NO_NODE) {
+ /* we'll just make a list of "lists" that are actually just cons-cells of a key and value */
+ sbAst hash_entry = considering->seq.left;
+ if (hash_entry->type != AST_NODE_HASHENTRY) {
+ PANIC("Hash table literals should only contain hashentries (%d)", hash_entry->type);
+ }
+ sbIrExpr *compiled_entry = expr_list(ck, compile_ast_expr(ck, hash_entry->seq.left)); // key
+ compiled_entry->list.next = compile_ast_expr(ck, hash_entry->seq.right); // value
+
+ *place_here = expr_hash(ck, compiled_entry);
+ place_here = &(*place_here)->list.next;
+ considering = considering->seq.right;
+ }
+ return list;
+}
+
static sbIrExpr *compile_ast_expr(hIrChunk ck, sbAst node) {
if (node == NO_NODE) return NULL;
} else if (node->type == AST_VAL_LIST) {
sbIrExpr *list = compile_ast_list(ck, node->seq.left);
return list;
+ } else if (node->type == AST_VAL_HASH) {
+ sbIrExpr *hash = compile_ast_hash(ck, node->seq.left);
+ return hash;
} else if (node->type == AST_NODE_OP) {
sbIrExpr *left = NULL, *right = NULL;
if (node->op.left != NO_NODE) {
return expr_value(ck, &HVINT(node->i));
} else if (node->type == AST_VAL_STRING) {
return expr_value(ck, &HVSTR(node->str));
+ } else if (node->type == AST_VAL_SYMBOL) {
+ return expr_value(ck, &HVSYM(node->symb));
} else if (node->type == AST_NODE_NAME) {
/* TODO: I don't want to use strlen. Can we remember the lengths of symbols? */
sbIrVariable *var = compile_ast_var(ck, node);
/* TODO eliminate globals */
sbPool g_hashtable_pool = {0};
-typedef struct hashentry {
- hV key;
- hV value;
-} hashentry;
-
typedef struct hashtbl {
usize size;
usize used;
flag allocated;
flag is_inline;
union {
- /* TODO should be SoA instead of AoS */
- hashentry inline_entries[INLINE_TABLE_LENGTH];
- hashentry *external_entries;
+ struct {
+ hV keys[INLINE_TABLE_LENGTH];
+ hV values[INLINE_TABLE_LENGTH];
+ } internal;
+ struct {
+ hV *keys;
+ hV *values;
+ } external;
};
} hashtbl;
static hashtbl *new_tbl(usize initial_size);
-static hashentry *get_entry_ptr_for_tbl(hashtbl *t, usize *length_out);
+static hV *get_key_ptr_for_tbl(hashtbl *t, usize *length_out);
+static void get_ptrs_for_tbl(hashtbl *t, hV **keys, hV **values);
static hashtbl *find_tbl_for_handle(hHash handle);
-static hashentry *set_key(hashtbl *t, hV *key, hV *value);
-static hashentry delete_key(hashtbl *t, hV *key);
+static usize set_key(hashtbl *t, hV *key, hV *value);
+static hV delete_key(hashtbl *t, hV *key);
static void set_hashtbl_size(hashtbl *t, usize new_size, flag rehash_all);
-static hashentry *set_key_in_array(hashentry *entries, usize length, hV *key, hV *value);
-static hashentry *find_entry_by_key(hashtbl *t, hV *key);
+static usize set_key_in_array(hV *keys, hV *values, usize length, hV *key, hV *value);
+static usize find_index_by_key(hashtbl *t, hV *key);
void sbHash_sys_init() {
sbPool_initialize(&g_hashtable_pool, sizeof(hashtbl), HASHES_PER_BLOCK);
if (obj->type == IT_NIL) {
char zero = 0;
return sbHash_hash_bytes(&zero, 1);
+ } else if (obj->type == IT_SYMBOL) {
+ return sbHash_hash_bytes((char*)&obj->symbol, sizeof(obj->symbol));
+ } else if (obj->type == IT_INTEGER) {
+ /* TODO bigint should be different i think */
+ return sbHash_hash_bytes((char*)&obj->integer, sizeof(obj->integer));
} else if (obj->type == IT_STRING) {
char scratch[8];
usize length;
hV sbHash_find(hHash h, hV *key) {
hashtbl *t = find_tbl_for_handle(h);
- hashentry *e = find_entry_by_key(t, key);
- if (e->key.type == IT_NOTHING) {
- return (hV) { .type = IT_NOTHING };
+ usize index = find_index_by_key(t, key);
+ hV *keys, *values;
+ get_ptrs_for_tbl(t, &keys, &values);
+ if (keys[index].type == IT_NOTHING) {
+ return HVNOTHING;
} else {
- return e->value;
+ return values[index];
}
}
hV sbHash_find_or_insert(hHash h, hV *key, hV *value) {
hashtbl *t = find_tbl_for_handle(h);
- hashentry *e = find_entry_by_key(t, key);
- if (e->key.type == IT_NOTHING) {
- e->value = *value;
+ usize index = find_index_by_key(t, key);
+ hV *keys, *values;
+ get_ptrs_for_tbl(t, &keys, &values);
+ if (keys[index].type == IT_NOTHING) {
+ values[index] = *value;
}
- return e->value;
+ return values[index];
}
void sbHash_delete(hHash h, hV *key, hV *value) {
if (new_size == INLINE_TABLE_LENGTH) {
t->is_inline = 1;
} else {
- hashentry *new_data = calloc(new_size, sizeof(hashentry));
+ hV *new_keys = calloc(new_size, sizeof(hV));
+ hV *new_values = calloc(new_size, sizeof(hV));
if (rehash_all) {
- hashentry *current_data = get_entry_ptr_for_tbl(t, NULL);
+ hV *current_keys, *current_values;
+ get_ptrs_for_tbl(t, ¤t_keys, ¤t_values);
for (usize i = 0; i < t->size; i++) {
- if (current_data[i].key.type != IT_NOTHING && current_data[i].key.type != ITX_TOMBSTONE) {
- set_key_in_array(new_data, new_size, ¤t_data[i].key, ¤t_data[i].value);
- /* adding a new k/v pair will retain the values, so we need to release them
- * from the previous allocation */
- sbV_release(¤t_data[i].key);
- sbV_release(¤t_data[i].value);
- }
+ if (current_keys[i].type == IT_NOTHING || current_keys[i].type == ITX_TOMBSTONE) continue;
+ set_key_in_array(new_keys, new_values, new_size, ¤t_keys[i], ¤t_values[i]);
+ /* adding a new k/v pair will retain the values, so we need to release them
+ * from the previous allocation */
+ sbV_release(¤t_keys[i]);
+ sbV_release(¤t_values[i]);
}
/* now that we've migrated all our things to the new version, we can free the old
* version (unless it was not alloc'd to begin with) */
- if (!t->is_inline) free(current_data);
+ if (!t->is_inline) {
+ free(current_keys);
+ free(current_values);
+ }
}
- t->external_entries = new_data;
+ t->external.keys = new_keys;
+ t->external.values = new_values;
t->is_inline = 0;
}
t->size = new_size;
}
-static hashentry *get_entry_ptr_for_tbl(hashtbl *t, usize *length_out) {
+static hV *get_key_ptr_for_tbl(hashtbl *t, usize *length_out) {
if (length_out) *length_out = t->size;
if (t->is_inline) {
- return t->inline_entries;
+ return t->internal.keys;
+ } else {
+ return t->external.keys;
+ }
+}
+
+static void get_ptrs_for_tbl(hashtbl *t, hV **keys, hV **values) {
+ if (t->is_inline) {
+ if (keys) *keys = t->internal.keys;
+ if (values) *values = t->internal.values;
} else {
- return t->external_entries;
+ if (keys) *keys = t->external.keys;
+ if (values) *values = t->external.values;
}
}
-static hashentry *find_entry_in_array(hashentry *entries, usize length, hV *key) {
+static usize find_key_index_in_array(hV *keys, usize length, hV *key) {
sbHashValue hash = sbHash_hash_obj(key);
u32 start = (hash & 0xFFFFFFFF);
u32 move = (hash >> 32);
move ++;
}
usize index = start % length;
- while (!sbV_c_eq(&entries[index].key, key) && entries[index].key.type != IT_NOTHING) {
+ while (!sbV_c_eq(&keys[index], key) && keys[index].type != IT_NOTHING) {
index += move;
index %= length;
}
- return &entries[index];
+ return index;
}
-static hashentry *find_entry_by_key(hashtbl *t, hV *key) {
- return find_entry_in_array(get_entry_ptr_for_tbl(t, NULL), t->size, key);
+static usize find_index_by_key(hashtbl *t, hV *key) {
+ return find_key_index_in_array(get_key_ptr_for_tbl(t, NULL), t->size, key);
}
-static hashentry *set_key_in_array(hashentry *entries, usize length, hV *key, hV *value) {
- hashentry *location = find_entry_in_array(entries, length, key);
+static usize set_key_in_array(hV *keys, hV *values, usize length, hV *key, hV *value) {
+ usize index = find_key_index_in_array(keys, length, key);
/* now, we either found the current entry for this key,
* or we found an empty slot that fits this key. */
- if (location->key.type == IT_NOTHING) {
+ if (keys[index].type == IT_NOTHING) {
/* key was not here before, so we need to retain the key
* as well so it doesn't change out from under us */
sbV_retain(key);
- location->key = *key;
+ keys[index] = *key;
} else {
/* replacing something that already exists. we can keep
* the key the same, but need to release the previous value. */
- sbV_release(&location->value);
+ sbV_release(&values[index]);
}
sbV_retain(value);
- location->value = *value;
+ values[index] = *value;
- return location;
+ return index;
}
-static hashentry *set_key(hashtbl *t, hV *key, hV *value) {
- hashentry *location = find_entry_by_key(t, key);
+static usize set_key(hashtbl *t, hV *key, hV *value) {
+ usize index = find_index_by_key(t, key);
+
+ hV *keys, *values;
+ get_ptrs_for_tbl(t, &keys, &values);
/* now, we either found the current entry for this key,
* or we found an empty slot that fits this key. */
- if (location->key.type == IT_NOTHING) {
+ if (keys[index].type == IT_NOTHING) {
/* key was not here before, so we need to retain the key
* as well so it doesn't change out from under us */
sbV_retain(key);
- location->key = *key;
+ keys[index] = *key;
+ values[index] = *value;
t->used ++;
if (t->used >= t->size * 3 / 4) {
set_hashtbl_size(t, t->size * 2, TRUE);
} else {
/* replacing something that already exists. we can keep
* the key the same, but need to release the previous value. */
- sbV_release(&location->value);
+ sbV_release(&values[index]);
}
sbV_retain(value);
- location->value = *value;
+ values[index] = *value;
- return location;
+ return index;
}
-static hashentry delete_key(hashtbl *t, hV *key) {
- hashentry *location = find_entry_by_key(t, key);
- hashentry to_return = *location;
- location->key.type = ITX_TOMBSTONE;
+static hV delete_key(hashtbl *t, hV *key) {
+ usize index = find_index_by_key(t, key);
+ hV *keys, *values;
+ get_ptrs_for_tbl(t, &keys, &values);
+ hV to_return = values[index];
+ keys[index].type = ITX_TOMBSTONE;
return to_return;
}