Using Erlang's NIF, How should I use malloc on ERL_NIF_TERM?

587 views Asked by At

I'm using Erlang's NIF, and the result of the C function is an array that I want to send back to erlang in the form of a list of tuples of three points, each is a tuple of two doubles.

to create this array currently I'm doing this:

ans = (ERL_NIF_TERM *)malloc(6*ntri*sizeof(ERL_NIF_TERM));

for (i=0;i<ntri;i++) {
    ans[i] = enif_make_tuple3(env,
            enif_make_tuple2(env,enif_make_double(env,x1[i]),enif_make_double(env,y1[i])),
            enif_make_tuple2(env,enif_make_double(env,x2[i]),enif_make_double(env,y2[i])),
            enif_make_tuple2(env,enif_make_double(env,x3[i]),enif_make_double(env,y3[i]))
    );
}

So far, it seems to work. but is it correct? My reasoning was that on every cell of the array ans I have 6 doubles each with a size of ERL_NIF_TERM, so I allocate according to this.

But is it true?

Should I count the tuples?

What is the size of ERL_NIF_TERM anyway? Would double inside ERL_NIF_TERM be the same size of an int inside ERL_NIF_TERM? a tuple of 2 ints is also an ERL_NIF_TERM, is it the same size?

1

There are 1 answers

2
Hynek -Pichi- Vychodil On BEST ANSWER

No, your double values and 2,3-tuples are allocated on the heap. It is accessed by the env variable. So you have to allocate only space for ntri ERL_NIF_TERMs which you store. They are a result of your enif_make_tuple3 call. You also should use enif_alloc for allocation instead of malloc whenever you can (in your own code). Sou your code should look like:

ERL_NIF_TERM *ans = enif_alloc(ntri*sizeof(ERL_NIF_TERM));
if(!ans) return enif_raise_exception(env, enif_make_atom(env, "insufficient_memory"));
// use enif_make_badarg(env) prior to R18

for (i=0;i<ntri;i++) {
    ans[i] = enif_make_tuple3(env,
            enif_make_tuple2(env,enif_make_double(env,x1[i]),enif_make_double(env,y1[i])),
            enif_make_tuple2(env,enif_make_double(env,x2[i]),enif_make_double(env,y2[i])),
            enif_make_tuple2(env,enif_make_double(env,x3[i]),enif_make_double(env,y3[i]))
    );
}

ERL_NIF_TERM result = enif_make_list_from_array(env, ans, ntri);
enif_free(ans);
return result;

Now about second question, What is the size of ERL_NIF_TERM anyway? The size of ERL_NIF_TERM is a word on your platform. It is 8B on 64b and 4B on 32b or 64b halfword one. It can be stored in the stack or register so you don't have to allocate required memory and you can pass it as a simple parameter to the functions.

Edit: You don't have to allocate memory at all. It is more efficient to construct result list directly.

ERL_NIF_TERM result = enif_make_list(env, 0);

for (i = ntri; i;) {
    i--;
    result = enif_make_list_cell(env,
        enif_make_tuple3(env,
            enif_make_tuple2(env,enif_make_double(env,x1[i]),enif_make_double(env,y1[i])),
            enif_make_tuple2(env,enif_make_double(env,x2[i]),enif_make_double(env,y2[i])),
            enif_make_tuple2(env,enif_make_double(env,x3[i]),enif_make_double(env,y3[i]))
        ),
        result
    );
}

return result;