Skip to content

Support duplicate bag in insert and lookup #1718

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

Open
wants to merge 17 commits into
base: main
Choose a base branch
from

Conversation

jgonet
Copy link
Contributor

@jgonet jgonet commented Jun 17, 2025

Merge after #1614. New code after "ets hash table: rename status enum".

These changes are made under both the "Apache 2.0" and the "GNU Lesser General
Public License 2.1 or later" license terms (dual license).

SPDX-License-Identifier: Apache-2.0 OR LGPL-2.1-or-later

Comment on lines +3341 to +3618
switch (key_atom) {
case NAMED_TABLE_ATOM: {
is_named = true;
break;
}
case PRIVATE_ATOM: {
private = true;
public = false;
break;
}
case PUBLIC_ATOM: {
private = false;
public = true;
break;
}
case SET_ATOM: {
is_duplicate_bag = false;
break;
}
case DUPLICATE_BAG_ATOM: {
is_duplicate_bag = true;
break;
}
case KEYPOS_ATOM: {
VALIDATE_VALUE(value, term_is_integer);
avm_int_t keypos = term_to_int(value);
if (UNLIKELY(keypos < 1)) {
RAISE_ERROR(BADARG_ATOM);
}
key_index = keypos - 1;
break;
}
default:
RAISE_ERROR(BADARG_ATOM);
}
options = term_get_list_tail(options);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was thinking about adding interop_parse_kw_opts(term kw, &opts) but this loop relies on ability to override keys when they're repeated (e.g. [set, duplicate_bag, set]). Maybe in the future.

}

// TODO: create list elsewhere, by copying terms from orig heap, appending new copied tuple and using ets_hashtable_new_node
struct HNode *ets_hashtable_new_node_from_list(term old_tuples, term tuple, size_t key_index)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This copies old tuple list from previous node's heap, copies tuple from process' heap and merges them together on the new heap. TODO is for removing this function and using utility function to do that and then just pass new heap to new_node.

@@ -248,78 +243,105 @@ static bool true_pred(struct EtsTable *table, void *data)
return true;
}

static void ets_delete_all_tables(struct Ets *ets, GlobalContext *global)
static void delete_all_tables(struct Ets *ets, GlobalContext *global)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was constantly confusing exported functions and internal functions so I opted for rename removing prefix for internal ones (previously: ets_lookup_maybe_gc vs ets_lookup_table_maybe_gc, now ets_lookup_maybe_gc vs lookup_maybe_gc).

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds a reasonable point, I will note this on the idea for the updated coding style.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The alternative we have in the fork is to use internal prefix/suffix but it's get lengthy.

@@ -52,7 +58,7 @@ struct EtsTable
term name;
bool is_named;
int32_t owner_process_id;
size_t keypos;
size_t key_index;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was already using 0-based indexes but was named position which suggests 1-based indexes. Cleaned all up.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good catch.

Copy link
Collaborator

@bettio bettio left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm adding a first round of comments here, I did not yet finished this review, so more comments will follow.
A rebase on latest main would defintely help, since the previous ets PR has been merged.

@@ -52,7 +58,7 @@ struct EtsTable
term name;
bool is_named;
int32_t owner_process_id;
size_t keypos;
size_t key_index;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good catch.

@@ -3297,35 +3297,96 @@ static term nif_erlang_raise(Context *ctx, int argc, term argv[])
return term_invalid_term();
}

static inline term get_option_key(term option, term *maybe_value)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it took me a moment to find the purpose of this function, so I suggest naming it extract_option_key_value().

Also, if you plan to make something generic (while keeping a clean and simple API) that can be used elsewhere as well, returning true as value for atoms might be a good choice (while using undefined when no property is found).

VALIDATE_VALUE(key_atom, term_is_atom);

switch (key_atom) {
case NAMED_TABLE_ATOM: {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fun fact: on OTP it is not a proplist, so ets:new(foo, [{named_table, true}]). raises badarg, but ets:new(fiz, [named_table]) doesn't.
So I think this can be handled with a simple change:

switch (head) {
   case NAMED_TABLE_ATOM:
      ...
   default:
       VALIDATE_VALUE(head, term_is_tuple);
       term value;
       term key_atom = get_option_key(head, &value);
       VALIDATE_VALUE(key_atom, term_is_atom);

ets:new(foo, [{named_table, true}]). also, let's make sure in tests that fails as expected.

@@ -248,78 +243,105 @@ static bool true_pred(struct EtsTable *table, void *data)
return true;
}

static void ets_delete_all_tables(struct Ets *ets, GlobalContext *global)
static void delete_all_tables(struct Ets *ets, GlobalContext *global)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds a reasonable point, I will note this on the idea for the updated coding style.

jgonet added 17 commits July 8, 2025 21:46
Signed-off-by: Jakub Gonet <jakub.gonet@swmansion.com>
Signed-off-by: Jakub Gonet <jakub.gonet@swmansion.com>
Signed-off-by: Jakub Gonet <jakub.gonet@swmansion.com>
ETS mixes position (1-based) with indexing (0-based).
Tuple are 0-indexed so we should convert to it at earliest possible moment.

This commit doesn't introduce any change in behavior.

Signed-off-by: Jakub Gonet <jakub.gonet@swmansion.com>
Signed-off-by: Jakub Gonet <jakub.gonet@swmansion.com>
Signed-off-by: Jakub Gonet <jakub.gonet@swmansion.com>
ETS supports setting different key position (default: first element in tuple) in ets:new/2.
Checking if updated element shouldn't be placed as first element is wrong.

Signed-off-by: Jakub Gonet <jakub.gonet@swmansion.com>
Additionally, unifies tuple arity checks (no change in behavior).

Signed-off-by: Jakub Gonet <jakub.gonet@swmansion.com>
Signed-off-by: Jakub Gonet <jakub.gonet@swmansion.com>
Signed-off-by: Jakub Gonet <jakub.gonet@swmansion.com>
Signed-off-by: Jakub Gonet <jakub.gonet@swmansion.com>
Signed-off-by: Jakub Gonet <jakub.gonet@swmansion.com>
Signed-off-by: Jakub Gonet <jakub.gonet@swmansion.com>
Signed-off-by: Jakub Gonet <jakub.gonet@swmansion.com>
Signed-off-by: Jakub Gonet <jakub.gonet@swmansion.com>
Signed-off-by: Jakub Gonet <jakub.gonet@swmansion.com>
Signed-off-by: Jakub Gonet <jakub.gonet@swmansion.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants