Skip to content

Commit

Permalink
Add support for encoded local pids 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 3, 2024
1 parent 24084c5 commit 0738e39
Show file tree
Hide file tree
Showing 5 changed files with 153 additions and 6 deletions.
4 changes: 4 additions & 0 deletions src/libAtomVM/defaultatoms.c
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,8 @@ static const char *const unicode_atom = "\x7" "unicode";

static const char *const global_atom = "\x6" "global";

static const char *const nonode_at_nohost_atom = "\xD" "nonode@nohost";

void defaultatoms_init(GlobalContext *glb)
{
int ok = 1;
Expand Down Expand Up @@ -308,6 +310,8 @@ void defaultatoms_init(GlobalContext *glb)

ok &= globalcontext_insert_atom(glb, global_atom) == GLOBAL_ATOM_INDEX;

ok &= globalcontext_insert_atom(glb, nonode_at_nohost_atom) == NONODE_AT_NOHOST_ATOM_INDEX;

if (!ok) {
AVM_ABORT();
}
Expand Down
6 changes: 5 additions & 1 deletion src/libAtomVM/defaultatoms.h
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,9 @@ extern "C" {

#define GLOBAL_ATOM_INDEX 111

#define PLATFORM_ATOMS_BASE_INDEX 112
#define NONODE_AT_NOHOST_ATOM_INDEX 112

#define PLATFORM_ATOMS_BASE_INDEX 113

#define FALSE_ATOM TERM_FROM_ATOM_INDEX(FALSE_ATOM_INDEX)
#define TRUE_ATOM TERM_FROM_ATOM_INDEX(TRUE_ATOM_INDEX)
Expand Down Expand Up @@ -317,6 +319,8 @@ extern "C" {

#define GLOBAL_ATOM TERM_FROM_ATOM_INDEX(GLOBAL_ATOM_INDEX)

#define NONODE_AT_NOHOST_ATOM TERM_FROM_ATOM_INDEX(NONODE_AT_NOHOST_ATOM_INDEX)

void defaultatoms_init(GlobalContext *glb);

void platform_defaultatoms_init(GlobalContext *glb);
Expand Down
52 changes: 52 additions & 0 deletions src/libAtomVM/externalterm.c
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,17 @@
#include <stdlib.h>

#include "bitstring.h"
#include "defaultatoms.h"
#include "term.h"
#include "unicode.h"
#include "utils.h"

#define NEW_FLOAT_EXT 70
#define NEW_PID_EXT 88
#define SMALL_INTEGER_EXT 97
#define INTEGER_EXT 98
#define ATOM_EXT 100
#define PID_EXT 103
#define SMALL_TUPLE_EXT 104
#define LARGE_TUPLE_EXT 105
#define NIL_EXT 106
Expand Down Expand Up @@ -390,6 +394,18 @@ static int serialize_term(uint8_t *buf, term t, GlobalContext *glb)
k += serialize_term(IS_NULL_PTR(buf) ? NULL : buf + k, mfa, glb);
}
return k;
} else if (term_is_local_pid(t)) {
if (!IS_NULL_PTR(buf)) {
buf[0] = NEW_PID_EXT;
}
size_t k = 1;
k += serialize_term(IS_NULL_PTR(buf) ? NULL : buf + k, NONODE_AT_NOHOST_ATOM, glb);
if (!IS_NULL_PTR(buf)) {
WRITE_32_UNALIGNED(buf + k, term_to_local_process_id(t));
WRITE_32_UNALIGNED(buf + k + 4, 0); // serial
WRITE_32_UNALIGNED(buf + k + 8, 0); // creation
}
return k + 12;
} else {
fprintf(stderr, "Unknown external term type: %" TERM_U_FMT "\n", t);
AVM_ABORT();
Expand Down Expand Up @@ -659,6 +675,26 @@ static term parse_external_terms(const uint8_t *external_term_buf, size_t *eterm
return term_from_atom_index(global_atom_id);
}

case NEW_PID_EXT: {
size_t node_size;
term node = parse_external_terms(external_term_buf + 1, &node_size, copy, heap, glb);
if (UNLIKELY(!term_is_atom(node))) {
return node;
}
uint32_t number = READ_32_UNALIGNED(external_term_buf + node_size + 1);
uint32_t serial = READ_32_UNALIGNED(external_term_buf + node_size + 5);
uint32_t creation = READ_32_UNALIGNED(external_term_buf + node_size + 9);
// We only support local pids for now.
if (UNLIKELY(node != NONODE_AT_NOHOST_ATOM)) {
return term_invalid_term();
} else {
if (UNLIKELY(serial != 0 || creation != 0)) {
return term_invalid_term();
}
return term_from_local_process_id(number);
}
}

default:
return term_invalid_term();
}
Expand Down Expand Up @@ -948,6 +984,22 @@ static int calculate_heap_usage(const uint8_t *external_term_buf, size_t remaini
return 0;
}

case NEW_PID_EXT: {
if (UNLIKELY(remaining < 1)) {
return INVALID_TERM_SIZE;
}
remaining -= 1;
int buf_pos = 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;
}
buf_pos += node_size;
*eterm_size = buf_pos;
return 1 + u;
}

