diff options
-rw-r--r-- | erts/doc/src/erl_nif.xml | 40 | ||||
-rw-r--r-- | erts/emulator/beam/big.c | 88 | ||||
-rw-r--r-- | erts/emulator/beam/big.h | 4 | ||||
-rw-r--r-- | erts/emulator/beam/erl_nif.c | 34 | ||||
-rw-r--r-- | erts/emulator/beam/erl_nif.h | 13 | ||||
-rw-r--r-- | erts/emulator/beam/erl_nif_api_funcs.h | 21 | ||||
-rw-r--r-- | erts/emulator/test/nif_SUITE_data/nif_SUITE.c | 63 |
7 files changed, 248 insertions, 15 deletions
diff --git a/erts/doc/src/erl_nif.xml b/erts/doc/src/erl_nif.xml index f7b7b2f346..8e4d8130f5 100644 --- a/erts/doc/src/erl_nif.xml +++ b/erts/doc/src/erl_nif.xml @@ -34,11 +34,10 @@ <lib>erl_nif</lib> <libsummary>API functions for an Erlang NIF library</libsummary> <description> - <warning><p>The NIF concept was introduced in R13B03 as an - EXPERIMENTAL feature. The interfaces may be changed in any way - in coming releases. The plan is however to lift the experimental label and - maintain interface backward compatibility from R14B.</p> - <p>Incompatible changes in <em>R14A</em>:</p> + <note><p>The NIF concept is officially supported from R14B. NIF source code + written for earlier experimental versions might need adaption to run on R14B.</p> + <p>No incompatible changes between <em>R14B</em> and R14A.</p> + <p>Incompatible changes between <em>R14A</em> and R13B04:</p> <list> <item>Environment argument removed for <c>enif_alloc</c>, <c>enif_realloc</c>, <c>enif_free</c>, <c>enif_alloc_binary</c>, @@ -50,14 +49,14 @@ <item>Module argument added to <c>enif_open_resource_type</c> while changing name spaces of resource types from global to module local.</item> </list> - <p>Incompatible changes in <em>R13B04</em>:</p> + <p>Incompatible changes between <em>R13B04</em> and R13B03:</p> <list> <item>The function prototypes of the NIFs have changed to expect <c>argc</c> and <c>argv</c> arguments. The arity of a NIF is by that no longer limited to 3.</item> <item><c>enif_get_data</c> renamed as <c>enif_priv_data</c>.</item> <item><c>enif_make_string</c> got a third argument for character encoding.</item> </list> - </warning> + </note> <p>A NIF library contains native implementation of some functions of an Erlang module. The native implemented functions (NIFs) are @@ -456,6 +455,10 @@ typedef enum { to return information about the runtime system. Contains currently the exact same content as <seealso marker="erl_driver#ErlDrvSysInfo">ErlDrvSysInfo</seealso>.</p> </item> + <tag><marker id="ErlNifSInt64"/>ErlNifSInt64</tag> + <item><p>A native signed 64-bit integer type.</p></item> + <tag><marker id="ErlNifUInt64"/>ErlNifUInt64</tag> + <item><p>A native unsigned 64-bit integer type.</p></item> </taglist> </section> @@ -571,7 +574,13 @@ typedef enum { <fsummary>Read an integer term</fsummary> <desc><p>Set <c>*ip</c> to the integer value of <c>term</c>. Return true on success or false if <c>term</c> is not an - integer or is outside the bounds of type <c>int</c></p></desc> + integer or is outside the bounds of type <c>int</c>.</p></desc> + </func> + <func><name><ret>int</ret><nametext>enif_get_int64(ErlNifEnv* env, ERL_NIF_TERM term, ErlNifSInt64* ip)</nametext></name> + <fsummary>Read a 64-bit integer term</fsummary> + <desc><p>Set <c>*ip</c> to the integer value of + <c>term</c>. Return true on success or false if <c>term</c> is not an + integer or is outside the bounds of a signed 64-bit integer.</p></desc> </func> <func><name><ret>int</ret><nametext>enif_get_local_pid(ErlNifEnv* env, ERL_NIF_TERM term, ErlNifPid* pid)</nametext></name> <fsummary>Read an local pid term</fsummary> @@ -633,7 +642,12 @@ typedef enum { return true, or return false if <c>term</c> is not an unsigned integer or is outside the bounds of type <c>unsigned int</c>.</p></desc> </func> - + <func><name><ret>int</ret><nametext>enif_get_uint64(ErlNifEnv* env, ERL_NIF_TERM term, ErlNifUInt64* ip)</nametext></name> + <fsummary>Read an unsigned 64-bit integer term.</fsummary> + <desc><p>Set <c>*ip</c> to the unsigned integer value of <c>term</c> and + return true, or return false if <c>term</c> is not an unsigned integer or + is outside the bounds of an unsigned 64-bit integer.</p></desc> + </func> <func><name><ret>int</ret><nametext>enif_get_ulong(ErlNifEnv* env, ERL_NIF_TERM term, unsigned long* ip)</nametext></name> <fsummary>Read an unsigned integer term.</fsummary> <desc><p>Set <c>*ip</c> to the unsigned long integer value of <c>term</c> @@ -758,6 +772,10 @@ typedef enum { <fsummary>Create an integer term</fsummary> <desc><p>Create an integer term.</p></desc> </func> + <func><name><ret>ERL_NIF_TERM</ret><nametext>enif_make_int64(ErlNifEnv* env, ErlNifSInt64 i)</nametext></name> + <fsummary>Create an integer term</fsummary> + <desc><p>Create an integer term from a signed 64-bit integer.</p></desc> + </func> <func><name><ret>ERL_NIF_TERM</ret><nametext>enif_make_list(ErlNifEnv* env, unsigned cnt, ...)</nametext></name> <fsummary>Create a list term.</fsummary> <desc><p>Create an ordinary list term of length <c>cnt</c>. Expects @@ -894,6 +912,10 @@ typedef enum { <fsummary>Create an unsigned integer term</fsummary> <desc><p>Create an integer term from an <c>unsigned int</c>.</p></desc> </func> + <func><name><ret>ERL_NIF_TERM</ret><nametext>enif_make_uint64(ErlNifEnv* env, ErlNifUInt64 i)</nametext></name> + <fsummary>Create an unsigned integer term</fsummary> + <desc><p>Create an integer term from an unsigned 64-bit integer.</p></desc> + </func> <func><name><ret>ERL_NIF_TERM</ret><nametext>enif_make_ulong(ErlNifEnv* env, unsigned long i)</nametext></name> <fsummary>Create an integer term from an unsigned long int</fsummary> <desc><p>Create an integer term from an <c>unsigned long int</c>.</p></desc> diff --git a/erts/emulator/beam/big.c b/erts/emulator/beam/big.c index 2d250f32cf..ff15d834ab 100644 --- a/erts/emulator/beam/big.c +++ b/erts/emulator/beam/big.c @@ -1881,6 +1881,9 @@ term_to_Uint(Eterm term, Uint *up) int term_to_UWord(Eterm term, UWord *up) { +#if SIZEOF_VOID_P == ERTS_SIZEOF_ETERM + return term_to_Uint(term,up); +#else if (is_small(term)) { Sint i = signed_val(term); if (i < 0) { @@ -1903,7 +1906,47 @@ term_to_UWord(Eterm term, UWord *up) return 0; } while (xl-- > 0) { - uval |= ((Uint)(*xr++)) << n; + uval |= ((UWord)(*xr++)) << n; + n += D_EXP; + } + *up = uval; + return 1; + } else { + *up = BADARG; + return 0; + } +#endif +} + +int +term_to_Uint64(Eterm term, Uint64 *up) +{ +#if SIZEOF_VOID_P == 8 + return term_to_UWord(term,up); +#else + if (is_small(term)) { + Sint i = signed_val(term); + if (i < 0) { + *up = BADARG; + return 0; + } + *up = (Uint64) i; + return 1; + } else if (is_big(term)) { + ErtsDigit* xr = big_v(term); + dsize_t xl = big_size(term); + Uint64 uval = 0; + int n = 0; + + if (big_sign(term)) { + *up = BADARG; + return 0; + } else if (xl*D_EXP > sizeof(Uint64)*8) { + *up = SYSTEM_LIMIT; + return 0; + } + while (xl-- > 0) { + uval |= ((Uint64)(*xr++)) << n; n += D_EXP; } *up = uval; @@ -1912,8 +1955,10 @@ term_to_UWord(Eterm term, UWord *up) *up = BADARG; return 0; } +#endif } + int term_to_Sint(Eterm term, Sint *sp) { if (is_small(term)) { @@ -1948,6 +1993,47 @@ int term_to_Sint(Eterm term, Sint *sp) } } +#if HAVE_INT64 +int term_to_Sint64(Eterm term, Sint64 *sp) +{ +#if ERTS_SIZEOF_ETERM == 8 + return term_to_Sint(term, sp); +#else + if (is_small(term)) { + *sp = signed_val(term); + return 1; + } else if (is_big(term)) { + ErtsDigit* xr = big_v(term); + dsize_t xl = big_size(term); + int sign = big_sign(term); + Uint64 uval = 0; + int n = 0; + + if (xl*D_EXP > sizeof(Uint64)*8) { + return 0; + } + while (xl-- > 0) { + uval |= ((Uint64)(*xr++)) << n; + n += D_EXP; + } + if (sign) { + uval = -uval; + if ((Sint64)uval > 0) + return 0; + } else { + if ((Sint64)uval < 0) + return 0; + } + *sp = uval; + return 1; + } else { + return 0; + } +#endif +} +#endif /* HAVE_INT64 */ + + /* ** Add and subtract */ diff --git a/erts/emulator/beam/big.h b/erts/emulator/beam/big.h index 56f3be372a..25466cd3c2 100644 --- a/erts/emulator/beam/big.h +++ b/erts/emulator/beam/big.h @@ -152,6 +152,10 @@ byte* big_to_bytes(Eterm, byte*); int term_to_Uint(Eterm, Uint*); int term_to_UWord(Eterm, UWord*); int term_to_Sint(Eterm, Sint*); +#if HAVE_INT64 +int term_to_Uint64(Eterm, Uint64*); +int term_to_Sint64(Eterm, Sint64*); +#endif Uint32 big_to_uint32(Eterm b); int term_equals_2pow32(Eterm); diff --git a/erts/emulator/beam/erl_nif.c b/erts/emulator/beam/erl_nif.c index eadaf137e3..1dd9c8bd4a 100644 --- a/erts/emulator/beam/erl_nif.c +++ b/erts/emulator/beam/erl_nif.c @@ -750,7 +750,19 @@ int enif_get_ulong(ErlNifEnv* env, Eterm term, unsigned long* ip) #endif } -int enif_get_double(ErlNifEnv* env, Eterm term, double* dp) +#if HAVE_INT64 && SIZEOF_LONG != 8 +int enif_get_int64(ErlNifEnv* env, ERL_NIF_TERM term, ErlNifSInt64* ip) +{ + return term_to_Sint64(term, ip); +} + +int enif_get_uint64(ErlNifEnv* env, ERL_NIF_TERM term, ErlNifUInt64* ip) +{ + return term_to_Uint64(term, ip); +} +#endif /* HAVE_INT64 && SIZEOF_LONG != 8 */ + +int enif_get_double(ErlNifEnv* env, ERL_NIF_TERM term, double* dp) { FloatDef f; if (is_not_float(term)) { @@ -817,6 +829,26 @@ ERL_NIF_TERM enif_make_ulong(ErlNifEnv* env, unsigned long i) return IS_USMALL(0,i) ? make_small(i) : uint_to_big(i,alloc_heap(env,2)); } +#if HAVE_INT64 && SIZEOF_LONG != 8 +ERL_NIF_TERM enif_make_int64(ErlNifEnv* env, ErlNifSInt64 i) +{ + Uint* hp; + Uint need = 0; + erts_bld_sint64(NULL, &need, i); + hp = alloc_heap(env, need); + return erts_bld_sint64(&hp, NULL, i); +} + +ERL_NIF_TERM enif_make_uint64(ErlNifEnv* env, ErlNifUInt64 i) +{ + Uint* hp; + Uint need = 0; + erts_bld_uint64(NULL, &need, i); + hp = alloc_heap(env, need); + return erts_bld_uint64(&hp, NULL, i); +} +#endif /* HAVE_INT64 && SIZEOF_LONG != 8 */ + ERL_NIF_TERM enif_make_double(ErlNifEnv* env, double d) { Eterm* hp = alloc_heap(env,FLOAT_SIZE_OBJECT); diff --git a/erts/emulator/beam/erl_nif.h b/erts/emulator/beam/erl_nif.h index 936f03bce1..ee3a7cd5f4 100644 --- a/erts/emulator/beam/erl_nif.h +++ b/erts/emulator/beam/erl_nif.h @@ -66,6 +66,19 @@ extern "C" { #endif +#if (defined(__WIN32__) || defined(_WIN32) || defined(_WIN32_)) +typedef unsigned __int64 ErlNifUInt64; +typedef __int64 ErlNifSInt64; +#elif SIZEOF_LONG == 8 +typedef unsigned long ErlNifUInt64; +typedef long ErlNifSInt64; +#elif SIZEOF_LONG_LONG == 8 +typedef unsigned long long ErlNifUInt64; +typedef long long ErlNifSInt64; +#else +#error No 64-bit integer type +#endif + #ifdef HALFWORD_HEAP_EMULATOR typedef unsigned int ERL_NIF_TERM; #else diff --git a/erts/emulator/beam/erl_nif_api_funcs.h b/erts/emulator/beam/erl_nif_api_funcs.h index ef4e9580b0..eca506593d 100644 --- a/erts/emulator/beam/erl_nif_api_funcs.h +++ b/erts/emulator/beam/erl_nif_api_funcs.h @@ -122,6 +122,12 @@ ERL_NIF_API_FUNC_DECL(ErlNifPid*,enif_self,(ErlNifEnv* caller_env, ErlNifPid* pi ERL_NIF_API_FUNC_DECL(int,enif_get_local_pid,(ErlNifEnv* env, ERL_NIF_TERM, ErlNifPid* pid)); ERL_NIF_API_FUNC_DECL(void,enif_keep_resource,(void* obj)); ERL_NIF_API_FUNC_DECL(ERL_NIF_TERM,enif_make_resource_binary,(ErlNifEnv*,void* obj,const void* data, size_t size)); +#if SIZEOF_LONG != 8 +ERL_NIF_API_FUNC_DECL(int,enif_get_int64,(ErlNifEnv*, ERL_NIF_TERM term, ErlNifSInt64* ip)); +ERL_NIF_API_FUNC_DECL(int,enif_get_uint64,(ErlNifEnv*, ERL_NIF_TERM term, ErlNifUInt64* ip)); +ERL_NIF_API_FUNC_DECL(ERL_NIF_TERM,enif_make_int64,(ErlNifEnv*, ErlNifSInt64)); +ERL_NIF_API_FUNC_DECL(ERL_NIF_TERM,enif_make_uint64,(ErlNifEnv*, ErlNifUInt64)); +#endif /* ** Add last to keep compatibility on Windows!!! @@ -230,6 +236,13 @@ ERL_NIF_API_FUNC_DECL(ERL_NIF_TERM,enif_make_resource_binary,(ErlNifEnv*,void* o # define enif_get_local_pid ERL_NIF_API_FUNC_MACRO(enif_get_local_pid) # define enif_keep_resource ERL_NIF_API_FUNC_MACRO(enif_keep_resource) # define enif_make_resource_binary ERL_NIF_API_FUNC_MACRO(enif_make_resource_binary) +#if SIZEOF_LONG != 8 +# define enif_get_int64 ERL_NIF_API_FUNC_MACRO(enif_get_int64) +# define enif_get_uint64 ERL_NIF_API_FUNC_MACRO(enif_get_uint64) +# define enif_make_int64 ERL_NIF_API_FUNC_MACRO(enif_make_int64) +# define enif_make_uint64 ERL_NIF_API_FUNC_MACRO(enif_make_uint64) +#endif + #endif #ifndef enif_make_list1 @@ -253,5 +266,13 @@ ERL_NIF_API_FUNC_DECL(ERL_NIF_TERM,enif_make_resource_binary,(ErlNifEnv*,void* o # define enif_make_tuple9(ENV,E1,E2,E3,E4,E5,E6,E7,E8,E9) enif_make_tuple(ENV,9,E1,E2,E3,E4,E5,E6,E7,E8,E9) # define enif_make_pid(ENV, PID) ((const ERL_NIF_TERM)((PID)->pid)) + +#if SIZEOF_LONG == 8 +# define enif_get_int64 enif_get_long +# define enif_get_uint64 enif_get_ulong +# define enif_make_int64 enif_make_long +# define enif_make_uint64 enif_make_ulong +#endif + #endif diff --git a/erts/emulator/test/nif_SUITE_data/nif_SUITE.c b/erts/emulator/test/nif_SUITE_data/nif_SUITE.c index 17f644829f..5384a32f21 100644 --- a/erts/emulator/test/nif_SUITE_data/nif_SUITE.c +++ b/erts/emulator/test/nif_SUITE_data/nif_SUITE.c @@ -296,6 +296,30 @@ static int test_ulong(ErlNifEnv* env, unsigned long i1) return 1; } +static int test_int64(ErlNifEnv* env, ErlNifSInt64 i1) +{ + ErlNifSInt64 i2 = 0; + ERL_NIF_TERM int_term = enif_make_int64(env, i1); + if (!enif_get_int64(env,int_term, &i2) || i1 != i2) { + fprintf(stderr, "test_int64(%ld) ...FAILED i2=%ld\r\n", + (long)i1, (long)i2); + return 0; + } + return 1; +} + +static int test_uint64(ErlNifEnv* env, ErlNifUInt64 i1) +{ + ErlNifUInt64 i2 = 0; + ERL_NIF_TERM int_term = enif_make_uint64(env, i1); + if (!enif_get_uint64(env,int_term, &i2) || i1 != i2) { + fprintf(stderr, "test_ulong(%lu) ...FAILED i2=%lu\r\n", + (unsigned long)i1, (unsigned long)i2); + return 0; + } + return 1; +} + static int test_double(ErlNifEnv* env, double d1) { double d2 = 0; @@ -319,6 +343,8 @@ static ERL_NIF_TERM type_test(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[ unsigned uint; long slong; unsigned long ulong; + ErlNifSInt64 sint64; + ErlNifUInt64 uint64; double d; ERL_NIF_TERM atom, ref1, ref2; @@ -352,11 +378,25 @@ static ERL_NIF_TERM type_test(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[ slong -= slong / 3 + 1; } while (slong >= 0); + sint64 = ((ErlNifSInt64)1 << 63); /* INT64_MIN */ + do { + if (!test_int64(env,sint64)) { + goto error; + } + sint64 += ~sint64 / 3 + 1; + } while (sint64 < 0); + sint64 = ((ErlNifUInt64)1 << 63) - 1; /* INT64_MAX */ + do { + if (!test_int64(env,sint64)) { + goto error; + } + sint64 -= sint64 / 3 + 1; + } while (sint64 >= 0); uint = UINT_MAX; for (;;) { if (!test_uint(env,uint)) { - + goto error; } if (uint == 0) break; uint -= uint / 3 + 1; @@ -364,11 +404,19 @@ static ERL_NIF_TERM type_test(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[ ulong = ULONG_MAX; for (;;) { if (!test_ulong(env,ulong)) { - + goto error; } if (ulong == 0) break; ulong -= ulong / 3 + 1; } + uint64 = (ErlNifUInt64)-1; /* UINT64_MAX */ + for (;;) { + if (!test_uint64(env,uint64)) { + goto error; + } + if (uint64 == 0) break; + uint64 -= uint64 / 3 + 1; + } if (MAX_SMALL < INT_MAX) { /* 32-bit */ for (i=-10 ; i <= 10; i++) { @@ -391,11 +439,18 @@ static ERL_NIF_TERM type_test(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[ for (i=-10 ; i < 10; i++) { if (!test_long(env,MAX_SMALL+i) || !test_ulong(env,MAX_SMALL+i) || - !test_long(env,MIN_SMALL+i)) { + !test_long(env,MIN_SMALL+i) || + !test_int64(env,MAX_SMALL+i) || !test_uint64(env,MAX_SMALL+i) || + !test_int64(env,MIN_SMALL+i)) { goto error; } + if (MAX_SMALL < INT_MAX) { + if (!test_int(env,MAX_SMALL+i) || !test_uint(env,MAX_SMALL+i) || + !test_int(env,MIN_SMALL+i)) { + goto error; + } + } } - for (d=3.141592e-100 ; d < 1e100 ; d *= 9.97) { if (!test_double(env,d) || !test_double(env,-d)) { goto error; |