Skip to content

Commit

Permalink
Insert list refactor. Moved node creation to separate function
Browse files Browse the repository at this point in the history
Signed-off-by: Tomasz Sobkiewicz <tomasz.sobkiewicz@swmansion.com>
  • Loading branch information
TheSobkiewicz committed Jan 15, 2025
1 parent 01456a3 commit 45eccdc
Show file tree
Hide file tree
Showing 3 changed files with 75 additions and 29 deletions.
41 changes: 26 additions & 15 deletions src/libAtomVM/ets.c
Original file line number Diff line number Diff line change
Expand Up @@ -262,21 +262,15 @@ static EtsErrorCode ets_table_insert(struct EtsTable *ets_table, term entry, Con
return EtsBadEntry;
}

Heap *heap = malloc(sizeof(Heap));
if (IS_NULL_PTR(heap)) {
return EtsAllocationFailure;
}
size_t size = (size_t) memory_estimate_usage(entry);
if (memory_init_heap(heap, size) != MEMORY_GC_OK) {
free(heap);
int keypos = (int) ets_table->keypos;

struct HNode *new_node = ets_hashtable_new_node(entry, keypos, ctx->global);
if (IS_NULL_PTR(new_node)) {
return EtsAllocationFailure;
}

term new_entry = memory_copy_term_tree(heap, entry);
term key = term_get_tuple_element(new_entry, (int) ets_table->keypos);

EtsErrorCode result = EtsOk;
EtsHashtableErrorCode res = ets_hashtable_insert(ets_table->hashtable, key, new_entry, EtsHashtableAllowOverwrite, heap, ctx->global);
EtsHashtableErrorCode res = ets_hashtable_insert(ets_table->hashtable, new_node, EtsHashtableAllowOverwrite, ctx->global);
if (UNLIKELY(res != EtsHashtableOk)) {
result = EtsAllocationFailure;
}
Expand All @@ -286,29 +280,46 @@ static EtsErrorCode ets_table_insert(struct EtsTable *ets_table, term entry, Con

static EtsErrorCode ets_table_insert_list(struct EtsTable *ets_table, term list, Context *ctx)
{
if (ets_table->access_type != EtsAccessPublic && ets_table->owner_process_id != ctx->process_id) {
return EtsPermissionDenied;
}
term iter = list;
size_t size = 0;

while (term_is_nonempty_list(iter)) {
term tuple = term_get_list_head(iter);
iter = term_get_list_tail(iter);
if (!term_is_tuple(tuple) || (size_t) term_get_tuple_arity(tuple) < (ets_table->keypos + 1)) {
return EtsBadEntry;
}
++size;
}
if (!term_is_nil(iter)) {
return EtsBadEntry;
}

struct HNode **hnode_list = malloc(size * sizeof(struct HNode *));
if (IS_NULL_PTR(hnode_list)) {
return EtsAllocationFailure;
}

int cur = 0;
while (term_is_nonempty_list(list)) {
term tuple = term_get_list_head(list);
EtsErrorCode result = ets_table_insert(ets_table, tuple, ctx);
if (UNLIKELY(result != EtsOk)) {
AVM_ABORT(); // Abort because operation might not be atomic.
hnode_list[cur] = ets_hashtable_new_node(tuple, ets_table->keypos, ctx->global);
if (IS_NULL_PTR(hnode_list[cur])) {
free_hashtable_node_array(hnode_list, cur, ctx->global);
return EtsAllocationFailure;
}

++cur;
list = term_get_list_tail(list);
}

for (size_t i = 0; i < size; i++) {
ets_hashtable_insert(ets_table->hashtable, hnode_list[i], EtsHashtableAllowOverwrite, ctx->global); // EtsHashtableAllowOverwrite cannot be changed here because it will result in data inconsistency.
}

free(hnode_list);
return EtsOk;
}

Expand Down
59 changes: 46 additions & 13 deletions src/libAtomVM/ets_hashtable.c
Original file line number Diff line number Diff line change
Expand Up @@ -81,9 +81,49 @@ static void print_info(struct EtsHashTable *hash_table)
}
}
#endif
struct HNode *ets_hashtable_new_node(term entry, int keypos, GlobalContext *global)
{

Heap *heap = malloc(sizeof(Heap));
if (IS_NULL_PTR(heap)) {
return NULL;
}
size_t size = (size_t) memory_estimate_usage(entry);
if (memory_init_heap(heap, size) != MEMORY_GC_OK) {
free(heap);
return NULL;
}

term new_entry = memory_copy_term_tree(heap, entry);
struct HNode *new_node = malloc(sizeof(struct HNode));
if (IS_NULL_PTR(new_node)) {
memory_destroy_heap(heap, global);
return NULL;
}
term key = term_get_tuple_element(new_entry, keypos);

new_node->next = NULL;
new_node->key = key;
new_node->entry = new_entry;
new_node->heap = heap;

EtsHashtableErrorCode ets_hashtable_insert(struct EtsHashTable *hash_table, term key, term entry, EtsHashtableOptions opts, Heap *heap, GlobalContext *global)
return new_node;
}

void free_hashtable_node_array(struct HNode **allocated, size_t size, GlobalContext *global)
{
for (size_t j = 0; j < size; j++) {
if (allocated[j]) {
memory_destroy_heap(allocated[j]->heap, global);
free(allocated[j]);
}
}
free(allocated);
}

EtsHashtableErrorCode ets_hashtable_insert(struct EtsHashTable *hash_table, struct HNode *new_node, EtsHashtableOptions opts, GlobalContext *global)
{
term key = new_node->key;
uint32_t hash = hash_term(key, global);
uint32_t index = hash % hash_table->capacity;

Expand All @@ -98,15 +138,17 @@ EtsHashtableErrorCode ets_hashtable_insert(struct EtsHashTable *hash_table, term
while (1) {
if (term_compare(key, node->key, TermCompareExact, global) == TermEquals) {
if (opts & EtsHashtableAllowOverwrite) {
node->entry = entry;
node->entry = new_node->entry;
memory_destroy_heap(node->heap, global);
node->heap = heap;
node->heap = new_node->heap;
free(new_node);
return EtsHashtableOk;
} else {
memory_destroy_heap(new_node->heap, global);
free(new_node);
return EtsHashtableFailure;
}
}

if (node->next) {
node = node->next;
} else {
Expand All @@ -115,15 +157,6 @@ EtsHashtableErrorCode ets_hashtable_insert(struct EtsHashTable *hash_table, term
}
}

struct HNode *new_node = malloc(sizeof(struct HNode));
if (IS_NULL_PTR(new_node)) {
return EtsHashtableError;
}
new_node->next = NULL;
new_node->key = key;
new_node->entry = entry;
new_node->heap = heap;

if (node) {
node->next = new_node;
} else {
Expand Down
4 changes: 3 additions & 1 deletion src/libAtomVM/ets_hashtable.h
Original file line number Diff line number Diff line change
Expand Up @@ -52,9 +52,11 @@ typedef enum EtsHashtableErrorCode
struct EtsHashTable *ets_hashtable_new();
void ets_hashtable_destroy(struct EtsHashTable *hash_table, GlobalContext *global);

EtsHashtableErrorCode ets_hashtable_insert(struct EtsHashTable *hash_table, term key, term entry, EtsHashtableOptions opts, Heap *heap, GlobalContext *global);
EtsHashtableErrorCode ets_hashtable_insert(struct EtsHashTable *hash_table, struct HNode *new_node, EtsHashtableOptions opts, GlobalContext *global);
term ets_hashtable_lookup(struct EtsHashTable *hash_table, term key, size_t keypos, GlobalContext *global);
bool ets_hashtable_remove(struct EtsHashTable *hash_table, term key, size_t keypos, GlobalContext *global);
struct HNode *ets_hashtable_new_node(term entry, int keypos, GlobalContext *global);
void free_hashtable_node_array(struct HNode **allocated, size_t len, GlobalContext *global);

#ifdef __cplusplus
}
Expand Down

0 comments on commit 45eccdc

Please sign in to comment.