default:
return INVALID_TERM_SIZE;
}
Expand Down
64 changes: 59 additions & 5 deletions src/libAtomVM/term.h
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,11 @@ extern "C" {
#define TERM_BOXED_FLOAT 0x18
#define TERM_BOXED_REFC_BINARY 0x20
#define TERM_BOXED_HEAP_BINARY 0x24
#define TERM_BOXED_MAP 0x3C
#define TERM_BOXED_SUB_BINARY 0x28
#define TERM_BOXED_MAP 0x2C
#define TERM_BOXED_EXTERNAL_PID 0x30
#define TERM_BOXED_EXTERNAL_PORT 0x34
#define TERM_BOXED_EXTERNAL_REF 0x38

#define TERM_UNUSED 0x2B
#define TERM_RESERVED_MARKER(x) ((x << 6) | TERM_UNUSED)
Expand Down Expand Up @@ -476,18 +479,68 @@ static inline bool term_is_catch_label(term t)
}

/**
* @brief Checks if a term is a pid
* @brief Checks if a term is a local pid
*
* @details Returns \c true if a term is a process id, otherwise \c false.
* @param t the term that will be checked.
* @return \c true if check succeeds, \c false otherwise.
*/
static inline bool term_is_pid(term t)
static inline bool term_is_local_pid(term t)
{
/* integer: 00 11 */
return ((t & 0xF) == 0x3);
}

/**
* @brief Checks if a term is an external pid
*
* @details Returns \c true if a term is an external process id, otherwise \c false.
* @param t the term that will be checked.
* @return \c true if check succeeds, \c false otherwise.
*/
static inline bool term_is_external_pid(term t)
{
if (term_is_boxed(t)) {
const term *boxed_value = term_to_const_term_ptr(t);
if ((boxed_value[0] & 0x3F) == TERM_BOXED_EXTERNAL_PID) {
return true;
}
}

return false;
}

/**
* @brief Checks if a term is a pid
*
* @details Returns \c true if a term is a process id, otherwise \c false.
* @param t the term that will be checked.
* @return \c true if check succeeds, \c false otherwise.
*/
static inline bool term_is_pid(term t)
{
return term_is_local_pid(t) || term_is_external_pid(t);
}

/**
* @brief Checks if a term is an external port
*
* @details Returns \c true if a term is an external port, otherwise \c false.
* @param t the term that will be checked.
* @return \c true if check succeeds, \c false otherwise.
*/
static inline bool term_is_external_port(term t)
{
if (term_is_boxed(t)) {
const term *boxed_value = term_to_const_term_ptr(t);
if ((boxed_value[0] & 0x3F) == TERM_BOXED_EXTERNAL_PORT) {
return true;
}
}

return false;
}

/**
* @brief Checks if a term is a tuple
*
Expand All @@ -499,7 +552,7 @@ static inline bool term_is_tuple(term t)
{
if (term_is_boxed(t)) {
const term *boxed_value = term_to_const_term_ptr(t);
if ((boxed_value[0] & 0x3F) == 0) {
if ((boxed_value[0] & 0x3F) == TERM_BOXED_TUPLE) {
return true;
}
}
Expand All @@ -518,7 +571,8 @@ static inline bool term_is_reference(term t)
{
if (term_is_boxed(t)) {
const term *boxed_value = term_to_const_term_ptr(t);
if ((boxed_value[0] & 0x3F) == TERM_BOXED_REF) {
const uint32_t header = boxed_value[0] & 0x3F;
if (header == TERM_BOXED_REF || header == TERM_BOXED_EXTERNAL_REF) {
return true;
}
}
Expand Down
33 changes: 33 additions & 0 deletions tests/erlang_tests/test_binary_to_term.erl
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
get_atom/1,
get_binary/1,
test_atom_decoding_checks/0,
test_encode_pid/0,
id/1
]).

Expand Down Expand Up @@ -172,6 +173,7 @@ start() ->
ok = test_mutate_encodings(),
ok = test_atom_decoding(),
ok = test_atom_decoding_checks(),
ok = test_encode_pid(),
0.

test_reverse(T, Interop) ->
Expand Down Expand Up @@ -399,6 +401,37 @@ test_atom_decoding_checks() ->
ok = expect_badarg(make_binterm_fun(invalid_utf8_seq_3)),
ok.

test_encode_pid() ->
Bin = term_to_binary(self()),
Pid = binary_to_term(Bin),
Pid ! hello,
true = is_pid(
binary_to_term(
<<131, 88, 119, 13, 110, 111, 110, 111, 100, 101, 64, 110, 111, 104, 111, 115, 116, 0,
0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0>>
)
),
ok =
receive
hello -> ok
after 500 -> error
end,
ExpectedSize =
case erlang:system_info(machine) of
"ATOM" ->
29;
"BEAM" ->
OTPRelease = erlang:system_info(otp_release),
if
OTPRelease < "23" -> 27;
OTPRelease < "26" -> 30;
% small utf8 atom
true -> 29
end
end,
ExpectedSize = byte_size(Bin),
ok.

make_binterm_fun(Id) ->
fun() ->
Bin = ?MODULE:get_binary(Id),
Expand Down

0 comments on commit 0738e39

Please sign in to comment.