Skip to content

Commit 123a704

Browse files
committed
Add option to insert list in ets:insert, ets:lookup refactor
Signed-off-by: Tomasz Sobkiewicz <tomasz.sobkiewicz@swmansion.com>
1 parent 312f7c6 commit 123a704

File tree

5 files changed

+83
-29
lines changed

5 files changed

+83
-29
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1616
- Added `socket:getopt/2`
1717
- Added `supervisor:terminate_child/2`, `supervisor:restart_child/2` and `supervisor:delete_child/2`
1818
- Added `esp:partition_read/3`, and documentation for `esp:partition_erase_range/2/3` and `esp:partition_write/3`
19+
- Added support for list insertion in 'ets:insert/2'.
1920

2021
### Fixed
2122
- ESP32: improved sntp sync speed from a cold boot.

libs/estdlib/src/ets.erl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ new(_Name, _Options) ->
6363
%% @doc Insert an entry into an ets table.
6464
%% @end
6565
%%-----------------------------------------------------------------------------
66-
-spec insert(Table :: table(), Entry :: tuple()) -> true.
66+
-spec insert(Table :: table(), Entry :: tuple() | [tuple()]) -> true.
6767
insert(_Table, _Entry) ->
6868
erlang:nif_error(undefined).
6969

src/libAtomVM/ets.c

Lines changed: 66 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -252,58 +252,90 @@ static void ets_delete_all_tables(struct Ets *ets, GlobalContext *global)
252252
ets_delete_tables_internal(ets, true_pred, NULL, global);
253253
}
254254

255-
EtsErrorCode ets_insert(term ref, term entry, Context *ctx)
255+
static EtsErrorCode ets_table_insert(struct EtsTable *ets_table, term entry, Context *ctx)
256256
{
257-
struct EtsTable *ets_table = term_is_atom(ref) ? ets_get_table_by_name(&ctx->global->ets, ref, TableAccessWrite) : ets_get_table_by_ref(&ctx->global->ets, term_to_ref_ticks(ref), TableAccessWrite);
258-
if (ets_table == NULL) {
259-
return EtsTableNotFound;
260-
}
261-
262257
if (ets_table->access_type != EtsAccessPublic && ets_table->owner_process_id != ctx->process_id) {
263-
SMP_UNLOCK(ets_table);
264258
return EtsPermissionDenied;
265259
}
266260

267261
if ((size_t) term_get_tuple_arity(entry) < (ets_table->keypos + 1)) {
268-
SMP_UNLOCK(ets_table);
269262
return EtsBadEntry;
270263
}
271264

272265
Heap *heap = malloc(sizeof(Heap));
273266
if (IS_NULL_PTR(heap)) {
274-
SMP_UNLOCK(ets_table);
275267
return EtsAllocationFailure;
276268
}
277269
size_t size = (size_t) memory_estimate_usage(entry);
278270
if (memory_init_heap(heap, size) != MEMORY_GC_OK) {
279271
free(heap);
280-
SMP_UNLOCK(ets_table);
281272
return EtsAllocationFailure;
282273
}
283274

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

287-
EtsErrorCode ret = EtsOk;
278+
EtsErrorCode result = EtsOk;
288279
EtsHashtableErrorCode res = ets_hashtable_insert(ets_table->hashtable, key, new_entry, EtsHashtableAllowOverwrite, heap, ctx->global);
289280
if (UNLIKELY(res != EtsHashtableOk)) {
290-
ret = EtsAllocationFailure;
281+
result = EtsAllocationFailure;
291282
}
292283

293-
SMP_UNLOCK(ets_table);
284+
return result;
285+
}
294286

