Skip to content

Commit

Permalink
Add support for encoded refs in external terms
Browse files Browse the repository at this point in the history
Signed-off-by: Paul Guyot <[email protected]>
  • Loading branch information
pguyot committed Nov 7, 2024
1 parent 73518c6 commit a5c5eed
Show file tree
Hide file tree
Showing 11 changed files with 356 additions and 24 deletions.
2 changes: 1 addition & 1 deletion src/libAtomVM/context.c
Original file line number Diff line number Diff line change
Expand Up @@ -225,7 +225,7 @@ void context_process_flush_monitor_signal(Context *ctx, uint64_t ref_ticks, bool
if (term_is_tuple(msg)
&& term_get_tuple_arity(msg) == 5
&& term_get_tuple_element(msg, 0) == DOWN_ATOM
&& term_is_reference(term_get_tuple_element(msg, 1))
&& term_is_local_reference(term_get_tuple_element(msg, 1))
&& term_to_ref_ticks(term_get_tuple_element(msg, 1)) == ref_ticks) {
mailbox_remove_message(&ctx->mailbox, &ctx->heap);
// If option info is combined with option flush, false is returned if a flush was needed, otherwise true.
Expand Down
2 changes: 1 addition & 1 deletion src/libAtomVM/ets_hashtable.c
Original file line number Diff line number Diff line change
Expand Up @@ -287,7 +287,7 @@ static uint32_t hash_term_incr(term t, int32_t h, GlobalContext *global)
return hash_float(t, h, global);
} else if (term_is_local_pid(t)) {
return hash_pid(t, h, global);
} else if (term_is_reference(t)) {
} else if (term_is_local_reference(t)) {
return hash_reference(t, h, global);
} else if (term_is_binary(t)) {
return hash_binary(t, h, global);
Expand Down
91 changes: 91 additions & 0 deletions src/libAtomVM/externalterm.c
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@

#define NEW_FLOAT_EXT 70
#define NEW_PID_EXT 88
#define NEWER_REFERENCE_EXT 90
#define SMALL_INTEGER_EXT 97
#define INTEGER_EXT 98
#define ATOM_EXT 100
Expand Down Expand Up @@ -419,6 +420,46 @@ static int serialize_term(uint8_t *buf, term t, GlobalContext *glb)
WRITE_32_UNALIGNED(buf + k + 8, term_to_external_node_creation(t));
}
return k + 12;
} else if (term_is_local_reference(t)) {
if (!IS_NULL_PTR(buf)) {
buf[0] = NEWER_REFERENCE_EXT;
}
size_t k = 1;
if (!IS_NULL_PTR(buf)) {
WRITE_16_UNALIGNED(buf + k, 2); // len
}
k += 2;
k += serialize_term(IS_NULL_PTR(buf) ? NULL : buf + k, NONODE_AT_NOHOST_ATOM, glb);
if (!IS_NULL_PTR(buf)) {
uint64_t ticks = term_to_ref_ticks(t);
WRITE_64_UNALIGNED(buf + k, ticks);
WRITE_32_UNALIGNED(buf + k + 8, 0); // creation
}
return k + 12;
} else if (term_is_external_reference(t)) {
if (!IS_NULL_PTR(buf)) {
buf[0] = NEWER_REFERENCE_EXT;
}
size_t k = 1;
if (!IS_NULL_PTR(buf)) {
WRITE_16_UNALIGNED(buf + k, 2); // len
}
k += 2;
term node = term_to_external_node(t);
k += serialize_term(IS_NULL_PTR(buf) ? NULL : buf + k, node, glb);
uint32_t len = term_to_external_reference_len(t);
if (!IS_NULL_PTR(buf)) {
WRITE_32_UNALIGNED(buf + k, term_to_external_node_creation(t));
}
k += 4;
if (!IS_NULL_PTR(buf)) {
uint32_t data[len];
term_to_external_reference_words(t, data);
for (uint32_t i = 0; i < len; i++) {
WRITE_64_UNALIGNED(buf + k + (i * 4), data[i]);
}
}
return k + (4 * len);
} else {
fprintf(stderr, "Unknown external term type: %" TERM_U_FMT "\n", t);
AVM_ABORT();
Expand Down Expand Up @@ -709,6 +750,30 @@ static term parse_external_terms(const uint8_t *external_term_buf, size_t *eterm
}
}

