From 5ae3238d15664a1b55ee0d13c38e59d3335c8767 Mon Sep 17 00:00:00 2001 From: Benedikt Reinartz Date: Tue, 22 Sep 2026 09:48:50 +0200 Subject: [PATCH] Drop rustler::wrapper Instead of using the intermediate half-safe function layer, we use the `enif_*` functions now directly in the high level functions. --- rustler/src/codegen_runtime.rs | 19 +--- rustler/src/dynamic.rs | 27 +++-- rustler/src/env.rs | 26 ++++- rustler/src/lib.rs | 3 - rustler/src/schedule.rs | 8 +- rustler/src/serde/ser.rs | 2 +- rustler/src/sys/types.rs | 77 +++++++++---- rustler/src/term.rs | 51 ++++++++- rustler/src/types/atom.rs | 135 +++++++++++++++++++++-- rustler/src/types/binary.rs | 31 +++++- rustler/src/types/list.rs | 66 +++++++++-- rustler/src/types/local_pid.rs | 21 +++- rustler/src/types/map.rs | 193 ++++++++++++++++++++++++++++----- rustler/src/types/tuple.rs | 31 +++++- rustler/src/wrapper.rs | 47 -------- rustler/src/wrapper/atom.rs | 149 ------------------------- rustler/src/wrapper/binary.rs | 32 ------ rustler/src/wrapper/check.rs | 21 ---- rustler/src/wrapper/env.rs | 33 ------ rustler/src/wrapper/list.rs | 56 ---------- rustler/src/wrapper/map.rs | 159 --------------------------- rustler/src/wrapper/pid.rs | 15 --- rustler/src/wrapper/term.rs | 43 -------- rustler/src/wrapper/tuple.rs | 21 ---- rustler_codegen/src/init.rs | 61 +++++------ 25 files changed, 590 insertions(+), 737 deletions(-) delete mode 100644 rustler/src/wrapper.rs delete mode 100644 rustler/src/wrapper/atom.rs delete mode 100644 rustler/src/wrapper/binary.rs delete mode 100644 rustler/src/wrapper/check.rs delete mode 100644 rustler/src/wrapper/env.rs delete mode 100644 rustler/src/wrapper/list.rs delete mode 100644 rustler/src/wrapper/map.rs delete mode 100644 rustler/src/wrapper/pid.rs delete mode 100644 rustler/src/wrapper/term.rs delete mode 100644 rustler/src/wrapper/tuple.rs diff --git a/rustler/src/codegen_runtime.rs b/rustler/src/codegen_runtime.rs index d73a66147..703ae8f15 100644 --- a/rustler/src/codegen_runtime.rs +++ b/rustler/src/codegen_runtime.rs @@ -14,10 +14,7 @@ pub use inventory; pub use crate::resource::Registration as ResourceRegistration; // Names used by the `rustler::init!` macro or other generated code. -pub use crate::wrapper::{ - c_char, c_int, c_uint, c_void, get_nif_resource_type_init_size, NIF_MAJOR_VERSION, - NIF_MINOR_VERSION, -}; +pub use crate::sys::{c_char, c_int, c_uint, c_void}; pub use crate::sys::{ internal_set_symbols, internal_write_symbols, DynNifCallbacks, ErlNifEntry, ErlNifEnv, @@ -141,17 +138,3 @@ where } } } - -pub const fn min_erts() -> &'static [u8] { - if cfg!(feature = "nif_version_2_18") { - b"OTP-29.0\0" - } else if cfg!(feature = "nif_version_2_17") { - b"OTP-26.0\0" - } else if cfg!(feature = "nif_version_2_16") { - b"OTP-24.0\0" - } else if cfg!(feature = "nif_version_2_15") { - b"OTP-22.0\0" - } else { - b"OTP-21.0\0" - } -} diff --git a/rustler/src/dynamic.rs b/rustler/src/dynamic.rs index 0f16031a6..b0ecdc895 100644 --- a/rustler/src/dynamic.rs +++ b/rustler/src/dynamic.rs @@ -3,7 +3,6 @@ use std::ffi::c_double; #[cfg(feature = "nif_version_2_15")] use crate::sys::ErlNifTermType; -use crate::wrapper::check; use crate::Term; #[derive(Clone, Copy, Debug, Eq, PartialEq)] @@ -77,9 +76,9 @@ pub fn get_type(term: Term) -> TermType { } macro_rules! impl_check { - ($check_fun:ident) => { + ($check_fun:ident, $sys_fun:ident) => { pub fn $check_fun(self) -> bool { - unsafe { check::$check_fun(self.get_env().as_c_arg(), self.as_c_arg()) } + unsafe { crate::sys::$sys_fun(self.get_env().as_c_arg(), self.as_c_arg()) == 1 } } }; } @@ -93,17 +92,17 @@ impl Term<'_> { get_type(self) } - impl_check!(is_atom); - impl_check!(is_binary); - impl_check!(is_empty_list); - impl_check!(is_fun); - impl_check!(is_list); - impl_check!(is_map); - impl_check!(is_number); - impl_check!(is_pid); - impl_check!(is_port); - impl_check!(is_ref); - impl_check!(is_tuple); + impl_check!(is_atom, enif_is_atom); + impl_check!(is_binary, enif_is_binary); + impl_check!(is_empty_list, enif_is_empty_list); + impl_check!(is_fun, enif_is_fun); + impl_check!(is_list, enif_is_list); + impl_check!(is_map, enif_is_map); + impl_check!(is_number, enif_is_number); + impl_check!(is_pid, enif_is_pid); + impl_check!(is_port, enif_is_port); + impl_check!(is_ref, enif_is_ref); + impl_check!(is_tuple, enif_is_tuple); pub fn is_float(self) -> bool { let mut val: c_double = 0.0; diff --git a/rustler/src/env.rs b/rustler/src/env.rs index 14a49122d..1b5f25f8d 100644 --- a/rustler/src/env.rs +++ b/rustler/src/env.rs @@ -173,7 +173,7 @@ impl<'a> Env<'a> { /// [External Term Format](http://erlang.org/doc/apps/erts/erl_ext_dist.html). pub fn binary_to_term(self, data: &[u8]) -> Option<(Term<'a>, usize)> { unsafe { - crate::wrapper::env::binary_to_term(self.as_c_arg(), data, true) + binary_to_term(self.as_c_arg(), data, true) .map(|(term, size)| (Term::new(self, term), size)) } } @@ -181,11 +181,33 @@ impl<'a> Env<'a> { /// Like `binary_to_term`, but can only be called on valid /// and trusted data. pub unsafe fn binary_to_term_trusted(self, data: &[u8]) -> Option<(Term<'a>, usize)> { - crate::wrapper::env::binary_to_term(self.as_c_arg(), data, false) + binary_to_term(self.as_c_arg(), data, false) .map(|(term, size)| (Term::new(self, term), size)) } } +unsafe fn binary_to_term( + env: *mut ErlNifEnv, + data: &[u8], + safe: bool, +) -> Option<(ErlNifTerm, usize)> { + let opts = if safe { + crate::sys::ERL_NIF_BIN2TERM_SAFE + } else { + 0 + }; + + let mut result = std::mem::MaybeUninit::uninit(); + let read_count = + crate::sys::enif_binary_to_term(env, data.as_ptr(), data.len(), result.as_mut_ptr(), opts); + + if read_count == 0 { + return None; + } + + Some((result.assume_init(), read_count)) +} + /// A process-independent environment, a place where Erlang terms can be created outside of a NIF /// call. /// diff --git a/rustler/src/lib.rs b/rustler/src/lib.rs index fca161c7d..9c84e67e9 100644 --- a/rustler/src/lib.rs +++ b/rustler/src/lib.rs @@ -23,9 +23,6 @@ //! For more information about this, see [the documentation for //! rustler](https://hexdocs.pm/rustler). -#[doc(hidden)] -pub mod wrapper; - #[doc(hidden)] pub mod codegen_runtime; diff --git a/rustler/src/schedule.rs b/rustler/src/schedule.rs index 21f393bbe..0a53b9993 100644 --- a/rustler/src/schedule.rs +++ b/rustler/src/schedule.rs @@ -1,11 +1,11 @@ use crate::sys::enif_consume_timeslice; -use crate::wrapper::ErlNifTaskFlags; use crate::Env; +#[derive(Clone, Copy, Debug)] pub enum SchedulerFlags { - Normal = ErlNifTaskFlags::ERL_NIF_NORMAL_JOB as isize, - DirtyCpu = ErlNifTaskFlags::ERL_NIF_DIRTY_JOB_CPU_BOUND as isize, - DirtyIo = ErlNifTaskFlags::ERL_NIF_DIRTY_JOB_IO_BOUND as isize, + Normal = crate::sys::ERL_NIF_NORMAL_JOB as isize, + DirtyCpu = crate::sys::ERL_NIF_DIRTY_JOB_CPU_BOUND as isize, + DirtyIo = crate::sys::ERL_NIF_DIRTY_JOB_IO_BOUND as isize, } pub fn consume_timeslice(env: Env, percent: i32) -> bool { diff --git a/rustler/src/serde/ser.rs b/rustler/src/serde/ser.rs index 4bc1c96c0..1d82b0d1a 100644 --- a/rustler/src/serde/ser.rs +++ b/rustler/src/serde/ser.rs @@ -1,7 +1,7 @@ use std::io::Write; use crate::serde::{atoms, error::Error}; -use crate::wrapper::list::make_list; +use crate::types::list::make_list; use crate::{types::tuple, Encoder, Env, OwnedBinary, Term}; use serde::ser::{self, Serialize}; diff --git a/rustler/src/sys/types.rs b/rustler/src/sys/types.rs index 5ace39d2f..42b85afd7 100644 --- a/rustler/src/sys/types.rs +++ b/rustler/src/sys/types.rs @@ -1,7 +1,7 @@ #![allow(clippy::missing_safety_doc)] #![allow(clippy::upper_case_acronyms)] -pub use std::ffi::{c_char, c_double, c_int, c_long, c_uchar, c_uint, c_ulong, c_void}; +pub use std::ffi::{c_char, c_double, c_int, c_long, c_uchar, c_uint, c_ulong, c_void, CStr}; use std::os; @@ -49,6 +49,21 @@ pub struct ErlNifFunc { pub flags: c_uint, } +type LoadFun = unsafe extern "C" fn( + env: *mut ErlNifEnv, + priv_data: *mut *mut c_void, + load_info: ErlNifTerm, +) -> c_int; + +type UpgradeFun = unsafe extern "C" fn( + env: *mut ErlNifEnv, + priv_data: *mut *mut c_void, + old_priv_data: *mut *mut c_void, + load_info: ErlNifTerm, +) -> c_int; + +type UnloadFun = unsafe extern "C" fn(env: *mut ErlNifEnv, priv_data: *mut c_void) -> (); + // #[allow(missing_copy_implementations)] #[doc(hidden)] #[derive(Debug)] @@ -60,35 +75,48 @@ pub struct ErlNifEntry { pub name: *const c_char, pub num_of_funcs: c_int, pub funcs: *const ErlNifFunc, - pub load: Option< - unsafe extern "C" fn( - env: *mut ErlNifEnv, - priv_data: *mut *mut c_void, - load_info: ErlNifTerm, - ) -> c_int, - >, - pub reload: Option< - unsafe extern "C" fn( - env: *mut ErlNifEnv, - priv_data: *mut *mut c_void, - load_info: ErlNifTerm, - ) -> c_int, - >, - pub upgrade: Option< - unsafe extern "C" fn( - env: *mut ErlNifEnv, - priv_data: *mut *mut c_void, - old_priv_data: *mut *mut c_void, - load_info: ErlNifTerm, - ) -> c_int, - >, - pub unload: Option ()>, + pub load: Option, + pub reload: Option, + pub upgrade: Option, + pub unload: Option, pub vm_variant: *const c_char, pub options: c_uint, // added in 2.7 pub sizeof_ErlNifResourceTypeInit: usize, // added in 2.12 pub min_erts: *const c_char, // added in 2.14 } +impl ErlNifEntry { + pub const fn new(name: &CStr, nif_funcs: &[ErlNifFunc], load: LoadFun) -> Self { + let min_erts = if cfg!(feature = "nif_version_2_18") { + c"OTP-29.0" + } else if cfg!(feature = "nif_version_2_17") { + c"OTP-26.0" + } else if cfg!(feature = "nif_version_2_16") { + c"OTP-24.0" + } else if cfg!(feature = "nif_version_2_15") { + c"OTP-22.0" + } else { + c"OTP-21.0" + }; + + Self { + major: super::NIF_MAJOR_VERSION, + minor: super::NIF_MINOR_VERSION, + name: name.as_ptr(), + num_of_funcs: nif_funcs.len() as c_int, + funcs: nif_funcs.as_ptr(), + load: Some(load), + reload: None, + upgrade: None, + unload: None, + vm_variant: c"beam.vanilla".as_ptr(), + options: 0, + sizeof_ErlNifResourceTypeInit: std::mem::size_of::(), + min_erts: min_erts.as_ptr(), + } + } +} + pub const ERL_NIF_DIRTY_NIF_OPTION: c_uint = 1; /// See [ErlNifBinary](http://www.erlang.org/doc/man/erl_nif.html#ErlNifBinary) in the Erlang docs. @@ -234,6 +262,7 @@ pub struct ErlNifSysInfo { // } pub type ErlNifDirtyTaskFlags = c_uint; +pub const ERL_NIF_NORMAL_JOB: ErlNifDirtyTaskFlags = 0; pub const ERL_NIF_DIRTY_JOB_CPU_BOUND: ErlNifDirtyTaskFlags = 1; pub const ERL_NIF_DIRTY_JOB_IO_BOUND: ErlNifDirtyTaskFlags = 2; diff --git a/rustler/src/term.rs b/rustler/src/term.rs index 4568ee3d4..b68258428 100644 --- a/rustler/src/term.rs +++ b/rustler/src/term.rs @@ -1,6 +1,5 @@ use crate::sys::*; use crate::types::binary::OwnedBinary; -use crate::wrapper::env::term_to_binary; use crate::{Binary, Decoder, Env, NifResult}; use std::cmp::Ordering; use std::fmt::{self, Debug}; @@ -16,9 +15,50 @@ pub struct Term<'a> { env: Env<'a>, } +pub(crate) fn fmt(term: ErlNifTerm, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { + const SIZE: usize = 1024; + let mut bytes: Vec = Vec::with_capacity(SIZE); + + let mut n = 0; + for _ in 0..10 { + let i = unsafe { + enif_snprintf!( + bytes.as_mut_ptr() as *mut c_char, + bytes.capacity(), + b"%T\x00" as *const u8 as *const c_char, + term + ) + }; + if i < 0 { + // Do not propagate an error, because string formatting is + // supposed to be infallible. + break; + } + + n = i as usize; + if n >= bytes.capacity() { + // Bizarrely, enif_snprintf consistently underestimates the + // amount of memory it will need to write long lists. To try to + // avoid going around the loop again, double the estimate. + bytes.reserve_exact(2 * n + 1); + + // Ensure that the `set_len` call below does not expose + // uninitialized bytes if we give up after 10 attempts. + n = 0; + } else { + break; + } + } + + unsafe { + bytes.set_len(n); + } + f.write_str(&String::from_utf8_lossy(&bytes)) +} + impl Debug for Term<'_> { fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { - crate::wrapper::term::fmt(self.as_c_arg(), f) + fmt(self.as_c_arg(), f) } } @@ -94,7 +134,12 @@ impl<'a> Term<'a> { #[inline] pub fn to_binary(self) -> OwnedBinary { - let raw_binary = unsafe { term_to_binary(self.env.as_c_arg(), self.as_c_arg()) }.unwrap(); + let mut binary = std::mem::MaybeUninit::uninit(); + let success = unsafe { + enif_term_to_binary(self.env.as_c_arg(), self.as_c_arg(), binary.as_mut_ptr()) + }; + assert_ne!(success, 0, "term_to_binary failed"); + let raw_binary = unsafe { binary.assume_init() }; unsafe { OwnedBinary::from_raw(raw_binary) } } diff --git a/rustler/src/types/atom.rs b/rustler/src/types/atom.rs index efbd27cb7..82fa00cb0 100644 --- a/rustler/src/types/atom.rs +++ b/rustler/src/types/atom.rs @@ -1,8 +1,15 @@ -use crate::sys::{ErlNifCharEncoding, ErlNifTerm}; -use crate::wrapper::atom; +#[cfg(feature = "nif_version_2_17")] +use crate::sys::enif_make_new_atom_len; +use crate::sys::{ + c_char, c_uint, enif_get_atom, enif_get_atom_length, enif_make_existing_atom_len, + ErlNifCharEncoding, ErlNifEnv, ErlNifTerm, +}; +#[cfg(not(feature = "nif_version_2_17"))] +use crate::sys::{enif_is_exception, enif_make_atom_len}; use crate::{Decoder, Encoder, Env, Error, NifResult, Term}; use std::fmt; use std::hash::{Hash, Hasher}; +use std::mem::MaybeUninit; // Atoms are a special case of a term. They can be stored and used on all envs regardless of where // it lives and when it is created. @@ -75,9 +82,7 @@ impl Atom { } fn from_encoded_bytes(env: Env, bytes: &[u8], encoding: ErlNifCharEncoding) -> NifResult { - unsafe { - atom::make_atom(env.as_c_arg(), bytes, encoding).map(|term| Self::from_nif_term(term)) - } + unsafe { make_atom(env.as_c_arg(), bytes, encoding).map(|term| Self::from_nif_term(term)) } } /// Return the atom whose text representation is Latin1 `bytes`, like `erlang:binary_to_existing_atom/1`, @@ -140,7 +145,7 @@ impl Atom { encoding: ErlNifCharEncoding, ) -> NifResult { unsafe { - atom::make_existing_atom(env.as_c_arg(), bytes, encoding) + make_existing_atom(env.as_c_arg(), bytes, encoding) .map(|term| Self::from_nif_term(term)) } } @@ -172,7 +177,7 @@ impl Atom { impl fmt::Debug for Atom { fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { - crate::wrapper::term::fmt(self.as_c_arg(), f) + crate::term::fmt(self.as_c_arg(), f) } } @@ -203,7 +208,7 @@ impl Term<'_> { /// /// Will return None if the term is not an atom. pub fn atom_to_string(&self) -> NifResult { - unsafe { atom::get_atom(self.get_env().as_c_arg(), self.as_c_arg()) } + unsafe { get_atom(self.get_env().as_c_arg(), self.as_c_arg()) } } } @@ -373,3 +378,117 @@ atoms! { /// The `step` atom used by `Elixir.Range` vor Elixir >= v1.12 step, } + +#[cfg(not(feature = "nif_version_2_17"))] +unsafe fn make_atom( + env: *mut ErlNifEnv, + name: &[u8], + _encoding: ErlNifCharEncoding, +) -> Result { + let res = enif_make_atom_len(env, name.as_ptr() as *const c_char, name.len()); + + if enif_is_exception(env, res) != 0 { + Err(Error::BadArg) + } else { + Ok(res) + } +} + +#[cfg(feature = "nif_version_2_17")] +unsafe fn make_atom( + env: *mut ErlNifEnv, + name: &[u8], + encoding: ErlNifCharEncoding, +) -> Result { + let mut atom_out = MaybeUninit::uninit(); + + if enif_make_new_atom_len( + env, + name.as_ptr() as *const c_char, + name.len(), + atom_out.as_mut_ptr(), + encoding, + ) != 0 + { + Ok(atom_out.assume_init()) + } else { + Err(Error::BadArg) + } +} + +unsafe fn make_existing_atom( + env: *mut ErlNifEnv, + name: &[u8], + encoding: ErlNifCharEncoding, +) -> Result { + let mut atom_out = MaybeUninit::uninit(); + + if enif_make_existing_atom_len( + env, + name.as_ptr() as *const c_char, + name.len(), + atom_out.as_mut_ptr(), + encoding, + ) != 0 + { + Ok(atom_out.assume_init()) + } else { + Err(Error::BadArg) + } +} + +#[cfg(feature = "nif_version_2_17")] +unsafe fn get_atom(env: *mut ErlNifEnv, term: ErlNifTerm) -> Result { + let mut len = 0; + let success = enif_get_atom_length(env, term, &mut len, ErlNifCharEncoding::ERL_NIF_UTF8); + if success == 0 { + return Err(Error::BadArg); + } + + let mut string = String::with_capacity(len as usize + 1); + let bytes = string.as_mut_vec(); + let nbytes = enif_get_atom( + env, + term, + bytes.as_mut_ptr() as *mut c_char, + len + 1, + ErlNifCharEncoding::ERL_NIF_UTF8, + ); + assert!(nbytes as c_uint == len + 1); + + bytes.set_len(len as usize); + + Ok(string) +} + +#[cfg(not(feature = "nif_version_2_17"))] +unsafe fn get_atom(env: *mut ErlNifEnv, term: ErlNifTerm) -> Result { + let mut len = 0; + let success = enif_get_atom_length(env, term, &mut len, ErlNifCharEncoding::ERL_NIF_LATIN1); + if success == 0 { + return Err(Error::BadArg); + } + + let mut bytes: Vec = Vec::with_capacity(len as usize + 1); + let nbytes = enif_get_atom( + env, + term, + bytes.as_mut_ptr() as *mut c_char, + len + 1, + ErlNifCharEncoding::ERL_NIF_LATIN1, + ); + assert!(nbytes as c_uint == len + 1); + + bytes.set_len(len as usize); + + let nonascii_count = bytes.iter().filter(|&&b| b >= 128).count(); + if nonascii_count == 0 { + Ok(String::from_utf8_unchecked(bytes)) + } else { + let mut out = String::with_capacity(bytes.len() + nonascii_count); + for b in bytes { + out.push(b as char); + } + Ok(out) + } +} diff --git a/rustler/src/types/binary.rs b/rustler/src/types/binary.rs index 408d2692d..07572fdf7 100644 --- a/rustler/src/types/binary.rs +++ b/rustler/src/types/binary.rs @@ -86,20 +86,45 @@ //! [`OwnedBinary`]: struct.OwnedBinary.html use crate::{ + alloc::array_layout, sys::{ - enif_inspect_binary, enif_inspect_iolist_as_binary, enif_make_binary, enif_make_sub_binary, - enif_release_binary, + enif_alloc_binary, enif_inspect_binary, enif_inspect_iolist_as_binary, enif_make_binary, + enif_make_new_binary, enif_make_sub_binary, enif_realloc_binary, enif_release_binary, + ErlNifBinary, }, - wrapper::binary::{alloc, new_binary, realloc, ErlNifBinary}, Decoder, Encoder, Env, Error, NifResult, Term, }; use std::{ + alloc::handle_alloc_error, borrow::{Borrow, BorrowMut}, hash::{Hash, Hasher}, mem::MaybeUninit, ops::{Deref, DerefMut}, }; +unsafe fn alloc(size: usize) -> ErlNifBinary { + let mut binary = MaybeUninit::uninit(); + let success = enif_alloc_binary(size, binary.as_mut_ptr()); + if success == 0 { + handle_alloc_error(array_layout::(size)); + } + binary.assume_init() +} + +unsafe fn realloc(binary: &mut ErlNifBinary, size: usize) -> bool { + let success = enif_realloc_binary(binary, size); + success != 0 +} + +unsafe fn new_binary(env: Env, size: usize) -> (*mut u8, Term) { + let mut term = MaybeUninit::uninit(); + let buf = enif_make_new_binary(env.as_c_arg(), size, term.as_mut_ptr()); + if buf.is_null() { + handle_alloc_error(array_layout::(size)); + } + (buf, Term::new(env, term.assume_init())) +} + /// An mutable smart-pointer to an Erlang binary. /// /// See [module-level doc](index.html) for more information. diff --git a/rustler/src/types/list.rs b/rustler/src/types/list.rs index 72fe741ea..de735dd10 100644 --- a/rustler/src/types/list.rs +++ b/rustler/src/types/list.rs @@ -2,9 +2,12 @@ //! //! Right now the only supported way to read lists are through the ListIterator. -use crate::sys::ErlNifTerm; -use crate::wrapper::list; +use crate::sys::{ + enif_get_list_cell, enif_get_list_length, enif_make_list_cell, enif_make_list_from_array, + enif_make_reverse_list, ErlNifEnv, ErlNifTerm, +}; use crate::{Decoder, Encoder, Env, Error, NifResult, Term}; +use std::mem::MaybeUninit; /// Enables iteration over the items in the list. /// @@ -60,7 +63,7 @@ impl<'a> Iterator for ListIterator<'a> { #[inline] fn next(&mut self) -> Option> { let env = self.term.get_env(); - let cell = unsafe { list::get_list_cell(env.as_c_arg(), self.term.as_c_arg()) }; + let cell = unsafe { get_list_cell(env.as_c_arg(), self.term.as_c_arg()) }; match cell { Some((head, tail)) => unsafe { @@ -124,7 +127,7 @@ where #[inline] fn encode<'b>(&self, env: Env<'b>) -> Term<'b> { let term_array: Vec = self.iter().map(|x| x.encode(env).as_c_arg()).collect(); - unsafe { Term::new(env, list::make_list(env.as_c_arg(), &term_array)) } + unsafe { Term::new(env, make_list(env.as_c_arg(), &term_array)) } } } @@ -135,7 +138,7 @@ where #[inline] fn encode<'b>(&self, env: Env<'b>) -> Term<'b> { let term_array: Vec = self.iter().map(|x| x.encode(env).as_c_arg()).collect(); - unsafe { Term::new(env, list::make_list(env.as_c_arg(), &term_array)) } + unsafe { Term::new(env, make_list(env.as_c_arg(), &term_array)) } } } @@ -167,8 +170,7 @@ impl<'a> Term<'a> { /// ``` #[inline] pub fn list_length(self) -> NifResult { - unsafe { list::get_list_length(self.get_env().as_c_arg(), self.as_c_arg()) } - .ok_or(Error::BadArg) + unsafe { get_list_length(self.get_env().as_c_arg(), self.as_c_arg()) }.ok_or(Error::BadArg) } /// Unpacks a single cell at the head of a list term, @@ -185,7 +187,7 @@ impl<'a> Term<'a> { pub fn list_get_cell(self) -> NifResult<(Term<'a>, Term<'a>)> { let env = self.get_env(); unsafe { - list::get_list_cell(env.as_c_arg(), self.as_c_arg()) + get_list_cell(env.as_c_arg(), self.as_c_arg()) .map(|(t1, t2)| (Term::new(env, t1), Term::new(env, t2))) .ok_or(Error::BadArg) } @@ -198,7 +200,7 @@ impl<'a> Term<'a> { pub fn list_reverse(self) -> NifResult> { let env = self.get_env(); unsafe { - list::make_reverse_list(env.as_c_arg(), self.as_c_arg()) + make_reverse_list(env.as_c_arg(), self.as_c_arg()) .map(|t| Term::new(env, t)) .ok_or(Error::BadArg) } @@ -209,8 +211,52 @@ impl<'a> Term<'a> { let env = self.get_env(); let head = head.encode(env); unsafe { - let term = list::make_list_cell(env.as_c_arg(), head.as_c_arg(), self.as_c_arg()); + let term = make_list_cell(env.as_c_arg(), head.as_c_arg(), self.as_c_arg()); Term::new(env, term) } } } + +#[inline] +unsafe fn get_list_cell(env: *mut ErlNifEnv, list: ErlNifTerm) -> Option<(ErlNifTerm, ErlNifTerm)> { + let mut head = MaybeUninit::uninit(); + let mut tail = MaybeUninit::uninit(); + let success = enif_get_list_cell(env, list, head.as_mut_ptr(), tail.as_mut_ptr()); + + if success != 1 { + return None; + } + Some((head.assume_init(), tail.assume_init())) +} + +#[inline] +unsafe fn get_list_length(env: *mut ErlNifEnv, list: ErlNifTerm) -> Option { + let mut len: u32 = 0; + let success = enif_get_list_length(env, list, &mut len); + + if success != 1 { + return None; + } + Some(len as usize) +} + +#[inline] +pub(crate) unsafe fn make_list(env: *mut ErlNifEnv, arr: &[ErlNifTerm]) -> ErlNifTerm { + enif_make_list_from_array(env, arr.as_ptr(), arr.len() as u32) +} + +#[inline] +unsafe fn make_list_cell(env: *mut ErlNifEnv, head: ErlNifTerm, tail: ErlNifTerm) -> ErlNifTerm { + enif_make_list_cell(env, head, tail) +} + +#[inline] +unsafe fn make_reverse_list(env: *mut ErlNifEnv, list: ErlNifTerm) -> Option { + let mut list_out = MaybeUninit::uninit(); + let success = enif_make_reverse_list(env, list, list_out.as_mut_ptr()); + + if success != 1 { + return None; + } + Some(list_out.assume_init()) +} diff --git a/rustler/src/types/local_pid.rs b/rustler/src/types/local_pid.rs index 3d5557e08..abe3ea3b3 100644 --- a/rustler/src/types/local_pid.rs +++ b/rustler/src/types/local_pid.rs @@ -1,5 +1,7 @@ -use crate::sys::{enif_compare_pids, enif_is_process_alive, enif_self}; -use crate::wrapper::{pid, ErlNifPid}; +use crate::sys::{ + enif_compare_pids, enif_get_local_pid, enif_is_process_alive, enif_make_pid, enif_self, + ErlNifPid, +}; use crate::{Decoder, Encoder, Env, Error, NifResult, Term}; use std::cmp::Ordering; use std::hash::{Hash, Hasher}; @@ -30,16 +32,23 @@ impl LocalPid { impl<'a> Decoder<'a> for LocalPid { #[inline] fn decode(term: Term<'a>) -> NifResult { - unsafe { pid::get_local_pid(term.get_env().as_c_arg(), term.as_c_arg()) } - .map(|pid| LocalPid { c: pid }) - .ok_or(Error::BadArg) + let mut pid = MaybeUninit::uninit(); + if unsafe { + enif_get_local_pid(term.get_env().as_c_arg(), term.as_c_arg(), pid.as_mut_ptr()) + } == 0 + { + return Err(Error::BadArg); + } + Ok(LocalPid { + c: unsafe { pid.assume_init() }, + }) } } impl Encoder for LocalPid { #[inline] fn encode<'a>(&self, env: Env<'a>) -> Term<'a> { - unsafe { Term::new(env, pid::make_pid(env.as_c_arg(), self.c)) } + unsafe { Term::new(env, enif_make_pid(env.as_c_arg(), self.c)) } } } diff --git a/rustler/src/types/map.rs b/rustler/src/types/map.rs index 3e7385250..aa31e4286 100644 --- a/rustler/src/types/map.rs +++ b/rustler/src/types/map.rs @@ -1,13 +1,19 @@ //! Utilities used to access and create Erlang maps. use super::atom; -use crate::wrapper::map; +use crate::sys::{ + enif_get_map_size, enif_get_map_value, enif_make_map_from_arrays, enif_make_map_put, + enif_make_map_remove, enif_make_map_update, enif_make_new_map, enif_map_iterator_create, + enif_map_iterator_destroy, enif_map_iterator_get_pair, enif_map_iterator_next, + enif_map_iterator_prev, ErlNifEnv, ErlNifMapIterator, ErlNifMapIteratorEntry, ErlNifTerm, +}; use crate::{Decoder, Encoder, Env, Error, NifResult, Term}; +use std::mem::MaybeUninit; use std::ops::RangeInclusive; #[inline] pub fn map_new(env: Env) -> Term { - unsafe { Term::new(env, map::map_new(env.as_c_arg())) } + unsafe { Term::new(env, enif_make_new_map(env.as_c_arg())) } } /// ## Map terms @@ -42,7 +48,7 @@ impl<'a> Term<'a> { let values: Vec<_> = values.iter().map(|v| v.encode(env).as_c_arg()).collect(); unsafe { - map::make_map_from_arrays(env.as_c_arg(), &keys, &values) + make_map_from_arrays(env.as_c_arg(), &keys, &values) .map_or_else(|| Err(Error::BadArg), |map| Ok(Term::new(env, map))) } } else { @@ -65,7 +71,7 @@ impl<'a> Term<'a> { let values: Vec<_> = values.iter().map(|v| v.as_c_arg()).collect(); unsafe { - map::make_map_from_arrays(env.as_c_arg(), &keys, &values) + make_map_from_arrays(env.as_c_arg(), &keys, &values) .map_or_else(|| Err(Error::BadArg), |map| Ok(Term::new(env, map))) } } else { @@ -94,7 +100,7 @@ impl<'a> Term<'a> { .unzip(); unsafe { - map::make_map_from_arrays(env.as_c_arg(), &keys, &values) + make_map_from_arrays(env.as_c_arg(), &keys, &values) .map_or_else(|| Err(Error::BadArg), |map| Ok(Term::new(env, map))) } } @@ -111,9 +117,8 @@ impl<'a> Term<'a> { #[inline] pub fn map_get(self, key: impl Encoder) -> NifResult> { let env = self.get_env(); - match unsafe { - map::get_map_value(env.as_c_arg(), self.as_c_arg(), key.encode(env).as_c_arg()) - } { + match unsafe { get_map_value(env.as_c_arg(), self.as_c_arg(), key.encode(env).as_c_arg()) } + { Some(value) => Ok(unsafe { Term::new(env, value) }), None => Err(Error::BadArg), } @@ -130,7 +135,7 @@ impl<'a> Term<'a> { #[inline] pub fn map_size(self) -> NifResult { let env = self.get_env(); - unsafe { map::get_map_size(env.as_c_arg(), self.as_c_arg()).ok_or(Error::BadArg) } + unsafe { get_map_size(env.as_c_arg(), self.as_c_arg()).ok_or(Error::BadArg) } } /// Makes a copy of the self map term and sets key to value. @@ -147,7 +152,7 @@ impl<'a> Term<'a> { let env = self.get_env(); match unsafe { - map::map_put( + map_put( env.as_c_arg(), self.as_c_arg(), key.encode(env).as_c_arg(), @@ -172,9 +177,7 @@ impl<'a> Term<'a> { pub fn map_remove(self, key: impl Encoder) -> NifResult> { let env = self.get_env(); - match unsafe { - map::map_remove(env.as_c_arg(), self.as_c_arg(), key.encode(env).as_c_arg()) - } { + match unsafe { map_remove(env.as_c_arg(), self.as_c_arg(), key.encode(env).as_c_arg()) } { Some(inner) => Ok(unsafe { Term::new(env, inner) }), None => Err(Error::BadArg), } @@ -189,7 +192,7 @@ impl<'a> Term<'a> { let env = self.get_env(); match unsafe { - map::map_update( + map_update( env.as_c_arg(), self.as_c_arg(), key.encode(env).as_c_arg(), @@ -204,8 +207,8 @@ impl<'a> Term<'a> { struct SimpleMapIterator<'a> { map: Term<'a>, - entry: map::MapIteratorEntry, - iter: Option, + entry: MapIteratorEntry, + iter: Option, last_key: Option>, done: bool, } @@ -220,7 +223,7 @@ impl<'a> SimpleMapIterator<'a> { match self.iter.as_mut() { None => { match unsafe { - map::map_iterator_create( + map_iterator_create( self.map.get_env().as_c_arg(), self.map.as_c_arg(), self.entry, @@ -245,14 +248,14 @@ impl<'a> SimpleMapIterator<'a> { let env = self.map.get_env(); unsafe { - match map::map_iterator_get_pair(env.as_c_arg(), iter) { + match map_iterator_get_pair(env.as_c_arg(), iter) { Some((key, value)) => { match self.entry { - map::MapIteratorEntry::First => { - map::map_iterator_next(env.as_c_arg(), iter); + MapIteratorEntry::First => { + map_iterator_next(env.as_c_arg(), iter); } - map::MapIteratorEntry::Last => { - map::map_iterator_prev(env.as_c_arg(), iter); + MapIteratorEntry::Last => { + map_iterator_prev(env.as_c_arg(), iter); } } let key = Term::new(env, key); @@ -272,7 +275,7 @@ impl Drop for SimpleMapIterator<'_> { fn drop(&mut self) { if let Some(iter) = self.iter.as_mut() { unsafe { - map::map_iterator_destroy(self.map.get_env().as_c_arg(), iter); + map_iterator_destroy(self.map.get_env().as_c_arg(), iter); } } } @@ -289,14 +292,14 @@ impl<'a> MapIterator<'a> { Some(MapIterator { forward: SimpleMapIterator { map, - entry: map::MapIteratorEntry::First, + entry: MapIteratorEntry::First, iter: None, last_key: None, done: false, }, reverse: SimpleMapIterator { map, - entry: map::MapIteratorEntry::Last, + entry: MapIteratorEntry::Last, iter: None, last_key: None, done: false, @@ -369,3 +372,143 @@ where Ok(first..=last) } } + +#[derive(Clone, Copy, Debug)] +enum MapIteratorEntry { + First, + Last, +} + +unsafe fn get_map_value( + env: *mut ErlNifEnv, + map: ErlNifTerm, + key: ErlNifTerm, +) -> Option { + let mut result = MaybeUninit::uninit(); + let success = enif_get_map_value(env, map, key, result.as_mut_ptr()); + + if success != 1 { + return None; + } + Some(result.assume_init()) +} + +unsafe fn get_map_size(env: *mut ErlNifEnv, map: ErlNifTerm) -> Option { + let mut size = MaybeUninit::uninit(); + let success = enif_get_map_size(env, map, size.as_mut_ptr()); + + if success != 1 { + return None; + } + Some(size.assume_init()) +} + +unsafe fn map_put( + env: *mut ErlNifEnv, + map: ErlNifTerm, + key: ErlNifTerm, + value: ErlNifTerm, +) -> Option { + let mut result = MaybeUninit::uninit(); + let success = enif_make_map_put(env, map, key, value, result.as_mut_ptr()); + + if success != 1 { + return None; + } + Some(result.assume_init()) +} + +unsafe fn map_remove(env: *mut ErlNifEnv, map: ErlNifTerm, key: ErlNifTerm) -> Option { + let mut result = MaybeUninit::uninit(); + let success = enif_make_map_remove(env, map, key, result.as_mut_ptr()); + + if success != 1 { + return None; + } + Some(result.assume_init()) +} + +unsafe fn map_update( + env: *mut ErlNifEnv, + map: ErlNifTerm, + key: ErlNifTerm, + new_value: ErlNifTerm, +) -> Option { + let mut result = MaybeUninit::uninit(); + let success = enif_make_map_update(env, map, key, new_value, result.as_mut_ptr()); + + if success != 1 { + return None; + } + Some(result.assume_init()) +} + +unsafe fn map_iterator_create( + env: *mut ErlNifEnv, + map: ErlNifTerm, + entry: MapIteratorEntry, +) -> Option { + let mut iter = MaybeUninit::uninit(); + let success = enif_map_iterator_create( + env, + map, + iter.as_mut_ptr(), + match entry { + MapIteratorEntry::First => ErlNifMapIteratorEntry::ERL_NIF_MAP_ITERATOR_HEAD, + MapIteratorEntry::Last => ErlNifMapIteratorEntry::ERL_NIF_MAP_ITERATOR_TAIL, + }, + ); + if success == 0 { + None + } else { + Some(iter.assume_init()) + } +} + +unsafe fn map_iterator_destroy(env: *mut ErlNifEnv, iter: &mut ErlNifMapIterator) { + enif_map_iterator_destroy(env, iter); +} + +unsafe fn map_iterator_get_pair( + env: *mut ErlNifEnv, + iter: &mut ErlNifMapIterator, +) -> Option<(ErlNifTerm, ErlNifTerm)> { + let mut key = MaybeUninit::uninit(); + let mut value = MaybeUninit::uninit(); + if enif_map_iterator_get_pair(env, iter, key.as_mut_ptr(), value.as_mut_ptr()) == 0 { + None + } else { + Some((key.assume_init(), value.assume_init())) + } +} + +#[inline] +unsafe fn map_iterator_next(env: *mut ErlNifEnv, iter: &mut ErlNifMapIterator) { + enif_map_iterator_next(env, iter); +} + +#[inline] +unsafe fn map_iterator_prev(env: *mut ErlNifEnv, iter: &mut ErlNifMapIterator) { + enif_map_iterator_prev(env, iter); +} + +#[inline] +unsafe fn make_map_from_arrays( + env: *mut ErlNifEnv, + keys: &[ErlNifTerm], + values: &[ErlNifTerm], +) -> Option { + let mut map = MaybeUninit::uninit(); + if enif_make_map_from_arrays( + env, + keys.as_ptr(), + values.as_ptr(), + keys.len(), + map.as_mut_ptr(), + ) == 0 + { + return None; + } + + Some(map.assume_init()) +} diff --git a/rustler/src/types/tuple.rs b/rustler/src/types/tuple.rs index 0c6e69dfb..e8804eec4 100644 --- a/rustler/src/types/tuple.rs +++ b/rustler/src/types/tuple.rs @@ -1,6 +1,25 @@ -use crate::sys::ErlNifTerm; -use crate::wrapper::tuple; +use crate::sys::{c_int, enif_get_tuple, enif_make_tuple_from_array, ErlNifEnv, ErlNifTerm}; use crate::{Decoder, Encoder, Env, Error, NifResult, Term}; +use std::mem::MaybeUninit; + +unsafe fn get_tuple_raw<'a>( + env: *mut ErlNifEnv, + term: ErlNifTerm, +) -> Result<&'a [ErlNifTerm], Error> { + let mut arity: c_int = 0; + let mut array_ptr = MaybeUninit::uninit(); + let success = enif_get_tuple(env, term, &mut arity, array_ptr.as_mut_ptr()); + if success != 1 { + return Err(Error::BadArg); + } + let term_array = ::std::slice::from_raw_parts(array_ptr.assume_init(), arity as usize); + Ok(term_array) +} + +#[inline] +unsafe fn make_tuple_raw(env: *mut ErlNifEnv, terms: &[ErlNifTerm]) -> ErlNifTerm { + enif_make_tuple_from_array(env, terms.as_ptr(), terms.len() as u32) +} /// Convert an Erlang tuple to a Rust vector. (To convert to a Rust tuple, use `term.decode()` /// instead.) @@ -10,7 +29,7 @@ use crate::{Decoder, Encoder, Env, Error, NifResult, Term}; pub fn get_tuple(term: Term) -> Result, Error> { let env = term.get_env(); unsafe { - match tuple::get_tuple(env.as_c_arg(), term.as_c_arg()) { + match get_tuple_raw(env.as_c_arg(), term.as_c_arg()) { Ok(terms) => Ok(terms .iter() .map(|x| Term::new(env, *x)) @@ -24,7 +43,7 @@ pub fn get_tuple(term: Term) -> Result, Error> { /// use `Encoder` instead.) pub fn make_tuple<'a>(env: Env<'a>, terms: &[Term]) -> Term<'a> { let c_terms: Vec = terms.iter().map(|term| term.as_c_arg()).collect(); - unsafe { Term::new(env, tuple::make_tuple(env.as_c_arg(), &c_terms)) } + unsafe { Term::new(env, make_tuple_raw(env.as_c_arg(), &c_terms)) } } /// Helper macro to emit tuple-like syntax. Wraps its arguments in parentheses, and adds a comma if @@ -52,7 +71,7 @@ macro_rules! impl_nifencoder_nifdecoder_for_tuple { fn encode<'a>(&self, env: Env<'a>) -> Term<'a> { let arr = [ $( Encoder::encode(&self.$index, env).as_c_arg() ),* ]; unsafe { - Term::new(env, tuple::make_tuple(env.as_c_arg(), &arr)) + Term::new(env, make_tuple_raw(env.as_c_arg(), &arr)) } } } @@ -62,7 +81,7 @@ macro_rules! impl_nifencoder_nifdecoder_for_tuple { { fn decode(term: Term<'a>) -> NifResult { - match unsafe { tuple::get_tuple(term.get_env().as_c_arg(), term.as_c_arg()) } { + match unsafe { get_tuple_raw(term.get_env().as_c_arg(), term.as_c_arg()) } { Ok(elements) if elements.len() == count!( $( $index ),* ) => Ok(tuple!( $( (<$tyvar as Decoder>::decode( diff --git a/rustler/src/wrapper.rs b/rustler/src/wrapper.rs deleted file mode 100644 index c8ed46a21..000000000 --- a/rustler/src/wrapper.rs +++ /dev/null @@ -1,47 +0,0 @@ -//! Contains basic wrappers for the Erlang NIF API. Should not be used directly. -//! -//! This module should perform validation and make them (reasonably) safe and easy to -//! use from Rust. This module should try to be as non-opinionated as possible, and -//! should try to stick as close as possible to the original C API. -//! -//! Making the APIs nice to use from Rust should be done in the root `rustler` crate. -#![allow(clippy::upper_case_acronyms)] - -pub mod atom; -pub mod binary; -pub mod check; -pub mod env; -pub mod list; -pub mod map; -pub mod pid; -pub mod term; -pub mod tuple; - -pub use crate::sys::{ - enif_clear_env, enif_free_env, enif_get_local_pid, enif_make_pid, enif_map_iterator_create, - enif_map_iterator_destroy, enif_map_iterator_get_pair, enif_map_iterator_next, enif_self, - ErlNifMapIterator, ErlNifMapIteratorEntry, ErlNifPid, ERL_NIF_THR_DIRTY_CPU_SCHEDULER, - ERL_NIF_THR_DIRTY_IO_SCHEDULER, ERL_NIF_THR_NORMAL_SCHEDULER, ERL_NIF_THR_UNDEFINED, -}; - -pub use crate::sys::{c_char, c_double, c_int, c_uchar, c_uint, c_void}; -pub type size_t = usize; - -pub type NIF_RESOURCE_TYPE = *const crate::sys::ErlNifResourceType; - -pub fn get_nif_resource_type_init_size() -> usize { - std::mem::size_of::() -} - -pub use crate::sys::NIF_MAJOR_VERSION; -pub use crate::sys::NIF_MINOR_VERSION; - -pub use crate::sys::ErlNifBinaryToTerm as NIF_BINARY_TO_TERM_OPTS; -pub use crate::sys::ERL_NIF_BIN2TERM_SAFE; - -#[repr(C)] -pub enum ErlNifTaskFlags { - ERL_NIF_NORMAL_JOB = 0, - ERL_NIF_DIRTY_JOB_CPU_BOUND = 1, - ERL_NIF_DIRTY_JOB_IO_BOUND = 2, -} diff --git a/rustler/src/wrapper/atom.rs b/rustler/src/wrapper/atom.rs deleted file mode 100644 index 70f5667b1..000000000 --- a/rustler/src/wrapper/atom.rs +++ /dev/null @@ -1,149 +0,0 @@ -use std::mem::MaybeUninit; - -#[cfg(feature = "nif_version_2_17")] -use crate::sys::enif_make_new_atom_len; -use crate::sys::{ - c_char, c_uint, enif_get_atom, enif_get_atom_length, enif_make_existing_atom_len, - ErlNifCharEncoding, ErlNifEnv, ErlNifTerm, -}; -#[cfg(not(feature = "nif_version_2_17"))] -use crate::sys::{enif_is_exception, enif_make_atom_len}; -use crate::Error; - -#[cfg(not(feature = "nif_version_2_17"))] -pub unsafe fn make_atom( - env: *mut ErlNifEnv, - name: &[u8], - _encoding: ErlNifCharEncoding, -) -> Result { - let res = enif_make_atom_len(env, name.as_ptr() as *const c_char, name.len()); - - if enif_is_exception(env, res) != 0 { - Err(Error::BadArg) - } else { - Ok(res) - } -} - -#[cfg(feature = "nif_version_2_17")] -pub unsafe fn make_atom( - env: *mut ErlNifEnv, - name: &[u8], - encoding: ErlNifCharEncoding, -) -> Result { - let mut atom_out = MaybeUninit::uninit(); - - // Create a new atom with the requested encoding. - // Returns 0 if creation fails (e.g. invalid text/encoding). - if enif_make_new_atom_len( - env, - name.as_ptr() as *const c_char, - name.len(), - atom_out.as_mut_ptr(), - encoding, - ) != 0 - { - Ok(atom_out.assume_init()) - } else { - Err(Error::BadArg) - } -} - -pub unsafe fn make_existing_atom( - env: *mut ErlNifEnv, - name: &[u8], - encoding: ErlNifCharEncoding, -) -> Result { - let mut atom_out = MaybeUninit::uninit(); - - if enif_make_existing_atom_len( - env, - name.as_ptr() as *const c_char, - name.len(), - atom_out.as_mut_ptr(), - encoding, - ) != 0 - { - Ok(atom_out.assume_init()) - } else { - Err(Error::BadArg) - } -} - -/// Get the contents of this atom as a string. -/// -/// If you only need to test for equality, comparing the terms directly -/// is much faster. -/// -/// # Errors -/// -/// `Error::BadArg` if `term` is not an atom. -/// -#[cfg(feature = "nif_version_2_17")] -pub unsafe fn get_atom(env: *mut ErlNifEnv, term: ErlNifTerm) -> Result { - // Determine the length of the atom, in bytes. - let mut len = 0; - let success = enif_get_atom_length(env, term, &mut len, ErlNifCharEncoding::ERL_NIF_UTF8); - if success == 0 { - return Err(Error::BadArg); - } - - // Get the bytes from the atom into a buffer. - // enif_get_atom() writes a null terminated string, - // so add 1 to the atom's length to make room for it. - let mut string = String::with_capacity(len as usize + 1); - let bytes = string.as_mut_vec(); - let nbytes = enif_get_atom( - env, - term, - bytes.as_mut_ptr() as *mut c_char, - len + 1, - ErlNifCharEncoding::ERL_NIF_UTF8, - ); - assert!(nbytes as c_uint == len + 1); - - // This relies on Erlang guaranteeing valid UTF-8 for ERL_NIF_UTF8 reads. - bytes.set_len(len as usize); // drop the null byte - - Ok(string) -} - -#[cfg(not(feature = "nif_version_2_17"))] -pub unsafe fn get_atom(env: *mut ErlNifEnv, term: ErlNifTerm) -> Result { - // Determine the length of the atom, in bytes. - let mut len = 0; - let success = enif_get_atom_length(env, term, &mut len, ErlNifCharEncoding::ERL_NIF_LATIN1); - if success == 0 { - return Err(Error::BadArg); - } - - // Get the bytes from the atom into a buffer. - // enif_get_atom() writes a null terminated string, - // so add 1 to the atom's length to make room for it. - let mut bytes: Vec = Vec::with_capacity(len as usize + 1); - let nbytes = enif_get_atom( - env, - term, - bytes.as_mut_ptr() as *mut c_char, - len + 1, - ErlNifCharEncoding::ERL_NIF_LATIN1, - ); - assert!(nbytes as c_uint == len + 1); - - // This is safe unless the VM is lying to us. - bytes.set_len(len as usize); // drop the null byte - - // Convert from Latin-1 bytes to a String. - let nonascii_count = bytes.iter().filter(|&&b| b >= 128).count(); - if nonascii_count == 0 { - // The string is ASCII, so it is safe to convert without copying. - Ok(String::from_utf8_unchecked(bytes)) - } else { - // Transcode from Latin-1 to UTF-8. - let mut out = String::with_capacity(bytes.len() + nonascii_count); - for b in bytes { - out.push(b as char); - } - Ok(out) - } -} diff --git a/rustler/src/wrapper/binary.rs b/rustler/src/wrapper/binary.rs deleted file mode 100644 index ac9ee7cfe..000000000 --- a/rustler/src/wrapper/binary.rs +++ /dev/null @@ -1,32 +0,0 @@ -pub(crate) use crate::sys::ErlNifBinary; -use crate::sys::{enif_alloc_binary, enif_make_new_binary, enif_realloc_binary}; -use crate::{Env, Term}; -use std::alloc::handle_alloc_error; -use std::mem::MaybeUninit; - -use crate::alloc::array_layout; - -pub use crate::sys::enif_make_sub_binary as make_subbinary; - -pub unsafe fn alloc(size: usize) -> ErlNifBinary { - let mut binary = MaybeUninit::uninit(); - let success = enif_alloc_binary(size, binary.as_mut_ptr()); - if success == 0 { - handle_alloc_error(array_layout::(size)); - } - binary.assume_init() -} - -pub unsafe fn realloc(binary: &mut ErlNifBinary, size: usize) -> bool { - let success = enif_realloc_binary(binary, size); - success != 0 -} - -pub unsafe fn new_binary(env: Env, size: usize) -> (*mut u8, Term) { - let mut term = MaybeUninit::uninit(); - let buf = enif_make_new_binary(env.as_c_arg(), size, term.as_mut_ptr()); - if buf.is_null() { - handle_alloc_error(array_layout::(size)); - } - (buf, Term::new(env, term.assume_init())) -} diff --git a/rustler/src/wrapper/check.rs b/rustler/src/wrapper/check.rs deleted file mode 100644 index efc788fb7..000000000 --- a/rustler/src/wrapper/check.rs +++ /dev/null @@ -1,21 +0,0 @@ -use crate::sys::{ErlNifEnv, ErlNifTerm}; - -macro_rules! impl_check_fun { - ($name:ident, $inner:ident) => { - pub unsafe fn $name(env: *mut ErlNifEnv, term: ErlNifTerm) -> bool { - crate::sys::$inner(env, term) == 1 - } - }; -} - -impl_check_fun!(is_atom, enif_is_atom); -impl_check_fun!(is_binary, enif_is_binary); -impl_check_fun!(is_empty_list, enif_is_empty_list); -impl_check_fun!(is_fun, enif_is_fun); -impl_check_fun!(is_list, enif_is_list); -impl_check_fun!(is_map, enif_is_map); -impl_check_fun!(is_number, enif_is_number); -impl_check_fun!(is_pid, enif_is_pid); -impl_check_fun!(is_port, enif_is_port); -impl_check_fun!(is_ref, enif_is_ref); -impl_check_fun!(is_tuple, enif_is_tuple); diff --git a/rustler/src/wrapper/env.rs b/rustler/src/wrapper/env.rs deleted file mode 100644 index 5658a345e..000000000 --- a/rustler/src/wrapper/env.rs +++ /dev/null @@ -1,33 +0,0 @@ -use crate::sys::{ - enif_binary_to_term, enif_term_to_binary, ErlNifBinary, ErlNifEnv, ErlNifTerm, - ERL_NIF_BIN2TERM_SAFE, -}; -use std::mem::MaybeUninit; - -pub unsafe fn binary_to_term( - env: *mut ErlNifEnv, - data: &[u8], - safe: bool, -) -> Option<(ErlNifTerm, usize)> { - let opts = if safe { ERL_NIF_BIN2TERM_SAFE } else { 0 }; - - let mut result = MaybeUninit::uninit(); - let read_count = enif_binary_to_term(env, data.as_ptr(), data.len(), result.as_mut_ptr(), opts); - - if read_count == 0 { - return None; - } - - Some((result.assume_init(), read_count)) -} - -pub unsafe fn term_to_binary(env: *mut ErlNifEnv, term: ErlNifTerm) -> Option { - let mut binary = MaybeUninit::uninit(); - let success = enif_term_to_binary(env, term, binary.as_mut_ptr()); - - if success == 0 { - return None; - } - - Some(binary.assume_init()) -} diff --git a/rustler/src/wrapper/list.rs b/rustler/src/wrapper/list.rs deleted file mode 100644 index 277a439a7..000000000 --- a/rustler/src/wrapper/list.rs +++ /dev/null @@ -1,56 +0,0 @@ -use crate::sys::{ - enif_get_list_cell, enif_get_list_length, enif_make_list_cell, enif_make_list_from_array, - enif_make_reverse_list, ErlNifEnv, ErlNifTerm, -}; -use std::mem::MaybeUninit; - -#[inline] -pub unsafe fn get_list_cell( - env: *mut ErlNifEnv, - list: ErlNifTerm, -) -> Option<(ErlNifTerm, ErlNifTerm)> { - let mut head = MaybeUninit::uninit(); - let mut tail = MaybeUninit::uninit(); - let success = enif_get_list_cell(env, list, head.as_mut_ptr(), tail.as_mut_ptr()); - - if success != 1 { - return None; - } - Some((head.assume_init(), tail.assume_init())) -} - -#[inline] -pub unsafe fn get_list_length(env: *mut ErlNifEnv, list: ErlNifTerm) -> Option { - let mut len: u32 = 0; - let success = enif_get_list_length(env, list, &mut len); - - if success != 1 { - return None; - } - Some(len as usize) -} - -#[inline] -pub unsafe fn make_list(env: *mut ErlNifEnv, arr: &[ErlNifTerm]) -> ErlNifTerm { - enif_make_list_from_array(env, arr.as_ptr(), arr.len() as u32) -} - -#[inline] -pub unsafe fn make_list_cell( - env: *mut ErlNifEnv, - head: ErlNifTerm, - tail: ErlNifTerm, -) -> ErlNifTerm { - enif_make_list_cell(env, head, tail) -} - -#[inline] -pub unsafe fn make_reverse_list(env: *mut ErlNifEnv, list: ErlNifTerm) -> Option { - let mut list_out = MaybeUninit::uninit(); - let success = enif_make_reverse_list(env, list, list_out.as_mut_ptr()); - - if success != 1 { - return None; - } - Some(list_out.assume_init()) -} diff --git a/rustler/src/wrapper/map.rs b/rustler/src/wrapper/map.rs deleted file mode 100644 index 74c71bc8f..000000000 --- a/rustler/src/wrapper/map.rs +++ /dev/null @@ -1,159 +0,0 @@ -use crate::sys::{ - enif_get_map_size, enif_get_map_value, enif_make_map_from_arrays, enif_make_map_put, - enif_make_map_remove, enif_make_map_update, enif_make_new_map, enif_map_iterator_prev, - ErlNifEnv, ErlNifMapIteratorEntry, ErlNifTerm, -}; -pub use crate::wrapper::ErlNifMapIterator; -use std::mem::MaybeUninit; - -use super::{ - enif_map_iterator_create, enif_map_iterator_destroy, enif_map_iterator_get_pair, - enif_map_iterator_next, -}; - -pub unsafe fn get_map_value( - env: *mut ErlNifEnv, - map: ErlNifTerm, - key: ErlNifTerm, -) -> Option { - let mut result = MaybeUninit::uninit(); - let success = enif_get_map_value(env, map, key, result.as_mut_ptr()); - - if success != 1 { - return None; - } - Some(result.assume_init()) -} - -pub unsafe fn get_map_size(env: *mut ErlNifEnv, map: ErlNifTerm) -> Option { - let mut size = MaybeUninit::uninit(); - let success = enif_get_map_size(env, map, size.as_mut_ptr()); - - if success != 1 { - return None; - } - Some(size.assume_init()) -} - -pub unsafe fn map_new(env: *mut ErlNifEnv) -> ErlNifTerm { - enif_make_new_map(env) -} - -pub unsafe fn map_put( - env: *mut ErlNifEnv, - map: ErlNifTerm, - key: ErlNifTerm, - value: ErlNifTerm, -) -> Option { - let mut result = MaybeUninit::uninit(); - let success = enif_make_map_put(env, map, key, value, result.as_mut_ptr()); - - if success != 1 { - return None; - } - Some(result.assume_init()) -} - -pub unsafe fn map_remove( - env: *mut ErlNifEnv, - map: ErlNifTerm, - key: ErlNifTerm, -) -> Option { - let mut result = MaybeUninit::uninit(); - let success = enif_make_map_remove(env, map, key, result.as_mut_ptr()); - - if success != 1 { - return None; - } - Some(result.assume_init()) -} - -pub unsafe fn map_update( - env: *mut ErlNifEnv, - map: ErlNifTerm, - key: ErlNifTerm, - new_value: ErlNifTerm, -) -> Option { - let mut result = MaybeUninit::uninit(); - let success = enif_make_map_update(env, map, key, new_value, result.as_mut_ptr()); - - if success != 1 { - return None; - } - Some(result.assume_init()) -} - -#[derive(Clone, Copy, Debug)] -pub enum MapIteratorEntry { - First, - Last, -} - -pub unsafe fn map_iterator_create( - env: *mut ErlNifEnv, - map: ErlNifTerm, - entry: MapIteratorEntry, -) -> Option { - let mut iter = MaybeUninit::uninit(); - let success = enif_map_iterator_create( - env, - map, - iter.as_mut_ptr(), - match entry { - MapIteratorEntry::First => ErlNifMapIteratorEntry::ERL_NIF_MAP_ITERATOR_HEAD, - MapIteratorEntry::Last => ErlNifMapIteratorEntry::ERL_NIF_MAP_ITERATOR_TAIL, - }, - ); - if success == 0 { - None - } else { - Some(iter.assume_init()) - } -} - -pub unsafe fn map_iterator_destroy(env: *mut ErlNifEnv, iter: &mut ErlNifMapIterator) { - enif_map_iterator_destroy(env, iter); -} - -pub unsafe fn map_iterator_get_pair( - env: *mut ErlNifEnv, - iter: &mut ErlNifMapIterator, -) -> Option<(ErlNifTerm, ErlNifTerm)> { - let mut key = MaybeUninit::uninit(); - let mut value = MaybeUninit::uninit(); - if enif_map_iterator_get_pair(env, iter, key.as_mut_ptr(), value.as_mut_ptr()) == 0 { - None - } else { - Some((key.assume_init(), value.assume_init())) - } -} - -#[inline] -pub unsafe fn map_iterator_next(env: *mut ErlNifEnv, iter: &mut ErlNifMapIterator) { - enif_map_iterator_next(env, iter); -} - -pub unsafe fn map_iterator_prev(env: *mut ErlNifEnv, iter: &mut ErlNifMapIterator) { - enif_map_iterator_prev(env, iter); -} - -#[inline] -pub unsafe fn make_map_from_arrays( - env: *mut ErlNifEnv, - keys: &[ErlNifTerm], - values: &[ErlNifTerm], -) -> Option { - let mut map = MaybeUninit::uninit(); - if enif_make_map_from_arrays( - env, - keys.as_ptr(), - values.as_ptr(), - keys.len(), - map.as_mut_ptr(), - ) == 0 - { - return None; - } - - Some(map.assume_init()) -} diff --git a/rustler/src/wrapper/pid.rs b/rustler/src/wrapper/pid.rs deleted file mode 100644 index 7c597ea92..000000000 --- a/rustler/src/wrapper/pid.rs +++ /dev/null @@ -1,15 +0,0 @@ -use std::mem::MaybeUninit; - -use crate::sys::{enif_get_local_pid, enif_make_pid, ErlNifEnv, ErlNifPid, ErlNifTerm}; - -pub unsafe fn get_local_pid(env: *mut ErlNifEnv, term: ErlNifTerm) -> Option { - let mut pid = MaybeUninit::uninit(); - if enif_get_local_pid(env, term, pid.as_mut_ptr()) == 0 { - return None; - } - Some(pid.assume_init()) -} - -pub unsafe fn make_pid(env: *mut ErlNifEnv, pid: ErlNifPid) -> ErlNifTerm { - enif_make_pid(env, pid) -} diff --git a/rustler/src/wrapper/term.rs b/rustler/src/wrapper/term.rs deleted file mode 100644 index e6af1edb7..000000000 --- a/rustler/src/wrapper/term.rs +++ /dev/null @@ -1,43 +0,0 @@ -use crate::sys::{c_char, enif_snprintf, ErlNifTerm}; -use std::fmt; - -pub fn fmt(term: ErlNifTerm, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { - const SIZE: usize = 1024; - let mut bytes: Vec = Vec::with_capacity(SIZE); - - let mut n = 0; - for _ in 0..10 { - let i = unsafe { - enif_snprintf!( - bytes.as_mut_ptr() as *mut c_char, - bytes.capacity(), - b"%T\x00" as *const u8 as *const c_char, - term - ) - }; - if i < 0 { - // Do not propagate an error, because string formatting is - // supposed to be infallible. - break; - } - - n = i as usize; - if n >= bytes.capacity() { - // Bizarrely, enif_snprintf consistently underestimates the - // amount of memory it will need to write long lists. To try to - // avoid going around the loop again, double the estimate. - bytes.reserve_exact(2 * n + 1); - - // Ensure that the `set_len` call below does not expose - // uninitialized bytes if we give up after 10 attempts. - n = 0; - } else { - break; - } - } - - unsafe { - bytes.set_len(n); - } - f.write_str(&String::from_utf8_lossy(&bytes)) -} diff --git a/rustler/src/wrapper/tuple.rs b/rustler/src/wrapper/tuple.rs deleted file mode 100644 index d1a177f2f..000000000 --- a/rustler/src/wrapper/tuple.rs +++ /dev/null @@ -1,21 +0,0 @@ -use crate::sys::{c_int, enif_get_tuple, enif_make_tuple_from_array, ErlNifEnv, ErlNifTerm}; -use crate::Error; -use std::mem::MaybeUninit; - -pub unsafe fn get_tuple<'a>( - env: *mut ErlNifEnv, - term: ErlNifTerm, -) -> Result<&'a [ErlNifTerm], Error> { - let mut arity: c_int = 0; - let mut array_ptr = MaybeUninit::uninit(); - let success = enif_get_tuple(env, term, &mut arity, array_ptr.as_mut_ptr()); - if success != 1 { - return Err(Error::BadArg); - } - let term_array = ::std::slice::from_raw_parts(array_ptr.assume_init(), arity as usize); - Ok(term_array) -} - -pub unsafe fn make_tuple(env: *mut ErlNifEnv, terms: &[ErlNifTerm]) -> ErlNifTerm { - enif_make_tuple_from_array(env, terms.as_ptr(), terms.len() as u32) -} diff --git a/rustler_codegen/src/init.rs b/rustler_codegen/src/init.rs index ca49dfe5f..0585e2214 100644 --- a/rustler_codegen/src/init.rs +++ b/rustler_codegen/src/init.rs @@ -60,44 +60,37 @@ impl From for proc_macro2::TokenStream { .map(rustler::Nif::get_def) .collect(); - let entry = rustler::codegen_runtime::ErlNifEntry { - major: rustler::codegen_runtime::NIF_MAJOR_VERSION, - minor: rustler::codegen_runtime::NIF_MINOR_VERSION, - name: concat!(#name, "\0").as_ptr() as *const rustler::codegen_runtime::c_char, - num_of_funcs: nif_funcs.len() as rustler::codegen_runtime::c_int, - funcs: nif_funcs.as_ptr(), - load: { - extern "C" fn nif_load( - env: *mut rustler::sys::ErlNifEnv, - _priv_data: *mut *mut rustler::codegen_runtime::c_void, - load_info: rustler::sys::ErlNifTerm - ) -> rustler::codegen_runtime::c_int { - unsafe { - let mut env = rustler::Env::new_init_env(&env, env); - let load_info = rustler::Term::new(env, load_info); - - if !rustler::codegen_runtime::ResourceRegistration::register_all_collected(env).is_ok() { - return 1; - } - - #load.map_or(0, |inner| { - rustler::codegen_runtime::handle_nif_init_call( - inner, env, load_info - ) - }) + let load = { + extern "C" fn nif_load( + env: *mut rustler::sys::ErlNifEnv, + _priv_data: *mut *mut rustler::codegen_runtime::c_void, + load_info: rustler::sys::ErlNifTerm + ) -> rustler::codegen_runtime::c_int { + unsafe { + let mut env = rustler::Env::new_init_env(&env, env); + let load_info = rustler::Term::new(env, load_info); + + if !rustler::codegen_runtime::ResourceRegistration::register_all_collected(env).is_ok() { + return 1; } + + #load.map_or(0, |inner| { + rustler::codegen_runtime::handle_nif_init_call( + inner, env, load_info + ) + }) } - Some(nif_load) - }, - reload: None, - upgrade: None, - unload: None, - vm_variant: b"beam.vanilla\0".as_ptr() as *const rustler::codegen_runtime::c_char, - options: 0, - sizeof_ErlNifResourceTypeInit: rustler::codegen_runtime::get_nif_resource_type_init_size(), - min_erts: rustler::codegen_runtime::min_erts().as_ptr() as *const rustler::codegen_runtime::c_char, + }; + + nif_load }; + let entry = rustler::codegen_runtime::ErlNifEntry::new( + unsafe { ::core::ffi::CStr::from_bytes_with_nul_unchecked(concat!(#name, "\0").as_bytes()) }, + &nif_funcs, + load + ); + unsafe { // Leak nif_funcs std::mem::forget(nif_funcs);