use std::alloc::{alloc, dealloc, handle_alloc_error, realloc, Layout};
use std::mem;
use std::ptr::NonNull;
pub(crate) struct RawRingBuffer<T> {
pub ptr: NonNull<T>,
pub cap: usize,
}
impl<T> RawRingBuffer<T> {
pub fn new(cap: usize) -> Self {
assert_ne!(cap, 0, "RingBuffer's capacity must be a non-zero value");
if mem::size_of::<T>() == 0 {
return RawRingBuffer {
ptr: NonNull::dangling(),
cap: !0,
};
}
unsafe {
let elem_size = mem::size_of::<T>();
let ptr = alloc(array_layout::<T>(cap).unwrap());
if ptr.is_null() {
handle_alloc_error(Layout::from_size_align_unchecked(
cap * elem_size,
mem::align_of::<T>(),
))
}
RawRingBuffer {
ptr: NonNull::new_unchecked(ptr as *mut _),
cap,
}
}
}
pub fn resize(&mut self, new_cap: usize) {
if mem::size_of::<T>() == 0 {
return;
}
unsafe {
let new_ptr = realloc(
self.ptr.as_ptr() as *mut _,
array_layout::<T>(self.cap).unwrap(),
array_layout::<T>(new_cap).unwrap().size(),
);
if new_ptr.is_null() {
handle_alloc_error(Layout::from_size_align_unchecked(
new_cap * mem::size_of::<T>(),
mem::align_of::<T>(),
))
}
self.cap = new_cap;
self.ptr = NonNull::new_unchecked(new_ptr as *mut _);
}
}
}
impl<T> Drop for RawRingBuffer<T> {
fn drop(&mut self) {
let elem_size = mem::size_of::<T>();
if self.cap != 0 && elem_size != 0 {
unsafe {
dealloc(
self.ptr.as_ptr() as *mut _,
array_layout::<T>(self.cap).unwrap(),
)
}
}
}
}
fn padding_needed_for(layout: &Layout, align: usize) -> usize {
let len = layout.size();
let len_rounded_up = len.wrapping_add(align).wrapping_sub(1) & !align.wrapping_sub(1);
len_rounded_up.wrapping_sub(len)
}
fn layout_repeat(layout: Layout, n: usize) -> Result<(Layout, usize), ()> {
let padded_size = layout
.size()
.checked_add(padding_needed_for(&layout, layout.align()))
.ok_or(())?;
let alloc_size = padded_size.checked_mul(n).ok_or(())?;
unsafe {
Ok((
Layout::from_size_align_unchecked(alloc_size, layout.align()),
padded_size,
))
}
}
fn array_layout<T>(n: usize) -> Result<Layout, ()> {
layout_repeat(Layout::new::<T>(), n).map(|(k, offs)| {
debug_assert!(offs == mem::size_of::<T>());
k
})
}