Skip to content

Commit 7640eaa

Browse files
committed
Insert list refactor. Moved node creation to separate function
Signed-off-by: Tomasz Sobkiewicz <tomasz.sobkiewicz@swmansion.com>
1 parent 123a704 commit 7640eaa

File tree

4 files changed

+97
-51
lines changed

4 files changed

+97
-51
lines changed

src/libAtomVM/ets.c

Lines changed: 33 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -258,57 +258,71 @@ static EtsErrorCode ets_table_insert(struct EtsTable *ets_table, term entry, Con
258258
return EtsPermissionDenied;
259259
}
260260

261-
if ((size_t) term_get_tuple_arity(entry) < (ets_table->keypos + 1)) {
261+
size_t keypos = ets_table->keypos;
262+
263+
if ((size_t) term_get_tuple_arity(entry) < keypos + 1) {
262264
return EtsBadEntry;
263265
}
264266

265-
Heap *heap = malloc(sizeof(Heap));
266-
if (IS_NULL_PTR(heap)) {
267-
return EtsAllocationFailure;
268-
}
269-
size_t size = (size_t) memory_estimate_usage(entry);
270-
if (memory_init_heap(heap, size) != MEMORY_GC_OK) {
271-
free(heap);
267+
struct HNode *new_node = ets_hashtable_new_node(entry, keypos);
268+
if (IS_NULL_PTR(new_node)) {
272269
return EtsAllocationFailure;
273270
}
274271

275-
term new_entry = memory_copy_term_tree(heap, entry);
276-
term key = term_get_tuple_element(new_entry, (int) ets_table->keypos);
277-
278-
EtsErrorCode result = EtsOk;
279-
EtsHashtableErrorCode res = ets_hashtable_insert(ets_table->hashtable, key, new_entry, EtsHashtableAllowOverwrite, heap, ctx->global);
272+
EtsHashtableErrorCode res = ets_hashtable_insert(ets_table->hashtable, new_node, EtsHashtableAllowOverwrite, ctx->global);
280273
if (UNLIKELY(res != EtsHashtableOk)) {
281-
result = EtsAllocationFailure;
274+
return EtsAllocationFailure;
282275
}
283276

284-
return result;
277+
return EtsOk;
285278
}
286279

287280
static EtsErrorCode ets_table_insert_list(struct EtsTable *ets_table, term list, Context *ctx)
288281
{
282+
if (ets_table->access_type != EtsAccessPublic && ets_table->owner_process_id != ctx->process_id) {
283+
return EtsPermissionDenied;
284+
}
285+
289286
term iter = list;
287+
size_t size = 0;
290288

291289
while (term_is_nonempty_list(iter)) {
292290
term tuple = term_get_list_head(iter);
293291
iter = term_get_list_tail(iter);
294292
if (!term_is_tuple(tuple) || (size_t) term_get_tuple_arity(tuple) < (ets_table->keypos + 1)) {
295293
return EtsBadEntry;
296294
}
295+
++size;
297296
}
298297
if (!term_is_nil(iter)) {
299298
return EtsBadEntry;
300299
}
301300

301+
struct HNode **nodes = malloc(size * sizeof(struct HNode *));
302+
if (IS_NULL_PTR(nodes)) {
303+
return EtsAllocationFailure;
304+
}
305+
306+
size_t i = 0;
302307
while (term_is_nonempty_list(list)) {
303308
term tuple = term_get_list_head(list);
304-
EtsErrorCode result = ets_table_insert(ets_table, tuple, ctx);
305-
if (UNLIKELY(result != EtsOk)) {
306-
AVM_ABORT(); // Abort because operation might not be atomic.
309+
nodes[i] = ets_hashtable_new_node(tuple, ets_table->keypos);
310+
if (IS_NULL_PTR(nodes[i])) {
311+
ets_hashtable_free_node_array(nodes, i, ctx->global);
312+
free(nodes);
313+
return EtsAllocationFailure;
307314
}
308-
315+
++i;
309316
list = term_get_list_tail(list);
310317
}
311318

319+
for (size_t i = 0; i < size; ++i) {
320+
321+
EtsHashtableErrorCode res = ets_hashtable_insert(ets_table->hashtable, nodes[i], EtsHashtableAllowOverwrite, ctx->global);
322+
assert(res == EtsHashtableOk);
323+
}
324+
325+
free(nodes);
312326
return EtsOk;
313327
}
314328

src/libAtomVM/ets_hashtable.c

Lines changed: 60 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ struct HNode
3535
struct HNode *next;
3636
term key;
3737
term entry;
38-
Heap *heap;
38+
Heap heap;
3939
};
4040

4141
static uint32_t hash_term(term t, GlobalContext *global);
@@ -53,14 +53,26 @@ struct EtsHashTable *ets_hashtable_new()
5353
return htable;
5454
}
5555

