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