Wrong large 64bit return-values from Erlang Linkedin Driver

Thijs Terlouw thijsterlouw@REDACTED
Thu Jun 17 13:01:54 CEST 2010


Hello,

I'm trying to return signed 64bit integers (on a 32bit system) from a
custom Erlang linkedin driver. I try to use the ERL_DRV_INT64 type on
the C-side. Because ERL_DRV_INT64 requires a pointer to ErlDrvSInt64,
I first malloc a buffer, store it there, get a pointer to the result
and then return that pointer. For small values this works great.

Whenever I use values >134217727 (0x7FFFFFF) it will return wrong
values. I have gone over my own code many times and cannot find any
bugs. Then I looked at the Erlang code, and it seems that inside the
function erts_bld_sint64(Uint **hpp, Uint *szp, Sint64 si64)
(erts/emulator/beam/utils.c) the value is checked: if smaller than
this it will construct a regular integer, if bigger, it will go into
the BigNum branch.

On the Erlang side I use erl_ddll to load the driver and open several
ports (spawn_driver). I do not believe there is any bug in my
Erlang-side, because I use the same gen_linkedin_driver.erl module for
several drivers.

Is there a bug in the way Erlang handles the very large integers (long
long in C-language)? Or am I making some mistakes? I saw they were
quite new (ERTS 5.7.4).

I am using :
Erlang R13B04 (erts-5.7.5) [source] [smp:4:4] [rq:4]
[async-threads:30] [hipe] [kernel-poll:true]

Below is my test driver bignum.c with hardcoded test-value:

==================
#include <stdio.h>
#include <erl_driver.h>

#define INIT_INT64_LEN 512
#define INIT_TERM_LEN 4096

int
test_decode(ErlDrvPort port, char* buf, int len, char** rbuf, int rlen)
{
	long long signed testvalue = 4294967296LL;		// 2^32, fail
	//long long signed testvalue = 234LL;			// this is OK	

	//buffer to store the terms
    ErlDrvTermData*  	term_data;
    int 				term_length;
    int 				term_used;
	int 				resp;

	//temp buffer for my int64 data
    ErlDrvSInt64*  	int64_data;
    int             int64_length;
    int             int64_used;

	//init term-data
    term_data = (ErlDrvTermData*) malloc(INIT_TERM_LEN *
sizeof(ErlDrvTermData));
    if(term_data == NULL) goto done;
    term_length = INIT_TERM_LEN;
    term_used = 0;

	//init int64-data
    int64_data = (ErlDrvSInt64*) malloc(INIT_INT64_LEN * sizeof(ErlDrvSInt64));
    if(int64_data == NULL) goto done;
    int64_length = INIT_INT64_LEN;
    int64_used = 0;

    int ret = -1;

	//add a large integer to the output
	term_data[term_used++] = ERL_DRV_INT64;
	int64_data[int64_used++] = (ErlDrvSInt64) testvalue;
	ErlDrvSInt64* pos = int64_data + (int64_used-1);
	term_data[term_used++] = (ErlDrvTermData) pos;

	//sanity check
	fprintf(stderr, "testvalue: %lld, pos: %ld, *pos: %lld\n", testvalue,
pos, *pos);

    *rbuf = NULL;
    resp = driver_send_term(
        port,
        driver_caller(port),
        term_data,
        term_used
    );
    if(resp == 1) ret = 0;

	if(term_data != NULL) free(term_data);
	if(int64_data != NULL) free(int64_data);
done:
    return ret;
}


static ErlDrvData
bignum_start(ErlDrvPort port, char *buff)
{
    if(port == NULL) return ERL_DRV_ERROR_GENERAL;
    set_port_control_flags(port, PORT_CONTROL_FLAG_BINARY);
    return (ErlDrvData) port;
}

static int
bignum_control(
        ErlDrvData drv_data,
        unsigned int command,
        char* buf,
        int len,
        char** rbuf,
        int rlen
)
{
    switch(command)
    {
        case 0:
            return test_decode((ErlDrvPort) drv_data, buf, len, rbuf, rlen);
        default:
            return -1;
    }
}

static ErlDrvEntry
bignum_driver_entry =
{
    NULL,               /* Init */
    bignum_start,
    NULL,               /* Stop */
    NULL,               /* Output */
    NULL,               /* Input Ready */
    NULL,               /* Output Ready */
    "bignum_drv",      /* Driver Name */
    NULL,               /* Finish */
    NULL,               /* Handle */
    bignum_control,    /* Control */
    NULL,               /* Timeout */
    NULL,               /* Outputv */
    NULL,               /* Ready Async */
    NULL,               /* Flush */
    NULL,               /* Call */
    NULL,               /* Event */
    ERL_DRV_EXTENDED_MARKER,
    ERL_DRV_EXTENDED_MAJOR_VERSION,
    ERL_DRV_EXTENDED_MINOR_VERSION,
    ERL_DRV_FLAG_USE_PORT_LOCKING,
    NULL,               /* Reserved */
    NULL,               /* Process Exit */
};

DRIVER_INIT(bignum_drv)    /* must match name in driver_entry */
{
    return &bignum_driver_entry;
}

===============


More information about the erlang-questions mailing list