case NEWER_REFERENCE_EXT: {
uint16_t len = READ_16_UNALIGNED(external_term_buf + 1);
if (UNLIKELY(len > 5)) {
return term_invalid_term();
}
size_t node_size;
term node = parse_external_terms(external_term_buf + 3, &node_size, copy, heap, glb);
if (UNLIKELY(!term_is_atom(node))) {
return term_invalid_term();
}
uint32_t creation = READ_32_UNALIGNED(external_term_buf + node_size + 3);
uint32_t data[len];
for (uint16_t i = 0; i < len; i++) {
data[i] = READ_32_UNALIGNED(external_term_buf + node_size + 7 + (i * 4));
}
*eterm_size = node_size + 7 + (len * 4);
if (node != NONODE_AT_NOHOST_ATOM || len != 2 || creation != 0) {
return term_from_external_reference(node, len, data, creation, heap);
} else {
uint64_t ticks = ((uint64_t) data[0]) << 32 | data[1];
return term_from_ref_ticks(ticks, heap);
}
}

default:
return term_invalid_term();
}
Expand Down Expand Up @@ -1023,6 +1088,32 @@ static int calculate_heap_usage(const uint8_t *external_term_buf, size_t remaini
return heap_size + u;
}

case NEWER_REFERENCE_EXT: {
if (UNLIKELY(remaining < 3)) {
return INVALID_TERM_SIZE;
}
remaining -= 3;
int buf_pos = 3;
size_t heap_size = EXTERNAL_REF_SIZE;
uint16_t len = READ_16_UNALIGNED(external_term_buf + 1);
size_t node_size = 0;
int u = calculate_heap_usage(external_term_buf + buf_pos, remaining, &node_size, copy);
if (UNLIKELY(u == INVALID_TERM_SIZE)) {
return INVALID_TERM_SIZE;
}
if (external_term_buf[3] == SMALL_ATOM_UTF8_EXT) {
// Check if it's local node
if (len == 2 && external_term_buf[4] == strlen("nonode@nohost") && memcmp(external_term_buf + 5, "nonode@nohost", strlen("nonode@nohost")) == 0) {
heap_size = REF_SIZE;
}
} else if (UNLIKELY(external_term_buf[3] != ATOM_EXT)) {
return INVALID_TERM_SIZE;
}
buf_pos += node_size;
*eterm_size = buf_pos + 4 + (len * 4);
return heap_size + u;
}

