cc++11erlangerlang-nif

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.


Solution

  • 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.