use crate::c_api::{xcalloc, xrealloc};
use std::{
mem,
ops::{Deref, DerefMut},
ptr, slice,
};
pub unsafe trait SafelyZero {}
unsafe impl SafelyZero for bool {}
unsafe impl SafelyZero for u8 {}
unsafe impl SafelyZero for usize {}
unsafe impl SafelyZero for i32 {}
unsafe impl SafelyZero for i64 {}
unsafe impl<T> SafelyZero for *mut T {}
pub fn xcalloc_zeroed<T: SafelyZero>(len: usize) -> Option<&'static mut [T]> {
if len == 0 || mem::size_of::<T>() == 0 {
return None;
}
let ptr = unsafe { xcalloc(len, mem::size_of::<T>()) };
if ptr.is_null() {
None
} else {
unsafe { ptr::write_bytes(ptr, 0, len) };
Some(unsafe { slice::from_raw_parts_mut(ptr.cast(), len) })
}
}
pub fn xrealloc_zeroed<T: SafelyZero>(
old: &'static mut [T],
new_len: usize,
) -> Option<&'static mut [T]> {
let old_len = old.len();
let new_size = new_len * mem::size_of::<T>();
let ptr = unsafe { xrealloc((old as *mut [_]).cast(), new_size) }.cast::<T>();
if ptr.is_null() {
None
} else {
if new_len > old_len {
unsafe { ptr::write_bytes(ptr.add(old_len), 0, new_len - old_len) };
}
Some(unsafe { slice::from_raw_parts_mut(ptr.cast(), new_len) })
}
}
#[derive(Debug)]
pub(crate) struct XBuf<T: Copy + 'static>(&'static mut [T]);
impl<T: SafelyZero + Copy + 'static> XBuf<T> {
pub fn new(init_len: usize) -> XBuf<T> {
XBuf(xcalloc_zeroed(init_len + 1).unwrap())
}
pub fn grow(&mut self, grow_by: usize) {
let slice = mem::take(&mut self.0);
let old_len = slice.len();
self.0 = xrealloc_zeroed(slice, grow_by + old_len).unwrap();
}
}
impl<T: Copy + 'static> Deref for XBuf<T> {
type Target = [T];
fn deref(&self) -> &Self::Target {
self.0
}
}
impl<T: Copy + 'static> DerefMut for XBuf<T> {
fn deref_mut(&mut self) -> &mut Self::Target {
self.0
}
}
impl<T: Copy + 'static> Drop for XBuf<T> {
fn drop(&mut self) {
unsafe { libc::free((self.0 as *mut [_]).cast()) };
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_new() {
let buf = XBuf::<u8>::new(16);
assert_eq!(buf.len(), 17);
assert_eq!(&*buf, &[0; 17]);
let buf2 = XBuf::<i64>::new(16);
assert_eq!(buf2.len(), 17);
assert_eq!(&*buf2, &[0; 17]);
}
#[test]
fn test_grow() {
let mut buf = XBuf::<usize>::new(16);
buf.iter_mut().enumerate().for_each(|(idx, val)| *val = idx);
buf.grow(16);
assert_eq!(buf.len(), 33);
let expected = &[
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
];
assert_eq!(&*buf, expected);
}
}