use crate::{
alloc::array_layout,
sys::{
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,
},
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::<u8>(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::<u8>(size));
}
(buf, Term::new(env, term.assume_init()))
}
pub struct OwnedBinary(ErlNifBinary);
impl OwnedBinary {
pub unsafe fn from_raw(inner: ErlNifBinary) -> OwnedBinary {
OwnedBinary(inner)
}
pub fn new(size: usize) -> OwnedBinary {
OwnedBinary(unsafe { alloc(size) })
}
pub fn from_unowned(src: &Binary) -> OwnedBinary {
let mut b = OwnedBinary::new(src.len());
b.as_mut_slice().copy_from_slice(src);
b
}
pub fn from_slice(data: &[u8]) -> Self {
let mut bin = OwnedBinary::new(data.len());
bin.as_mut_slice().copy_from_slice(data);
bin
}
#[must_use]
pub fn realloc(&mut self, size: usize) -> bool {
unsafe { realloc(&mut self.0, size) }
}
pub fn realloc_or_copy(&mut self, size: usize) {
if !self.realloc(size) {
let mut new = OwnedBinary::new(size);
new.as_mut_slice().copy_from_slice(self);
::std::mem::swap(&mut self.0, &mut new.0);
}
}
pub fn as_slice(&self) -> &[u8] {
unsafe { ::std::slice::from_raw_parts(self.0.data, self.0.size) }
}
pub fn as_mut_slice(&mut self) -> &mut [u8] {
unsafe { ::std::slice::from_raw_parts_mut(self.0.data, self.0.size) }
}
pub fn release(self, env: Env) -> Binary {
Binary::from_owned(self, env)
}
}
impl Borrow<[u8]> for OwnedBinary {
fn borrow(&self) -> &[u8] {
self.as_slice()
}
}
impl BorrowMut<[u8]> for OwnedBinary {
fn borrow_mut(&mut self) -> &mut [u8] {
self.as_mut_slice()
}
}
impl Deref for OwnedBinary {
type Target = [u8];
fn deref(&self) -> &[u8] {
self.as_slice()
}
}
impl DerefMut for OwnedBinary {
fn deref_mut(&mut self) -> &mut [u8] {
self.as_mut_slice()
}
}
impl Hash for OwnedBinary {
fn hash<H: Hasher>(&self, state: &mut H) {
self.as_slice().hash(state);
}
}
impl PartialEq for OwnedBinary {
fn eq(&self, other: &Self) -> bool {
self.as_slice() == other.as_slice()
}
}
impl Eq for OwnedBinary {}
impl PartialEq<Binary<'_>> for OwnedBinary {
fn eq(&self, other: &Binary) -> bool {
self.as_slice() == other.as_slice()
}
}
impl Drop for OwnedBinary {
fn drop(&mut self) {
unsafe { enif_release_binary(&mut self.0) };
}
}
unsafe impl Send for OwnedBinary {}
unsafe impl Sync for OwnedBinary {}
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 size = upper.unwrap_or(lower);
let mut bin = OwnedBinary::new(size);
let mut i = 0;
loop {
match iter.next() {
None => {
if bin.len() != i {
bin.realloc_or_copy(i);
}
return bin;
}
Some(x) => {
if bin.len() <= i {
bin.realloc_or_copy(i + i / 2 + 1);
}
bin.as_mut_slice()[i] = x;
i += 1;
}
}
}
}
}
impl<T: AsRef<[u8]>> From<T> for OwnedBinary {
fn from(data: T) -> Self {
Self::from_slice(data.as_ref())
}
}
#[derive(Copy, Clone)]
pub struct Binary<'a> {
buf: *const u8,
size: usize,
term: Term<'a>,
}
impl<'a> Binary<'a> {
#[inline]
pub fn from_owned(owned: OwnedBinary, env: Env<'a>) -> Self {
let mut owned = std::mem::ManuallyDrop::new(owned);
let term = unsafe { Term::new(env, enif_make_binary(env.as_c_arg(), &mut owned.0)) };
Binary {
buf: owned.0.data,
size: owned.0.size,
term,
}
}
#[allow(clippy::wrong_self_convention)]
#[inline]
pub fn to_owned(&self) -> OwnedBinary {
OwnedBinary::from_unowned(self)
}
#[inline]
pub fn from_term(term: Term<'a>) -> Result<Self, Error> {
let mut binary = MaybeUninit::uninit();
if unsafe {
enif_inspect_binary(
term.get_env().as_c_arg(),
term.as_c_arg(),
binary.as_mut_ptr(),
)
} == 0
{
return Err(Error::BadArg);
}
let binary = unsafe { binary.assume_init() };
Ok(Binary {
buf: binary.data,
size: binary.size,
term,
})
}
pub(crate) unsafe fn from_term_and_slice(term: Term<'a>, binary: &[u8]) -> Self {
Binary {
term,
buf: binary.as_ptr(),
size: binary.len(),
}
}
#[inline]
pub fn from_iolist(term: Term<'a>) -> Result<Self, Error> {
let env = term.get_env();
let mut binary = MaybeUninit::uninit();
if unsafe {
enif_inspect_iolist_as_binary(env.as_c_arg(), term.as_c_arg(), binary.as_mut_ptr())
} == 0
{
return Err(Error::BadArg);
}
let mut binary = unsafe { binary.assume_init() };
let term = unsafe { Term::new(env, enif_make_binary(env.as_c_arg(), &mut binary)) };
Ok(Binary {
buf: binary.data,
size: binary.size,
term,
})
}
#[allow(clippy::wrong_self_convention)]
#[inline]
pub fn to_term<'b>(&self, env: Env<'b>) -> Term<'b> {
self.term.in_env(env)
}
#[inline]
pub fn as_slice(&self) -> &'a [u8] {
unsafe { ::std::slice::from_raw_parts(self.buf, self.size) }
}
#[inline]
pub fn make_subbinary(&self, offset: usize, length: usize) -> NifResult<Binary<'a>> {
let min_len = length.checked_add(offset);
if min_len.ok_or(Error::BadArg)? > self.size {
return Err(Error::BadArg);
}
Ok(unsafe { self.make_subbinary_unchecked(offset, length) })
}
#[allow(unused_unsafe)]
#[inline]
pub unsafe fn make_subbinary_unchecked(&self, offset: usize, length: usize) -> Binary<'a> {
let raw_term = unsafe {
enif_make_sub_binary(
self.term.get_env().as_c_arg(),
self.term.as_c_arg(),
offset,
length,
)
};
let term = unsafe { Term::new(self.term.get_env(), raw_term) };
Binary {
buf: unsafe { self.buf.add(offset) },
size: length,
term,
}
}
}
impl Borrow<[u8]> for Binary<'_> {
fn borrow(&self) -> &[u8] {
self.as_slice()
}
}
impl Deref for Binary<'_> {
type Target = [u8];
fn deref(&self) -> &[u8] {
self.as_slice()
}
}
impl<'a> Decoder<'a> for Binary<'a> {
fn decode(term: Term<'a>) -> Result<Self, Error> {
Binary::from_term(term)
}
}
impl Encoder for Binary<'_> {
fn encode<'b>(&self, env: Env<'b>) -> Term<'b> {
self.to_term(env)
}
}
impl Hash for Binary<'_> {
fn hash<H: Hasher>(&self, state: &mut H) {
self.as_slice().hash(state);
}
}
impl PartialEq for Binary<'_> {
fn eq(&self, other: &Self) -> bool {
self.as_slice() == other.as_slice()
}
}
impl Eq for Binary<'_> {}
impl PartialEq<OwnedBinary> for Binary<'_> {
fn eq(&self, other: &OwnedBinary) -> bool {
self.as_slice() == other.as_slice()
}
}
impl<'a> Term<'a> {
pub fn into_binary(self) -> NifResult<Binary<'a>> {
Binary::from_term(self)
}
}
impl<'a> From<Binary<'a>> for Term<'a> {
#[inline]
fn from(binary: Binary<'a>) -> Self {
binary.term
}
}
pub struct NewBinary<'a> {
buf: *mut u8,
size: usize,
term: Term<'a>,
}
impl<'a> NewBinary<'a> {
#[inline]
pub fn new(env: Env<'a>, size: usize) -> Self {
let (buf, term) = unsafe { new_binary(env, size) };
NewBinary { buf, term, size }
}
#[inline]
pub fn from_slice(env: Env<'a>, data: &[u8]) -> Self {
let mut bin = Self::new(env, data.len());
bin.as_mut_slice().copy_from_slice(data);
bin
}
#[inline]
pub fn as_slice(&self) -> &[u8] {
unsafe { ::std::slice::from_raw_parts(self.buf, self.size) }
}
#[inline]
pub fn as_mut_slice(&mut self) -> &mut [u8] {
unsafe { ::std::slice::from_raw_parts_mut(self.buf, self.size) }
}
pub fn from_iter(env: Env<'a>, iter: impl ExactSizeIterator<Item = u8>) -> Self {
let mut bin = Self::new(env, iter.len());
for (src, dst) in ::std::iter::zip(iter, bin.iter_mut()) {
*dst = src;
}
bin
}
}
impl<'a> From<NewBinary<'a>> for Binary<'a> {
#[inline]
fn from(new_binary: NewBinary<'a>) -> Self {
Binary::from_term(new_binary.term).unwrap()
}
}
impl<'a> From<NewBinary<'a>> for Term<'a> {
#[inline]
fn from(new_binary: NewBinary<'a>) -> Self {
new_binary.term
}
}
impl Deref for NewBinary<'_> {
type Target = [u8];
#[inline]
fn deref(&self) -> &[u8] {
self.as_slice()
}
}
impl DerefMut for NewBinary<'_> {
#[inline]
fn deref_mut(&mut self) -> &mut [u8] {
self.as_mut_slice()
}
}