default:
return INVALID_TERM_SIZE;
}
Expand Down
8 changes: 8 additions & 0 deletions src/libAtomVM/memory.c
Original file line number Diff line number Diff line change
Expand Up @@ -628,6 +628,10 @@ static void memory_scan_and_copy(HeapFragment *old_fragment, term *mem_start, co
TRACE("- Found external pid.\n");
break;

case TERM_BOXED_EXTERNAL_REF:
TRACE("- Found external ref.\n");
break;

case TERM_BOXED_FUN: {
int fun_size = term_get_size_from_boxed_header(t);
TRACE("- Found fun, size: %i.\n", fun_size);
Expand Down Expand Up @@ -752,6 +756,10 @@ static void memory_scan_and_rewrite(size_t count, term *terms, const term *old_s
ptr += EXTERNAL_PID_SIZE - 1;
break;

case TERM_BOXED_EXTERNAL_REF:
ptr += EXTERNAL_REF_SIZE - 1;
break;

case TERM_BOXED_FUN:
// Skip header and module and process next terms
ptr++;
Expand Down
10 changes: 5 additions & 5 deletions src/libAtomVM/nifs.c
Original file line number Diff line number Diff line change
Expand Up @@ -3301,7 +3301,7 @@ static term nif_ets_new(Context *ctx, int argc, term argv[])

static inline bool is_ets_table_id(term t)
{
return term_is_reference(t) || term_is_atom(t);
return term_is_local_reference(t) || term_is_atom(t);
}

static term nif_ets_insert(Context *ctx, int argc, term argv[])
Expand Down Expand Up @@ -3462,10 +3462,10 @@ static term nif_erlang_ref_to_list(Context *ctx, int argc, term argv[])
UNUSED(argc);

term t = argv[0];
VALIDATE_VALUE(t, term_is_reference);
VALIDATE_VALUE(t, term_is_local_reference);

char buf[REF_AS_CSTRING_LEN];
int str_len = term_snprint(buf, REF_AS_CSTRING_LEN, t, ctx->global);
char buf[LOCAL_REF_AS_CSTRING_LEN];
int str_len = term_snprint(buf, LOCAL_REF_AS_CSTRING_LEN, t, ctx->global);
if (UNLIKELY(str_len < 0)) {
// TODO: change to internal error or something like that
RAISE_ERROR(OUT_OF_MEMORY_ATOM);
Expand Down Expand Up @@ -3767,7 +3767,7 @@ static term nif_erlang_demonitor(Context *ctx, int argc, term argv[])
info = interop_proplist_get_value_default(options, INFO_ATOM, FALSE_ATOM) == TRUE_ATOM;
}

VALIDATE_VALUE(ref, term_is_reference);
VALIDATE_VALUE(ref, term_is_local_reference);
uint64_t ref_ticks = term_to_ref_ticks(ref);

bool result = globalcontext_demonitor(ctx->global, ref_ticks);
Expand Down
4 changes: 2 additions & 2 deletions src/libAtomVM/otp_socket.c
Original file line number Diff line number Diff line change
Expand Up @@ -593,7 +593,7 @@ bool term_is_otp_socket(term socket_term)
bool ret = term_is_tuple(socket_term)
&& term_get_tuple_arity(socket_term) == 2
&& term_is_binary(term_get_tuple_element(socket_term, 0))
&& term_is_reference(term_get_tuple_element(socket_term, 1));
&& term_is_local_reference(term_get_tuple_element(socket_term, 1));

TRACE("term is a socket: %i\n", ret);

Expand Down Expand Up @@ -898,7 +898,7 @@ static term nif_socket_select_read(Context *ctx, int argc, term argv[])

term select_ref_term = argv[1];
if (select_ref_term != UNDEFINED_ATOM) {
VALIDATE_VALUE(select_ref_term, term_is_reference);
VALIDATE_VALUE(select_ref_term, term_is_local_reference);
}
struct SocketResource *rsrc_obj;
if (UNLIKELY(!term_to_otp_socket(argv[0], &rsrc_obj, ctx))) {
Expand Down
2 changes: 1 addition & 1 deletion src/libAtomVM/posix_nifs.c
Original file line number Diff line number Diff line change
Expand Up @@ -431,7 +431,7 @@ static term nif_atomvm_posix_select(Context *ctx, term argv[], enum ErlNifSelect
int32_t process_pid = term_to_local_process_id(process_pid_term);
term select_ref_term = argv[2];
if (select_ref_term != UNDEFINED_ATOM) {
VALIDATE_VALUE(select_ref_term, term_is_reference);
VALIDATE_VALUE(select_ref_term, term_is_local_reference);
}
void *fd_obj_ptr;
if (UNLIKELY(!enif_get_resource(erl_nif_env_from_context(ctx), argv[0], ctx->global->posix_fd_resource_type, &fd_obj_ptr))) {
Expand Down
2 changes: 1 addition & 1 deletion src/libAtomVM/resources.c
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ int enif_select(ErlNifEnv *env, ErlNifEvent event, enum ErlNifSelectFlags mode,
if (!(mode & (ERL_NIF_SELECT_STOP | ERL_NIF_SELECT_READ | ERL_NIF_SELECT_WRITE))) {
return ERL_NIF_SELECT_BADARG;
}
if (UNLIKELY(mode & (ERL_NIF_SELECT_READ | ERL_NIF_SELECT_WRITE) && !term_is_reference(ref) && ref != UNDEFINED_ATOM)) {
if (UNLIKELY(mode & (ERL_NIF_SELECT_READ | ERL_NIF_SELECT_WRITE) && !term_is_local_reference(ref) && ref != UNDEFINED_ATOM)) {
return ERL_NIF_SELECT_BADARG;
}

Expand Down
70 changes: 61 additions & 9 deletions src/libAtomVM/term.c
Original file line number Diff line number Diff line change
Expand Up @@ -384,11 +384,25 @@ int term_funprint(PrinterFun *fun, term t, const GlobalContext *global)
ret += printed;
return ret;

} else if (term_is_reference(t)) {
} else if (term_is_local_reference(t)) {
uint64_t ref_ticks = term_to_ref_ticks(t);

// Update also REF_AS_CSTRING_LEN when changing this format string
return fun->print(fun, "#Ref<0.0.0.%" PRIu64 ">", ref_ticks);
// Update also LOCAL_REF_AS_CSTRING_LEN when changing this format string
return fun->print(fun, "#Ref<0.%" PRIu32 ".%" PRIu32 ">", (uint32_t) (ref_ticks >> 32), (uint32_t) ref_ticks);

} else if (term_is_external_reference(t)) {
// Update also EXTERNAL_REF_AS_CSTRING_LEN when changing this format string
uint32_t node_atom_index = term_to_atom_index(term_to_external_node(t));
uint32_t len = term_to_external_reference_len(t);
uint32_t data[len];
term_to_external_reference_words(t, data);
// creation is not printed
int ret = fun->print(fun, "#Ref<%" PRIu32, node_atom_index);
for (uint32_t i = 0; i < len; i++) {
ret += fun->print(fun, ".%" PRIu32, data[i]);
}
ret += fun->print(fun, ">");
return ret;

} else if (term_is_boxed_integer(t)) {
int size = term_boxed_size(t);
Expand Down Expand Up @@ -499,14 +513,52 @@ TermCompareResult term_compare(term t, term other, TermCompareOpts opts, GlobalC
break;

} else if (term_is_reference(t) && term_is_reference(other)) {
int64_t t_ticks = term_to_ref_ticks(t);
int64_t other_ticks = term_to_ref_ticks(other);
if (t_ticks == other_ticks) {
CMP_POP_AND_CONTINUE();
if (!term_is_external(t) && !term_is_external(other)) {
int64_t t_ticks = term_to_ref_ticks(t);
int64_t other_ticks = term_to_ref_ticks(other);
if (t_ticks == other_ticks) {
CMP_POP_AND_CONTINUE();
} else {
result = (t_ticks > other_ticks) ? TermGreaterThan : TermLessThan;
break;
}
} else if (term_is_external(t) && term_is_external(other)) {
term node = term_to_external_node(t);
term other_node = term_to_external_node(other);
if (node == other_node) {
uint32_t creation = term_to_external_node_creation(t);
uint32_t other_creation = term_to_external_node_creation(other);
if (creation == other_creation) {
uint32_t len = term_to_external_reference_len(t);
uint32_t other_len = term_to_external_reference_len(other);
if (len == other_len) {
uint32_t data[len];
uint32_t other_data[len];
term_to_external_reference_words(t, data);
term_to_external_reference_words(other, other_data);
int cmp = memcmp(data, other_data, len * sizeof(uint32_t));
if (cmp == 0) {
CMP_POP_AND_CONTINUE();
} else {
result = (cmp > 0) ? TermGreaterThan : TermLessThan;
break;
}
} else {
result = (len > other_len) ? TermGreaterThan : TermLessThan;
break;
}
} else {
result = (creation > other_creation) ? TermGreaterThan : TermLessThan;
break;
}
} else {
result = (node > other_node) ? TermGreaterThan : TermLessThan;
break;
}
} else {
result = (t_ticks > other_ticks) ? TermGreaterThan : TermLessThan;
break;
result = term_is_external(t) ? TermGreaterThan : TermLessThan;
}
break;

} else if (term_is_nonempty_list(t) && term_is_nonempty_list(other)) {
term t_tail = term_get_list_tail(t);
Expand Down
Loading

0 comments on commit a5c5eed

Please sign in to comment.