295-
return ret;
287+
static EtsErrorCode ets_table_insert_list(struct EtsTable *ets_table, term list, Context *ctx)
288+
{
289+
term iter = list;
290+
291+
while (term_is_nonempty_list(iter)) {
292+
term tuple = term_get_list_head(iter);
293+
iter = term_get_list_tail(iter);
294+
if (!term_is_tuple(tuple) || (size_t) term_get_tuple_arity(tuple) < (ets_table->keypos + 1)) {
295+
return EtsBadEntry;
296+
}
297+
}
298+
if (!term_is_nil(iter)) {
299+
return EtsBadEntry;
300+
}
301+
302+
while (term_is_nonempty_list(list)) {
303+
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.
307+
}
308+
309+
list = term_get_list_tail(list);
310+
}
311+
312+
return EtsOk;
296313
}
297314

298-
EtsErrorCode ets_lookup(term ref, term key, term *ret, Context *ctx)
315+
EtsErrorCode ets_insert(term name_or_ref, term entry, Context *ctx)
299316
{
300-
struct EtsTable *ets_table = term_is_atom(ref) ? ets_get_table_by_name(&ctx->global->ets, ref, TableAccessRead) : ets_get_table_by_ref(&ctx->global->ets, term_to_ref_ticks(ref), TableAccessRead);
317+
struct EtsTable *ets_table = term_is_atom(name_or_ref) ? ets_get_table_by_name(&ctx->global->ets, name_or_ref, TableAccessWrite) : ets_get_table_by_ref(&ctx->global->ets, term_to_ref_ticks(name_or_ref), TableAccessWrite);
301318
if (ets_table == NULL) {
302319
return EtsTableNotFound;
303320
}
304321

322+
EtsErrorCode result;
323+
if (term_is_tuple(entry)) {
324+
result = ets_table_insert(ets_table, entry, ctx);
325+
} else if (term_is_list(entry)) {
326+
result = ets_table_insert_list(ets_table, entry, ctx);
327+
} else {
328+
result = EtsBadEntry;
329+
}
330+
331+
SMP_UNLOCK(ets_table);
332+
333+
return result;
334+
}
335+
336+
static EtsErrorCode ets_table_lookup(struct EtsTable *ets_table, term key, term *ret, Context *ctx)
337+
{
305338
if (ets_table->access_type == EtsAccessPrivate && ets_table->owner_process_id != ctx->process_id) {
306-
SMP_UNLOCK(ets_table);
307339
return EtsPermissionDenied;
308340
}
309341

@@ -316,24 +348,35 @@ EtsErrorCode ets_lookup(term ref, term key, term *ret, Context *ctx)
316348
size_t size = (size_t) memory_estimate_usage(res);
317349
// allocate [object]
318350
if (UNLIKELY(memory_ensure_free_opt(ctx, size + CONS_SIZE, MEMORY_CAN_SHRINK) != MEMORY_GC_OK)) {
319-
SMP_UNLOCK(ets_table);
320351
return EtsAllocationFailure;
321352
}
322353
term new_res = memory_copy_term_tree(&ctx->heap, res);
323354
*ret = term_list_prepend(new_res, term_nil(), &ctx->heap);
324355
}
325-
SMP_UNLOCK(ets_table);
326356

327357
return EtsOk;
328358
}
329359

