use std::marker::PhantomData;
use std::mem;
use std::mem::MaybeUninit;
use std::slice;
use super::DoubleMappedBufferError;
use super::DoubleMappedBufferImpl;
pub struct DoubleMappedBuffer<T> {
buffer: DoubleMappedBufferImpl,
_p: PhantomData<T>,
}
impl<T> DoubleMappedBuffer<T> {
pub fn new(min_items: usize) -> Result<Self, DoubleMappedBufferError>
where
T: Copy + Default,
{
if mem::size_of::<T>() == 0 {
return Err(DoubleMappedBufferError::ZeroSized);
}
let buffer =
DoubleMappedBufferImpl::new(min_items, mem::size_of::<T>(), mem::align_of::<T>())?;
let buffer = DoubleMappedBuffer {
buffer,
_p: PhantomData,
};
let ptr = buffer.buffer.addr() as *mut MaybeUninit<T>;
for offset in 0..buffer.capacity() {
unsafe {
ptr.add(offset).write(MaybeUninit::new(T::default()));
}
}
Ok(buffer)
}
pub unsafe fn slice(&self) -> &[T] {
let addr = self.buffer.addr();
debug_assert_eq!(addr % mem::align_of::<T>(), 0);
unsafe { slice::from_raw_parts(addr as *const T, self.buffer.capacity()) }
}
#[allow(clippy::mut_from_ref)]
pub unsafe fn slice_mut(&self) -> &mut [T] {
let addr = self.buffer.addr();
debug_assert_eq!(addr % mem::align_of::<T>(), 0);
unsafe { slice::from_raw_parts_mut(addr as *mut T, self.buffer.capacity()) }
}
pub unsafe fn slice_with_offset(&self, offset: usize) -> &[T] {
let addr = self.buffer.addr();
debug_assert_eq!(addr % mem::align_of::<T>(), 0);
debug_assert!(offset <= self.buffer.capacity());
unsafe { slice::from_raw_parts((addr as *const T).add(offset), self.buffer.capacity()) }
}
#[allow(clippy::mut_from_ref)]
pub unsafe fn slice_with_offset_mut(&self, offset: usize) -> &mut [T] {
let addr = self.buffer.addr();
debug_assert_eq!(addr % mem::align_of::<T>(), 0);
debug_assert!(offset <= self.buffer.capacity());
unsafe { slice::from_raw_parts_mut((addr as *mut T).add(offset), self.buffer.capacity()) }
}
pub fn capacity(&self) -> usize {
self.buffer.capacity()
}
}
#[cfg(test)]
mod test {
use super::*;
use crate::double_mapped_buffer::pagesize;
use std::mem;
use std::sync::atomic::Ordering;
use std::sync::atomic::compiler_fence;
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
enum Sample {
Idle,
#[default]
Active,
}
#[test]
fn initialized_buffer_contains_valid_default_values() {
let b = DoubleMappedBuffer::<Sample>::new(123).expect("failed to create buffer");
unsafe {
assert!(b.slice().iter().all(|sample| *sample == Sample::Active));
b.slice_mut()[0] = Sample::Idle;
assert_eq!(b.slice_with_offset(b.capacity())[0], Sample::Idle);
}
}
#[test]
fn zero_sized_items_are_rejected() {
assert!(matches!(
DoubleMappedBuffer::<()>::new(1),
Err(DoubleMappedBufferError::ZeroSized)
));
}
#[test]
fn byte_buffer() {
let b = DoubleMappedBuffer::<u8>::new(123).expect("failed to create buffer");
let ps = pagesize();
assert_eq!(b.capacity() * mem::size_of::<u8>() % ps, 0);
assert_eq!(b.buffer.addr() % mem::align_of::<u8>(), 0);
unsafe {
let s = b.slice_mut();
assert_eq!(s.len(), b.capacity());
assert_eq!(s.as_mut_ptr() as usize, b.buffer.addr());
for (i, v) in s.iter_mut().enumerate() {
*v = (i % 128) as u8;
}
compiler_fence(Ordering::SeqCst);
let s = b.slice_with_offset(b.capacity());
assert_eq!(
s.as_ptr() as usize,
b.buffer.addr() + b.capacity() * mem::size_of::<u8>()
);
for (i, v) in s.iter().enumerate() {
assert_eq!(*v, (i % 128) as u8);
}
compiler_fence(Ordering::SeqCst);
b.slice_mut()[0] = 123;
compiler_fence(Ordering::SeqCst);
assert_eq!(b.slice_with_offset(b.capacity())[0], 123);
}
}
#[test]
fn u32_buffer() {
let b = DoubleMappedBuffer::<u32>::new(12311).expect("failed to create buffer");
let ps = pagesize();
assert_eq!(b.capacity() * mem::size_of::<u32>() % ps, 0);
assert_eq!(b.buffer.addr() % mem::align_of::<u32>(), 0);
unsafe {
let s = b.slice_mut();
assert_eq!(s.len(), b.capacity());
assert_eq!(s.as_mut_ptr() as usize, b.buffer.addr());
for (i, v) in s.iter_mut().enumerate() {
*v = (i % 128) as u32;
}
compiler_fence(Ordering::SeqCst);
let s = b.slice_with_offset(b.capacity());
assert_eq!(
s.as_ptr() as usize,
b.buffer.addr() + b.capacity() * mem::size_of::<u32>()
);
for (i, v) in s.iter().enumerate() {
assert_eq!(*v, (i % 128) as u32);
}
compiler_fence(Ordering::SeqCst);
b.slice_mut()[0] = 123;
compiler_fence(Ordering::SeqCst);
assert_eq!(b.slice_with_offset(b.capacity())[0], 123);
}
}
#[test]
fn many_buffers() {
let _b0 = DoubleMappedBuffer::<u32>::new(123).expect("failed to create buffer");
let _b1 = DoubleMappedBuffer::<u32>::new(456).expect("failed to create buffer");
let mut v = Vec::new();
for _ in 0..100 {
v.push(DoubleMappedBuffer::<u32>::new(123).expect("failed to create buffer"));
}
}
}