Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add ets:delete/1, ets:delete refactor #1461

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Added `socket:getopt/2`
- Added `supervisor:terminate_child/2`, `supervisor:restart_child/2` and `supervisor:delete_child/2`
- Added `esp:partition_read/3`, and documentation for `esp:partition_erase_range/2/3` and `esp:partition_write/3`
- Added support for list insertion in 'ets:insert/2'.
- Added support for 'ets:delete/1'.

### Fixed
- ESP32: improved sntp sync speed from a cold boot.
Expand Down
12 changes: 11 additions & 1 deletion libs/estdlib/src/ets.erl
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
insert/2,
lookup/2,
lookup_element/3,
delete/1,
delete/2
]).

Expand Down Expand Up @@ -63,7 +64,7 @@ new(_Name, _Options) ->
%% @doc Insert an entry into an ets table.
%% @end
%%-----------------------------------------------------------------------------
-spec insert(Table :: table(), Entry :: tuple()) -> true.
-spec insert(Table :: table(), Entry :: tuple() | [tuple()]) -> true.
insert(_Table, _Entry) ->
erlang:nif_error(undefined).

Expand Down Expand Up @@ -101,3 +102,12 @@ lookup_element(_Table, _Key, _Pos) ->
-spec delete(Table :: table(), Key :: term()) -> true.
delete(_Table, _Key) ->
erlang:nif_error(undefined).
%%-----------------------------------------------------------------------------
%% @param Table a reference to the ets table
%% @returns true; otherwise, an error is raised if arguments are bad
%% @doc Delete an ets table.
%% @end
%%-----------------------------------------------------------------------------
-spec delete(Table :: table()) -> true.
delete(_Table) ->
erlang:nif_error(undefined).
161 changes: 122 additions & 39 deletions src/libAtomVM/ets.c
Original file line number Diff line number Diff line change
Expand Up @@ -252,58 +252,104 @@ static void ets_delete_all_tables(struct Ets *ets, GlobalContext *global)
ets_delete_tables_internal(ets, true_pred, NULL, global);
}

EtsErrorCode ets_insert(term ref, term entry, Context *ctx)
static EtsErrorCode ets_table_insert(struct EtsTable *ets_table, term entry, Context *ctx)
{
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);
if (ets_table == NULL) {
return EtsTableNotFound;
}

if (ets_table->access_type != EtsAccessPublic && ets_table->owner_process_id != ctx->process_id) {
SMP_UNLOCK(ets_table);
return EtsPermissionDenied;
}

if ((size_t) term_get_tuple_arity(entry) < (ets_table->keypos + 1)) {
SMP_UNLOCK(ets_table);
size_t keypos = ets_table->keypos;

if ((size_t) term_get_tuple_arity(entry) < keypos + 1) {
return EtsBadEntry;
}

Heap *heap = malloc(sizeof(Heap));
if (IS_NULL_PTR(heap)) {
SMP_UNLOCK(ets_table);
struct HNode *new_node = ets_hashtable_new_node(entry, keypos, ctx->global);
if (IS_NULL_PTR(new_node)) {
return EtsAllocationFailure;
}
size_t size = (size_t) memory_estimate_usage(entry);
if (memory_init_heap(heap, size) != MEMORY_GC_OK) {
free(heap);
SMP_UNLOCK(ets_table);

EtsHashtableErrorCode res = ets_hashtable_insert(ets_table->hashtable, new_node, EtsHashtableAllowOverwrite, ctx->global);
if (UNLIKELY(res != EtsHashtableOk)) {
return EtsAllocationFailure;
}

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

EtsErrorCode ret = EtsOk;
EtsHashtableErrorCode res = ets_hashtable_insert(ets_table->hashtable, key, new_entry, EtsHashtableAllowOverwrite, heap, ctx->global);
if (UNLIKELY(res != EtsHashtableOk)) {
ret = EtsAllocationFailure;
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;
}

SMP_UNLOCK(ets_table);
term iter = list;
size_t size = 0;

return ret;
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 **nodes = malloc(size * sizeof(struct HNode *));
if (IS_NULL_PTR(nodes)) {
return EtsAllocationFailure;
}

size_t i = 0;
while (term_is_nonempty_list(list)) {
term tuple = term_get_list_head(list);
nodes[i] = ets_hashtable_new_node(tuple, ets_table->keypos, ctx->global);
if (IS_NULL_PTR(nodes[i])) {
ets_hashtable_free_node_array(nodes, i, ctx->global);
free(nodes);
return EtsAllocationFailure;
}
++i;
list = term_get_list_tail(list);
}

for (size_t i = 0; i < size; ++i) {

EtsHashtableErrorCode res = ets_hashtable_insert(ets_table->hashtable, nodes[i], EtsHashtableAllowOverwrite, ctx->global);
assert(res == EtsHashtableOk);
}

free(nodes);
return EtsOk;
}

EtsErrorCode ets_lookup(term ref, term key, term *ret, Context *ctx)
EtsErrorCode ets_insert(term name_or_ref, term entry, Context *ctx)
{
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);
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);
if (ets_table == NULL) {
return EtsTableNotFound;
}

EtsErrorCode result;
if (term_is_tuple(entry)) {
result = ets_table_insert(ets_table, entry, ctx);
} else if (term_is_list(entry)) {
result = ets_table_insert_list(ets_table, entry, ctx);
} else {
result = EtsBadEntry;
}

SMP_UNLOCK(ets_table);

return result;
}

