Skip to content

Commit 98acad0

Browse files
committed
ets: support duplicate_bag in insert/2 list
Signed-off-by: Jakub Gonet <jakub.gonet@swmansion.com>
1 parent 684d4b8 commit 98acad0

File tree

2 files changed

+57
-39
lines changed

2 files changed

+57
-39
lines changed

src/libAtomVM/ets.c

Lines changed: 54 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
#include "list.h"
2727
#include "memory.h"
2828
#include "overflow_helpers.h"
29+
#include "smp.h"
2930
#include "term.h"
3031
#include "utils.h"
3132
#include <assert.h>
@@ -252,38 +253,70 @@ static inline bool has_key_at(term tuple, size_t key_index)
252253
return key_index < (size_t) term_get_tuple_arity(tuple);
253254
}
254255

255-
static EtsErrorCode insert(struct EtsTable *ets_table, term tuple, Context *ctx)
256+
static struct HNode *tuple_to_insertion_node(struct EtsTable *ets_table, term tuple, GlobalContext *global)
256257
{
258+
bool is_duplicate_bag = ets_table->table_type == EtsTableDuplicateBag;
257259
size_t key_index = ets_table->key_index;
258-
if (UNLIKELY(!has_key_at(tuple, key_index))) {
259-
return EtsBadEntry;
260-
}
261260

262-
bool is_duplicate_bag = ets_table->table_type == EtsTableDuplicateBag;
263-
struct HNode *node = NULL;
264261
if (is_duplicate_bag) {
265262
term key = term_get_tuple_element(tuple, key_index);
266-
term old_tuples = ets_hashtable_lookup(ets_table->hashtable, key, ctx->global);
263+
term old_tuples = ets_hashtable_lookup(ets_table->hashtable, key, global);
267264
assert(term_is_list(old_tuples));
268265
bool is_new = term_is_nil(old_tuples);
269-
node = ets_hashtable_new_node_from_list(is_new ? term_nil() : old_tuples, tuple, key_index);
270-
} else {
271-
node = ets_hashtable_new_node(tuple, key_index);
266+
return ets_hashtable_new_node_from_list(is_new ? term_nil() : old_tuples, tuple, key_index);
267+
}
268+
return ets_hashtable_new_node(tuple, key_index);
269+
}
270+
271+
static struct HNode **list_to_insertion_nodes(struct EtsTable *ets_table, term list, size_t size, GlobalContext *global)
272+
{
273+
size_t last_i = 0;
274+
struct HNode **nodes = malloc(size * sizeof(struct HNode *));
275+
if (IS_NULL_PTR(nodes)) {
276+
goto oom;
277+
}
278+
279+
while (term_is_nonempty_list(list)) {
280+
term tuple = term_get_list_head(list);
281+
nodes[last_i] = tuple_to_insertion_node(ets_table, tuple, global);
282+
if (IS_NULL_PTR(nodes[last_i])) {
283+
goto oom;
284+
}
285+
++last_i;
286+
list = term_get_list_tail(list);
287+
}
288+
return nodes;
289+
oom:
290+
// skip last node, it's NULL
291+
for (size_t i = 0; i < last_i; ++i) {
292+
ets_hashtable_free_node(nodes[i], global);
272293
}
294+
free(nodes);
295+
return NULL;
296+
}
297+
298+
static EtsErrorCode insert_tuple(struct EtsTable *ets_table, term tuple, GlobalContext *global)
299+
{
300+
size_t key_index = ets_table->key_index;
301+
if (UNLIKELY(!has_key_at(tuple, key_index))) {
302+
return EtsBadEntry;
303+
}
304+
305+
struct HNode *node = tuple_to_insertion_node(ets_table, tuple, global);
273306
if (IS_NULL_PTR(node)) {
274307
return EtsAllocationFailure;
275308
}
276309

277-
EtsHashtableStatus res = ets_hashtable_insert(ets_table->hashtable, node, EtsHashtableAllowOverwrite, ctx->global);
310+
EtsHashtableStatus res = ets_hashtable_insert(ets_table->hashtable, node, EtsHashtableAllowOverwrite, global);
278311
if (UNLIKELY(res != EtsHashtableOk)) {
279-
ets_hashtable_free_node(node, ctx->global);
312+
ets_hashtable_free_node(node, global);
280313
return EtsAllocationFailure;
281314
}
282315

283316
return EtsOk;
284317
}
285318