56+
static void ets_hashtable_free_node(struct HNode *node, GlobalContext *global)
57+
{
58+
memory_destroy_heap(&node->heap, global);
59+
free(node);
60+
}
61+
62+
void ets_hashtable_free_node_array(struct HNode **allocated, size_t size, GlobalContext *global)
63+
{
64+
for (size_t i = 0; i < size; ++i) {
65+
ets_hashtable_free_node(allocated[i], global);
66+
}
67+
}
68+
5669
void ets_hashtable_destroy(struct EtsHashTable *hash_table, GlobalContext *global)
5770
{
5871
for (size_t i = 0; i < hash_table->capacity; ++i) {
5972
struct HNode *node = hash_table->buckets[i];
60-
while (node != 0) {
61-
memory_destroy_heap(node->heap, global);
73+
while (node != NULL) {
6274
struct HNode *next_node = node->next;
63-
free(node);
75+
ets_hashtable_free_node(node, global);
6476
node = next_node;
6577
}
6678
}
@@ -82,8 +94,33 @@ static void print_info(struct EtsHashTable *hash_table)
8294
}
8395
#endif
8496

85-
EtsHashtableErrorCode ets_hashtable_insert(struct EtsHashTable *hash_table, term key, term entry, EtsHashtableOptions opts, Heap *heap, GlobalContext *global)
97+
struct HNode *ets_hashtable_new_node(term entry, int keypos)
8698
{
99+
100+
struct HNode *new_node = malloc(sizeof(struct HNode));
101+
if (IS_NULL_PTR(new_node)) {
102+
return NULL;
103+
}
104+
105+
size_t size = (size_t) memory_estimate_usage(entry);
106+
if (memory_init_heap(&new_node->heap, size) != MEMORY_GC_OK) {
107+
free(new_node);
108+
return NULL;
109+
}
110+
111+
term new_entry = memory_copy_term_tree(&new_node->heap, entry);
112+
term key = term_get_tuple_element(new_entry, keypos);
113+
114+
new_node->next = NULL;
115+
new_node->key = key;
116+
new_node->entry = new_entry;
117+
118+
return new_node;
119+
}
120+
121+
EtsHashtableErrorCode ets_hashtable_insert(struct EtsHashTable *hash_table, struct HNode *new_node, EtsHashtableOptions opts, GlobalContext *global)
122+
{
123+
term key = new_node->key;
87124
uint32_t hash = hash_term(key, global);
88125
uint32_t index = hash % hash_table->capacity;
89126

@@ -94,38 +131,30 @@ EtsHashtableErrorCode ets_hashtable_insert(struct EtsHashTable *hash_table, term
94131
#endif
95132

96133
struct HNode *node = hash_table->buckets[index];
97-
if (node) {
98-
while (1) {
99-
if (term_compare(key, node->key, TermCompareExact, global) == TermEquals) {
100-
if (opts & EtsHashtableAllowOverwrite) {
101-
node->entry = entry;
102-
memory_destroy_heap(node->heap, global);
103-
node->heap = heap;
104-
return EtsHashtableOk;
134+
struct HNode *last_node = NULL;
135+
while (node) {
136+
if (term_compare(key, node->key, TermCompareExact, global) == TermEquals) {
137+
if (opts & EtsHashtableAllowOverwrite) {
138+
if (IS_NULL_PTR(last_node)) {
139+
new_node->next = node->next;
140+
hash_table->buckets[index] = new_node;
105141
} else {
106-
return EtsHashtableFailure;
142+
last_node->next = new_node;
143+
new_node->next = node->next;
107144
}
108-
}
109-
110-
if (node->next) {
111-
node = node->next;
145+
ets_hashtable_free_node(node, global);
146+
return EtsHashtableOk;
112147
} else {
113-
break;
148+
ets_hashtable_free_node(new_node, global);
149+
return EtsHashtableFailure;
114150
}
115151
}
152+
last_node = node;
153+
node = node->next;
116154
}
117155

118-
struct HNode *new_node = malloc(sizeof(struct HNode));
119-
if (IS_NULL_PTR(new_node)) {
120-
return EtsHashtableError;
121-
}
122-
new_node->next = NULL;
123-
new_node->key = key;
124-
new_node->entry = entry;
125-
new_node->heap = heap;
126-
127-
if (node) {
128-
node->next = new_node;
156+
if (last_node) {
157+
last_node->next = new_node;
129158
} else {
130159
hash_table->buckets[index] = new_node;
131160
}
@@ -165,7 +194,7 @@ bool ets_hashtable_remove(struct EtsHashTable *hash_table, term key, size_t keyp
165194
term key_to_compare = term_get_tuple_element(node->entry, keypos);
166195
if (term_compare(key, key_to_compare, TermCompareExact, global) == TermEquals) {
167196

168-
memory_destroy_heap(node->heap, global);
197+
memory_destroy_heap(&node->heap, global);
169198
struct HNode *next_node = node->next;
170199
free(node);
171200

src/libAtomVM/ets_hashtable.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,9 +52,11 @@ typedef enum EtsHashtableErrorCode
5252
struct EtsHashTable *ets_hashtable_new();
5353
void ets_hashtable_destroy(struct EtsHashTable *hash_table, GlobalContext *global);
5454

55-
EtsHashtableErrorCode ets_hashtable_insert(struct EtsHashTable *hash_table, term key, term entry, EtsHashtableOptions opts, Heap *heap, GlobalContext *global);
55+
EtsHashtableErrorCode ets_hashtable_insert(struct EtsHashTable *hash_table, struct HNode *new_node, EtsHashtableOptions opts, GlobalContext *global);
5656
term ets_hashtable_lookup(struct EtsHashTable *hash_table, term key, size_t keypos, GlobalContext *global);
5757
bool ets_hashtable_remove(struct EtsHashTable *hash_table, term key, size_t keypos, GlobalContext *global);
58+
struct HNode *ets_hashtable_new_node(term entry, int keypos);
59+
void ets_hashtable_free_node_array(struct HNode **allocated, size_t len, GlobalContext *global);
5860

5961
#ifdef __cplusplus
6062
}

tests/erlang_tests/test_ets.erl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -356,6 +356,7 @@ test_lookup_element() ->
356356
test_insert_list() ->
357357
Tid = ets:new(test_insert_list, []),
358358
true = ets:insert(Tid, [{foo, tapas}, {batat, batat}, {patat, patat}]),
359+
true = ets:insert(Tid, [{foo, tapas}, {batat, batat}, {patat, patat}]),
359360
[{patat, patat}] = ets:lookup(Tid, patat),
360361
[{batat, batat}] = ets:lookup(Tid, batat),
361362
true = ets:insert(Tid, []),

0 commit comments

Comments
 (0)