Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 16 additions & 3 deletions UPGRADE.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,20 @@ This document is intended to simplify upgrading to newer versions by extending t

## 0.37 -> 0.38

The following deprecated codegen features have been dropped:
1. The following deprecated codegen features have been dropped:
- `resource!` macro, please use `resource_impl`
- Explicit NIF function listing in `init!`, please remove the list

- `resource!` macro, please use `resource_impl`
- Explicit NIF function listing in `init!`, please remove the list
2. The following methods are now infallible, returning the result directly
instead of an `Option` or `Result` that would require matching or handling:

- `OwnedBinary::new`
- `OwnedBinary::from_unowned`
- `Binary::to_owned`

Instead, allocation errors will now call `std::alloc::handle_alloc_error`.
This is the same behaviour that the Rust standard library applies (e.g. for
`Vec`) and in line with other allocating functions in `rustler`.

## 0.34 -> 0.35

Expand Down Expand Up @@ -60,9 +70,11 @@ documentation on how to convert from the old to the new set of macros.
2. `Env::send` and `OwnedEnv::send_and_clear` will now return a `Result`.
Updating will thus introduce warnings about unused `Result`s. To remove the
warnings without changing behaviour, the `Result`s can be "used" as

```rust
let _ = env.send(...)
```

Neither the `Ok` nor the `Err` case carry additional information so far. An
error is returned if either the receiving or the sending process is dead.
See also
Expand All @@ -81,6 +93,7 @@ documentation on how to convert from the old to the new set of macros.
use a compiled NIF with an older version than OTP22, disable the default
features and expliictly use the `nif_version_2_14` feature in the library's
`Cargo.toml`:

```toml
rustler = { version = "0.30", default-features = false, features = ["derive", "nif_version_2_14"] }
```
Expand Down
8 changes: 8 additions & 0 deletions rustler/src/alloc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,14 @@ const MAX_ALIGN: usize = 8;
#[global_allocator]
static ALLOCATOR: EnifAllocator = EnifAllocator;

/// Safe array layout with Erlang's allocator alignment.
#[inline]
pub(crate) fn array_layout<T>(n: usize) -> Layout {
let element_size = std::mem::size_of::<T>();
let size = element_size.saturating_mul(n).clamp(1, isize::MAX as usize);
unsafe { Layout::from_size_align_unchecked(size, MAX_ALIGN) }
}