286-
static EtsErrorCode insert_multiple(struct EtsTable *ets_table, term list, Context *ctx)
319+
static EtsErrorCode insert_tuple_list(struct EtsTable *ets_table, term list, GlobalContext *global)
287320
{
288321
term iter = list;
289322
size_t size = 0;
@@ -296,32 +329,19 @@ static EtsErrorCode insert_multiple(struct EtsTable *ets_table, term list, Conte
296329
}
297330
++size;
298331
}
299-
if (!term_is_nil(iter)) {
332+
bool improper = !term_is_nil(iter);
333+
if (UNLIKELY(improper)) {
300334
return EtsBadEntry;
301335
}
302336

303-
struct HNode **nodes = malloc(size * sizeof(struct HNode *));
337+
struct HNode **nodes = list_to_insertion_nodes(ets_table, list, size, global);
304338
if (IS_NULL_PTR(nodes)) {
305339
return EtsAllocationFailure;
306340
}
307341

308-
size_t i = 0;
309-
while (term_is_nonempty_list(list)) {
310-
term tuple = term_get_list_head(list);
311-
nodes[i] = ets_hashtable_new_node(tuple, ets_table->key_index);
312-
if (IS_NULL_PTR(nodes[i])) {
313-
for (size_t it = 0; it < i; ++it) {
314-
ets_hashtable_free_node(nodes[it], ctx->global);
315-
}
316-
free(nodes);
317-
return EtsAllocationFailure;
318-
}
319-
++i;
320-
list = term_get_list_tail(list);
321-
}
322-
323342
for (size_t i = 0; i < size; ++i) {
324-
EtsHashtableStatus res = ets_hashtable_insert(ets_table->hashtable, nodes[i], EtsHashtableAllowOverwrite, ctx->global);
343+
EtsHashtableStatus res = ets_hashtable_insert(ets_table->hashtable, nodes[i], EtsHashtableAllowOverwrite, global);
344+
// insert can fail when comparing keys
325345
assert(res == EtsHashtableOk);
326346
}
327347

@@ -338,9 +358,9 @@ EtsErrorCode ets_insert(term name_or_ref, term entry, Context *ctx)
338358

339359
EtsErrorCode result;
340360
if (term_is_tuple(entry)) {
341-
result = insert(ets_table, entry, ctx);
361+
result = insert_tuple(ets_table, entry, ctx->global);
342362
} else if (term_is_list(entry)) {
343-
result = insert_multiple(ets_table, entry, ctx);
363+
result = insert_tuple_list(ets_table, entry, ctx->global);
344364
} else {
345365
result = EtsBadEntry;
346366
}
@@ -596,7 +616,7 @@ EtsErrorCode ets_update_counter_maybe_gc(term ref, term key, term operation, ter
596616

597617
term final_value = term_from_int(elem_value);
598618
term_put_tuple_element(to_insert, elem_index, final_value);
599-
EtsErrorCode insert_result = insert(ets_table, to_insert, ctx);
619+
EtsErrorCode insert_result = insert_tuple(ets_table, to_insert, ctx->global);
600620
if (insert_result == EtsOk) {
601621
*ret = final_value;
602622
}

tests/erlang_tests/test_ets.erl

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -271,11 +271,9 @@ test_duplicate_bag() ->
271271
% false = ets:insert_new(Tid, T),
272272
true = ets:insert(Tid, T),
273273
true = ets:insert(Tid, T),
274-
true = [T, T] == ets:lookup(Tid, foo),
275-
276-
% true = ets:insert(Tid, [T, T]),
277-
% true = ets:insert(Tid, [T2]),
278-
% true = [T, T, T, T, T2] == ets:lookup(Tid, foo),
274+
true = ets:insert(Tid, [T, T]),
275+
true = ets:insert(Tid, [T2]),
276+
true = [T, T, T, T2] == ets:lookup(Tid, foo),
279277
% true = ets:member(Tid, foo),
280278

281279
% % nothing inserted, T exists in table

0 commit comments

Comments
 (0)