330-
EtsErrorCode ets_lookup_element(term ref, term key, size_t pos, term *ret, Context *ctx)
360+
EtsErrorCode ets_lookup(term name_or_ref, term key, term *ret, Context *ctx)
361+
{
362+
struct EtsTable *ets_table = term_is_atom(name_or_ref) ? ets_get_table_by_name(&ctx->global->ets, name_or_ref, TableAccessRead) : ets_get_table_by_ref(&ctx->global->ets, term_to_ref_ticks(name_or_ref), TableAccessRead);
363+
if (ets_table == NULL) {
364+
return EtsTableNotFound;
365+
}
366+
367+
EtsErrorCode result = ets_table_lookup(ets_table, key, ret, ctx);
368+
SMP_UNLOCK(ets_table);
369+
370+
return result;
371+
}
372+
373+
EtsErrorCode ets_lookup_element(term name_or_ref, term key, size_t pos, term *ret, Context *ctx)
331374
{
332375
if (UNLIKELY(pos == 0)) {
333376
return EtsBadPosition;
334377
}
335378

336-
struct EtsTable *ets_table = term_is_atom(ref) ? ets_get_table_by_name(&ctx->global->ets, ref, TableAccessRead) : ets_get_table_by_ref(&ctx->global->ets, term_to_ref_ticks(ref), TableAccessRead);
379+
struct EtsTable *ets_table = term_is_atom(name_or_ref) ? ets_get_table_by_name(&ctx->global->ets, name_or_ref, TableAccessRead) : ets_get_table_by_ref(&ctx->global->ets, term_to_ref_ticks(name_or_ref), TableAccessRead);
337380
if (ets_table == NULL) {
338381
return EtsTableNotFound;
339382
}
@@ -368,9 +411,9 @@ EtsErrorCode ets_lookup_element(term ref, term key, size_t pos, term *ret, Conte
368411
return EtsOk;
369412
}
370413

371-
EtsErrorCode ets_delete(term ref, term key, term *ret, Context *ctx)
414+
EtsErrorCode ets_delete(term name_or_ref, term key, term *ret, Context *ctx)
372415
{
373-
struct EtsTable *ets_table = term_is_atom(ref) ? ets_get_table_by_name(&ctx->global->ets, ref, TableAccessRead) : ets_get_table_by_ref(&ctx->global->ets, term_to_ref_ticks(ref), TableAccessRead);
416+
struct EtsTable *ets_table = term_is_atom(name_or_ref) ? ets_get_table_by_name(&ctx->global->ets, name_or_ref, TableAccessRead) : ets_get_table_by_ref(&ctx->global->ets, term_to_ref_ticks(name_or_ref), TableAccessRead);
374417
if (ets_table == NULL) {
375418
return EtsTableNotFound;
376419
}

src/libAtomVM/nifs.c

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3327,10 +3327,6 @@ static term nif_ets_insert(Context *ctx, int argc, term argv[])
33273327
VALIDATE_VALUE(ref, is_ets_table_id);
33283328

33293329
term entry = argv[1];
3330-
VALIDATE_VALUE(entry, term_is_tuple);
3331-
if (term_get_tuple_arity(entry) < 1) {
3332-
RAISE_ERROR(BADARG_ATOM);
3333-
}
33343330

33353331
EtsErrorCode result = ets_insert(ref, entry, ctx);
33363332
switch (result) {

tests/erlang_tests/test_ets.erl

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ start() ->
3131
ok = test_protected_access(),
3232
ok = test_public_access(),
3333
ok = test_lookup_element(),
34-
34+
ok = test_insert_list(),
3535
0.
3636

3737
test_basic() ->
@@ -352,3 +352,17 @@ test_lookup_element() ->
352352
expect_failure(fun() -> ets:lookup_element(Tid, foo, 3) end),
353353
expect_failure(fun() -> ets:lookup_element(Tid, foo, 0) end),
354354
ok.
355+
356+
test_insert_list() ->
357+
Tid = ets:new(test_insert_list, []),
358+
true = ets:insert(Tid, [{foo, tapas}, {batat, batat}, {patat, patat}]),
359+
[{patat, patat}] = ets:lookup(Tid, patat),
360+
[{batat, batat}] = ets:lookup(Tid, batat),
361+
true = ets:insert(Tid, []),
362+
expect_failure(fun() -> ets:insert(Tid, [{foo, tapas} | {patat, patat}]) end),
363+
expect_failure(fun() -> ets:insert(Tid, [{foo, tapas}, {batat, batat}, {patat, patat}, {}]) end),
364+
expect_failure(fun() ->
365+
ets:insert(Tid, [{foo, tapas}, pararara, {batat, batat}, {patat, patat}])
366+
end),
367+
expect_failure(fun() -> ets:insert(Tid, [{}]) end),
368+
ok.

0 commit comments

Comments
 (0)