-type api_opt() ::
exception | getenv | time | {ulongsize, integer()} | dirty_schedulers .
version_opts("2.7") -> [{major,2}, {minor,7} ]; version_opts("2.8") -> [{major,2}, {minor,8}, exception]; version_opts("2.9") -> [{major,2}, {minor,9}, exception, getenv]; version_opts("2.10") -> [{major,2}, {minor,10}, exception, getenv, time]; version_opts(_) ->
io:format("Unsupported Erlang version.\nPlease report to get this version supported.\n"),
halt(1).
ulong_opts("4") -> [{ulongsize, 4}];
ulong_opts("8") -> [{ulongsize, 8}].
dirty_scheduler_opts() ->
case catch erlang:system_info(dirty_cpu_schedulers) of
_X when is_integer(_X) -> [dirty_schedulers];
_ -> []
end.
-spec api_list([api_opt()]) -> [term()].
api_list(Opts) -> [
{"*mut c_void", "enif_priv_data", "arg1: *mut ErlNifEnv"},
{"*mut c_void", "enif_alloc", "size: size_t"},
{"", "enif_free", "ptr: *mut c_void"},
{"c_int", "enif_is_atom", "arg1: *mut ErlNifEnv, term: ERL_NIF_TERM"},
{"c_int", "enif_is_binary", "arg1: *mut ErlNifEnv, term: ERL_NIF_TERM"},
{"c_int", "enif_is_ref", "arg1: *mut ErlNifEnv, term: ERL_NIF_TERM"},
{"c_int", "enif_inspect_binary", "arg1: *mut ErlNifEnv, bin_term: ERL_NIF_TERM, bin: *mut ErlNifBinary"},
{"c_int", "enif_alloc_binary", "size: size_t, bin: *mut ErlNifBinary"},
{"c_int", "enif_realloc_binary", "bin: *mut ErlNifBinary, size: size_t"},
{"", "enif_release_binary", "bin: *mut ErlNifBinary"},
{"c_int", "enif_get_int", "arg1: *mut ErlNifEnv, term: ERL_NIF_TERM, ip: *mut c_int"},
{"c_int", "enif_get_ulong", "arg1: *mut ErlNifEnv, term: ERL_NIF_TERM, ip: *mut c_ulong"},
{"c_int", "enif_get_double", "arg1: *mut ErlNifEnv, term: ERL_NIF_TERM, dp: *mut c_double"},
{"c_int", "enif_get_list_cell", "env: *mut ErlNifEnv, term: ERL_NIF_TERM, head: *mut ERL_NIF_TERM, tail: *mut ERL_NIF_TERM"},
{"c_int", "enif_get_tuple", "env: *mut ErlNifEnv, tpl: ERL_NIF_TERM, arity: *mut c_int, array: *mut *const ERL_NIF_TERM"},
{"c_int", "enif_is_identical", "lhs: ERL_NIF_TERM, rhs: ERL_NIF_TERM"},
{"c_int", "enif_compare", "lhs: ERL_NIF_TERM, rhs: ERL_NIF_TERM"},
{"ERL_NIF_TERM", "enif_make_binary", "env: *mut ErlNifEnv, bin: *mut ErlNifBinary"},
{"ERL_NIF_TERM", "enif_make_badarg", "env: *mut ErlNifEnv"},
{"ERL_NIF_TERM", "enif_make_int", "env: *mut ErlNifEnv, i: c_int"},
{"ERL_NIF_TERM", "enif_make_ulong", "env: *mut ErlNifEnv, i: c_ulong"},
{"ERL_NIF_TERM", "enif_make_double", "env: *mut ErlNifEnv, d: c_double"},
{"ERL_NIF_TERM", "enif_make_atom", "env: *mut ErlNifEnv, name: *const c_uchar"},
{"c_int", "enif_make_existing_atom", "env: *mut ErlNifEnv, name: *const c_uchar, atom: *mut ERL_NIF_TERM, arg1: ErlNifCharEncoding"},
{"", "dummy_enif_make_tuple", ""}, {"", "dummy_enif_make_list", ""},
{"ERL_NIF_TERM", "enif_make_list_cell", "env: *mut ErlNifEnv, car: ERL_NIF_TERM, cdr: ERL_NIF_TERM"},
{"ERL_NIF_TERM", "enif_make_string", "env: *mut ErlNifEnv, string: *const c_uchar, arg1: ErlNifCharEncoding"},
{"ERL_NIF_TERM", "enif_make_ref", "env: *mut ErlNifEnv"},
{"", "dummy_enif_mutex_create", ""},
{"", "dummy_enif_mutex_destroy", ""},
{"", "dummy_enif_mutex_trylock", ""},
{"", "dummy_enif_mutex_lock", ""},
{"", "dummy_enif_mutex_unlock", ""},
{"", "dummy_enif_cond_create", ""},
{"", "dummy_enif_cond_destroy", ""},
{"", "dummy_enif_cond_signal", ""},
{"", "dummy_enif_cond_broadcast", ""},
{"", "dummy_enif_cond_wait", ""},
{"", "dummy_enif_rwlock_create", ""},
{"", "dummy_enif_rwlock_destroy", ""},
{"", "dummy_enif_rwlock_tryrlock", ""},
{"", "dummy_enif_rwlock_rlock", ""},
{"", "dummy_enif_rwlock_runlock", ""},
{"", "dummy_enif_rwlock_tryrwlock", ""},
{"", "dummy_enif_rwlock_rwlock", ""},
{"", "dummy_enif_rwlock_rwunlock", ""},
{"", "dummy_enif_tsd_key_create", ""},
{"", "dummy_enif_tsd_key_destroy", ""},
{"", "dummy_enif_tsd_set", ""},
{"", "dummy_enif_tsd_get", ""},
{"", "dummy_enif_thread_opts_create", ""},
{"", "dummy_enif_thread_opts_destroy", ""},
{"", "dummy_enif_thread_create", ""},
{"", "dummy_enif_thread_self", ""},
{"", "dummy_enif_equal_tids", ""},
{"", "dummy_enif_thread_exit", ""},
{"", "dummy_enif_thread_join", ""},
{"*mut c_void", "enif_realloc", "ptr: *mut c_void, size: size_t"},
{"", "enif_system_info", "sip: *mut ErlNifSysInfo, si_size: size_t"},
{"", "dummy_enif_fprintf", ""},
{"c_int", "enif_inspect_iolist_as_binary", "arg1: *mut ErlNifEnv, term: ERL_NIF_TERM, bin: *mut ErlNifBinary"},
{"ERL_NIF_TERM", "enif_make_sub_binary", "arg1: *mut ErlNifEnv, bin_term: ERL_NIF_TERM, pos: size_t, size: size_t"},
{"c_int", "enif_get_string", "arg1: *mut ErlNifEnv, list: ERL_NIF_TERM, buf: *mut c_uchar, len: c_uint, arg2: ErlNifCharEncoding"},
{"c_int", "enif_get_atom", "arg1: *mut ErlNifEnv, atom: ERL_NIF_TERM, buf: *mut c_uchar, len: c_uint, arg2: ErlNifCharEncoding"},
{"c_int", "enif_is_fun", "arg1: *mut ErlNifEnv, term: ERL_NIF_TERM"},
{"c_int", "enif_is_pid", "arg1: *mut ErlNifEnv, term: ERL_NIF_TERM"},
{"c_int", "enif_is_port", "arg1: *mut ErlNifEnv, term: ERL_NIF_TERM"},
{"c_int", "enif_get_uint", "arg1: *mut ErlNifEnv, term: ERL_NIF_TERM, ip: *mut c_uint"},
{"c_int", "enif_get_long", "arg1: *mut ErlNifEnv, term: ERL_NIF_TERM, ip: *mut c_long"},
{"ERL_NIF_TERM", "enif_make_uint", "arg1: *mut ErlNifEnv, i: c_uint"},
{"ERL_NIF_TERM", "enif_make_long", "arg1: *mut ErlNifEnv, i: c_long"},
{"ERL_NIF_TERM", "enif_make_tuple_from_array", "arg1: *mut ErlNifEnv, arr: *const ERL_NIF_TERM, cnt: c_uint"},
{"ERL_NIF_TERM", "enif_make_list_from_array", "arg1: *mut ErlNifEnv, arr: *const ERL_NIF_TERM, cnt: c_uint"},
{"c_int", "enif_is_empty_list", "arg1: *mut ErlNifEnv, term: ERL_NIF_TERM"},
{"*mut ErlNifResourceType", "enif_open_resource_type", "arg1: *mut ErlNifEnv, module_str: *const c_uchar, name_str: *const c_uchar, dtor: Option<extern \"C\" fn (arg1: *mut ErlNifEnv, arg2: *mut c_void)>, flags: ErlNifResourceFlags, tried: *mut ErlNifResourceFlags"},
{"*mut c_void", "enif_alloc_resource", "_type: *mut ErlNifResourceType, size: size_t"},
{"", "enif_release_resource", "obj: *mut c_void"},
{"ERL_NIF_TERM", "enif_make_resource", "arg1: *mut ErlNifEnv, obj: *mut c_void"},
{"c_int", "enif_get_resource", "arg1: *mut ErlNifEnv, term: ERL_NIF_TERM, _type: *mut ErlNifResourceType, objp: *mut *mut c_void"},
{"size_t", "enif_sizeof_resource", "obj: *mut c_void"},
{"*mut c_uchar", "enif_make_new_binary", "arg1: *mut ErlNifEnv, size: size_t, termp: *mut ERL_NIF_TERM"},
{"c_int", "enif_is_list", "arg1: *mut ErlNifEnv, term: ERL_NIF_TERM"},
{"c_int", "enif_is_tuple", "arg1: *mut ErlNifEnv, term: ERL_NIF_TERM"},
{"c_int", "enif_get_atom_length", "arg1: *mut ErlNifEnv, atom: ERL_NIF_TERM, len: *mut c_uint, arg2: ErlNifCharEncoding"},
{"c_int", "enif_get_list_length", "env: *mut ErlNifEnv, term: ERL_NIF_TERM, len: *mut c_uint"},
{"ERL_NIF_TERM", "enif_make_atom_len", "env: *mut ErlNifEnv, name: *const c_uchar, len: size_t"},
{"c_int", "enif_make_existing_atom_len", "env: *mut ErlNifEnv, name: *const c_uchar, len: size_t, atom: *mut ERL_NIF_TERM, arg1: ErlNifCharEncoding"},
{"ERL_NIF_TERM", "enif_make_string_len", "env: *mut ErlNifEnv, string: *const c_uchar, len: size_t, arg1: ErlNifCharEncoding"},
{"*mut ErlNifEnv", "enif_alloc_env", ""},
{"", "enif_free_env", "env: *mut ErlNifEnv"},
{"", "enif_clear_env", "env: *mut ErlNifEnv"},
{"c_int", "enif_send", "env: *mut ErlNifEnv, to_pid: *const ErlNifPid, msg_env: *mut ErlNifEnv, msg: ERL_NIF_TERM"},
{"ERL_NIF_TERM", "enif_make_copy", "dst_env: *mut ErlNifEnv, src_term: ERL_NIF_TERM"},
{"*mut ErlNifPid", "enif_self", "caller_env: *mut ErlNifEnv, pid: *mut ErlNifPid"},
{"c_int", "enif_get_local_pid", "env: *mut ErlNifEnv, arg1: ERL_NIF_TERM, pid: *mut ErlNifPid"},
{"", "enif_keep_resource", "obj: *mut c_void"},
{"ERL_NIF_TERM", "enif_make_resource_binary", "arg1: *mut ErlNifEnv, obj: *mut c_void, data: *const c_void, size: size_t"}
] ++
case proplists:get_value(ulongsize, Opts) of
8 -> [];
4 ->
[
{"c_int", "enif_get_int64", "arg1: *mut ErlNifEnv, term: ERL_NIF_TERM, ip: *mut c_longlong"},
{"c_int", "enif_get_uint64", "arg1: *mut ErlNifEnv, term: ERL_NIF_TERM, ip: *mut c_ulonglong"},
{"ERL_NIF_TERM", "enif_make_int64", "env: *mut ErlNifEnv, i: c_longlong"},
{"ERL_NIF_TERM", "enif_make_uint64", "env: *mut ErlNifEnv, i: c_ulonglong"}
]
end ++ [
{"c_int", "enif_is_exception", "arg1: *mut ErlNifEnv, term: ERL_NIF_TERM"},
{"c_int", "enif_make_reverse_list", "arg1: *mut ErlNifEnv, term: ERL_NIF_TERM, list: *mut ERL_NIF_TERM"},
{"c_int", "enif_is_number", "arg1: *mut ErlNifEnv, term: ERL_NIF_TERM"},
{"*mut c_void", "enif_dlopen", "lib: *const c_uchar, err_handler: Option<extern \"C\" fn (arg1: *mut c_void, arg2: *const c_uchar)>, err_arg: *mut c_void"},
{"*mut c_void", "enif_dlsym", "handle: *mut c_void, symbol: *const c_uchar, err_handler: Option<extern \"C\" fn (arg1: *mut c_void, arg2: *const c_uchar)>, err_arg: *mut c_void"},
{"c_int", "enif_consume_timeslice", "arg1: *mut ErlNifEnv, percent: c_int"},
{"c_int", "enif_is_map", "env: *mut ErlNifEnv, term: ERL_NIF_TERM"},
{"c_int", "enif_get_map_size", "env: *mut ErlNifEnv, term: ERL_NIF_TERM, size: *mut size_t"},
{"ERL_NIF_TERM", "enif_make_new_map", "env: *mut ErlNifEnv"},
{"c_int", "enif_make_map_put", "env: *mut ErlNifEnv, map_in: ERL_NIF_TERM, key: ERL_NIF_TERM, value: ERL_NIF_TERM, map_out: *mut ERL_NIF_TERM"},
{"c_int", "enif_get_map_value", "env: *mut ErlNifEnv, map: ERL_NIF_TERM, key: ERL_NIF_TERM, value: *mut ERL_NIF_TERM"},
{"c_int", "enif_make_map_update", "env: *mut ErlNifEnv, map_in: ERL_NIF_TERM, key: ERL_NIF_TERM, value: ERL_NIF_TERM, map_out: *mut ERL_NIF_TERM"},
{"c_int", "enif_make_map_remove", "env: *mut ErlNifEnv, map_in: ERL_NIF_TERM, key: ERL_NIF_TERM, map_out: *mut ERL_NIF_TERM"},
{"c_int", "enif_map_iterator_create", "env: *mut ErlNifEnv, map: ERL_NIF_TERM, iter: *mut ErlNifMapIterator, entry: ErlNifMapIteratorEntry"},
{"", "enif_map_iterator_destroy", "env: *mut ErlNifEnv, iter: *mut ErlNifMapIterator"},
{"c_int", "enif_map_iterator_is_head", "env: *mut ErlNifEnv, iter: *mut ErlNifMapIterator"},
{"c_int", "enif_map_iterator_is_tail", "env: *mut ErlNifEnv, iter: *mut ErlNifMapIterator"},
{"c_int", "enif_map_iterator_next", "env: *mut ErlNifEnv, iter: *mut ErlNifMapIterator"},
{"c_int", "enif_map_iterator_prev", "env: *mut ErlNifEnv, iter: *mut ErlNifMapIterator"},
{"c_int", "enif_map_iterator_get_pair", "env: *mut ErlNifEnv, iter: *mut ErlNifMapIterator, key: *mut ERL_NIF_TERM, value: *mut ERL_NIF_TERM"},
{"ERL_NIF_TERM", "enif_schedule_nif", "env: *mut ErlNifEnv, fun_name: *const c_uchar, flags:c_int, dtor: Option<extern \"C\" fn(env: *mut ErlNifEnv, argc:c_int, argv:*const ERL_NIF_TERM)>, argc:c_int, argv:*const ERL_NIF_TERM"}
] ++
case proplists:get_bool(exception, Opts) of
true -> [
{"c_int", "enif_has_pending_exception", "env: *mut ErlNifEnv, reason: *mut ERL_NIF_TERM"},
{"ERL_NIF_TERM", "enif_raise_exception", "env: *mut ErlNifEnv, reason: ERL_NIF_TERM"}
];
false -> []
end ++
case proplists:get_bool(getenv, Opts) of
true -> [
{"c_int", "enif_getenv", "key: *const c_uchar, value: *mut c_uchar, value_size: *mut size_t"}
];
false -> []
end ++
case proplists:get_bool(time, Opts) of
true -> [
{"ErlNifTime", "enif_monotonic_time", "unit: ErlNifTimeUnit"},
{"ErlNifTime", "enif_time_offset", "unit: ErlNifTimeUnit"},
{"ErlNifTime", "enif_convert_time_unit", "time: ErlNifTime, from_unit: ErlNifTimeUnit, to_unit: ErlNifTimeUnit"}
];
false -> []
end ++
case proplists:get_bool(dirty_schedulers, Opts) of
true -> [{"c_int", "enif_is_on_dirty_scheduler", "env: *mut ErlNifEnv"} ];
false -> []
end.
main([UlongSizeT]) -> main([UlongSizeT,"."]);
main([UlongSizeT, OutputDir]) ->
Version = (catch erlang:system_info(nif_version)),
Opts = version_opts(Version) ++ ulong_opts(UlongSizeT) ++ dirty_scheduler_opts(),
Entries = api_list(Opts),
Rust = [
nif_version_rust(proplists:get_value(major, Opts), proplists:get_value(minor, Opts)),
api_bindings_rust(erlang:system_info(system_architecture), Entries),
int64_mappers_rust(proplists:get_value(ulongsize, Opts))
],
Filename = filename:join(OutputDir, "nif_api.snippet"),
file:write_file(Filename, Rust),
ok.
nif_version_rust(Major, Minor) ->
[io_lib:format("pub const NIF_MAJOR_VERSION: c_int = ~p;\n", [Major]),
io_lib:format("pub const NIF_MINOR_VERSION: c_int = ~p;\n\n", [Minor])].
api_bindings_rust("win32", Entries) ->
[ "#[derive(Copy, Clone)]\n",
"pub struct TWinDynNifCallbacks {\n",
[ case Return of
"" ->
io_lib:format(" ~s: fn(~s),\n",[Name,Params]);
_ ->
io_lib:format(" ~s: fn(~s) -> ~s,\n",[Name,Params,Return])
end || {Return,Name,Params} <- Entries],
"}\n\n",
"pub static mut WinDynNifCallbacks:Option<TWinDynNifCallbacks> = None;\n\n",
[ [io_lib:format("/// See [~s](http://www.erlang.org/doc/man/erl_nif.html#~s) in the Erlang docs.\n", [Name, Name]),
case Return of
"" ->
io_lib:format("#[inline]\npub fn ~s(~s) {\n unsafe{(WinDynNifCallbacks.unwrap().~s)(~s)\n}}\n\n",[Name,Params,Name,strip_types_from_params(Params)]);
_ ->
io_lib:format("#[inline]\npub fn ~s(~s) -> ~s {\n unsafe{(WinDynNifCallbacks.unwrap().~s)(~s)\n}}\n\n",[Name,Params,Return,Name,strip_types_from_params(Params)])
end] || {Return,Name,Params} <- Entries, not is_dummy(Name)]
];
api_bindings_rust(_Arch, Entries) ->
[
"extern \"C\" {\n",
[ [io_lib:format("/// See [~s](http://www.erlang.org/doc/man/erl_nif.html#~s) in the Erlang docs.\n", [Name, Name]),
case Return of
"" ->
io_lib:format("pub fn ~s(~s);\n",[Name,Params]);
_ ->
io_lib:format("pub fn ~s(~s) -> ~s;\n",[Name,Params,Return])
end] || {Return,Name,Params} <- Entries, not is_dummy(Name)],
"}\n"
].
is_dummy([$d,$u,$m,$m,$y|_]) -> true;
is_dummy(_) -> false.
strip_types_from_params(Params) ->
ParamsCleaned0 = re:replace(Params, " ", ""), ParamsCleaned = re:replace(ParamsCleaned0, "\\(.*\\)", ""), ParamsL = re:split(ParamsCleaned, ","),
ArgsL = [ re:replace(Param, ":.*", "") || Param <- ParamsL ], join(ArgsL, ", ").
join(List, Joiner) -> join([], List, Joiner).
join(Acc, [H], _Joiner) -> lists:reverse([H|Acc]);
join(Acc, [H|T], Joiner) -> join([Joiner, H|Acc], T, Joiner).
int64_mappers_rust(4) -> join([
"use libc::c_ulonglong;",
"use libc::c_longlong;"
],
"\n");
int64_mappers_rust(8) -> join([
"/// See [enif_make_int64](http://www.erlang.org/doc/man/erl_nif.html#enif_make_int64) at erlang.org",
"#[inline]",
"pub fn enif_make_int64(env: *mut ErlNifEnv, i: i64) -> ERL_NIF_TERM",
" { unsafe {enif_make_long(env, i)}}",
"",
"/// See [enif_make_uint64](http://www.erlang.org/doc/man/erl_nif.html#enif_make_uint64) at erlang.org",
"#[inline]",
"pub fn enif_make_uint64(env: *mut ErlNifEnv, i: u64) -> ERL_NIF_TERM",
" { unsafe {enif_make_ulong(env, i) }}",
"",
"/// See [enif_get_int64](http://www.erlang.org/doc/man/erl_nif.html#enif_get_int64) at erlang.org",
"#[inline]",
"pub fn enif_get_int64(env: *mut ErlNifEnv, term: ERL_NIF_TERM, ip: *mut i64) -> c_int",
" { unsafe {enif_get_long(env, term, ip) }}",
"",
"/// See [enif_get_uint64](http://www.erlang.org/doc/man/erl_nif.html#enif_get_uint64) at erlang.org",
"#[inline]",
"pub fn enif_get_uint64(env: *mut ErlNifEnv, term: ERL_NIF_TERM, ip: *mut u64) -> c_int",
" { unsafe {enif_get_ulong(env, term, ip) }}"],
"\n").