How to use binary strings in Elixir NIF

383 views Asked by At

How do I get the char* data from an ERL_NIF_TERM coming from an Elixir binary string?

I see term_to_binary/1 function but it does not seem to be the right thing.

3

There are 3 answers

0
Aleksei Matiushkin On

As it’s stated in NIF documentation

Terms of type binary are accessed with the help of struct type ErlNifBinary, which contains a pointer (data) to the raw binary data and the length (size) of the data in bytes. Both data and size are read-only and are only to be written using calls to API functions. Instances of ErlNifBinary are, however, always allocated by the user (usually as local variables).

That said, your NIF function should have a signature alongside

static ERL_NIF_TERM
foo(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[])

where argc is supposed to be 1, argv[0] would be a constant pointer to ErlNifBinary and the respective call would be like

def foo(binary), do: :erlang.nif_error("Load NIF!")
0
Hari Roshan On

While using NIF, you have to use charlist and NOT Elixir binary Strings. Use to_charlist/1 function to convert binary string to charlist and then call the NIF function.

In the NIF function, you can read charlist like so,

#define MAXBUFLEN 1024

char src[MAXBUFLEN];
enif_get_string(env, argv[0], src, MAXBUFLEN, ERL_NIF_LATIN1);

src now holds the string value.

0
meloidae On

You first need to prepare ErlNifBinary and then populate it using enif_inspect_binary().

According to NIF doc, ErlNifBinary is a struct with two visible fields like below.

typedef struct {
    size_t size;
    unsigned char* data;
} ErlNifBinary;

data is the char* you want. enif_inspect_binary() fills ErlNifBinary with information about the binary, making its data accessible.

Assuming that the binary string is the first argument of your nif function, the C code should look something like this.

static ERL_NIF_TERM
do_stuff_with_binary(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) {
    unsigned char data[100];
    ErlNifBinary bin;
    if (!enif_inspect_binary(env, argv[0], &bin)) {
        return enif_make_atom(env, "error");
    }
    // Do stuff with binary. Its data is now accessible via bin.data
    // memcpy(data, bin.data, 100);
    
    return enif_make_atom(env, "ok");
}