use super::*;
fn no_required_allocation_units(bytes: usize) -> usize {
let ag = allocation_granularity();
let r = ((bytes + ag - 1) / ag).max(1);
let r = if r % 2 == 0 { r } else { r + 1 };
debug_assert!(r * ag >= bytes);
debug_assert!(r % 2 == 0);
r
}
pub struct Buffer<T> {
ptr: NonNull<T>,
len: usize,
}
impl<T> Buffer<T> {
pub fn len(&self) -> usize {
self.len
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
pub unsafe fn ptr(&self) -> *mut T {
self.ptr.as_ptr()
}
pub unsafe fn as_slice(&self) -> &[T] {
slice::from_raw_parts(self.ptr.as_ptr(), self.len())
}
pub unsafe fn as_mut_slice(&mut self) -> &mut [T] {
slice::from_raw_parts_mut(self.ptr.as_ptr(), self.len())
}
pub unsafe fn get(&self, i: usize) -> &T {
&self.as_slice()[i]
}
pub unsafe fn get_mut(&mut self, i: usize) -> &mut T {
&mut self.as_mut_slice()[i]
}
fn empty_len() -> usize {
if mem::size_of::<T>() == 0 {
isize::max_value() as usize * 2
} else {
0
}
}
pub fn new() -> Self {
Self {
ptr: NonNull::dangling(),
len: Self::empty_len(),
}
}
pub unsafe fn from_raw_parts(ptr: *mut T, len: usize) -> Self {
assert!(len % 2 == 0);
assert!(!ptr.is_null());
if mem::size_of::<T>() == 0 {
debug_assert_eq!(len, Self::empty_len());
}
Self {
ptr: NonNull::new_unchecked(ptr),
len,
}
}
pub fn size_in_bytes(len: usize) -> usize {
let v = no_required_allocation_units(len * mem::size_of::<T>())
* allocation_granularity();
debug_assert!(
v >= len * mem::size_of::<T>(),
"len: {}, so<T>: {}, v: {}",
len,
mem::size_of::<T>(),
v
);
v
}
pub fn uninitialized(len: usize) -> Result<Self, AllocError> {
if mem::size_of::<T>() == 0 {
return Ok(Self {
ptr: NonNull::dangling(),
len: Self::empty_len(),
});
}
assert!(mem::align_of::<T>() <= allocation_granularity());
if len == 0 {
return Ok(Self::new());
}
assert!(len % 2 == 0);
let alloc_size = Self::size_in_bytes(len);
debug_assert!(alloc_size > 0);
debug_assert!(alloc_size % 2 == 0);
debug_assert!(alloc_size % allocation_granularity() == 0);
debug_assert!(alloc_size >= len * mem::size_of::<T>());
let ptr = allocate_mirrored(alloc_size)?;
Ok(Self {
ptr: unsafe { NonNull::new_unchecked(ptr as *mut T) },
len: alloc_size / mem::size_of::<T>(),
})
}
}
impl<T> Drop for Buffer<T> {
fn drop(&mut self) {
if mem::size_of::<T>() == 0 {
debug_assert_eq!(self.len, Self::empty_len());
return;
}
if self.is_empty() {
return;
}
let buffer_size_in_bytes = Self::size_in_bytes(self.len());
let first_half_ptr = self.ptr.as_ptr() as *mut u8;
unsafe { deallocate_mirrored(first_half_ptr, buffer_size_in_bytes) };
}
}
impl<T> Clone for Buffer<T>
where
T: Clone,
{
fn clone(&self) -> Self {
unsafe {
let mid = self.len() / 2;
let mut c = Self::uninitialized(self.len())
.expect("allocating a new mirrored buffer failed");
let (from, _) = self.as_slice().split_at(mid);
{
let (to, _) = c.as_mut_slice().split_at_mut(mid);
to[..mid].clone_from_slice(&from[..mid]);
}
c
}
}
}
impl<T> Default for Buffer<T> {
fn default() -> Self {
Self::new()
}
}
unsafe impl<T> Send for Buffer<T> where T: Send {}
unsafe impl<T> Sync for Buffer<T> where T: Sync {}
#[cfg(test)]
mod tests {
use super::*;
fn is_send_sync<T>() -> bool
where
T: Send + Sync,
{
true
}
#[test]
fn buffer_send_sync() {
assert!(is_send_sync::<Buffer<usize>>());
}
#[test]
fn test_new() {
let a = Buffer::<u64>::new();
assert!(a.is_empty());
}
fn test_alloc(size: usize) {
unsafe {
let mut a = Buffer::<u64>::uninitialized(size).unwrap();
let sz = a.len();
assert!(sz >= size);
assert_eq!(
sz,
Buffer::<u64>::size_in_bytes(size) / mem::size_of::<u64>()
);
for i in 0..sz / 2 {
*a.get_mut(i) = i as u64;
}
let (first_half_mut, second_half_mut) =
a.as_mut_slice().split_at_mut(sz / 2);
let mut c = 0;
for (i, j) in first_half_mut.iter().zip(second_half_mut) {
assert_eq!(i, j);
c += 1;
}
assert_eq!(c, sz / 2);
}
}
#[test]
fn allocations() {
let elements_per_alloc_unit =
allocation_granularity() / mem::size_of::<u64>();
let sizes = [
8,
elements_per_alloc_unit / 2,
elements_per_alloc_unit,
elements_per_alloc_unit * 4,
];
for &i in &sizes {
test_alloc(i);
}
}
#[test]
fn no_alloc_units_required() {
assert_eq!(
no_required_allocation_units(allocation_granularity() / 4),
2
);
assert_eq!(
no_required_allocation_units(allocation_granularity() / 2),
2
);
assert_eq!(no_required_allocation_units(allocation_granularity()), 2);
assert_eq!(
no_required_allocation_units(allocation_granularity() + 1),
2
);
assert_eq!(
no_required_allocation_units(2 * allocation_granularity()),
2
);
assert_eq!(
no_required_allocation_units(3 * allocation_granularity()),
4
);
assert_eq!(
no_required_allocation_units(4 * allocation_granularity()),
4
);
assert_eq!(
no_required_allocation_units(5 * allocation_granularity()),
6
);
}
}