[erlang-questions] Wrong large 64bit return-values from Erlang Linkedin Driver
Mikael Pettersson
mikpe@REDACTED
Thu Jun 17 14:40:01 CEST 2010
Thijs Terlouw writes:
> 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;
> }
>
> ===============
Can you please provide a self-contained test? I.e., an Erlang
module that loads your driver, invokes it, and checks the result?
And whatever build instructions are needed for your driver.
More information about the erlang-questions
mailing list