use std::{
cell::UnsafeCell,
mem,
ops::{Deref, DerefMut, Not},
sync::{Arc, RwLock, RwLockReadGuard},
};
use crate::{mmap::Mmap, Magic, Sealed};
#[allow(clippy::len_without_is_empty)]
pub(crate) trait Memory: DerefMut<Target = [u8]> + Send + Sync + 'static + Sealed {
fn len(&self) -> usize;
fn as_ptr(&self) -> *const u8;
fn as_mut_ptr(&mut self) -> *mut u8;
}
pub(crate) type Couple<M> = (Buffer<M>, Buffer<M>);
pub(crate) fn initialize<M>(memory: M) -> Couple<M>
where
M: Memory,
{
let inner = BufferInner::new(memory);
let inner = Arc::new(RwLock::new(inner));
let left = Buffer { inner: inner.clone(), side: Side::Left };
let right = Buffer { inner, side: Side::Right };
(left, right)
}
pub(crate) struct Buffer<M> {
inner: Arc<RwLock<BufferInner<M>>>,
side: Side,
}
impl<M> Buffer<M>
where
M: Memory,
{
#[inline]
pub(crate) fn handle(&mut self) -> BufferHandle<M> {
let inner = self.inner.read().unwrap();
BufferHandle { inner, side: self.side }
}
#[inline]
pub(crate) fn switch(&mut self) {
self.inner.write().unwrap().switch();
}
}
pub(crate) struct BufferHandle<'a, M> {
inner: RwLockReadGuard<'a, BufferInner<M>>,
side: Side,
}
impl<'a, M> BufferHandle<'a, M>
where
M: Memory,
{
#[inline]
pub(crate) fn as_slice(&self) -> &[u8] {
unsafe { self.inner.buffer(self.side) }
}
#[inline]
pub(crate) fn as_mut_slice(&mut self) -> &mut [u8] {
unsafe { self.inner.buffer(self.side) }
}
}
impl<'a, M> Deref for BufferHandle<'a, M>
where
M: Memory,
{
type Target = [u8];
#[inline]
fn deref(&self) -> &Self::Target {
self.as_slice()
}
}
impl<'a, M> DerefMut for BufferHandle<'a, M>
where
M: Memory,
{
#[inline]
fn deref_mut(&mut self) -> &mut Self::Target {
self.as_mut_slice()
}
}
pub(crate) enum EitherMemory {
Mmap(Mmap),
Vec(Vec<u8>),
}
impl Deref for EitherMemory {
type Target = [u8];
#[inline]
fn deref(&self) -> &Self::Target {
match self {
Self::Mmap(mmap) => mmap.deref(),
Self::Vec(vec) => vec.deref(),
}
}
}
impl DerefMut for EitherMemory {
#[inline]
fn deref_mut(&mut self) -> &mut Self::Target {
match self {
Self::Mmap(mmap) => mmap.deref_mut(),
Self::Vec(vec) => vec.deref_mut(),
}
}
}
impl Memory for EitherMemory {
#[inline]
fn len(&self) -> usize {
match self {
Self::Mmap(mmap) => mmap.len(),
Self::Vec(vec) => vec.len(),
}
}
#[inline]
fn as_ptr(&self) -> *const u8 {
match self {
Self::Mmap(mmap) => mmap.as_ptr(),
Self::Vec(vec) => vec.as_ptr(),
}
}
#[inline]
fn as_mut_ptr(&mut self) -> *mut u8 {
match self {
Self::Mmap(mmap) => mmap.as_mut_ptr(),
Self::Vec(vec) => vec.as_mut_ptr(),
}
}
}
impl Sealed for EitherMemory {}
impl Sealed for Mmap {}
impl Memory for Mmap {
#[inline]
fn len(&self) -> usize {
self.len()
}
#[inline]
fn as_ptr(&self) -> *const u8 {
self.as_ptr()
}
#[inline]
fn as_mut_ptr(&mut self) -> *mut u8 {
self.as_ptr()
}
}
impl Sealed for Vec<u8> {}
impl Memory for Vec<u8> {
#[inline]
fn len(&self) -> usize {
self.len()
}
#[inline]
fn as_ptr(&self) -> *const u8 {
self.as_ptr()
}
#[inline]
fn as_mut_ptr(&mut self) -> *mut u8 {
self.as_mut_ptr()
}
}
#[repr(u32)]
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
enum Side {
Left = 0xABC,
Right = 0xDEF,
}
impl Side {
#[inline]
const fn raw(&self) -> [u8; 4] {
(*self as u32).to_le_bytes()
}
}
impl TryFrom<[u8; 4]> for Side {
type Error = ();
#[inline]
fn try_from(value: [u8; 4]) -> Result<Self, Self::Error> {
const LEFT_RAW: [u8; 4] = Side::Left.raw();
const RIGHT_RAW: [u8; 4] = Side::Right.raw();
match value {
LEFT_RAW => Ok(Self::Left),
RIGHT_RAW => Ok(Self::Right),
_ => Err(()),
}
}
}
impl Not for Side {
type Output = Self;
#[inline]
fn not(self) -> Self::Output {
match self {
Self::Left => Self::Right,
Self::Right => Self::Left,
}
}
}
#[repr(C)]
#[derive(Debug)]
struct Header {
magic: [u8; 4],
alpha_side: [u8; 4],
}
impl Header {
const LEN: usize = mem::size_of::<Self>();
const MAGIC: Magic = Magic::new(0xFEEDCA7B);
}
impl Default for Header {
#[inline]
fn default() -> Self {
Self { magic: Self::MAGIC.into(), alpha_side: Side::Left.raw() }
}
}
#[repr(transparent)]
struct BufferInner<M>(UnsafeCell<M>);
unsafe impl<M> Send for BufferInner<M> where M: Send {}
unsafe impl<M> Sync for BufferInner<M> where M: Sync {}
impl<M> BufferInner<M>
where
M: Memory,
{
fn new(memory: M) -> Self {
debug_assert!(memory.len() >= Header::LEN, "the memory is too small");
let mut buffer = Self(UnsafeCell::new(memory));
buffer.initialize();
buffer
}
#[inline]
fn initialize(&mut self) {
if !self.validate() {
*self.header_mut() = Header::default();
}
}
#[inline]
fn validate(&self) -> bool {
let header = self.header();
(Header::MAGIC == header.magic.into())
&& ([Side::Left.raw(), Side::Right.raw()].contains(&header.alpha_side))
}
#[inline]
fn switch(&mut self) {
let header = self.header_mut();
let side = !Side::try_from(header.alpha_side).unwrap_or(Side::Left);
header.alpha_side = side.raw();
}
#[allow(clippy::mut_from_ref)]
unsafe fn buffer(&self, side: Side) -> &mut [u8] {
let memory = self.memory();
let len = (memory.len() - Header::LEN) / 2;
let is_alpha = self.header().alpha_side == side.raw();
let offset = if is_alpha { Header::LEN } else { Header::LEN + len };
&mut memory[offset..offset + len]
}
#[inline]
fn header(&self) -> &Header {
unsafe {
let ptr = self.memory().as_ptr() as *const Header;
&*ptr
}
}
#[inline]
fn header_mut(&mut self) -> &mut Header {
unsafe {
let ptr = self.memory().as_mut_ptr() as *mut Header;
&mut *ptr
}
}
#[inline]
#[allow(clippy::mut_from_ref)]
unsafe fn memory(&self) -> &mut M {
&mut *self.0.get()
}
}
#[cfg(test)]
mod tests {
use std::{io, thread};
use tempfile::tempdir;
use crate::{
buffer::{self, Memory},
mmap::Mmap,
};
#[test]
fn test_mmap_buffer() -> io::Result<()> {
let dir = tempdir()?;
let path = dir.path().join("test.pinebuf");
let mmap = Mmap::new(path, 4096)?;
test_buffer(mmap)
}
#[test]
fn test_vec_buffer() -> io::Result<()> {
let mut vec = Vec::with_capacity(256);
unsafe {
vec.set_len(256);
}
test_buffer(vec)
}
fn test_buffer<M>(memory: M) -> io::Result<()>
where
M: Memory,
{
let (mut left, mut right) = buffer::initialize(memory);
left.handle()[0..5].copy_from_slice(b"Alpha");
thread::spawn(move || {
right.handle()[0..4].copy_from_slice(b"Beta");
right.switch();
assert_eq!(&right.handle()[0..5], b"Alpha");
})
.join()
.unwrap();
assert_eq!(&left.handle()[0..4], b"Beta");
Ok(())
}
}