use crate::types::{Binary, Env};
pub struct BinaryBuf {
bin: enif_ffi::Binary,
len: usize,
released: bool,
}
impl BinaryBuf {
pub fn new() -> BinaryBuf {
BinaryBuf::with_capacity(0)
}
pub fn with_capacity(capacity: usize) -> BinaryBuf {
let mut bin: enif_ffi::Binary = unsafe { std::mem::zeroed() };
let ok = unsafe { enif_ffi::alloc_binary(capacity, &mut bin) != 0 };
assert!(ok, "enif_alloc_binary failed");
BinaryBuf { bin, len: 0, released: false }
}
#[crate::raw]
pub(crate) fn from_filled(bin: enif_ffi::Binary) -> BinaryBuf {
BinaryBuf { len: bin.size, bin, released: false }
}
pub fn len(&self) -> usize {
self.len
}
pub fn is_empty(&self) -> bool {
self.len == 0
}
pub fn capacity(&self) -> usize {
self.bin.size
}
pub fn as_bytes(&self) -> &[u8] {
unsafe { std::slice::from_raw_parts(self.bin.data, self.len) }
}
pub fn as_bytes_mut(&mut self) -> &mut [u8] {
unsafe { std::slice::from_raw_parts_mut(self.bin.data, self.len) }
}
pub fn resize(&mut self, new_len: usize, value: u8) {
if new_len > self.len {
self.reserve(new_len - self.len);
unsafe {
std::ptr::write_bytes(self.bin.data.add(self.len), value, new_len - self.len);
}
}
self.len = new_len;
}
pub fn push(&mut self, byte: u8) {
self.reserve(1);
unsafe { *self.bin.data.add(self.len) = byte };
self.len += 1;
}
pub fn extend_from_slice(&mut self, bytes: &[u8]) {
self.reserve(bytes.len());
unsafe {
std::ptr::copy_nonoverlapping(bytes.as_ptr(), self.bin.data.add(self.len), bytes.len());
}
self.len += bytes.len();
}
pub fn reserve(&mut self, additional: usize) {
let required = self.len + additional;
if required <= self.bin.size {
return;
}
let new_cap = required.max(self.bin.size.checked_mul(2).unwrap_or(required));
let ok = unsafe { enif_ffi::realloc_binary(&mut self.bin, new_cap) != 0 };
assert!(ok, "enif_realloc_binary failed");
}
pub fn into_binary<'id>(mut self, env: impl Env<'id>) -> Binary<'id> {
if self.len < self.bin.size {
let ok = unsafe { enif_ffi::realloc_binary(&mut self.bin, self.len) != 0 };
assert!(ok, "enif_realloc_binary failed on shrink");
}
self.released = true;
let raw_term = unsafe { enif_ffi::make_binary(env.raw_env(), &mut self.bin) };
Binary::from_raw(raw_term)
}
}
impl Default for BinaryBuf {
fn default() -> Self {
BinaryBuf::new()
}
}
impl std::io::Write for BinaryBuf {
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
self.extend_from_slice(buf);
Ok(buf.len())
}
fn flush(&mut self) -> std::io::Result<()> {
Ok(())
}
}
impl Drop for BinaryBuf {
fn drop(&mut self) {
if !self.released {
unsafe { enif_ffi::release_binary(&mut self.bin) };
}
}
}
impl std::ops::Deref for BinaryBuf {
type Target = [u8];
fn deref(&self) -> &[u8] {
self.as_bytes()
}
}
impl std::ops::DerefMut for BinaryBuf {
fn deref_mut(&mut self) -> &mut [u8] {
self.as_bytes_mut()
}
}
impl AsRef<[u8]> for BinaryBuf {
fn as_ref(&self) -> &[u8] {
self.as_bytes()
}
}
impl AsMut<[u8]> for BinaryBuf {
fn as_mut(&mut self) -> &mut [u8] {
self.as_bytes_mut()
}
}
impl Extend<u8> for BinaryBuf {
fn extend<I: IntoIterator<Item = u8>>(&mut self, iter: I) {
let iter = iter.into_iter();
let (lower, _) = iter.size_hint();
self.reserve(lower);
for byte in iter {
self.push(byte);
}
}
}
impl<'a> Extend<&'a u8> for BinaryBuf {
fn extend<I: IntoIterator<Item = &'a u8>>(&mut self, iter: I) {
self.extend(iter.into_iter().copied());
}
}
impl std::fmt::Debug for BinaryBuf {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("BinaryBuf")
.field("len", &self.len)
.field("capacity", &self.bin.size)
.finish()
}
}