static EtsErrorCode ets_table_lookup(struct EtsTable *ets_table, term key, term *ret, Context *ctx)
{
if (ets_table->access_type == EtsAccessPrivate && ets_table->owner_process_id != ctx->process_id) {
SMP_UNLOCK(ets_table);
return EtsPermissionDenied;
}

Expand All @@ -316,24 +362,35 @@ EtsErrorCode ets_lookup(term ref, term key, term *ret, Context *ctx)
size_t size = (size_t) memory_estimate_usage(res);
// allocate [object]
if (UNLIKELY(memory_ensure_free_opt(ctx, size + CONS_SIZE, MEMORY_CAN_SHRINK) != MEMORY_GC_OK)) {
SMP_UNLOCK(ets_table);
return EtsAllocationFailure;
}
term new_res = memory_copy_term_tree(&ctx->heap, res);
*ret = term_list_prepend(new_res, term_nil(), &ctx->heap);
}
SMP_UNLOCK(ets_table);

return EtsOk;
}

EtsErrorCode ets_lookup_element(term ref, term key, size_t pos, term *ret, Context *ctx)
EtsErrorCode ets_lookup(term name_or_ref, term key, term *ret, Context *ctx)
{
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);
if (ets_table == NULL) {
return EtsTableNotFound;
}

EtsErrorCode result = ets_table_lookup(ets_table, key, ret, ctx);
SMP_UNLOCK(ets_table);

return result;
}

EtsErrorCode ets_lookup_element(term name_or_ref, term key, size_t pos, term *ret, Context *ctx)
{
if (UNLIKELY(pos == 0)) {
return EtsBadPosition;
}

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);
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);
if (ets_table == NULL) {
return EtsTableNotFound;
}
Expand Down Expand Up @@ -368,23 +425,49 @@ EtsErrorCode ets_lookup_element(term ref, term key, size_t pos, term *ret, Conte
return EtsOk;
}

EtsErrorCode ets_delete(term ref, term key, term *ret, Context *ctx)
static EtsErrorCode ets_table_delete(struct EtsTable *ets_table, term key, term *ret, Context *ctx)
{
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);
if (ets_table == NULL) {
return EtsTableNotFound;
}

if (ets_table->access_type != EtsAccessPublic && ets_table->owner_process_id != ctx->process_id) {
SMP_UNLOCK(ets_table);
return EtsPermissionDenied;
}

bool _res = ets_hashtable_remove(ets_table->hashtable, key, ets_table->keypos, ctx->global);
UNUSED(_res);

SMP_UNLOCK(ets_table);
*ret = TRUE_ATOM;
return EtsOk;
}

EtsErrorCode ets_drop_table(term name_or_ref, term *ret, Context *ctx)
{
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);
if (IS_NULL_PTR(ets_table)) {
return EtsTableNotFound;
}
if (ets_table->access_type != EtsAccessPublic && ets_table->owner_process_id != ctx->process_id) {
return EtsPermissionDenied;
}

struct ListHead *_ets_tables_list = synclist_wrlock(&ctx->global->ets.ets_tables);
UNUSED(_ets_tables_list);
SMP_UNLOCK(ets_table);
list_remove(&ets_table->head);
ets_table_destroy(ets_table, ctx->global);
synclist_unlock(&ctx->global->ets.ets_tables);

*ret = TRUE_ATOM;
return EtsOk;
}

EtsErrorCode ets_delete(term name_or_ref, term key, term *ret, Context *ctx)
{
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);
if (IS_NULL_PTR(ets_table)) {
return EtsTableNotFound;
}

EtsErrorCode res = ets_table_delete(ets_table, key, ret, ctx);

SMP_UNLOCK(ets_table);
return res;
}
1 change: 1 addition & 0 deletions src/libAtomVM/ets.h
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ EtsErrorCode ets_insert(term ref, term entry, Context *ctx);
EtsErrorCode ets_lookup(term ref, term key, term *ret, Context *ctx);
EtsErrorCode ets_lookup_element(term ref, term key, size_t pos, term *ret, Context *ctx);
EtsErrorCode ets_delete(term ref, term key, term *ret, Context *ctx);
EtsErrorCode ets_drop_table(term ref, term *ret, Context *ctx);

#ifdef __cplusplus
}
Expand Down
Loading
Loading