abips
abips

Reputation: 209

Manipulating Binaries in C NIF Erlang

I'm an Erlang enthusiast and a newbie Erlang Prorammer.

I recently had to face a data crunching problem in Erlang. Hence I decided to use NIFs. I have two list of proplists and I have to return the hash join of the two proplists based upon a term.

To implement it I decided to learn binary manipulation in C. Also I'm using the nifpp library(C++11) to ease my learning experience. Source: https://github.com/goertzenator/nifpp

The following is the code I can think of for extracting and copying binary to another ErlNifBinary which is well documented. But I'm not able to manipulate it.

ErlNifBinary ebin, bin_term;
nifpp::get_throws(env, argv[0], ebin); // Assigns ebin to the input binary
enif_alloc_binary(16, &bin_term); // Size of new binary
memcpy(bin_term.data, ebin.data, ebin.size); // Copying the contents of binary

I have a pointer to an unsigned char in bin_term.data. How do I change the contents in bin_term.data?

Also, if I return the existing copied binary, I get different outputs for the same input and not equal to the input considering the fact that I have just coped the data from the input. return enif_make_binary(env, &bin_term);

In the erlang REPL, if I execute the function with params <<7>> or any other binary, I get the results as <<7,127,128,29,0,0,0,0,1,0,0,0,24,1,0,0>> or some random dynamic values each time. Can someone point as to what is the mistake in the code.

Upvotes: 2

Views: 979

Answers (1)

mpm
mpm

Reputation: 3584

First of all, when you initialize bin_term, you do it with static size of 16 bytes. That's why your function always returns binary bigger than expected (16 digits, if you count). So first thing you could do, is to create bin_term with size of binary passed from Erlang

enif_alloc_binary(16, &bin_term); // Constant size

enif_alloc_binary(ebin.size, &bin_term); // Same size as binary from Erlang

And in nifpp there is wrapper on ErlNifBinary called bianry with which you could simply write

binary bin_term = new binary(ebin.size);

Or even add your own copying constructor.

Second this is accessing binary data. If you look how ErlNifBinary is defined you can see that all data is accessible trough data field (just like you expected) which is pointer to unsigned char (array in other words, of length given by size field).

So if you would like to for instance increase and print each of values from this binary you could do something like this

ErlNifBinary ebin, bin_term;
nifpp::get_throws(env, argv[0], ebin); // Assigns ebin to the input binary
enif_alloc_binary(ebin.size, &bin_term); // Size of new binary
memcpy(bin_term.data, ebin.data, ebin.size); // Copying the contents of binary

for(int i=0; i<bin_term.size; ++i){
    bin_term.data[i] = bin_term.data[i] + 1;
    std::cout << bin_term.data[i] << std::endl;
}

If you need some other data representation than array of chars (raw memory) you could look into Resource objects which in essence are smart pointers (garbage collected) to your C-memory, that could be passed around in Erlang.

Upvotes: 3

Related Questions