/// Allocator implementation that forwards all allocation calls to Erlang's allocator. Allows the
/// memory usage to be tracked by the BEAM.
pub struct EnifAllocator;
Expand Down
7 changes: 2 additions & 5 deletions rustler/src/serde/ser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -143,10 +143,7 @@ impl<'a> ser::Serializer for Serializer<'a> {
fn serialize_str(self, v: &str) -> Result<Self::Ok, Self::Error> {
let env = self.env;
let str_len = v.len();
let mut bin = match OwnedBinary::new(str_len) {
Some(bin) => bin,
None => panic!("binary term allocation fail"),
};
let mut bin = OwnedBinary::new(str_len);
bin.as_mut_slice()
.write_all(v.as_bytes())
.expect("memory copy of string failed");
Expand All @@ -155,7 +152,7 @@ impl<'a> ser::Serializer for Serializer<'a> {

#[inline]
fn serialize_bytes(self, v: &[u8]) -> Result<Self::Ok, Self::Error> {
let mut binary = OwnedBinary::new(v.len()).unwrap();
let mut binary = OwnedBinary::new(v.len());
binary
.as_mut_slice()
.write_all(v)
Expand Down
53 changes: 18 additions & 35 deletions rustler/src/types/binary.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
//! ```no_run
//! # use rustler::OwnedBinary;
//! {
//! let mut bin = OwnedBinary::new(5).expect("allocation failed");
//! let mut bin = OwnedBinary::new(5);
//! bin.as_mut_slice().copy_from_slice("hello".as_bytes());
//! } // <- `bin` is dropped here
//! ```
Expand All @@ -37,10 +37,10 @@
//! where each element is exclusive-or'ed with a constant:
//!
//! ```no_run
//! # use rustler::{Env, OwnedBinary, Binary, NifResult, Error};
//! # use rustler::{Env, OwnedBinary, Binary, NifResult};
//! #[rustler::nif]
//! fn xor_example<'a>(env: Env<'a>, bin: Binary<'a>) -> NifResult<Binary<'a>> {
//! let mut owned: OwnedBinary = bin.to_owned().ok_or(Error::Term(Box::new("no mem")))?;
//! let mut owned: OwnedBinary = bin.to_owned();
//! for byte in owned.as_mut_slice() {
//! *byte ^= 0xAA;
//! }
Expand All @@ -63,10 +63,10 @@
//! # if *elem == 0 { *elem = 1 } else { panic!("Not a zero!") }
//! # }
//! # }
//! # use rustler::{Env, OwnedBinary, Binary, NifResult, Error};
//! # use rustler::{Env, OwnedBinary, Binary, NifResult};
//! #[rustler::nif]
//! fn wrapper_for_some_<'a>(env: Env<'a>) -> NifResult<Binary<'a>> {
//! let mut owned = OwnedBinary::new(100).ok_or(Error::Term(Box::new("no mem")))?;
//! let mut owned = OwnedBinary::new(100);
//! for byte in owned.as_mut_slice() {
//! *byte = 0;
//! }
Expand Down Expand Up @@ -96,7 +96,6 @@ use crate::{
use std::{
borrow::{Borrow, BorrowMut},
hash::{Hash, Hasher},
io::Write,
mem::MaybeUninit,
ops::{Deref, DerefMut},
};
Expand All @@ -116,28 +115,20 @@ impl OwnedBinary {
/// Memory is not initialized. If uninitialized memory is undesirable, set it
/// manually.
///
/// # Errors
///
/// If allocation fails, `None` is returned.
pub fn new(size: usize) -> Option<OwnedBinary> {
unsafe { alloc(size) }.map(OwnedBinary)
pub fn new(size: usize) -> OwnedBinary {
OwnedBinary(unsafe { alloc(size) })
}

/// Copies `src`'s data into a new `OwnedBinary`.
///
/// # Errors
///
/// If allocation fails, `None` is returned.
pub fn from_unowned(src: &Binary) -> Option<OwnedBinary> {
OwnedBinary::new(src.len()).map(|mut b| {
b.as_mut_slice().copy_from_slice(src);
b
})
pub fn from_unowned(src: &Binary) -> OwnedBinary {
let mut b = OwnedBinary::new(src.len());
b.as_mut_slice().copy_from_slice(src);
b
}

/// Copies 'data''s data into a new `OwnedBinary`.
pub fn from_slice(data: &[u8]) -> Self {
let mut bin = OwnedBinary::new(data.len()).expect("allocation failed");
let mut bin = OwnedBinary::new(data.len());
bin.as_mut_slice().copy_from_slice(data);
bin
}
Expand All @@ -163,15 +154,9 @@ impl OwnedBinary {
/// uninitialized memory is undesirable, set it manually.
pub fn realloc_or_copy(&mut self, size: usize) {
if !self.realloc(size) {
let mut new = OwnedBinary::new(size).unwrap();
if let Ok(num_written) = new.as_mut_slice().write(self.as_slice()) {
if !(num_written == self.len() || num_written == new.len()) {
panic!("Could not copy binary");
}
::std::mem::swap(&mut self.0, &mut new.0);
} else {
panic!("Could not copy binary");
}
let mut new = OwnedBinary::new(size);
new.as_mut_slice().copy_from_slice(self);
::std::mem::swap(&mut self.0, &mut new.0);
}
}

Expand Down Expand Up @@ -247,7 +232,8 @@ impl FromIterator<u8> for OwnedBinary {
fn from_iter<T: IntoIterator<Item = u8>>(iter: T) -> Self {
let mut iter = iter.into_iter();
let (lower, upper) = iter.size_hint();
let mut bin = OwnedBinary::new(upper.unwrap_or(lower)).expect("Allocation failed");
let size = upper.unwrap_or(lower);
let mut bin = OwnedBinary::new(size);
let mut i = 0;
loop {
match iter.next() {
Expand Down Expand Up @@ -305,12 +291,9 @@ impl<'a> Binary<'a> {

/// Copies `self`'s data into a new `OwnedBinary`.
///
/// # Errors
///
/// If allocation fails, an error will be returned.
#[allow(clippy::wrong_self_convention)]
#[inline]
pub fn to_owned(&self) -> Option<OwnedBinary> {
pub fn to_owned(&self) -> OwnedBinary {
OwnedBinary::from_unowned(self)
}

Expand Down
22 changes: 11 additions & 11 deletions rustler/src/wrapper/binary.rs
Original file line number Diff line number Diff line change
@@ -1,32 +1,32 @@
pub(crate) use crate::sys::ErlNifBinary;
use crate::{
sys::{enif_alloc_binary, enif_make_new_binary, enif_realloc_binary},
wrapper::size_t,
Env, Term,
};
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: size_t) -> Option<ErlNifBinary> {
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 {
return None;
handle_alloc_error(array_layout::<u8>(size));
}
Some(binary.assume_init())
binary.assume_init()
}

pub unsafe fn realloc(binary: &mut ErlNifBinary, size: size_t) -> bool {
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: size_t) -> (*mut u8, Term) {
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() {
panic!("enif_make_new_binary: allocation failed");
handle_alloc_error(array_layout::<u8>(size));
}
(buf, Term::new(env, term.assume_init()))
}
10 changes: 5 additions & 5 deletions rustler_tests/native/rustler_test/src/test_binary.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,14 @@ pub fn parse_integer(string: &str) -> NifResult<i64> {

#[rustler::nif]
pub fn binary_new(env: Env) -> Binary {
let mut binary = OwnedBinary::new(4).unwrap();
let mut binary = OwnedBinary::new(4);
binary.as_mut_slice().write_all(&[1, 2, 3, 4]).unwrap();
binary.release(env)
}

#[rustler::nif]
pub fn owned_binary_new() -> OwnedBinary {
let mut binary = OwnedBinary::new(4).unwrap();
let mut binary = OwnedBinary::new(4);
binary.as_mut_slice().write_all(&[1, 2, 3, 4]).unwrap();
binary
}
Expand Down Expand Up @@ -53,14 +53,14 @@ pub fn unowned_to_owned<'a>(env: Env<'a>, binary: Binary<'a>) -> NifResult<Binar
// Do nothing and suppress panic message. From https://stackoverflow.com/a/35559417
panic::set_hook(Box::new(|_info| {}));

let mut copied = binary.to_owned().unwrap();
let mut copied = binary.to_owned();
copied.as_mut_slice()[0] = 1;
Ok(copied.release(env))
}

#[rustler::nif]
pub fn realloc_shrink(env: Env) -> Binary {
let mut binary = OwnedBinary::new(8).unwrap();
let mut binary = OwnedBinary::new(8);
binary
.as_mut_slice()
.write_all(&[1, 2, 3, 4, 5, 6, 7, 8])
Expand All @@ -73,7 +73,7 @@ pub fn realloc_shrink(env: Env) -> Binary {

#[rustler::nif]
pub fn realloc_grow(env: Env) -> Binary {
let mut binary = OwnedBinary::new(4).unwrap();
let mut binary = OwnedBinary::new(4);
binary.as_mut_slice().write_all(&[1, 2, 3, 4]).unwrap();
binary.realloc_or_copy(5);
binary.as_mut_slice()[4] = 5;
Expand Down
Loading