├── .gitignore ├── get_erts_path.erl ├── .travis.yml ├── tests ├── testapp │ ├── crates │ │ └── mynifmod │ │ │ ├── Cargo.toml │ │ │ └── src │ │ │ └── lib.rs │ ├── dotest.escript │ └── mynifmod.erl ├── struct_size.c ├── tests.rs └── struct_size.rs ├── Cargo.toml ├── LICENSE-MIT ├── README.md ├── src ├── lib.rs ├── erlang_nif_sys_api.rs └── initmacro.rs ├── appveyor.yml ├── LICENSE-APACHE └── gen_api.erl /.gitignore: -------------------------------------------------------------------------------- 1 | Cargo.lock 2 | target/ 3 | *.*~ 4 | .*~ 5 | *.swp 6 | *.snippet 7 | *.dump 8 | -------------------------------------------------------------------------------- /get_erts_path.erl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/escript 2 | 3 | main(_) -> 4 | io:format("~s/erts-~s/include/", 5 | [code:root_dir(), 6 | erlang:system_info(version)]). 7 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: erlang 2 | otp_release: 3 | - 21.0 4 | 5 | sudo: required 6 | 7 | script: cargo test 8 | 9 | before_install: 10 | - curl https://sh.rustup.rs -sSf | sh -s -- -y 11 | 12 | before_script: 13 | - source $HOME/.cargo/env 14 | - kerl list installations 15 | - kerl list releases 16 | -------------------------------------------------------------------------------- /tests/testapp/crates/mynifmod/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "mynifmod" 3 | version = "0.1.0" 4 | authors = ["Daniel Goertzen "] 5 | 6 | [lib] 7 | name = "mynifmod" 8 | crate-type = ["dylib"] 9 | 10 | [dependencies] 11 | erlang_nif-sys = { path = "../../../../../../../../" } #please don't judge me 12 | -------------------------------------------------------------------------------- /tests/struct_size.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | int main() { 5 | printf("ERL_NIF_UINT %lu\n", sizeof(ERL_NIF_UINT)); 6 | printf("ERL_NIF_TERM %lu\n", sizeof(ERL_NIF_TERM)); 7 | printf("ErlNifFunc %lu\n", sizeof(ErlNifFunc)); 8 | printf("ErlNifEntry %lu\n", sizeof(ErlNifEntry)); 9 | printf("ErlNifBinary %lu\n", sizeof(ErlNifBinary)); 10 | printf("ErlNifPid %lu\n", sizeof(ErlNifPid)); 11 | printf("ErlNifSysInfo %lu\n", sizeof(ErlNifSysInfo)); 12 | printf("ErlNifMapIterator %lu\n", sizeof(ErlNifMapIterator)); 13 | 14 | return 0; 15 | } 16 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "erlang_nif-sys" 3 | version = "0.6.6-alpha.0" 4 | authors = ["Daniel Goertzen "] 5 | description = "Create Erlang NIF modules in Rust using the C NIF API." 6 | documentation = "https://docs.rs/erlang_nif-sys" 7 | repository = "https://github.com/goertzenator/erlang_nif-sys" 8 | license = "MIT/Apache-2.0" 9 | keywords = ["FFI", "Erlang", "NIF"] 10 | 11 | build = "build.rs" 12 | 13 | categories = ["external-ffi-bindings"] 14 | 15 | 16 | [badges] 17 | travis-ci = { repository = "goertzenator/erlang_nif-sys" } 18 | appveyor = { repository = "goertzenator/erlang-nif-sys" } 19 | 20 | [dependencies] 21 | unreachable = "0.1" 22 | 23 | [dev-dependencies] 24 | walkdir = "1.0" 25 | itertools = "0.5" 26 | cargo-erlangapp = "0.1" 27 | -------------------------------------------------------------------------------- /tests/testapp/dotest.escript: -------------------------------------------------------------------------------- 1 | 2 | %% dotest.script 3 | %% 4 | %% Compile the test module in-memory and execute it's test function. 5 | %% 6 | 7 | source() -> "mynifmod.erl". 8 | 9 | main([]) -> 10 | io:format("Compiling test module ~s\n", [source()]), 11 | case compile:file(source(), [binary,verbose,report_errors,report_warnings,debug_info]) of 12 | {ok, Name, Bin} -> 13 | run_tests(Name, Bin); 14 | 15 | {ok, Name, Bin, Warnings} -> 16 | show([], Warnings), 17 | run_tests(Name, Bin); 18 | 19 | error -> 20 | halt(1); 21 | 22 | {error, Errors, Warnings} -> 23 | show(Errors, Warnings), 24 | halt(1) 25 | end. 26 | 27 | run_tests(Name, Bin) -> 28 | io:format("Running tests in test module ~s\n", [source()]), 29 | {module, _} = code:load_binary(Name, source(), Bin), 30 | case Name:test() of 31 | ok -> halt(0); 32 | error -> halt(1) 33 | end. 34 | 35 | show(Errors, Warnings) -> 36 | io:format("errors = ~p\n", [Errors]), 37 | io:format("warnings = ~p\n", [Warnings]). 38 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014 The Rust Project Developers 2 | 3 | Permission is hereby granted, free of charge, to any 4 | person obtaining a copy of this software and associated 5 | documentation files (the "Software"), to deal in the 6 | Software without restriction, including without 7 | limitation the rights to use, copy, modify, merge, 8 | publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software 10 | is furnished to do so, subject to the following 11 | conditions: 12 | 13 | The above copyright notice and this permission notice 14 | shall be included in all copies or substantial portions 15 | of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 18 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 19 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 20 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 21 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 22 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 23 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 24 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 25 | DEALINGS IN THE SOFTWARE. 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # erlang_nif-sys 2 | [![](http://meritbadge.herokuapp.com/erlang_nif-sys)](https://crates.io/crates/erlang_nif-sys) 3 | [![Docs](https://docs.rs/erlang_nif-sys/badge.svg)](https://docs.rs/erlang_nif-sys) 4 | [![Build Status](https://travis-ci.org/goertzenator/erlang_nif-sys.svg?branch=master)](https://travis-ci.org/goertzenator/erlang_nif-sys) 5 | [![Build status](https://ci.appveyor.com/api/projects/status/rssa03e29mxou4hv/branch/master?svg=true)](https://ci.appveyor.com/project/goertzenator/erlang-nif-sys/branch/master) 6 | 7 | # **This repo is no longer in use. This code is now maintained as part of the main [rustler](github.com/rusterlium/rustler) repo.** 8 | 9 | A crate for creating [Erlang NIF modules](http://www.erlang.org/doc/man/erl_nif.html) in Rust. This crate exposes the raw C NIF API which can be used directly or as a foundation for higher layer interface crates. Supported under Unix and Windows. 10 | 11 | See the [crate documention](https://docs.rs/erlang_nif-sys). 12 | 13 | See examples of use: 14 | - [rust.mk](https://github.com/goertzenator/rust.mk) for a sample Rust NIF module. 15 | - [rebar3_rust](https://github.com/sdwolf/rebar3_rust) a rebar3 plugin inspired by `rust.mk` that helps integrate Rust code inside Erlang projects. 16 | - [Rustler](https://github.com/hansihe/Rustler) 17 | - [rustfromerl](https://github.com/sdwolf/rustfromerl) a demo project showing performance differences between Erlang code and a simmilar Rust NIF implementation. 18 | 19 | Thanks go to Radosław Szymczyszyn for bootstrapping me on this Rust FFI adventure and providing the original [automatic bindings](https://github.com/lavrin/erlang-rust-nif/blob/master/rust_src/src/c.rs). 20 | -------------------------------------------------------------------------------- /tests/testapp/mynifmod.erl: -------------------------------------------------------------------------------- 1 | -module(mynifmod). 2 | 3 | -include_lib("eunit/include/eunit.hrl"). 4 | 5 | 6 | %%-export([init/0]). 7 | -on_load(init/0). 8 | 9 | init() -> 10 | {ok, Lib} = find_library("mynifmod", "mynifmod"), 11 | ok = erlang:load_nif(Lib, 0). 12 | 13 | %% butchered version of https://github.com/goertzenator/find_crate/blob/master/src/find_crate.erl 14 | find_library(CrateName, LibName) -> 15 | Wildcard = "priv/crates/" ++ CrateName ++ "/{lib,}" ++ LibName ++ ".{so,dll}", 16 | case filelib:wildcard(Wildcard) of 17 | [Lib] -> {ok, filename:rootname(Lib)}; 18 | [] -> {error, not_found}; 19 | _ -> {error, multiple_matches} 20 | end. 21 | 22 | exercise_dtor(0) -> 23 | garbage_collect(), %% don't crash 24 | rustmap_dtor_count(); 25 | exercise_dtor(N) -> 26 | rustmap(), %% create and discard a resource 27 | exercise_dtor(N - 1). 28 | 29 | simple_test_() -> [ 30 | ?_assertEqual(6, times2(3)), 31 | ?_assertEqual(self(), test_enif_make_pid()), 32 | fun() -> rustmap() end, 33 | ?_assertEqual(11, exercise_dtor(10)), 34 | ?_assertEqual("123", to_str(123)), 35 | ?_assertEqual("[\"hello\",world]", to_str(["hello", world])), 36 | ?_assertEqual(hash(hash_this), hash(hash_this)), 37 | ?_assertEqual(hash(and_this), hash(and_this)), 38 | ?_assertNotEqual(hash(hash_this), hash(and_this)), 39 | ?_assertEqual(hash(394749857), hash(394749857)), 40 | ?_assertNotEqual(hash(394749857), hash(394749858)), 41 | ?_assertEqual(make_map(), #{one=>1, two=>2, three=>3}) 42 | ]. 43 | 44 | 45 | times2(_X) -> exit(nif_library_not_loaded). 46 | test_enif_make_pid() -> exit(nif_library_not_loaded). 47 | rustmap() -> exit(nif_library_not_loaded). 48 | rustmap_dtor_count() -> exit(nif_library_not_loaded). 49 | to_str(_X) -> exit(nif_library_not_loaded). 50 | hash(_X) -> exit(nif_library_not_loaded). 51 | make_map() -> exit(nif_library_not_loaded). 52 | 53 | 54 | -------------------------------------------------------------------------------- /tests/tests.rs: -------------------------------------------------------------------------------- 1 | extern crate walkdir; 2 | extern crate itertools; 3 | extern crate cargo_erlangapp; 4 | 5 | //use cargo_erlangapp::{Target, target_filenames}; 6 | //use std::ffi::{OsStr}; 7 | use std::{env, fs, io}; 8 | use std::path::{Path, PathBuf}; 9 | use walkdir::WalkDir; 10 | use itertools::Itertools; 11 | use std::process::Command; 12 | 13 | 14 | #[test] 15 | fn do_test() { 16 | 17 | 18 | // get working directory where we can create files 19 | let out_dir = env::var("OUT_DIR").unwrap(); 20 | 21 | 22 | // find the directory for the Erlang test application 23 | let mut appdir_src = PathBuf::new(); 24 | appdir_src.push("tests"); 25 | appdir_src.push("testapp"); 26 | 27 | // copy application to working dir 28 | copy_all(&appdir_src, &out_dir).unwrap(); 29 | 30 | // get working dir for Erlang test application 31 | let mut appdir = PathBuf::new(); 32 | appdir.push(&out_dir); 33 | appdir.push("testapp"); 34 | 35 | 36 | let escript = env::var("ESCRIPT").unwrap_or("escript".to_string()); 37 | 38 | // build test crate 39 | invoke_erlangapp(&["cargo-erlangapp", "build" ], &appdir); 40 | 41 | // compile and run erlang eunit tests 42 | match Command::new(escript).current_dir(&appdir).arg("dotest.escript").status().expect("failed to execute escript").success() { 43 | true => (), 44 | false => panic!("erlang tests failed"), 45 | }; 46 | } 47 | 48 | fn invoke_erlangapp(args: &[&str], path: &Path) { 49 | cargo_erlangapp::invoke_with_args_str(args, path) 50 | } 51 | 52 | 53 | fn copy_all, Q: AsRef>(from: P, to: Q) -> io::Result<()> { 54 | // calculate how many path elements to chop off entry when forming to path 55 | let chop_cnt = from.as_ref().components().count() - 1; 56 | for entry in WalkDir::new(from).follow_links(false) { 57 | let entry = try!(entry); 58 | let filetype = entry.file_type(); 59 | let compi = entry.path().components().dropping(chop_cnt); 60 | let to_path = to.as_ref().join(compi.as_path()); 61 | //let to_path = to.as_ref().join(entry.path()); 62 | if filetype.is_dir() { 63 | try!(fs::create_dir_all(to_path)); 64 | } else if filetype.is_file() { 65 | try!(fs::copy(entry.path(), to_path)); 66 | } 67 | } 68 | Ok(()) 69 | } 70 | -------------------------------------------------------------------------------- /tests/struct_size.rs: -------------------------------------------------------------------------------- 1 | extern crate erlang_nif_sys; 2 | 3 | #[cfg(unix)] 4 | #[test] 5 | fn test1() { 6 | 7 | use erlang_nif_sys::*; 8 | use std::process::Command; 9 | use std::env; 10 | use std::path::Path; 11 | use std::collections::HashMap; 12 | use std::iter::FromIterator; 13 | use std::mem::size_of; 14 | 15 | let out_dir = env::var("OUT_DIR").unwrap(); 16 | 17 | // use environment escript and cc if available 18 | let escript = env::var("ESCRIPT").unwrap_or("escript".to_string()); 19 | let cc = env::var("CC").unwrap_or("cc".to_string()); 20 | 21 | 22 | // get erts include path 23 | let erts_include = Command::new(escript).arg("get_erts_path.erl") 24 | .output() 25 | .map_err(|_| "Can't run escript") 26 | .map(|out| { 27 | match out.status.success() { 28 | true => out.stdout, 29 | false => panic!("Can't run get_erts_path.erl") 30 | } 31 | }) 32 | .map(String::from_utf8) 33 | .unwrap().unwrap(); 34 | 35 | 36 | 37 | //println!("include {:?}", erts_include); 38 | 39 | // Compile C program 40 | let exe = Path::new(&out_dir).join("struct_size"); 41 | match Command::new(cc).arg("-o").arg(&exe).arg("-I").arg(&erts_include).arg("tests/struct_size.c") 42 | .status() 43 | .map_err(|_|"Can't find c compiler (cc or value of environment CC)") 44 | .unwrap().success() { 45 | false => panic!("Can't compile struct_size.c"), 46 | _ => () 47 | } 48 | 49 | // Run C program that lists C NIF runtime information. 50 | let stdout:Vec = Command::new(&exe).output() 51 | .map_err(|_|"Can't run C runtime information program") 52 | .unwrap().stdout; 53 | 54 | let output:&str = std::str::from_utf8(&stdout).unwrap(); 55 | 56 | // Parse C program output into hashmap of (&str, u32) 57 | let sizemap = HashMap::<&str, usize>::from_iter( 58 | output.lines().map(|ln|ln.split(" ")) 59 | .map(|mut it| (it.next().unwrap(), it.next().unwrap().parse().unwrap()))); 60 | 61 | 62 | 63 | /* types to check are: 64 | 65 | ERL_NIF_UINT 66 | ERL_NIF_TERM 67 | ErlNifFunc 68 | ErlNifEntry 69 | ErlNifBinary 70 | ErlNifPid 71 | ErlNifSysInfo 72 | ErlNifMapIterator 73 | 74 | */ 75 | 76 | assert_eq!(&size_of::(), sizemap.get("ERL_NIF_UINT").unwrap()); 77 | assert_eq!(&size_of::(), sizemap.get("ERL_NIF_TERM").unwrap()); 78 | assert_eq!(&size_of::(), sizemap.get("ErlNifFunc").unwrap()); 79 | 80 | // Disabling this test because struct size grew in otp-20 but remains 81 | // backwards compatible. 82 | 83 | // assert_eq!(&size_of::(), sizemap.get("ErlNifEntry").unwrap()); 84 | 85 | assert_eq!(&size_of::(), sizemap.get("ErlNifBinary").unwrap()); 86 | assert_eq!(&size_of::(), sizemap.get("ErlNifPid").unwrap()); 87 | assert_eq!(&size_of::(), sizemap.get("ErlNifSysInfo").unwrap()); 88 | assert_eq!(&size_of::(), sizemap.get("ErlNifMapIterator").unwrap()); 89 | } 90 | -------------------------------------------------------------------------------- /tests/testapp/crates/mynifmod/src/lib.rs: -------------------------------------------------------------------------------- 1 | 2 | #[macro_use] 3 | extern crate erlang_nif_sys; 4 | use erlang_nif_sys::*; 5 | 6 | use std::{mem, ptr}; 7 | use std::cmp::min; 8 | use std::sync::atomic::{AtomicIsize, Ordering}; 9 | 10 | static mut RUSTMAP_TYPE: *const ErlNifResourceType = 0 as *const ErlNifResourceType; 11 | static mut DTOR_COUNTER: Option = None; 12 | 13 | nif_init!("mynifmod", [ 14 | ("times2", 1, slice_args!(times2)), 15 | ("test_enif_make_pid", 0, test_enif_make_pid), 16 | ("rustmap", 0, rustmap), 17 | ("rustmap_dtor_count", 0, rustmap_dtor_count), 18 | ("to_str", 1, slice_args!(to_str)), 19 | ("hash", 1, slice_args!(hash)), 20 | ("make_map", 0, slice_args!(make_map)), 21 | ], 22 | {load: mynifmod_load}); 23 | 24 | unsafe fn mynifmod_load(env: *mut ErlNifEnv, _priv_data: *mut *mut c_void, _load_info: ERL_NIF_TERM) -> c_int { 25 | let mut tried: ErlNifResourceFlags = mem::uninitialized(); 26 | DTOR_COUNTER = Some(AtomicIsize::new(0)); 27 | RUSTMAP_TYPE = enif_open_resource_type( 28 | env, 29 | ptr::null(), 30 | b"rustmap\0".as_ptr(), 31 | Some(rustmap_destructor), 32 | ErlNifResourceFlags::ERL_NIF_RT_CREATE, 33 | &mut tried); 34 | RUSTMAP_TYPE.is_null() as c_int 35 | } 36 | 37 | fn times2(env: *mut ErlNifEnv, args: &[ERL_NIF_TERM]) -> ERL_NIF_TERM { 38 | unsafe { 39 | let mut result: i32 = mem::uninitialized(); 40 | if 1==args.len() && 0!=enif_get_int(env, args[0], &mut result) { 41 | enif_make_int(env, 2*result) 42 | } 43 | else { 44 | enif_make_badarg(env) 45 | } 46 | } 47 | } 48 | 49 | fn test_enif_make_pid(env: *mut ErlNifEnv, _: c_int, _: *const ERL_NIF_TERM) -> ERL_NIF_TERM { 50 | let mut pid: ErlNifPid = unsafe { mem::uninitialized() }; 51 | unsafe { enif_self(env, &mut pid) }; 52 | unsafe { enif_make_pid(env, &pid) } 53 | } 54 | 55 | use std::collections::HashMap; 56 | type RustMap = HashMap; 57 | 58 | 59 | unsafe extern "C" fn rustmap_destructor(_env: *mut ErlNifEnv, handle: *mut c_void) { 60 | DTOR_COUNTER.as_mut().unwrap().fetch_add(1, Ordering::SeqCst); 61 | ptr::read(handle as *mut RustMap); 62 | } 63 | 64 | unsafe fn rustmap(env: *mut ErlNifEnv, _: c_int, _: *const ERL_NIF_TERM) -> ERL_NIF_TERM { 65 | // Create a value with nontrivial destructor cleanup. 66 | let mut map = RustMap::new(); 67 | map.insert("Rust".to_string(), "Erlang".to_string()); 68 | map.insert("Erlang".to_string(), "Rust".to_string()); 69 | 70 | let mem = enif_alloc_resource(RUSTMAP_TYPE, mem::size_of::()); 71 | assert_eq!(mem as usize % mem::align_of::(), 0); 72 | ptr::write(mem as *mut RustMap, map); 73 | let term = enif_make_resource(env, mem); 74 | enif_release_resource(mem); 75 | term 76 | } 77 | 78 | unsafe fn rustmap_dtor_count(env: *mut ErlNifEnv, _: c_int, _: *const ERL_NIF_TERM) -> ERL_NIF_TERM { 79 | let cnt = DTOR_COUNTER.as_mut().unwrap().load(Ordering::SeqCst); 80 | enif_make_int(env, cnt as i32) 81 | } 82 | 83 | unsafe fn to_str(env: *mut ErlNifEnv, args: &[ERL_NIF_TERM]) -> ERL_NIF_TERM { 84 | let mut buf = Vec::::with_capacity(1024); 85 | let n = enif_snprintf!(buf.as_mut_ptr() as *mut i8, 86 | buf.capacity(), 87 | "%T".as_ptr() as *mut i8, 88 | args[0]); 89 | if n < 0 { 90 | enif_make_badarg(env) 91 | } else { 92 | let len = min(n as usize, buf.capacity() - 1); 93 | buf.set_len(len); 94 | enif_make_string_len(env, buf.as_ptr(), len, 95 | ErlNifCharEncoding::ERL_NIF_LATIN1) 96 | } 97 | } 98 | 99 | unsafe fn hash(env: *mut ErlNifEnv, args: &[ERL_NIF_TERM]) -> ERL_NIF_TERM { 100 | if 1==args.len() { 101 | let res = enif_hash(ErlNifHash::ERL_NIF_INTERNAL_HASH, args[0], 1234); 102 | enif_make_uint64(env, res) 103 | } 104 | else { 105 | enif_make_badarg(env) 106 | } 107 | } 108 | 109 | unsafe fn make_map(env: *mut ErlNifEnv, args: &[ERL_NIF_TERM]) -> ERL_NIF_TERM { 110 | if 0==args.len() { 111 | let keys: Vec<_> = ["one", "two", "three"].iter() 112 | .map(|x| enif_make_atom_len(env, x.as_ptr(), x.len())) 113 | .collect(); 114 | let values: Vec<_> = (1..=3) 115 | .map(|x| enif_make_int(env, x)) 116 | .collect(); 117 | let mut map = mem::uninitialized(); 118 | if 0!=enif_make_map_from_arrays(env, keys.as_ptr(), values.as_ptr(), keys.len(), &mut map) { 119 | map 120 | } else { 121 | enif_make_badarg(env) 122 | } 123 | } 124 | else { 125 | enif_make_badarg(env) 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | /*! 2 | Low level Rust bindings to the [Erlang NIF API](http://www.erlang.org/doc/man/erl_nif.html). 3 | 4 | # NIF Crate 5 | 6 | A NIF module is built by creating a new crate that uses `erlang_nif-sys` as a dependency. 7 | (more) 8 | 9 | # NIF Functions 10 | 11 | All NIF functions must have the following signature: 12 | 13 | ``` 14 | #[macro_use] 15 | extern crate erlang_nif_sys; 16 | use erlang_nif_sys::*; 17 | # fn main(){} //0 18 | fn my_nif(env: *mut ErlNifEnv, 19 | argc: c_int, 20 | args: *const ERL_NIF_TERM) -> ERL_NIF_TERM { 21 | // ... 22 | # unsafe{enif_make_badarg(env)} 23 | } 24 | 25 | ``` 26 | 27 | # NIF Module Initialization 28 | 29 | ## For the Impatient 30 | ``` 31 | #[macro_use] 32 | extern crate erlang_nif_sys; 33 | use erlang_nif_sys::*; 34 | 35 | nif_init!("my_nif_module",[ 36 | ("my_nif_fun1", 1, my_nif_fun1), 37 | ("my_dirty_fun2", 1, my_dirty_fun2, ERL_NIF_DIRTY_JOB_CPU_BOUND) 38 | ], 39 | {load: my_load} 40 | ); 41 | # fn main(){} //1 42 | # fn my_load(env: *mut ErlNifEnv, priv_data: *mut *mut c_void, load_info: ERL_NIF_TERM)-> c_int { 0 } 43 | # fn my_nif_fun1(_: *mut ErlNifEnv,_: c_int,args: *const ERL_NIF_TERM) -> ERL_NIF_TERM {unsafe{*args}} 44 | # fn my_dirty_fun2(_: *mut ErlNifEnv,_: c_int,args: *const ERL_NIF_TERM) -> ERL_NIF_TERM {unsafe{*args}} 45 | ``` 46 | 47 | ## Details 48 | 49 | The `erlang_nif-sys` analog of [`ERL_NIF_INIT()`](http://www.erlang.org/doc/man/erl_nif_init.html) is `nif_init!` which has the following form: 50 | 51 | `nif_init!(module_name, [nif_funcs], {options})` 52 | 53 | `module_name` must be a string literal, for example `"mynifmodule"`. 54 | 55 | 56 | `nif_funcs` declares all the exported NIF functions for this module. Each entry is declared as 57 | 58 | `(name, arity, function, flags)` 59 | 60 | `name` is a string literal indicating the name of the function as seen from Erlang code. 61 | `arity` is an integer indicating how many parameter this function takes as seen from Erlang code. 62 | `function` is the Rust implentation of the NIF and must be of the form 63 | `Fn(env: *mut ErlNifEnv, argc: c_int, args: *const ERL_NIF_TERM) -> ERL_NIF_TERM`. This is usually a plain 64 | Rust function, but closures are permitted. 65 | `flags` is optional and allows you to specify if this NIF is to run on a dirty scheduler. See [dirty NIFs](http://www.erlang.org/doc/man/erl_nif.html#dirty_nifs) 66 | in the Erlang docs. 67 | 68 | The `options` are the NIF module intialization functions [`load`](http://www.erlang.org/doc/man/erl_nif.html#load), [`reload`](http://www.erlang.org/doc/man/erl_nif.html#reload), 69 | [`upgrade`](http://www.erlang.org/doc/man/erl_nif.html#upgrade), and [`unload`](http://www.erlang.org/doc/man/erl_nif.html#unload). 70 | Each is optional and is specified in struct-init style if present. If no options are needed, 71 | the curly braces may be elided. Stub implementation of all these functions looks something like: 72 | 73 | ``` 74 | #[macro_use] 75 | extern crate erlang_nif_sys; 76 | use erlang_nif_sys::*; 77 | 78 | nif_init!("mymod", [], {load: load, reload: reload, upgrade: upgrade, unload: unload}); 79 | 80 | fn load(env: *mut ErlNifEnv, 81 | priv_data: *mut *mut c_void, 82 | load_info: ERL_NIF_TERM)-> c_int { 0 } 83 | 84 | fn reload(env: *mut ErlNifEnv, 85 | priv_data: *mut *mut c_void, 86 | load_info: ERL_NIF_TERM) -> c_int { 0 } 87 | 88 | fn upgrade(env: *mut ErlNifEnv, 89 | priv_data: *mut *mut c_void, 90 | old_priv_data: *mut *mut c_void, 91 | load_info: ERL_NIF_TERM) -> c_int { 0 } 92 | 93 | fn unload(env: *mut ErlNifEnv, 94 | priv_data: *mut c_void) {} 95 | 96 | # fn main(){} //2 97 | ``` 98 | 99 | # Invoking NIF API 100 | 101 | As with any Rust FFI call, NIF API calls must be wrapped in `unsafe` blocks. 102 | Below is an example of invoking NIF APIs along with an approach for dealing with 103 | the the `args` parameter. 104 | 105 | ``` 106 | extern crate erlang_nif_sys; 107 | use erlang_nif_sys::*; 108 | use std::mem; 109 | fn native_add(env: *mut ErlNifEnv, 110 | argc: c_int, 111 | args: *const ERL_NIF_TERM) -> ERL_NIF_TERM { 112 | unsafe { 113 | let mut a: c_int = mem::uninitialized(); 114 | let mut b: c_int = mem::uninitialized(); 115 | if argc == 2 && 116 | 0 != enif_get_int(env, *args, &mut a) && 117 | 0 != enif_get_int(env, *args.offset(1), &mut b) { 118 | enif_make_int(env, a+b) 119 | } 120 | else { 121 | enif_make_badarg(env) 122 | } 123 | } 124 | } 125 | # fn main(){} //3 126 | ``` 127 | 128 | */ 129 | 130 | 131 | // Don't throw warnings on NIF naming conventions 132 | #![allow(non_camel_case_types)] 133 | 134 | #[cfg(windows)] 135 | extern crate unreachable; 136 | 137 | #[macro_use] 138 | mod initmacro; 139 | 140 | pub mod erlang_nif_sys_api; 141 | 142 | pub use erlang_nif_sys_api::*; 143 | -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | # Appveyor configuration template for Rust using rustup for Rust installation 2 | # https://github.com/starkat99/appveyor-rust 3 | 4 | ## Operating System (VM environment) ## 5 | 6 | # Rust needs at least Visual Studio 2013 Appveyor OS for MSVC targets. 7 | os: Visual Studio 2015 8 | 9 | ## Build Matrix ## 10 | 11 | # This configuration will setup a build for each channel & target combination (12 windows 12 | # combinations in all). 13 | # 14 | # There are 3 channels: stable, beta, and nightly. 15 | # 16 | # Alternatively, the full version may be specified for the channel to build using that specific 17 | # version (e.g. channel: 1.5.0) 18 | # 19 | # The values for target are the set of windows Rust build targets. Each value is of the form 20 | # 21 | # ARCH-pc-windows-TOOLCHAIN 22 | # 23 | # Where ARCH is the target architecture, either x86_64 or i686, and TOOLCHAIN is the linker 24 | # toolchain to use, either msvc or gnu. See https://www.rust-lang.org/downloads.html#win-foot for 25 | # a description of the toolchain differences. 26 | # See https://github.com/rust-lang-nursery/rustup.rs/#toolchain-specification for description of 27 | # toolchains and host triples. 28 | # 29 | # Comment out channel/target combos you do not wish to build in CI. 30 | # 31 | # You may use the `cargoflags` and `RUSTFLAGS` variables to set additional flags for cargo commands 32 | # and rustc, respectively. For instance, you can uncomment the cargoflags lines in the nightly 33 | # channels to enable unstable features when building for nightly. Or you could add additional 34 | # matrix entries to test different combinations of features. 35 | environment: 36 | matrix: 37 | 38 | ### MSVC Toolchains ### 39 | 40 | # Stable 64-bit MSVC 41 | - channel: stable 42 | target: x86_64-pc-windows-msvc 43 | # Stable 32-bit MSVC 44 | # - channel: stable 45 | # target: i686-pc-windows-msvc 46 | # # Beta 64-bit MSVC 47 | # - channel: beta 48 | # target: x86_64-pc-windows-msvc 49 | # # Beta 32-bit MSVC 50 | # - channel: beta 51 | # target: i686-pc-windows-msvc 52 | # # Nightly 64-bit MSVC 53 | # - channel: nightly 54 | # target: x86_64-pc-windows-msvc 55 | # #cargoflags: --features "unstable" 56 | # # Nightly 32-bit MSVC 57 | # - channel: nightly 58 | # target: i686-pc-windows-msvc 59 | # #cargoflags: --features "unstable" 60 | 61 | ### GNU Toolchains ### 62 | 63 | # # Stable 64-bit GNU 64 | # - channel: stable 65 | # target: x86_64-pc-windows-gnu 66 | # # Stable 32-bit GNU 67 | # - channel: stable 68 | # target: i686-pc-windows-gnu 69 | # # Beta 64-bit GNU 70 | # - channel: beta 71 | # target: x86_64-pc-windows-gnu 72 | # # Beta 32-bit GNU 73 | # - channel: beta 74 | # target: i686-pc-windows-gnu 75 | # # Nightly 64-bit GNU 76 | # - channel: nightly 77 | # target: x86_64-pc-windows-gnu 78 | # #cargoflags: --features "unstable" 79 | # # Nightly 32-bit GNU 80 | # - channel: nightly 81 | # target: i686-pc-windows-gnu 82 | # #cargoflags: --features "unstable" 83 | 84 | ### Allowed failures ### 85 | 86 | # See Appveyor documentation for specific details. In short, place any channel or targets you wish 87 | # to allow build failures on (usually nightly at least is a wise choice). This will prevent a build 88 | # or test failure in the matching channels/targets from failing the entire build. 89 | matrix: 90 | allow_failures: 91 | - channel: nightly 92 | 93 | # If you only care about stable channel build failures, uncomment the following line: 94 | #- channel: beta 95 | 96 | ## Install Script ## 97 | 98 | # This is the most important part of the Appveyor configuration. This installs the version of Rust 99 | # specified by the 'channel' and 'target' environment variables from the build matrix. This uses 100 | # rustup to install Rust. 101 | # 102 | # For simple configurations, instead of using the build matrix, you can simply set the 103 | # default-toolchain and default-host manually here. 104 | install: 105 | - appveyor DownloadFile https://win.rustup.rs/ -FileName rustup-init.exe 106 | - rustup-init -yv --default-toolchain %channel% --default-host %target% 107 | - set PATH=%PATH%;%USERPROFILE%\.cargo\bin 108 | - rustc -vV 109 | - cargo -vV 110 | 111 | ################################# 112 | # Download and install Erlang 113 | ################################# 114 | 115 | - echo Downloading Erlang... 116 | - ps: (new-object System.Net.WebClient).Downloadfile('http://www.erlang.org/download/otp_win64_21.0.exe', 'C:\Users\appveyor\erlang.exe') 117 | - echo Installing Erlang... 118 | - start /B /WAIT C:\Users\appveyor\erlang.exe /S /D=C:\Users\appveyor\erlang 119 | - set ERLANG_HOME=C:\Users\appveyor\erlang 120 | - set PATH=C:\Users\appveyor\erlang\bin;%PATH% 121 | - echo %PATH% 122 | 123 | ## Build Script ## 124 | 125 | # 'cargo test' takes care of building for us, so disable Appveyor's build stage. This prevents 126 | # the "directory does not contain a project or solution file" error. 127 | build: false 128 | 129 | # Uses 'cargo test' to run tests and build. Alternatively, the project may call compiled programs 130 | #directly or perform other testing commands. Rust will automatically be placed in the PATH 131 | # environment variable. 132 | test_script: 133 | - cargo test --verbose %cargoflags% 134 | -------------------------------------------------------------------------------- /LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /src/erlang_nif_sys_api.rs: -------------------------------------------------------------------------------- 1 | 2 | #[cfg(windows)] 3 | use unreachable::UncheckedOptionExt; // unchecked unwrap used in generated Windows code 4 | 5 | pub use std::os::raw::{c_int, c_void, c_uint, c_char, c_uchar, c_ulong, c_long, c_double}; 6 | 7 | use std::os; 8 | 9 | #[allow(non_camel_case_types)] 10 | pub type size_t = usize; 11 | 12 | use std::option::Option; 13 | //use std::mem::size_of; 14 | 15 | #[allow(non_camel_case_types)] 16 | pub type ERL_NIF_UINT = size_t; 17 | 18 | #[allow(non_camel_case_types)] 19 | pub type ERL_NIF_TERM = ERL_NIF_UINT; 20 | 21 | //#[derive(Debug, Copy, Clone)] 22 | //#[repr(C)] 23 | //pub struct ERL_NIF_TERM(ERL_NIF_UINT); // Don't do this, 32 bit calling convention is different for structs and ints. 24 | 25 | 26 | /// See [ErlNifEnv](http://www.erlang.org/doc/man/erl_nif.html#ErlNifEnv) in the Erlang docs. 27 | #[derive(Debug)] 28 | #[allow(missing_copy_implementations)] 29 | #[repr(C)] 30 | pub struct ErlNifEnv { 31 | dummy: *mut c_void, // block automatic Send and Sync traits. Ref https://doc.rust-lang.org/beta/nomicon/send-and-sync.html 32 | } 33 | 34 | // Ownership of an env may be safely transfers between threads, therefore ErlNifEnv is Send. 35 | // This is the common use case for process independent environments created with enif_alloc_env(). 36 | // ErlNifEnv is NOT Sync because it is thread unsafe. 37 | unsafe impl Send for ErlNifEnv {} 38 | 39 | 40 | /// See [ErlNifFunc](http://www.erlang.org/doc/man/erl_nif.html#ErlNifFunc) in the Erlang docs. 41 | // #[allow(missing_copy_implementations)] 42 | #[repr(C)] 43 | pub struct ErlNifFunc { 44 | pub name: *const u8, 45 | pub arity: c_uint, 46 | pub function: unsafe extern "C" fn(env: *mut ErlNifEnv, argc: c_int, argv: *const ERL_NIF_TERM) -> ERL_NIF_TERM, 47 | pub flags: c_uint, 48 | } 49 | 50 | // #[allow(missing_copy_implementations)] 51 | #[doc(hidden)] 52 | #[derive(Debug)] 53 | #[repr(C)] 54 | #[allow(non_snake_case)] 55 | pub struct ErlNifEntry { 56 | pub major: c_int, 57 | pub minor: c_int, 58 | pub name: *const u8, 59 | pub num_of_funcs: c_int, 60 | pub funcs: *const ErlNifFunc, 61 | pub load: Option c_int>, 62 | pub reload: Option c_int>, 63 | pub upgrade: Option c_int>, 64 | pub unload: Option ()>, 65 | pub vm_variant: *const u8, 66 | pub options: c_uint, // added in 2.7 67 | pub sizeof_ErlNifResourceTypeInit: usize, // added in 2.12 68 | } 69 | 70 | pub const ERL_NIF_DIRTY_NIF_OPTION: c_uint = 1; 71 | 72 | /// See [ErlNifBinary](http://www.erlang.org/doc/man/erl_nif.html#ErlNifBinary) in the Erlang docs. 73 | #[allow(missing_copy_implementations)] 74 | #[derive(Debug)] 75 | #[repr(C)] 76 | pub struct ErlNifBinary { 77 | pub size: size_t, 78 | pub data: *mut u8, 79 | ref_bin: *mut c_void, 80 | _spare: [*mut c_void; 2], 81 | } 82 | 83 | #[cfg(windows)] 84 | pub type ErlNifEvent = os::windows::raw::HANDLE; 85 | 86 | #[cfg(unix)] 87 | pub type ErlNifEvent = os::unix::io::RawFd; 88 | 89 | 90 | /// See [ErlNifResourceType](http://www.erlang.org/doc/man/erl_nif.html#ErlNifResourceType) in the Erlang docs. 91 | #[allow(missing_copy_implementations)] 92 | #[repr(C)] 93 | pub struct ErlNifResourceType {dummy:c_int} 94 | 95 | /// See [ErlNifResourceDtor](http://www.erlang.org/doc/man/erl_nif.html#ErlNifResourceDtor) in the Erlang docs. 96 | #[allow(missing_copy_implementations)] 97 | pub type ErlNifResourceDtor = unsafe extern "C" fn(env: *mut ErlNifEnv, obj: *mut c_void) -> (); 98 | 99 | /// See [ErlNifResourceStop](http://www.erlang.org/doc/man/erl_nif.html#ErlNifResourceStop) in the Erlang docs. 100 | #[allow(missing_copy_implementations)] 101 | pub type ErlNifResourceStop = unsafe extern "C" fn(env: *mut ErlNifEnv, obj: *mut c_void, event: ErlNifEvent, is_direct_call: c_int ) -> (); 102 | 103 | /// See [ErlNifResourceDown](http://www.erlang.org/doc/man/erl_nif.html#ErlNifResourceDown) in the Erlang docs. 104 | #[allow(missing_copy_implementations)] 105 | pub type ErlNifResourceDown = unsafe extern "C" fn(env: *mut ErlNifEnv, obj: *mut c_void, pid: *const ErlNifPid, mon: *const ErlNifMonitor) -> (); 106 | 107 | 108 | /// See [ErlNifResourceTypeInit](http://www.erlang.org/doc/man/erl_nif.html#ErlNifResourceTypeInit) in the Erlang docs. 109 | #[derive(Debug, Copy, Clone)] 110 | #[repr(C)] 111 | pub struct ErlNifResourceTypeInit { 112 | dtor: *const ErlNifResourceDtor, 113 | stop: *const ErlNifResourceStop, // at ERL_NIF_SELECT_STOP event 114 | down: *const ErlNifResourceDown, // enif_monitor_process 115 | } 116 | 117 | /// See [ErlNifSelectFlags](http://erlang.org/doc/man/erl_nif.html#ErlNifSelectFlags) in the Erlang docs. 118 | pub type ErlNifSelectFlags = c_int; 119 | pub const ERL_NIF_SELECT_READ: ErlNifSelectFlags = (1 << 0); 120 | pub const ERL_NIF_SELECT_WRITE: ErlNifSelectFlags = (1 << 1); 121 | pub const ERL_NIF_SELECT_STOP: ErlNifSelectFlags = (1 << 2); 122 | 123 | 124 | /// See [ErlNifMonitor](http://www.erlang.org/doc/man/erl_nif.html#ErlNifMonitor) in the Erlang docs. 125 | #[derive(Debug, Copy, Clone)] 126 | #[repr(C)] 127 | pub struct ErlNifMonitor { 128 | // from https://github.com/erlang/otp/blob/83e20c62057ebc1d8064bf57b01be560cd244e1d/erts/emulator/beam/erl_drv_nif.h#L64 129 | // data: [c_uchar; size_of::<*const c_void>()*4], size_of is non-const 130 | data: [usize; 4], 131 | } 132 | 133 | 134 | /// See [ErlNifResourceFlags](http://www.erlang.org/doc/man/erl_nif.html#ErlNifResourceFlags) in the Erlang docs. 135 | #[derive(Debug, Copy, Clone)] 136 | #[repr(C)] 137 | pub enum ErlNifResourceFlags { 138 | ERL_NIF_RT_CREATE = 1, 139 | ERL_NIF_RT_TAKEOVER = 2, 140 | } 141 | 142 | /// See [ErlNifCharEncoding](http://www.erlang.org/doc/man/erl_nif.html#ErlNifCharEncoding) in the Erlang docs. 143 | #[derive(Debug, Copy, Clone)] 144 | #[repr(C)] 145 | pub enum ErlNifCharEncoding { 146 | ERL_NIF_LATIN1 = 1, 147 | DUMMY = 999, // prevents "univariant enum" compile error 148 | } 149 | 150 | /// See [ErlNifPid](http://www.erlang.org/doc/man/erl_nif.html#ErlNifPid) in the Erlang docs. 151 | #[derive(Debug, Copy, Clone)] 152 | #[repr(C)] 153 | pub struct ErlNifPid { 154 | pid: ERL_NIF_TERM, 155 | } 156 | 157 | /// See [enif_make_pid](http://erlang.org/doc/man/erl_nif.html#enif_make_pid) in the Erlang docs 158 | pub unsafe fn enif_make_pid(_env: *mut ErlNifEnv, pid: & ErlNifPid) -> ERL_NIF_TERM { 159 | pid.pid 160 | } 161 | 162 | /// See [ErlNifSysInfo](http://www.erlang.org/doc/man/erl_nif.html#ErlNifSysInfo) in the Erlang docs. 163 | #[allow(missing_copy_implementations)] 164 | #[repr(C)] 165 | pub struct ErlNifSysInfo { 166 | pub driver_major_version: c_int, 167 | pub driver_minor_version: c_int, 168 | pub erts_version: *mut c_char, 169 | pub otp_release: *mut c_char, 170 | pub thread_support: c_int, 171 | pub smp_support: c_int, 172 | pub async_threads: c_int, 173 | pub scheduler_threads: c_int, 174 | pub nif_major_version: c_int, 175 | pub nif_minor_version: c_int, 176 | pub dirty_scheduler_support: c_int, 177 | } 178 | 179 | // /// See [ErlNifFunc](http://www.erlang.org/doc/man/erl_nif.html#ErlNifFunc) in the Erlang docs. 180 | // #[derive(Copy, Clone)] 181 | // #[repr(C)] 182 | // pub enum ErlNifDirtyTaskFlags { 183 | // ERL_NIF_DIRTY_JOB_CPU_BOUND = 1, 184 | // ERL_NIF_DIRTY_JOB_IO_BOUND = 2, 185 | // } 186 | 187 | pub type ErlNifDirtyTaskFlags = c_uint; 188 | pub const ERL_NIF_DIRTY_JOB_CPU_BOUND: ErlNifDirtyTaskFlags = 1; 189 | pub const ERL_NIF_DIRTY_JOB_IO_BOUND: ErlNifDirtyTaskFlags = 2; 190 | 191 | 192 | /// See [ErlNifMapIterator](http://www.erlang.org/doc/man/erl_nif.html#ErlNifMapIterator) in the Erlang docs. 193 | #[allow(missing_copy_implementations)] 194 | #[repr(C)] 195 | pub struct ErlNifMapIterator { 196 | map: ERL_NIF_TERM, 197 | t_limit: ERL_NIF_UINT, 198 | idx: ERL_NIF_UINT, 199 | ks: *mut ERL_NIF_TERM, 200 | vs: *mut ERL_NIF_TERM, 201 | __spare__: [*mut c_void; 2], 202 | } 203 | 204 | /// See [ErlNifMapIteratorEntry](http://www.erlang.org/doc/man/erl_nif.html#ErlNifMapIteratorEntry) in the Erlang docs. 205 | #[derive(Copy, Clone)] 206 | #[repr(C)] 207 | pub enum ErlNifMapIteratorEntry { 208 | ERL_NIF_MAP_ITERATOR_HEAD = 1, 209 | ERL_NIF_MAP_ITERATOR_TAIL = 2, 210 | } 211 | 212 | /// See [ErlNifTime](http://www.erlang.org/doc/man/erl_nif.html#ErlNifTime) in the Erlang docs. 213 | pub type ErlNifTime = i64; 214 | 215 | /// Error return value for `enif_monotonic_time()`, `enif_time_offset()`, and `enif_convert_time_unit()`. 216 | pub const ERL_NIF_TIME_ERROR: i64 = -9223372036854775808; 217 | //const ERL_NIF_TIME_ERROR:i64 = i64::min_value(); "error: const fn's not yet stable" 218 | 219 | /// See [ErlNifTimeUnit](http://www.erlang.org/doc/man/erl_nif.html#ErlNifTimeUnit) in the Erlang docs. 220 | #[derive(Copy, Clone)] 221 | #[repr(C)] 222 | pub enum ErlNifTimeUnit { 223 | // values yanked from https://github.com/erlang/otp/blob/7cb403e4aa044fd2cc7702dbe8e2d0eea68e81f3/erts/emulator/beam/erl_drv_nif.h#L132 224 | ERL_NIF_SEC = 0, 225 | ERL_NIF_MSEC = 1, 226 | ERL_NIF_USEC = 2, 227 | ERL_NIF_NSEC = 3, 228 | } 229 | 230 | /// See [ErlNifUniqueInteger](http://erlang.org/doc/man/erl_nif.html#ErlNifUniqueInteger) in the Erlang docs. 231 | pub type ErlNifUniqueInteger = c_int; 232 | pub const ERL_NIF_UNIQUE_POSITIVE: ErlNifUniqueInteger = (1 << 0); 233 | pub const ERL_NIF_UNIQUE_MONOTONIC: ErlNifUniqueInteger = (1 << 1); 234 | // ref https://github.com/erlang/otp/blob/maint/erts/emulator/beam/erl_nif.h#L203 235 | // FIXME: Should actually be C enum, but repr(C) enums in Rust can't be used as bitfields. 236 | // Fix if the right abstraction ever lands in Rust. 237 | 238 | 239 | /// See [ErlNifPort](http://erlang.org/doc/man/erl_nif.html#ErlNifPort) in the Erlang docs. 240 | #[derive(Copy, Clone)] 241 | #[repr(C)] 242 | pub struct ErlNifPort { 243 | port_id: ERL_NIF_TERM, // internal, may change 244 | } 245 | // ref https://github.com/erlang/otp/blob/maint/erts/emulator/beam/erl_nif.h#L155 246 | 247 | 248 | /// See [ErlNifBinaryToTerm](http://erlang.org/doc/man/erl_nif.html#ErlNifBinaryToTerm) in the Erlang docs. 249 | pub type ErlNifBinaryToTerm = c_int; 250 | pub const ERL_NIF_BIN2TERM_SAFE: ErlNifBinaryToTerm = 0x20000000; 251 | 252 | 253 | pub const ERL_NIF_THR_UNDEFINED: c_int = 0; 254 | pub const ERL_NIF_THR_NORMAL_SCHEDULER: c_int = 1; 255 | pub const ERL_NIF_THR_DIRTY_CPU_SCHEDULER: c_int = 2; 256 | pub const ERL_NIF_THR_DIRTY_IO_SCHEDULER: c_int = 3; 257 | 258 | /// See [ErlNifHash](http://www.erlang.org/doc/man/erl_nif.html#ErlNifHash) in the Erlang docs. 259 | #[derive(Copy, Clone)] 260 | #[repr(C)] 261 | pub enum ErlNifHash { 262 | // from https://github.com/erlang/otp/blob/83e20c62057ebc1d8064bf57b01be560cd244e1d/erts/emulator/beam/erl_nif.h#L242 263 | ERL_NIF_INTERNAL_HASH = 1, 264 | ERL_NIF_PHASH2 = 2, 265 | } 266 | 267 | 268 | 269 | include!(concat!(env!("OUT_DIR"), "/nif_api.snippet")); 270 | // example of included content: 271 | // extern "C" { 272 | // pub fn enif_priv_data(arg1: *mut ErlNifEnv) -> *mut c_void; 273 | // pub fn enif_alloc(size: size_t) -> *mut c_void; 274 | // pub fn enif_free(ptr: *mut c_void); 275 | // pub fn enif_is_atom(arg1: *mut ErlNifEnv, term: ERL_NIF_TERM) -> c_int; 276 | // pub fn enif_is_binary(arg1: *mut ErlNifEnv, term: ERL_NIF_TERM) -> c_int; 277 | // ... 278 | 279 | 280 | -------------------------------------------------------------------------------- /src/initmacro.rs: -------------------------------------------------------------------------------- 1 | 2 | /// Implement exported module init function needed by the Erlang runtime. 3 | /// 4 | /// See [the module level documentation](index.html) for usage of `nif_init!`. 5 | /// 6 | /// The pre-0.5.5 `nif_init!` format is deprecated but still supported. 7 | /// An example of this format is ... 8 | /// 9 | /// ```rust,ignore 10 | /// nif_init!(b"my_nif_module\0", Some(load), None, None, None, 11 | /// nif!(b"my_nif_fun1\0", 1, my_nif_fun1), 12 | /// nif!(b"my_dirty_fun2\0", 1, my_dirty_fun2, ERL_NIF_DIRTY_JOB_CPU_BOUND) 13 | /// ); 14 | /// ``` 15 | #[macro_export] 16 | macro_rules! nif_init { 17 | ($($rest:tt)*) => ( 18 | platform_nif_init!( 19 | get_entry!($($rest)*) 20 | ); 21 | ) 22 | } 23 | 24 | /// Platform specific NIF module initialization. 25 | /// 26 | /// This macro is intended for higher level NIF libraries and not for direct 27 | /// users of `erlang_nif-sys`. See implementation of `nif_init!` for usage. 28 | #[macro_export] 29 | macro_rules! platform_nif_init { 30 | ($get_entry:expr) => ( 31 | #[cfg(unix)] 32 | #[no_mangle] 33 | pub extern "C" fn nif_init() -> *const $crate::erlang_nif_sys_api::ErlNifEntry { 34 | $get_entry() 35 | } 36 | 37 | #[cfg(windows)] 38 | #[no_mangle] 39 | pub extern "C" fn nif_init(callbacks: *mut $crate::erlang_nif_sys_api::TWinDynNifCallbacks) -> *const $crate::erlang_nif_sys_api::ErlNifEntry { 40 | unsafe { 41 | WIN_DYN_NIF_CALLBACKS = Some(*callbacks); 42 | } 43 | //std::ptr::copy_nonoverlapping(callbacks, &WinDynNifCallbacks, std::mem::size_of()); 44 | $get_entry() 45 | } 46 | ) 47 | } 48 | 49 | 50 | 51 | /// Wrapper to deliver NIF args as Rust slice. 52 | /// 53 | /// A macro wrapper that combines the argc and args parameters into a 54 | /// more Rustic slice (`&[ERL_NIF_TERM]`). On release builds this macro 55 | /// incurs zero overhead. 56 | /// 57 | /// 58 | /// # Examples 59 | /// ``` 60 | /// #[macro_use] 61 | /// extern crate erlang_nif_sys; 62 | /// use erlang_nif_sys::*; 63 | /// use std::mem; 64 | /// 65 | /// nif_init!("mymod", [ 66 | /// ("native_add", 2, slice_args!(native_add)) 67 | /// ]); 68 | /// 69 | /// fn native_add(env: *mut ErlNifEnv, 70 | /// args: &[ERL_NIF_TERM]) -> ERL_NIF_TERM { 71 | /// unsafe { 72 | /// let mut a: c_int = mem::uninitialized(); 73 | /// let mut b: c_int = mem::uninitialized(); 74 | /// if args.len() == 2 && 75 | /// 0 != enif_get_int(env, args[0], &mut a) && 76 | /// 0 != enif_get_int(env, args[1], &mut b) { 77 | /// enif_make_int(env, a+b) 78 | /// } 79 | /// else { 80 | /// enif_make_badarg(env) 81 | /// } 82 | /// } 83 | /// } 84 | /// # fn main(){} //3 85 | #[macro_export] 86 | macro_rules! slice_args { 87 | ($f:expr) => ( { 88 | use $crate::erlang_nif_sys_api as ens; 89 | |env: *mut ens::ErlNifEnv, argc: ens::c_int, args: *const ens::ERL_NIF_TERM| -> ens::ERL_NIF_TERM { 90 | $f(env, std::slice::from_raw_parts(args, argc as usize)) 91 | } 92 | } 93 | ); 94 | } 95 | 96 | /// Internal macro for implenting a ErlNifEntry-creating function. 97 | #[doc(hidden)] 98 | #[macro_export] 99 | macro_rules! get_entry { 100 | // add default options if elided 101 | ( $module:expr, $funcs_tt:tt) => ( get_entry!($module, $funcs_tt, {}) ); 102 | 103 | // strip trailing comma in funcs 104 | ( $module:expr, [$($funcs:tt),+,], $inits_tt:tt ) => ( get_entry!($module, [$($funcs),*], $inits_tt) ); 105 | 106 | ( $module:expr, [$($funcs:tt),*], {$($inits:tt)*} ) => ( 107 | || { // start closure 108 | use $crate::erlang_nif_sys_api as ens; 109 | const FUNCS: &'static [ens::ErlNifFunc] = &[$(make_func_entry!($funcs)),*]; 110 | 111 | // initialize as much as possible statically 112 | static mut ENTRY: ens::ErlNifEntry = ens::ErlNifEntry{ 113 | major : ens::NIF_MAJOR_VERSION, 114 | minor : ens::NIF_MINOR_VERSION, 115 | name : concat!($module, "\0") as *const str as *const u8, 116 | num_of_funcs : 0 as ens::c_int, 117 | funcs : &[] as *const ens::ErlNifFunc, 118 | load: None, 119 | reload: None, 120 | upgrade: None, 121 | unload: None, 122 | vm_variant : b"beam.vanilla\0" as *const u8, 123 | options: ens::ERL_NIF_ENTRY_OPTIONS, 124 | sizeof_ErlNifResourceTypeInit: 0, 125 | }; 126 | 127 | // get a safe mutable reference once to avoid repeated unsafe 128 | let entry = unsafe { &mut ENTRY }; 129 | 130 | // perform dynamic insertions 131 | entry.num_of_funcs = FUNCS.len() as ens::c_int; 132 | entry.funcs = FUNCS.as_ptr(); 133 | set_optionals!(entry, $($inits)*); 134 | entry.sizeof_ErlNifResourceTypeInit = std::mem::size_of::(); 135 | entry // return static entry reference 136 | } // end closure 137 | ); 138 | 139 | // For legacy nif_init!() invocation, deprecated 140 | ($module:expr, $load:expr, $reload:expr, $upgrade:expr, $unload:expr, $($func:expr),* ) => ( 141 | || { // start closure 142 | use $crate::erlang_nif_sys_api as ens; 143 | const FUNCS: &'static [ens::ErlNifFunc] = &[$($func),*]; 144 | static mut ENTRY: ens::ErlNifEntry = ens::ErlNifEntry{ 145 | major : ens::NIF_MAJOR_VERSION, 146 | minor : ens::NIF_MINOR_VERSION, 147 | name : $module as *const u8, 148 | num_of_funcs : 0 as ens::c_int, 149 | funcs : &[] as *const ens::ErlNifFunc, 150 | load : $load, 151 | reload : $reload, 152 | upgrade : $upgrade, 153 | unload : $unload, 154 | vm_variant : b"beam.vanilla\0" as *const u8, 155 | options: ens::ERL_NIF_ENTRY_OPTIONS, 156 | sizeof_ErlNifResourceTypeInit: 0, 157 | }; 158 | // get a safe mutable reference once to avoid repeated unsafe 159 | let entry = unsafe { &mut ENTRY }; 160 | 161 | // perform dynamic insertions 162 | entry.num_of_funcs = FUNCS.len() as ens::c_int; 163 | entry.funcs = FUNCS.as_ptr(); 164 | entry.sizeof_ErlNifResourceTypeInit = std::mem::size_of::(); 165 | entry // return static entry reference 166 | } // end closure 167 | ); 168 | } 169 | 170 | 171 | /// Create ErlNifFunc structure. Use inside `nif_init!`. (Deprecated) 172 | /// 173 | /// This is deprecated; see [the module level documentation](index.html) 174 | /// for current `nif_init!` syntax. 175 | #[macro_export] 176 | macro_rules! nif{ 177 | ($name:expr, $arity:expr, $function:expr, $flags:expr) => ( 178 | ens::ErlNifFunc { name: $name as *const u8, 179 | arity: $arity, 180 | function: $function, 181 | flags: $flags}); 182 | 183 | ($name:expr, $arity:expr, $function:expr) => ( 184 | nif!($name, $arity, $function, 0)) 185 | } 186 | 187 | 188 | /// Internal macro to create an ErlNifEntry-creating function. 189 | #[doc(hidden)] 190 | #[macro_export] 191 | macro_rules! make_func_entry { 192 | (($name:expr, $arity:expr, $function:expr, $flags:expr)) => ( 193 | ens::ErlNifFunc { name: concat!($name, "\0") as *const str as *const u8, 194 | arity: $arity, 195 | function: { 196 | unsafe extern "C" fn wrapper(env: *mut ens::ErlNifEnv, argc: ens::c_int, args: *const ens::ERL_NIF_TERM) -> ens::ERL_NIF_TERM { 197 | $function(env, argc, args) 198 | } 199 | wrapper 200 | }, 201 | flags: $flags}); 202 | 203 | (($name:expr, $arity:expr, $function:expr)) => ( 204 | make_func_entry!(($name, $arity, $function, 0)); 205 | ); 206 | } 207 | 208 | 209 | /// Internal macro to deal with optional init functions. 210 | #[doc(hidden)] 211 | #[macro_export] 212 | macro_rules! set_optionals { 213 | ($entry:ident, $fname:ident: $val:expr, $($rest:tt)*) => ( 214 | set_optional!($entry, $fname, $val); 215 | set_optionals!($entry, $($rest)*); 216 | ); 217 | ($entry:ident, $fname:ident: $val:expr) => ( 218 | set_optional!($entry, $fname, $val); 219 | ); 220 | //($entry:ident$($rest:tt)*) => ($($rest)*); 221 | ($entry:ident,) => (); 222 | } 223 | 224 | /// Internal macro to deal with optional init functions. 225 | #[doc(hidden)] 226 | #[macro_export] 227 | macro_rules! set_optional { 228 | ($entry:ident, load, $val:expr) => ( { 229 | unsafe extern "C" fn wrapper(env: *mut ens::ErlNifEnv, priv_data: *mut *mut ens::c_void, load_info: ens::ERL_NIF_TERM)-> ens::c_int { 230 | $val(env, priv_data, load_info) 231 | } 232 | $entry.load = Some(wrapper); 233 | }); 234 | ($entry:ident, reload, $val:expr) => ( { 235 | unsafe extern "C" fn wrapper(env: *mut ens::ErlNifEnv, priv_data: *mut *mut ens::c_void, load_info: ens::ERL_NIF_TERM) -> ens::c_int { 236 | $val(env, priv_data, load_info) 237 | } 238 | $entry.reload = Some(wrapper); 239 | }); 240 | ($entry:ident, upgrade, $val:expr) => ( { 241 | unsafe extern "C" fn wrapper(env: *mut ens::ErlNifEnv, priv_data: *mut *mut ens::c_void, old_priv_data: *mut *mut ens::c_void, load_info: ens::ERL_NIF_TERM) -> ens::c_int { 242 | $val(env, priv_data, old_priv_data, load_info) 243 | } 244 | $entry.upgrade = Some(wrapper); 245 | }); 246 | ($entry:ident, unload, $val:expr) => ( { 247 | unsafe extern "C" fn wrapper(env: *mut ens::ErlNifEnv, priv_data: *mut ens::c_void) { 248 | $val(env, priv_data) 249 | } 250 | $entry.unload = Some(wrapper); 251 | }); 252 | } 253 | 254 | 255 | #[cfg(test)] 256 | mod initmacro_namespace_tests { 257 | 258 | // explicitly disable for this test: 259 | // use erlang_nif_sys_api::*; 260 | use erlang_nif_sys_api; 261 | 262 | use std; 263 | use std::ptr; 264 | use std::slice; 265 | use std::ffi::{CString, CStr}; 266 | 267 | // Initializer tests 268 | fn load(_env: *mut erlang_nif_sys_api::ErlNifEnv, _priv_data: *mut *mut erlang_nif_sys_api::c_void, _load_info: erlang_nif_sys_api::ERL_NIF_TERM) -> erlang_nif_sys_api::c_int { 269 | 14 270 | } 271 | 272 | fn unload(_env: *mut erlang_nif_sys_api::ErlNifEnv, _priv_data: *mut erlang_nif_sys_api::c_void) {} 273 | 274 | 275 | fn raw_nif1(_env: *mut erlang_nif_sys_api::ErlNifEnv, argc: erlang_nif_sys_api::c_int, _args: *const erlang_nif_sys_api::ERL_NIF_TERM) -> erlang_nif_sys_api::ERL_NIF_TERM { 276 | (argc*7) as usize 277 | } 278 | 279 | fn slice_nif(_env: *mut erlang_nif_sys_api::ErlNifEnv, args: &[erlang_nif_sys_api::ERL_NIF_TERM]) -> erlang_nif_sys_api::ERL_NIF_TERM { 280 | args.len() * 17 281 | } 282 | 283 | #[test] 284 | fn opt_some2() { 285 | let entry = get_entry!("empty", [], {load: load, unload:unload})(); 286 | assert_eq!(0, entry.num_of_funcs); 287 | assert_eq!(14, unsafe{entry.load.unwrap()(ptr::null_mut(), ptr::null_mut(), 0)}); 288 | assert_eq!(None, entry.reload); 289 | assert_eq!(None, entry.upgrade); 290 | unsafe{entry.unload.unwrap()(ptr::null_mut(), ptr::null_mut())}; // shouldn't panic or crash 291 | } 292 | 293 | #[test] 294 | fn nif1() { 295 | let entry = get_entry!("nifs", [("raw1", 3, raw_nif1)])(); 296 | let funcs = unsafe{slice::from_raw_parts(entry.funcs, entry.num_of_funcs as usize)}; 297 | assert_eq!(1, funcs.len()); 298 | assert_eq!(CString::new("raw1").unwrap().as_ref(), unsafe{CStr::from_ptr(funcs[0].name as *const i8)}); 299 | assert_eq!(3, funcs[0].arity); 300 | assert_eq!(28, unsafe{(funcs[0].function)(ptr::null_mut(), 4, ptr::null_mut())}); 301 | assert_eq!(0, funcs[0].flags); 302 | } 303 | 304 | #[test] 305 | fn nif_wrapped() { 306 | let entry = get_entry!("nifs", [("sliced", 6, slice_args!(slice_nif))])(); 307 | let funcs = unsafe{slice::from_raw_parts(entry.funcs, entry.num_of_funcs as usize)}; 308 | assert_eq!(1, funcs.len()); 309 | assert_eq!(CString::new("sliced").unwrap().as_ref(), unsafe{CStr::from_ptr(funcs[0].name as *const i8)}); 310 | assert_eq!(6, funcs[0].arity); 311 | assert_eq!(34, unsafe{(funcs[0].function)(ptr::null_mut(), 2, ptr::null_mut())}); 312 | assert_eq!(0, funcs[0].flags); 313 | } 314 | 315 | } 316 | 317 | #[cfg(test)] 318 | mod initmacro_tests { 319 | use erlang_nif_sys_api::*; 320 | use std; 321 | use std::ptr; 322 | use std::slice; 323 | use std::ffi::{CString, CStr}; 324 | 325 | 326 | // Initializer tests 327 | 328 | fn load(_env: *mut ErlNifEnv, _priv_data: *mut *mut c_void, _load_info: ERL_NIF_TERM) -> c_int { 329 | 14 330 | } 331 | 332 | fn unload(_env: *mut ErlNifEnv, _priv_data: *mut c_void) {} 333 | 334 | fn raw_nif1(_env: *mut ErlNifEnv, argc: c_int, _args: *const ERL_NIF_TERM) -> ERL_NIF_TERM { 335 | (argc*7) as usize 336 | } 337 | 338 | fn raw_nif2(_env: *mut ErlNifEnv, argc: c_int, _args: *const ERL_NIF_TERM) -> ERL_NIF_TERM { 339 | (argc*11) as usize 340 | } 341 | 342 | fn slice_nif(_env: *mut ErlNifEnv, args: &[ERL_NIF_TERM]) -> ERL_NIF_TERM { 343 | args.len() * 17 344 | } 345 | 346 | extern "C" fn c_load(_env: *mut ErlNifEnv, _priv_data: *mut *mut c_void, _load_info: ERL_NIF_TERM) -> c_int { 347 | 114 348 | } 349 | 350 | extern "C" fn c_unload(_env: *mut ErlNifEnv, _priv_data: *mut c_void) {} 351 | 352 | extern "C" fn c_nif1(_env: *mut ErlNifEnv, argc: c_int, _args: *const ERL_NIF_TERM) -> ERL_NIF_TERM { 353 | (argc*19) as usize 354 | } 355 | 356 | unsafe fn unsafe_load(_env: *mut ErlNifEnv, _priv_data: *mut *mut c_void, _load_info: ERL_NIF_TERM) -> c_int { 357 | 15 358 | } 359 | 360 | unsafe fn unsafe_nif(_env: *mut ErlNifEnv, argc: c_int, _args: *const ERL_NIF_TERM) -> ERL_NIF_TERM { 361 | (argc*23) as usize 362 | } 363 | 364 | 365 | #[test] 366 | fn opt_empty() { 367 | let entry = get_entry!("empty", [])(); 368 | assert_eq!(0, entry.num_of_funcs); 369 | assert_eq!(None, entry.load); 370 | assert_eq!(None, entry.reload); 371 | assert_eq!(None, entry.upgrade); 372 | assert_eq!(None, entry.unload); 373 | } 374 | 375 | #[test] 376 | fn opt_some1() { 377 | let entry = get_entry!("empty", [], {load: load})(); 378 | assert_eq!(0, entry.num_of_funcs); 379 | assert_eq!(14, unsafe{entry.load.unwrap()(ptr::null_mut(), ptr::null_mut(), 0)}); 380 | assert_eq!(None, entry.reload); 381 | assert_eq!(None, entry.upgrade); 382 | assert_eq!(None, entry.unload); 383 | } 384 | 385 | #[test] 386 | fn opt_some2() { 387 | let entry = get_entry!("empty", [], {load: load, unload:unload})(); 388 | assert_eq!(0, entry.num_of_funcs); 389 | assert_eq!(14, unsafe{entry.load.unwrap()(ptr::null_mut(), ptr::null_mut(), 0)}); 390 | assert_eq!(None, entry.reload); 391 | assert_eq!(None, entry.upgrade); 392 | unsafe{entry.unload.unwrap()(ptr::null_mut(), ptr::null_mut())}; // shouldn't panic or crash 393 | } 394 | 395 | #[test] 396 | fn opt_some2b() { // optionals in different order as opt_some2 397 | let entry = get_entry!("empty", [], {unload:unload, load: load})(); 398 | assert_eq!(0, entry.num_of_funcs); 399 | assert_eq!(14, unsafe{entry.load.unwrap()(ptr::null_mut(), ptr::null_mut(), 0)}); 400 | assert_eq!(None, entry.reload); 401 | assert_eq!(None, entry.upgrade); 402 | unsafe{entry.unload.unwrap()(ptr::null_mut(), ptr::null_mut())}; // shouldn't panic or crash 403 | } 404 | 405 | #[test] 406 | fn opt_closure() { // optionals in different order as opt_some2 407 | let entry = get_entry!("empty", [], {load: |_,_,_|15})(); 408 | assert_eq!(15, unsafe{entry.load.unwrap()(ptr::null_mut(), ptr::null_mut(), 0)}); 409 | } 410 | 411 | 412 | #[test] 413 | fn modname() { 414 | let entry = get_entry!("bananas", [])(); 415 | assert_eq!(CString::new("bananas").unwrap().as_ref(), unsafe{CStr::from_ptr(entry.name as *const i8)} ); 416 | } 417 | 418 | #[test] 419 | fn nif1() { 420 | let entry = get_entry!("nifs", [("raw1", 3, raw_nif1)])(); 421 | let funcs = unsafe{slice::from_raw_parts(entry.funcs, entry.num_of_funcs as usize)}; 422 | assert_eq!(1, funcs.len()); 423 | assert_eq!(CString::new("raw1").unwrap().as_ref(), unsafe{CStr::from_ptr(funcs[0].name as *const i8)}); 424 | assert_eq!(3, funcs[0].arity); 425 | assert_eq!(28, unsafe{(funcs[0].function)(ptr::null_mut(), 4, ptr::null_mut())}); 426 | assert_eq!(0, funcs[0].flags); 427 | } 428 | 429 | #[test] 430 | fn nif2() { 431 | let entry = get_entry!("nifs", [("raw1", 3, raw_nif1),("raw2", 33, raw_nif2, ERL_NIF_DIRTY_JOB_IO_BOUND)])(); 432 | let funcs = unsafe{slice::from_raw_parts(entry.funcs, entry.num_of_funcs as usize)}; 433 | assert_eq!(2, funcs.len()); 434 | assert_eq!(CString::new("raw1").unwrap().as_ref(), unsafe{CStr::from_ptr(funcs[0].name as *const i8)}); 435 | assert_eq!(3, funcs[0].arity); 436 | assert_eq!(28, unsafe{(funcs[0].function)(ptr::null_mut(), 4, ptr::null_mut())}); 437 | assert_eq!(0, funcs[0].flags); 438 | assert_eq!(CString::new("raw2").unwrap().as_ref(), unsafe{CStr::from_ptr(funcs[1].name as *const i8)}); 439 | assert_eq!(33, funcs[1].arity); 440 | assert_eq!(44, unsafe{(funcs[1].function)(ptr::null_mut(), 4, ptr::null_mut())}); 441 | assert_eq!(ERL_NIF_DIRTY_JOB_IO_BOUND, funcs[1].flags); 442 | } 443 | 444 | #[test] 445 | fn nif_closure() { 446 | let entry = get_entry!("nifs", [("closure", 5, |_,argc,_| (argc*13) as usize )])(); 447 | let funcs = unsafe{slice::from_raw_parts(entry.funcs, entry.num_of_funcs as usize)}; 448 | assert_eq!(1, funcs.len()); 449 | assert_eq!(CString::new("closure").unwrap().as_ref(), unsafe{CStr::from_ptr(funcs[0].name as *const i8)}); 450 | assert_eq!(5, funcs[0].arity); 451 | assert_eq!(52, unsafe{(funcs[0].function)(ptr::null_mut(), 4, ptr::null_mut())}); 452 | assert_eq!(0, funcs[0].flags); 453 | } 454 | 455 | #[test] 456 | fn nif_wrapped() { 457 | let entry = get_entry!("nifs", [("sliced", 6, slice_args!(slice_nif))])(); 458 | let funcs = unsafe{slice::from_raw_parts(entry.funcs, entry.num_of_funcs as usize)}; 459 | assert_eq!(1, funcs.len()); 460 | assert_eq!(CString::new("sliced").unwrap().as_ref(), unsafe{CStr::from_ptr(funcs[0].name as *const i8)}); 461 | assert_eq!(6, funcs[0].arity); 462 | assert_eq!(34, unsafe{(funcs[0].function)(ptr::null_mut(), 2, ptr::null_mut())}); 463 | assert_eq!(0, funcs[0].flags); 464 | } 465 | 466 | #[test] 467 | fn legacy() { 468 | let entry = get_entry!( 469 | b"legacymod\0", 470 | Some(c_load), None, None, Some(c_unload), 471 | nif!(b"cnif_1\0", 7, c_nif1, ERL_NIF_DIRTY_JOB_IO_BOUND), 472 | nif!(b"cnif_2\0", 8, c_nif1))(); 473 | let funcs = unsafe{slice::from_raw_parts(entry.funcs, entry.num_of_funcs as usize)}; 474 | 475 | assert_eq!(CString::new("legacymod").unwrap().as_ref(), unsafe{CStr::from_ptr(entry.name as *const i8)} ); 476 | 477 | assert_eq!(114, unsafe{entry.load.unwrap()(ptr::null_mut(), ptr::null_mut(), 0)}); 478 | assert_eq!(None, entry.reload); 479 | assert_eq!(None, entry.upgrade); 480 | unsafe{entry.unload.unwrap()(ptr::null_mut(), ptr::null_mut())}; // shouldn't panic or crash 481 | 482 | assert_eq!(2, funcs.len()); 483 | 484 | assert_eq!(CString::new("cnif_1").unwrap().as_ref(), unsafe{CStr::from_ptr(funcs[0].name as *const i8)}); 485 | assert_eq!(7, funcs[0].arity); 486 | assert_eq!(38, unsafe{(funcs[0].function)(ptr::null_mut(), 2, ptr::null_mut())}); 487 | assert_eq!(ERL_NIF_DIRTY_JOB_IO_BOUND, funcs[0].flags); 488 | 489 | assert_eq!(CString::new("cnif_2").unwrap().as_ref(), unsafe{CStr::from_ptr(funcs[1].name as *const i8)}); 490 | assert_eq!(8, funcs[1].arity); 491 | assert_eq!(57, unsafe{(funcs[1].function)(ptr::null_mut(), 3, ptr::null_mut())}); 492 | assert_eq!(0, funcs[1].flags); 493 | } 494 | 495 | #[test] 496 | fn trailing_comma() { 497 | get_entry!("nifs", 498 | [ 499 | ("raw1", 3, raw_nif1), 500 | ("raw2", 33, raw_nif2, ERL_NIF_DIRTY_JOB_IO_BOUND), // <- trailing comma 501 | ], 502 | { 503 | unload: unload, 504 | load: load, // <- trailing comma 505 | })(); 506 | 507 | } 508 | 509 | #[test] 510 | fn unsafe_callbacks() { 511 | let entry = get_entry!("unsafe_nifs", 512 | [ 513 | ("unsafe_nif", 3, unsafe_nif) 514 | ], 515 | { 516 | load: unsafe_load 517 | })(); 518 | let funcs = unsafe{slice::from_raw_parts(entry.funcs, entry.num_of_funcs as usize)}; 519 | assert_eq!(15, unsafe{entry.load.unwrap()(ptr::null_mut(), ptr::null_mut(), 0)}); 520 | assert_eq!(CString::new("unsafe_nif").unwrap().as_ref(), unsafe{CStr::from_ptr(funcs[0].name as *const i8)}); 521 | assert_eq!(3, funcs[0].arity); 522 | assert_eq!(46, unsafe{(funcs[0].function)(ptr::null_mut(), 2, ptr::null_mut())}); 523 | assert_eq!(0, funcs[0].flags); 524 | 525 | } 526 | 527 | 528 | } 529 | -------------------------------------------------------------------------------- /gen_api.erl: -------------------------------------------------------------------------------- 1 | %% gen_api 2 | 3 | %% api_list 4 | %% 5 | %% This is the API list that corresponds to the list found in erl_nif_api_funcs.h 6 | %% Initial conversion provided by https://github.com/lavrin/erlang-rust-nif/issues/2 7 | %% 8 | %% Functions that are not supported for Rust must still be listed so that 9 | %% the Windows callback struct keeps proper integrity. Such functions 10 | %% must begin with "dummy." 11 | %% 12 | 13 | -type api_opt() :: 14 | exception | % enif_xxx_exception() functions 15 | getenv | % enif_getenv() functions 16 | time | % new timer API 17 | {ulongsize, integer()} | % number of bytes in a C ulong 18 | dirty_schedulers | % enif_is_on_dirty_scheduler(). Only for 2.7-2.10 19 | dirty_scheduler_opt | % dirty scheduler nifentry option flag. For >=2.7 20 | nif_2_11 . % general 2.11 API additions 21 | 22 | 23 | version_opts("2.7") -> [{major,2}, {minor,7} ]; % erlang 17.3 24 | version_opts("2.8") -> [{major,2}, {minor,8}, exception]; % erlang 18.0 25 | version_opts("2.9") -> [{major,2}, {minor,9}, exception, getenv]; % erlang 18.2 26 | version_opts("2.10") -> [{major,2}, {minor,10}, exception, getenv, time]; % erlang 18.3 27 | version_opts("2.11") -> [{major,2}, {minor,11}, exception, getenv, time, % erlang 19.0 28 | dirty_scheduler_opt, nif_2_11]; 29 | version_opts("2.12") -> [{major,2}, {minor,12}, exception, getenv, time, % erlang 20.0 30 | dirty_scheduler_opt, nif_2_11, nif_2_12]; 31 | version_opts("2.13") -> [{major,2}, {minor,13}, exception, getenv, time, % erlang 20.1 32 | dirty_scheduler_opt, nif_2_11, nif_2_12, nif_2_13]; 33 | version_opts("2.14") -> [{major,2}, {minor,14}, exception, getenv, time, % erlang 21.0 34 | dirty_scheduler_opt, nif_2_11, nif_2_12, nif_2_13, 35 | nif_2_14]; 36 | version_opts(_) -> 37 | io:format("Unsupported Erlang version.\n\nIs the erlang_nif-sys version up to date in the Cargo.toml?\nDoes 'cargo update' fix it?\nIf not please report at https://github.com/goertzenator/erlang_nif-sys.\n"), 38 | halt(1). 39 | 40 | ulong_opts("4") -> [{ulongsize, 4}]; 41 | ulong_opts("8") -> [{ulongsize, 8}]. 42 | 43 | 44 | dirty_scheduler_opts("2.7") -> dirty_scheduler_opts(); 45 | dirty_scheduler_opts("2.8") -> dirty_scheduler_opts(); 46 | dirty_scheduler_opts("2.9") -> dirty_scheduler_opts(); 47 | dirty_scheduler_opts("2.10") -> dirty_scheduler_opts(); 48 | dirty_scheduler_opts(_) -> []. % dirty schedulers non-optional in 2.11 49 | 50 | dirty_scheduler_opts() -> 51 | case catch erlang:system_info(dirty_cpu_schedulers) of 52 | _X when is_integer(_X) -> [dirty_schedulers, dirty_scheduler_opt]; 53 | _ -> [] 54 | end. 55 | 56 | 57 | -spec api_list([api_opt()]) -> [term()]. 58 | api_list(Opts) -> [ 59 | {"*mut c_void", "enif_priv_data", "arg1: *mut ErlNifEnv"}, 60 | {"*mut c_void", "enif_alloc", "size: size_t"}, 61 | {"", "enif_free", "ptr: *mut c_void"}, 62 | {"c_int", "enif_is_atom", "arg1: *mut ErlNifEnv, term: ERL_NIF_TERM"}, 63 | {"c_int", "enif_is_binary", "arg1: *mut ErlNifEnv, term: ERL_NIF_TERM"}, 64 | {"c_int", "enif_is_ref", "arg1: *mut ErlNifEnv, term: ERL_NIF_TERM"}, 65 | {"c_int", "enif_inspect_binary", "arg1: *mut ErlNifEnv, bin_term: ERL_NIF_TERM, bin: *mut ErlNifBinary"}, 66 | {"c_int", "enif_alloc_binary", "size: size_t, bin: *mut ErlNifBinary"}, 67 | {"c_int", "enif_realloc_binary", "bin: *mut ErlNifBinary, size: size_t"}, 68 | {"", "enif_release_binary", "bin: *mut ErlNifBinary"}, 69 | {"c_int", "enif_get_int", "arg1: *mut ErlNifEnv, term: ERL_NIF_TERM, ip: *mut c_int"}, 70 | {"c_int", "enif_get_ulong", "arg1: *mut ErlNifEnv, term: ERL_NIF_TERM, ip: *mut c_ulong"}, 71 | {"c_int", "enif_get_double", "arg1: *mut ErlNifEnv, term: ERL_NIF_TERM, dp: *mut c_double"}, 72 | {"c_int", "enif_get_list_cell", "env: *mut ErlNifEnv, term: ERL_NIF_TERM, head: *mut ERL_NIF_TERM, tail: *mut ERL_NIF_TERM"}, 73 | {"c_int", "enif_get_tuple", "env: *mut ErlNifEnv, tpl: ERL_NIF_TERM, arity: *mut c_int, array: *mut *const ERL_NIF_TERM"}, 74 | {"c_int", "enif_is_identical", "lhs: ERL_NIF_TERM, rhs: ERL_NIF_TERM"}, 75 | {"c_int", "enif_compare", "lhs: ERL_NIF_TERM, rhs: ERL_NIF_TERM"}, 76 | {"ERL_NIF_TERM", "enif_make_binary", "env: *mut ErlNifEnv, bin: *mut ErlNifBinary"}, 77 | {"ERL_NIF_TERM", "enif_make_badarg", "env: *mut ErlNifEnv"}, 78 | {"ERL_NIF_TERM", "enif_make_int", "env: *mut ErlNifEnv, i: c_int"}, 79 | {"ERL_NIF_TERM", "enif_make_ulong", "env: *mut ErlNifEnv, i: c_ulong"}, 80 | {"ERL_NIF_TERM", "enif_make_double", "env: *mut ErlNifEnv, d: c_double"}, 81 | {"ERL_NIF_TERM", "enif_make_atom", "env: *mut ErlNifEnv, name: *const c_uchar"}, 82 | {"c_int", "enif_make_existing_atom", "env: *mut ErlNifEnv, name: *const c_uchar, atom: *mut ERL_NIF_TERM, arg1: ErlNifCharEncoding"}, 83 | 84 | {"ERL_NIF_TERM", "enif_make_tuple", "env: *mut ErlNifEnv, cnt: c_uint, ..."}, 85 | {"ERL_NIF_TERM", "enif_make_list", "env: *mut ErlNifEnv, cnt: c_uint, ..."}, 86 | 87 | {"ERL_NIF_TERM", "enif_make_list_cell", "env: *mut ErlNifEnv, car: ERL_NIF_TERM, cdr: ERL_NIF_TERM"}, 88 | {"ERL_NIF_TERM", "enif_make_string", "env: *mut ErlNifEnv, string: *const c_uchar, arg1: ErlNifCharEncoding"}, 89 | {"ERL_NIF_TERM", "enif_make_ref", "env: *mut ErlNifEnv"}, 90 | 91 | %% Skip threading API for now (perhaps forever) 92 | %% If anybody has a situation where they want to use this API instead of the very fine 93 | %% Rust API, please tell me. 94 | %% {"*mut ErlNifMutex", "enif_mutex_create", "name: *mut c_uchar"}, 95 | %% {"", "enif_mutex_destroy", "mtx: *mut ErlNifMutex"}, 96 | %% {"c_int", "enif_mutex_trylock", "mtx: *mut ErlNifMutex"}, 97 | %% {"", "enif_mutex_lock", "mtx: *mut ErlNifMutex"}, 98 | %% {"", "enif_mutex_unlock", "mtx: *mut ErlNifMutex"}, 99 | %% {"*mut ErlNifCond", "enif_cond_create", "name: *mut c_uchar"}, 100 | %% {"", "enif_cond_destroy", "cnd: *mut ErlNifCond"}, 101 | %% {"", "enif_cond_signal", "cnd: *mut ErlNifCond"}, 102 | %% {"", "enif_cond_broadcast", "cnd: *mut ErlNifCond"}, 103 | %% {"", "enif_cond_wait", "cnd: *mut ErlNifCond, mtx: *mut ErlNifMutex"}, 104 | %% {"*mut ErlNifRWLock", "enif_rwlock_create", "name: *mut c_uchar"}, 105 | %% {"", "enif_rwlock_destroy", "rwlck: *mut ErlNifRWLock"}, 106 | %% {"c_int", "enif_rwlock_tryrlock", "rwlck: *mut ErlNifRWLock"}, 107 | %% {"", "enif_rwlock_rlock", "rwlck: *mut ErlNifRWLock"}, 108 | %% {"", "enif_rwlock_runlock", "rwlck: *mut ErlNifRWLock"}, 109 | %% {"c_int", "enif_rwlock_tryrwlock", "rwlck: *mut ErlNifRWLock"}, 110 | %% {"", "enif_rwlock_rwlock", "rwlck: *mut ErlNifRWLock"}, 111 | %% {"", "enif_rwlock_rwunlock", "rwlck: *mut ErlNifRWLock"}, 112 | %% {"c_int", "enif_tsd_key_create", "name: *mut c_uchar, key: *mut ErlNifTSDKey"}, 113 | %% {"", "enif_tsd_key_destroy", "key: ErlNifTSDKey"}, 114 | %% {"", "enif_tsd_set", "key: ErlNifTSDKey, data: *mut c_void"}, 115 | %% {"*mut c_void", "enif_tsd_get", "key: ErlNifTSDKey"}, 116 | %% {"*mut ErlNifThreadOpts", "enif_thread_opts_create", "name: *mut c_uchar"}, 117 | %% {"", "enif_thread_opts_destroy", "opts: *mut ErlNifThreadOpts"}, 118 | %% {"c_int", "enif_thread_create", "name: *mut c_uchar, tid: *mut ErlNifTid, func: Option *mut c_void>, args: *mut c_void, opts: *mut ErlNifThreadOpts"}, 119 | %% {"ErlNifTid", "enif_thread_self", ""}, 120 | %% {"c_int", "enif_equal_tids", "tid1: ErlNifTid, tid2: ErlNifTid"}, 121 | %% {"", "enif_thread_exit", "resp: *mut c_void"}, 122 | %% {"c_int", "enif_thread_join", "arg1: ErlNifTid, respp: *mut *mut c_void"}, 123 | {"", "dummy_enif_mutex_create", ""}, 124 | {"", "dummy_enif_mutex_destroy", ""}, 125 | {"", "dummy_enif_mutex_trylock", ""}, 126 | {"", "dummy_enif_mutex_lock", ""}, 127 | {"", "dummy_enif_mutex_unlock", ""}, 128 | {"", "dummy_enif_cond_create", ""}, 129 | {"", "dummy_enif_cond_destroy", ""}, 130 | {"", "dummy_enif_cond_signal", ""}, 131 | {"", "dummy_enif_cond_broadcast", ""}, 132 | {"", "dummy_enif_cond_wait", ""}, 133 | {"", "dummy_enif_rwlock_create", ""}, 134 | {"", "dummy_enif_rwlock_destroy", ""}, 135 | {"", "dummy_enif_rwlock_tryrlock", ""}, 136 | {"", "dummy_enif_rwlock_rlock", ""}, 137 | {"", "dummy_enif_rwlock_runlock", ""}, 138 | {"", "dummy_enif_rwlock_tryrwlock", ""}, 139 | {"", "dummy_enif_rwlock_rwlock", ""}, 140 | {"", "dummy_enif_rwlock_rwunlock", ""}, 141 | {"", "dummy_enif_tsd_key_create", ""}, 142 | {"", "dummy_enif_tsd_key_destroy", ""}, 143 | {"", "dummy_enif_tsd_set", ""}, 144 | {"", "dummy_enif_tsd_get", ""}, 145 | {"", "dummy_enif_thread_opts_create", ""}, 146 | {"", "dummy_enif_thread_opts_destroy", ""}, 147 | {"", "dummy_enif_thread_create", ""}, 148 | {"", "dummy_enif_thread_self", ""}, 149 | {"", "dummy_enif_equal_tids", ""}, 150 | {"", "dummy_enif_thread_exit", ""}, 151 | {"", "dummy_enif_thread_join", ""}, 152 | 153 | {"*mut c_void", "enif_realloc", "ptr: *mut c_void, size: size_t"}, 154 | {"", "enif_system_info", "sip: *mut ErlNifSysInfo, si_size: size_t"}, 155 | 156 | {"c_int", "enif_fprintf", "filep: *mut c_void, format: *const c_uchar, ..."}, 157 | 158 | {"c_int", "enif_inspect_iolist_as_binary", "arg1: *mut ErlNifEnv, term: ERL_NIF_TERM, bin: *mut ErlNifBinary"}, 159 | {"ERL_NIF_TERM", "enif_make_sub_binary", "arg1: *mut ErlNifEnv, bin_term: ERL_NIF_TERM, pos: size_t, size: size_t"}, 160 | {"c_int", "enif_get_string", "arg1: *mut ErlNifEnv, list: ERL_NIF_TERM, buf: *mut c_uchar, len: c_uint, arg2: ErlNifCharEncoding"}, 161 | {"c_int", "enif_get_atom", "arg1: *mut ErlNifEnv, atom: ERL_NIF_TERM, buf: *mut c_uchar, len: c_uint, arg2: ErlNifCharEncoding"}, 162 | {"c_int", "enif_is_fun", "arg1: *mut ErlNifEnv, term: ERL_NIF_TERM"}, 163 | {"c_int", "enif_is_pid", "arg1: *mut ErlNifEnv, term: ERL_NIF_TERM"}, 164 | {"c_int", "enif_is_port", "arg1: *mut ErlNifEnv, term: ERL_NIF_TERM"}, 165 | {"c_int", "enif_get_uint", "arg1: *mut ErlNifEnv, term: ERL_NIF_TERM, ip: *mut c_uint"}, 166 | {"c_int", "enif_get_long", "arg1: *mut ErlNifEnv, term: ERL_NIF_TERM, ip: *mut c_long"}, 167 | {"ERL_NIF_TERM", "enif_make_uint", "arg1: *mut ErlNifEnv, i: c_uint"}, 168 | {"ERL_NIF_TERM", "enif_make_long", "arg1: *mut ErlNifEnv, i: c_long"}, 169 | {"ERL_NIF_TERM", "enif_make_tuple_from_array", "arg1: *mut ErlNifEnv, arr: *const ERL_NIF_TERM, cnt: c_uint"}, 170 | {"ERL_NIF_TERM", "enif_make_list_from_array", "arg1: *mut ErlNifEnv, arr: *const ERL_NIF_TERM, cnt: c_uint"}, 171 | {"c_int", "enif_is_empty_list", "arg1: *mut ErlNifEnv, term: ERL_NIF_TERM"}, 172 | {"*const ErlNifResourceType", "enif_open_resource_type", "arg1: *mut ErlNifEnv, module_str: *const c_uchar, name_str: *const c_uchar, dtor: Option, flags: ErlNifResourceFlags, tried: *mut ErlNifResourceFlags"}, 173 | {"*mut c_void", "enif_alloc_resource", "_type: *const ErlNifResourceType, size: size_t"}, 174 | {"", "enif_release_resource", "obj: *const c_void"}, 175 | {"ERL_NIF_TERM", "enif_make_resource", "arg1: *mut ErlNifEnv, obj: *const c_void"}, 176 | {"c_int", "enif_get_resource", "arg1: *mut ErlNifEnv, term: ERL_NIF_TERM, _type: *const ErlNifResourceType, objp: *mut *const c_void"}, 177 | {"size_t", "enif_sizeof_resource", "obj: *const c_void"}, 178 | {"*mut c_uchar", "enif_make_new_binary", "arg1: *mut ErlNifEnv, size: size_t, termp: *mut ERL_NIF_TERM"}, 179 | {"c_int", "enif_is_list", "arg1: *mut ErlNifEnv, term: ERL_NIF_TERM"}, 180 | {"c_int", "enif_is_tuple", "arg1: *mut ErlNifEnv, term: ERL_NIF_TERM"}, 181 | {"c_int", "enif_get_atom_length", "arg1: *mut ErlNifEnv, atom: ERL_NIF_TERM, len: *mut c_uint, arg2: ErlNifCharEncoding"}, 182 | {"c_int", "enif_get_list_length", "env: *mut ErlNifEnv, term: ERL_NIF_TERM, len: *mut c_uint"}, 183 | {"ERL_NIF_TERM", "enif_make_atom_len", "env: *mut ErlNifEnv, name: *const c_uchar, len: size_t"}, 184 | {"c_int", "enif_make_existing_atom_len", "env: *mut ErlNifEnv, name: *const c_uchar, len: size_t, atom: *mut ERL_NIF_TERM, arg1: ErlNifCharEncoding"}, 185 | {"ERL_NIF_TERM", "enif_make_string_len", "env: *mut ErlNifEnv, string: *const c_uchar, len: size_t, arg1: ErlNifCharEncoding"}, 186 | {"*mut ErlNifEnv", "enif_alloc_env", ""}, 187 | {"", "enif_free_env", "env: *mut ErlNifEnv"}, 188 | {"", "enif_clear_env", "env: *mut ErlNifEnv"}, 189 | {"c_int", "enif_send", "env: *mut ErlNifEnv, to_pid: *const ErlNifPid, msg_env: *mut ErlNifEnv, msg: ERL_NIF_TERM"}, 190 | {"ERL_NIF_TERM", "enif_make_copy", "dst_env: *mut ErlNifEnv, src_term: ERL_NIF_TERM"}, 191 | {"*mut ErlNifPid", "enif_self", "caller_env: *mut ErlNifEnv, pid: *mut ErlNifPid"}, 192 | {"c_int", "enif_get_local_pid", "env: *mut ErlNifEnv, arg1: ERL_NIF_TERM, pid: *mut ErlNifPid"}, 193 | {"", "enif_keep_resource", "obj: *const c_void"}, 194 | {"ERL_NIF_TERM", "enif_make_resource_binary", "arg1: *mut ErlNifEnv, obj: *const c_void, data: *const c_void, size: size_t"} 195 | ] ++ 196 | 197 | case proplists:get_value(ulongsize, Opts) of 198 | 8 -> []; 199 | 4 -> 200 | [ 201 | {"c_int", "enif_get_int64", "arg1: *mut ErlNifEnv, term: ERL_NIF_TERM, ip: *mut c_longlong"}, 202 | {"c_int", "enif_get_uint64", "arg1: *mut ErlNifEnv, term: ERL_NIF_TERM, ip: *mut c_ulonglong"}, 203 | {"ERL_NIF_TERM", "enif_make_int64", "env: *mut ErlNifEnv, i: c_longlong"}, 204 | {"ERL_NIF_TERM", "enif_make_uint64", "env: *mut ErlNifEnv, i: c_ulonglong"} 205 | ] 206 | 207 | end ++ [ 208 | {"c_int", "enif_is_exception", "arg1: *mut ErlNifEnv, term: ERL_NIF_TERM"}, 209 | {"c_int", "enif_make_reverse_list", "arg1: *mut ErlNifEnv, term: ERL_NIF_TERM, list: *mut ERL_NIF_TERM"}, 210 | {"c_int", "enif_is_number", "arg1: *mut ErlNifEnv, term: ERL_NIF_TERM"}, 211 | {"*mut c_void", "enif_dlopen", "lib: *const c_uchar, err_handler: Option, err_arg: *mut c_void"}, 212 | {"*mut c_void", "enif_dlsym", "handle: *mut c_void, symbol: *const c_uchar, err_handler: Option, err_arg: *mut c_void"}, 213 | {"c_int", "enif_consume_timeslice", "arg1: *mut ErlNifEnv, percent: c_int"}, 214 | {"c_int", "enif_is_map", "env: *mut ErlNifEnv, term: ERL_NIF_TERM"}, 215 | {"c_int", "enif_get_map_size", "env: *mut ErlNifEnv, term: ERL_NIF_TERM, size: *mut size_t"}, 216 | {"ERL_NIF_TERM", "enif_make_new_map", "env: *mut ErlNifEnv"}, 217 | {"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"}, 218 | {"c_int", "enif_get_map_value", "env: *mut ErlNifEnv, map: ERL_NIF_TERM, key: ERL_NIF_TERM, value: *mut ERL_NIF_TERM"}, 219 | {"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"}, 220 | {"c_int", "enif_make_map_remove", "env: *mut ErlNifEnv, map_in: ERL_NIF_TERM, key: ERL_NIF_TERM, map_out: *mut ERL_NIF_TERM"}, 221 | {"c_int", "enif_map_iterator_create", "env: *mut ErlNifEnv, map: ERL_NIF_TERM, iter: *mut ErlNifMapIterator, entry: ErlNifMapIteratorEntry"}, 222 | {"", "enif_map_iterator_destroy", "env: *mut ErlNifEnv, iter: *mut ErlNifMapIterator"}, 223 | {"c_int", "enif_map_iterator_is_head", "env: *mut ErlNifEnv, iter: *mut ErlNifMapIterator"}, 224 | {"c_int", "enif_map_iterator_is_tail", "env: *mut ErlNifEnv, iter: *mut ErlNifMapIterator"}, 225 | {"c_int", "enif_map_iterator_next", "env: *mut ErlNifEnv, iter: *mut ErlNifMapIterator"}, 226 | {"c_int", "enif_map_iterator_prev", "env: *mut ErlNifEnv, iter: *mut ErlNifMapIterator"}, 227 | {"c_int", "enif_map_iterator_get_pair", "env: *mut ErlNifEnv, iter: *mut ErlNifMapIterator, key: *mut ERL_NIF_TERM, value: *mut ERL_NIF_TERM"}, 228 | {"ERL_NIF_TERM", "enif_schedule_nif", "env: *mut ErlNifEnv, fun_name: *const c_uchar, flags:c_int, fp: unsafe extern \"C\" fn(env: *mut ErlNifEnv, argc:c_int, argv:*const ERL_NIF_TERM) -> ERL_NIF_TERM, argc:c_int, argv:*const ERL_NIF_TERM"} 229 | ] ++ 230 | 231 | 232 | case proplists:get_bool(exception, Opts) of 233 | true -> [ 234 | {"c_int", "enif_has_pending_exception", "env: *mut ErlNifEnv, reason: *mut ERL_NIF_TERM"}, 235 | {"ERL_NIF_TERM", "enif_raise_exception", "env: *mut ErlNifEnv, reason: ERL_NIF_TERM"} 236 | ]; 237 | false -> [] 238 | end ++ 239 | 240 | 241 | 242 | case proplists:get_bool(getenv, Opts) of 243 | true -> [ 244 | {"c_int", "enif_getenv", "key: *const c_uchar, value: *mut c_uchar, value_size: *mut size_t"} 245 | ]; 246 | false -> [] 247 | end ++ 248 | 249 | 250 | case proplists:get_bool(time, Opts) of 251 | true -> [ 252 | {"ErlNifTime", "enif_monotonic_time", "unit: ErlNifTimeUnit"}, 253 | {"ErlNifTime", "enif_time_offset", "unit: ErlNifTimeUnit"}, 254 | {"ErlNifTime", "enif_convert_time_unit", "time: ErlNifTime, from_unit: ErlNifTimeUnit, to_unit: ErlNifTimeUnit"} 255 | ]; 256 | false -> [] 257 | end ++ 258 | 259 | 260 | case proplists:get_bool(dirty_schedulers, Opts) of 261 | true -> [{"c_int", "enif_is_on_dirty_scheduler", "env: *mut ErlNifEnv"} ]; 262 | false -> [] 263 | end ++ 264 | 265 | 266 | case proplists:get_bool(nif_2_11, Opts) of 267 | true -> [ 268 | {"ERL_NIF_TERM", "enif_now_time", "env: *mut ErlNifEnv"}, 269 | {"ERL_NIF_TERM", "enif_cpu_time", "env: *mut ErlNifEnv"}, 270 | {"ERL_NIF_TERM", "enif_make_unique_integer", "env: *mut ErlNifEnv, properties: ErlNifUniqueInteger"}, 271 | {"c_int", "enif_is_current_process_alive", "env: *mut ErlNifEnv"}, 272 | {"c_int", "enif_is_process_alive", "env: *mut ErlNifEnv, pid: *const ErlNifPid"}, 273 | {"c_int", "enif_is_port_alive", "env: *mut ErlNifEnv, port_id: *const ErlNifPort"}, 274 | {"c_int", "enif_get_local_port", "env: *mut ErlNifEnv, term: ERL_NIF_TERM, port_id: *mut ErlNifPort"}, 275 | {"c_int", "enif_term_to_binary", "env: *mut ErlNifEnv, term: ERL_NIF_TERM, bin: *mut ErlNifBinary"}, 276 | {"usize", "enif_binary_to_term", "env: *mut ErlNifEnv, data: *const c_uchar, sz: usize, term: *mut ERL_NIF_TERM, opts: ErlNifBinaryToTerm"}, 277 | {"c_int", "enif_port_command", "env: *mut ErlNifEnv, to_port: *const ErlNifPort, msg_env: *mut ErlNifEnv, msg: ERL_NIF_TERM"}, 278 | {"c_int", "enif_thread_type", ""}, 279 | {"c_int", "enif_snprintf", "out: *mut c_char, size: usize, format: *const c_char, ..."} 280 | ]; 281 | false -> [] 282 | end ++ 283 | 284 | case proplists:get_bool(nif_2_12, Opts) of 285 | true -> [ 286 | {"c_int", "enif_select", "env: *mut ErlNifEnv, e: ErlNifEvent, flags: ErlNifSelectFlags, obj: *const c_void, pid: *const ErlNifPid, eref: ERL_NIF_TERM"}, 287 | {"*const ErlNifResourceType", "enif_open_resource_type_x", "env: *mut ErlNifEnv, name_str: *const c_uchar, init: *const ErlNifResourceTypeInit, flags: ErlNifResourceFlags, tried: *mut ErlNifResourceFlags"}, 288 | {"c_int", "enif_monitor_process", "env: *mut ErlNifEnv, obj: *const c_void, pid: *const ErlNifPid, monitor: *mut ErlNifMonitor"}, 289 | {"c_int", "enif_demonitor_process", "env: *mut ErlNifEnv, obj: *const c_void, monitor: *const ErlNifMonitor"}, 290 | {"c_int", "enif_compare_monitors", "monitor1: *const ErlNifMonitor, monitor2: *const ErlNifMonitor"}, 291 | {"u64", "enif_hash", "hashtype: ErlNifHash, term: ERL_NIF_TERM, salt: u64"}, 292 | {"c_int", "enif_whereis_pid", "env: *mut ErlNifEnv, name: ERL_NIF_TERM, pid: *mut ErlNifPid"}, 293 | {"c_int", "enif_whereis_port", "env: *mut ErlNifEnv, name: ERL_NIF_TERM, port: *mut ErlNifPort"} 294 | ]; 295 | false -> [] 296 | end ++ 297 | 298 | case proplists:get_bool(nif_2_13, Opts) of 299 | true -> [ 300 | %% Skip iovec API for now (perhaps forever). 301 | %% Consider safer Rust iovec crates like https://crates.io/crates/iovec instead of this API. 302 | %% If anybody really does need this API in Rust, please file a bug. 303 | %% {"ErlNifIOQueue *", "enif_ioq_create", "ErlNifIOQueueOpts opts"}, 304 | %% {"void", "enif_ioq_destroy", "ErlNifIOQueue *q"}, 305 | %% {"int", "enif_ioq_enq_binary", "ErlNifIOQueue *q, ErlNifBinary *bin, size_t skip"}, 306 | %% {"int", "enif_ioq_enqv", "ErlNifIOQueue *q, ErlNifIOVec *iov, size_t skip"}, 307 | %% {"size_t", "enif_ioq_size", "ErlNifIOQueue *q"}, 308 | %% {"int", "enif_ioq_deq", "ErlNifIOQueue *q, size_t count, size_t *size"}, 309 | %% {"SysIOVec*", "enif_ioq_peek", "ErlNifIOQueue *q, int *iovlen"}, 310 | %% {"int", "enif_inspect_iovec", "ErlNifEnv *env, size_t max_length, ERL_NIF_TERM iovec_term, ERL_NIF_TERM *tail, ErlNifIOVec **iovec"}, 311 | %% {"void", "enif_free_iovec", "ErlNifIOVec *iov"} 312 | {"", "dummy_enif_ioq_create", ""}, 313 | {"", "dummy_enif_ioq_destroy", ""}, 314 | {"", "dummy_enif_ioq_enq_binary", ""}, 315 | {"", "dummy_enif_ioq_enqv", ""}, 316 | {"", "dummy_enif_ioq_size", ""}, 317 | {"", "dummy_enif_ioq_deq", ""}, 318 | {"", "dummy_enif_ioq_peek", ""}, 319 | {"", "dummy_enif_inspect_iovec", ""}, 320 | {"", "dummy_enif_free_iovec", ""} 321 | ]; 322 | false -> [] 323 | end ++ 324 | case proplists:get_bool(nif_2_14, Opts) of 325 | true -> [ 326 | %% Skip iovec and synchronization APIs for now (perhaps forever). 327 | %% Consider safer Rust iovec crates like https://crates.io/crates/iovec instead of this API. 328 | %% If anybody really does need this API in Rust, please file a bug. 329 | % {"int", "enif_ioq_peek_head", "ErlNifEnv *env, ErlNifIOQueue *q, size_t *size, ERL_NIF_TERM *head"}, 330 | % {"char*, "enif_mutex_name", "ErlNifMutex*"}, 331 | % {"char*, "enif_cond_name", "ErlNifCond*"}, 332 | % {"char*, "enif_rwlock_name", "ErlNifRWLock*"}, 333 | % {"char*, "enif_thread_name", "ErlNifTid"}, 334 | {"", "dummy_enif_ioq_peek_head", ""}, 335 | {"", "dummy_enif_mutex_name", ""}, 336 | {"", "dummy_enif_cond_name", ""}, 337 | {"", "dummy_enif_rwlock_name", ""}, 338 | {"", "dummy_enif_thread_name", ""}, 339 | 340 | 341 | %% See format! and write! 342 | % {"int", "enif_vfprintf", "FILE*, const char *fmt, va_list"}, 343 | % {"int", "enif_vsnprintf", "char*, size_t, const char *fmt, va_list"}, 344 | {"", "dummy_enif_vfprintf", ""}, 345 | {"", "dummy_enif_vsnprintf", ""}, 346 | 347 | {"c_int", "enif_make_map_from_arrays", "env: *mut ErlNifEnv, keys: *const ERL_NIF_TERM, values: *const ERL_NIF_TERM, cnt: usize, map_out: *mut ERL_NIF_TERM"} 348 | ]; 349 | false -> [] 350 | end. 351 | 352 | 353 | 354 | main([UlongSizeT]) -> main([UlongSizeT,"."]); 355 | main([UlongSizeT, OutputDir]) -> 356 | %% Round up all configuration options 357 | Version = (catch erlang:system_info(nif_version)), 358 | Opts = version_opts(Version) ++ ulong_opts(UlongSizeT) ++ dirty_scheduler_opts(Version), 359 | %% Generate API list 360 | Entries = api_list(Opts), 361 | %% Generate Rust code 362 | Rust = [ 363 | nif_entry_options_rust(Opts), 364 | nif_version_rust(proplists:get_value(major, Opts), proplists:get_value(minor, Opts)), 365 | api_bindings_rust(erlang:system_info(system_architecture), Entries), 366 | int64_mappers_rust(proplists:get_value(ulongsize, Opts)) 367 | ], 368 | %% And write it 369 | Filename = filename:join(OutputDir, "nif_api.snippet"), 370 | file:write_file(Filename, Rust), 371 | ok. 372 | 373 | nif_entry_options_rust(Opts) -> 374 | DirtySchedulerOpt = proplists:get_bool(dirty_scheduler_opt, Opts), 375 | case DirtySchedulerOpt of 376 | true -> "pub const ERL_NIF_ENTRY_OPTIONS: c_uint = ERL_NIF_DIRTY_NIF_OPTION;\n"; 377 | false-> "pub const ERL_NIF_ENTRY_OPTIONS: c_uint = 0;\n" 378 | end. 379 | 380 | 381 | nif_version_rust(Major, Minor) -> 382 | [io_lib:format("pub const NIF_MAJOR_VERSION: c_int = ~p;\n", [Major]), 383 | io_lib:format("pub const NIF_MINOR_VERSION: c_int = ~p;\n\n", [Minor])]. 384 | 385 | api_bindings_rust("win32", Entries) -> 386 | [ "#[allow(dead_code)]\n", 387 | "#[derive(Copy, Clone)]\n", 388 | "pub struct TWinDynNifCallbacks {\n", 389 | [io_lib:format(" ~s: ~s,\n",[Name,fn_type(Params, Return)]) || {Return,Name,Params} <- Entries], 390 | "}\n\n", 391 | 392 | % The line below would be the "faithful" reproduction of the NIF Win API, but Rust 393 | % is currently not allowing statics to be uninitialized (1.3 beta). Revisit this when 394 | % RFC911 is implemented (or some other mechanism) 395 | %"static mut WinDynNifCallbacks:TWinDynNifCallbacks = unsafe{std::mem::uninitialized()};\n\n", 396 | 397 | % The work-around is to use Option. The problem here is that we have to do an unwrap() for 398 | % each API call which is extra work. 399 | "pub static mut WIN_DYN_NIF_CALLBACKS:Option = None;\n\n", 400 | 401 | [ [ io_lib:format("/// See [~s](http://www.erlang.org/doc/man/erl_nif.html#~s) in the Erlang docs.\n", [Name, Name]), 402 | case is_variadic(Params) of 403 | true -> 404 | io_lib:format("#[macro_export]\n" 405 | "macro_rules! ~s {\n" 406 | " ( $( $arg:expr ),* ) => { $crate::get_~s()($($arg),*) };\n" 407 | " ( $( $arg:expr ),+, ) => { ~s!($($arg),*) };\n" 408 | "}\n\n" 409 | "#[inline]\n" 410 | "#[doc(hidden)]\n" 411 | "pub unsafe fn get_~s() -> ~s {\n" 412 | " WIN_DYN_NIF_CALLBACKS.unchecked_unwrap().~s\n" 413 | "}\n\n", 414 | [Name,Name,Name,Name,fn_type(Params, Return),Name]); 415 | _ -> 416 | io_lib:format("#[inline]\n" 417 | "pub unsafe fn ~s(~s)~s {\n" 418 | " (WIN_DYN_NIF_CALLBACKS.unchecked_unwrap().~s)(~s)\n" 419 | "}\n\n", 420 | [Name,Params,ret_type(Return),Name,strip_types_from_params(Params)]) 421 | end] || {Return,Name,Params} <- Entries, not is_dummy(Name) 422 | ] 423 | ]; 424 | 425 | api_bindings_rust(_Arch, Entries) -> 426 | [ 427 | "extern \"C\" {\n", 428 | [ io_lib:format("/// See [~s](http://www.erlang.org/doc/man/erl_nif.html#~s) in the Erlang docs.\n" 429 | "pub fn ~s(~s)~s;\n", 430 | [Name,Name,Name,Params,ret_type(Return)]) 431 | || {Return,Name,Params} <- Entries, not is_dummy(Name), not is_variadic(Params)], 432 | "}\n\n", 433 | [ io_lib:format("extern \"C\" {\n" 434 | " #[doc(hidden)]\n" 435 | " #[link_name = \"~s\"]\n" 436 | " pub fn _~s(~s)~s;\n" 437 | "}\n\n" 438 | "/// See [~s](http://www.erlang.org/doc/man/erl_nif.html#~s) in the Erlang docs.\n" 439 | "#[macro_export]\n" 440 | "macro_rules! ~s {\n" 441 | " ( $( $arg:expr ),* ) => { $crate::_~s($($arg),*) };\n" 442 | " ( $( $arg:expr ),+, ) => { ~s!($($arg),*) };\n" 443 | "}\n\n", 444 | [Name,Name,Params,ret_type(Return),Name,Name,Name,Name,Name]) 445 | || {Return,Name,Params} <- Entries, not is_dummy(Name), is_variadic(Params)] 446 | ]. 447 | 448 | is_dummy([$d,$u,$m,$m,$y|_]) -> true; 449 | is_dummy(_) -> false. 450 | 451 | is_variadic("...") -> true; 452 | is_variadic("") -> false; 453 | is_variadic([_|T]) -> is_variadic(T). 454 | 455 | fn_type(Params, "") -> io_lib:format("extern \"C\" fn (~s)", [Params]); 456 | fn_type(Params, Return) -> io_lib:format("extern \"C\" fn (~s) -> ~s", [Params, Return]). 457 | 458 | ret_type("") -> ""; 459 | ret_type(Return) -> " -> " ++ Return. 460 | 461 | 462 | 463 | %% Strip types from Rust function definition, example: 464 | %% "arg1: *mut ErlNifEnv, i: c_uint" -> "arg1, i" 465 | strip_types_from_params(Params) -> 466 | ParamsCleaned0 = re:replace(Params, " ", ""), % strip spaces 467 | ParamsCleaned = re:replace(ParamsCleaned0, "\\(.*\\)", ""), % strip nested function params 468 | ParamsL = re:split(ParamsCleaned, ","), 469 | ArgsL = [ re:replace(Param, ":.*", "") || Param <- ParamsL ], % strip type info 470 | join(ArgsL, ", "). 471 | 472 | join(List, Joiner) -> join([], List, Joiner). 473 | join(Acc, [H], _Joiner) -> lists:reverse([H|Acc]); 474 | join(Acc, [H|T], Joiner) -> join([Joiner, H|Acc], T, Joiner). 475 | 476 | %% These functions are defined in the API above when sizeof(ulong)==8, or they map to 477 | %% long/ulong functions when sizeof(ulong)==4. 478 | int64_mappers_rust(4) -> 479 | "use std::os::raw::{c_ulonglong, c_longlong};"; 480 | 481 | int64_mappers_rust(8) -> join([ 482 | "/// See [enif_make_int64](http://www.erlang.org/doc/man/erl_nif.html#enif_make_int64) at erlang.org", 483 | "#[inline]", 484 | "pub unsafe fn enif_make_int64(env: *mut ErlNifEnv, i: i64) -> ERL_NIF_TERM", 485 | " { enif_make_long(env, i) }", 486 | "", 487 | "/// See [enif_make_uint64](http://www.erlang.org/doc/man/erl_nif.html#enif_make_uint64) at erlang.org", 488 | "#[inline]", 489 | "pub unsafe fn enif_make_uint64(env: *mut ErlNifEnv, i: u64) -> ERL_NIF_TERM", 490 | " { enif_make_ulong(env, i) }", 491 | "", 492 | "/// See [enif_get_int64](http://www.erlang.org/doc/man/erl_nif.html#enif_get_int64) at erlang.org", 493 | "#[inline]", 494 | "pub unsafe fn enif_get_int64(env: *mut ErlNifEnv, term: ERL_NIF_TERM, ip: *mut i64) -> c_int", 495 | " { enif_get_long(env, term, ip) }", 496 | "", 497 | "/// See [enif_get_uint64](http://www.erlang.org/doc/man/erl_nif.html#enif_get_uint64) at erlang.org", 498 | "#[inline]", 499 | "pub unsafe fn enif_get_uint64(env: *mut ErlNifEnv, term: ERL_NIF_TERM, ip: *mut u64) -> c_int", 500 | " { enif_get_ulong(env, term, ip) }"], 501 | "\n"). 502 | --------------------------------------------------------------------------------