diff --git a/src/rb/local.rs b/src/rb/local.rs index 5c05da8..4467524 100644 --- a/src/rb/local.rs +++ b/src/rb/local.rs @@ -11,7 +11,7 @@ use crate::{ wrap::{Cons, Prod}, }; #[cfg(feature = "alloc")] -use alloc::rc::Rc; +use alloc::{boxed::Box, rc::Rc}; use core::{ cell::Cell, mem::{ManuallyDrop, MaybeUninit}, @@ -36,10 +36,10 @@ impl End { /// Ring buffer for single-threaded use only. /// /// Slightly faster than multi-threaded version because it doesn't synchronize cache. -pub struct LocalRb { - storage: Shared, +pub struct LocalRb { read: End, write: End, + storage: Shared, } impl LocalRb { @@ -49,11 +49,14 @@ impl LocalRb { /// /// The items in storage inside `read..write` range must be initialized, items outside this range must be uninitialized. /// `read` and `write` positions must be valid (see implementation details). - pub unsafe fn from_raw_parts(storage: S, read: usize, write: usize) -> Self { + pub unsafe fn from_raw_parts(storage: S, read: usize, write: usize) -> Self + where + S::Internal: Sized, + { Self { - storage: Shared::new(storage), read: End::new(read), write: End::new(write), + storage: Shared::new(storage), } } /// Destructures ring buffer into underlying storage and `read` and `write` indices. @@ -61,13 +64,16 @@ impl LocalRb { /// # Safety /// /// Initialized contents of the storage must be properly dropped. - pub unsafe fn into_raw_parts(self) -> (S, usize, usize) { + pub unsafe fn into_raw_parts(self) -> (S, usize, usize) + where + S::Internal: Sized, + { let this = ManuallyDrop::new(self); (ptr::read(&this.storage).into_inner(), this.read_index(), this.write_index()) } } -impl Observer for LocalRb { +impl Observer for LocalRb { type Item = S::Item; #[inline] @@ -99,21 +105,21 @@ impl Observer for LocalRb { } } -impl Producer for LocalRb { +impl Producer for LocalRb { #[inline] unsafe fn set_write_index(&self, value: usize) { self.write.index.set(value); } } -impl Consumer for LocalRb { +impl Consumer for LocalRb { #[inline] unsafe fn set_read_index(&self, value: usize) { self.read.index.set(value); } } -impl RingBuffer for LocalRb { +impl RingBuffer for LocalRb { #[inline] unsafe fn hold_read(&self, flag: bool) -> bool { self.read.held.replace(flag) @@ -124,23 +130,43 @@ impl RingBuffer for LocalRb { } } -impl Drop for LocalRb { +impl Drop for LocalRb { fn drop(&mut self) { self.clear(); } } #[cfg(feature = "alloc")] -impl Split for LocalRb { +impl Split for LocalRb +where + S::Internal: Sized, +{ type Prod = Prod>; type Cons = Cons>; fn split(self) -> (Self::Prod, Self::Cons) { - let rc = Rc::new(self); - (Prod::new(rc.clone()), Cons::new(rc)) + Rc::new(self).split() + } +} +#[cfg(feature = "alloc")] +impl Split for Rc> { + type Prod = Prod; + type Cons = Cons; + + fn split(self) -> (Self::Prod, Self::Cons) { + (Prod::new(self.clone()), Cons::new(self)) + } +} +#[cfg(feature = "alloc")] +impl Split for Box> { + type Prod = Prod>>; + type Cons = Cons>>; + + fn split(self) -> (Self::Prod, Self::Cons) { + Rc::>::from(self).split() } } -impl SplitRef for LocalRb { +impl SplitRef for LocalRb { type RefProd<'a> = Prod<&'a Self> where Self: 'a; type RefCons<'a> = Cons<&'a Self> where Self: 'a; @@ -154,12 +180,12 @@ rb_impl_init!(LocalRb); impl_producer_traits!(LocalRb); impl_consumer_traits!(LocalRb); -impl AsRef for LocalRb { +impl AsRef for LocalRb { fn as_ref(&self) -> &Self { self } } -impl AsMut for LocalRb { +impl AsMut for LocalRb { fn as_mut(&mut self) -> &mut Self { self } diff --git a/src/rb/shared.rs b/src/rb/shared.rs index ea03a26..152953d 100644 --- a/src/rb/shared.rs +++ b/src/rb/shared.rs @@ -11,7 +11,7 @@ use crate::{ wrap::{CachingCons, CachingProd}, }; #[cfg(feature = "alloc")] -use alloc::sync::Arc; +use alloc::{boxed::Box, sync::Arc}; use core::{ mem::{ManuallyDrop, MaybeUninit}, num::NonZeroUsize, @@ -44,12 +44,12 @@ thread::spawn(move || { ``` "## )] -pub struct SharedRb { - storage: Shared, +pub struct SharedRb { read_index: CachePadded, write_index: CachePadded, read_held: AtomicBool, write_held: AtomicBool, + storage: Shared, } impl SharedRb { @@ -59,7 +59,10 @@ impl SharedRb { /// /// The items in storage inside `read..write` range must be initialized, items outside this range must be uninitialized. /// `read` and `write` positions must be valid (see implementation details). - pub unsafe fn from_raw_parts(storage: S, read: usize, write: usize) -> Self { + pub unsafe fn from_raw_parts(storage: S, read: usize, write: usize) -> Self + where + S::Internal: Sized, + { Self { storage: Shared::new(storage), read_index: CachePadded::new(AtomicUsize::new(read)), @@ -73,13 +76,16 @@ impl SharedRb { /// # Safety /// /// Initialized contents of the storage must be properly dropped. - pub unsafe fn into_raw_parts(self) -> (S, usize, usize) { + pub unsafe fn into_raw_parts(self) -> (S, usize, usize) + where + S::Internal: Sized, + { let this = ManuallyDrop::new(self); (ptr::read(&this.storage).into_inner(), this.read_index(), this.write_index()) } } -impl Observer for SharedRb { +impl Observer for SharedRb { type Item = S::Item; #[inline] @@ -111,21 +117,21 @@ impl Observer for SharedRb { } } -impl Producer for SharedRb { +impl Producer for SharedRb { #[inline] unsafe fn set_write_index(&self, value: usize) { self.write_index.store(value, Ordering::Release); } } -impl Consumer for SharedRb { +impl Consumer for SharedRb { #[inline] unsafe fn set_read_index(&self, value: usize) { self.read_index.store(value, Ordering::Release); } } -impl RingBuffer for SharedRb { +impl RingBuffer for SharedRb { #[inline] unsafe fn hold_read(&self, flag: bool) -> bool { self.read_held.swap(flag, Ordering::Relaxed) @@ -136,23 +142,43 @@ impl RingBuffer for SharedRb { } } -impl Drop for SharedRb { +impl Drop for SharedRb { fn drop(&mut self) { self.clear(); } } #[cfg(feature = "alloc")] -impl Split for SharedRb { +impl Split for SharedRb +where + S::Internal: Sized, +{ type Prod = CachingProd>; type Cons = CachingCons>; fn split(self) -> (Self::Prod, Self::Cons) { - let rc = Arc::new(self); - (CachingProd::new(rc.clone()), CachingCons::new(rc)) + Arc::new(self).split() + } +} +#[cfg(feature = "alloc")] +impl Split for Arc> { + type Prod = CachingProd; + type Cons = CachingCons; + + fn split(self) -> (Self::Prod, Self::Cons) { + (CachingProd::new(self.clone()), CachingCons::new(self)) + } +} +#[cfg(feature = "alloc")] +impl Split for Box> { + type Prod = CachingProd>>; + type Cons = CachingCons>>; + + fn split(self) -> (Self::Prod, Self::Cons) { + Arc::>::from(self).split() } } -impl SplitRef for SharedRb { +impl SplitRef for SharedRb { type RefProd<'a> = CachingProd<&'a Self> where Self: 'a; type RefCons<'a> = CachingCons<&'a Self> where Self: 'a; @@ -166,12 +192,12 @@ rb_impl_init!(SharedRb); impl_producer_traits!(SharedRb); impl_consumer_traits!(SharedRb); -impl AsRef for SharedRb { +impl AsRef for SharedRb { fn as_ref(&self) -> &Self { self } } -impl AsMut for SharedRb { +impl AsMut for SharedRb { fn as_mut(&mut self) -> &mut Self { self } diff --git a/src/rb/traits.rs b/src/rb/traits.rs index cddaff0..f0c0612 100644 --- a/src/rb/traits.rs +++ b/src/rb/traits.rs @@ -9,21 +9,21 @@ use alloc::{rc::Rc, sync::Arc}; /// Implementation must be fair (e.g. not replacing pointers between calls and so on). pub unsafe trait RbRef: Clone + AsRef { /// Underlying ring buffer. - type Rb: RingBuffer; + type Rb: RingBuffer + ?Sized; /// Get ring buffer reference. fn rb(&self) -> &Self::Rb { self.as_ref() } } -unsafe impl<'a, B: RingBuffer + AsRef> RbRef for &'a B { +unsafe impl<'a, B: RingBuffer + AsRef + ?Sized> RbRef for &'a B { type Rb = B; } #[cfg(feature = "alloc")] -unsafe impl RbRef for Rc { +unsafe impl RbRef for Rc { type Rb = B; } #[cfg(feature = "alloc")] -unsafe impl RbRef for Arc { +unsafe impl RbRef for Arc { type Rb = B; } diff --git a/src/storage.rs b/src/storage.rs index 30924dc..1b1990f 100644 --- a/src/storage.rs +++ b/src/storage.rs @@ -1,6 +1,6 @@ #[cfg(feature = "alloc")] use alloc::vec::Vec; -use core::{cell::UnsafeCell, mem::MaybeUninit, num::NonZeroUsize, ops::Range, slice}; +use core::{cell::UnsafeCell, mem::MaybeUninit, num::NonZeroUsize, ops::Range, ptr::NonNull, slice}; /// Abstract storage for the ring buffer. /// @@ -18,16 +18,22 @@ pub unsafe trait Storage { /// Internal representation of the storage. /// /// *Must not be aliased with its content.* - type Internal; + type Internal: ?Sized; /// Transform storage to internal representation. - fn into_internal(self) -> Self::Internal; + fn into_internal(self) -> Self::Internal + where + Self: Sized, + Self::Internal: Sized; /// Restore storage from internal representation. /// /// # Safety /// /// `this` must be a valid internal representation. - unsafe fn from_internal(this: Self::Internal) -> Self; + unsafe fn from_internal(this: Self::Internal) -> Self + where + Self: Sized, + Self::Internal: Sized; /// Return pointer to the beginning of the storage items. fn as_mut_ptr(this: &Self::Internal) -> *mut MaybeUninit; @@ -82,6 +88,35 @@ unsafe impl Storage for [MaybeUninit; N] { } } +unsafe impl Storage for [MaybeUninit] { + type Item = T; + + type Internal = UnsafeCell<[MaybeUninit]>; + + fn into_internal(self) -> Self::Internal + where + Self: Sized, + { + unreachable!() + } + unsafe fn from_internal(_this: Self::Internal) -> Self + where + Self: Sized, + { + unreachable!() + } + + #[inline] + fn as_mut_ptr(this: &Self::Internal) -> *mut MaybeUninit { + this.get() as *mut _ + } + + #[inline] + fn len(this: &Self::Internal) -> usize { + unsafe { NonNull::new_unchecked(this.get()) }.len() + } +} + #[cfg(feature = "alloc")] unsafe impl Storage for Vec> { type Item = T; @@ -107,17 +142,21 @@ unsafe impl Storage for Vec> { } /// Wrapper for storage that provides multiple write access to it. -pub(crate) struct Shared { +pub(crate) struct Shared { internal: S::Internal, } unsafe impl Sync for Shared where S::Item: Send {} -impl Shared { +impl Shared { /// Create new storage. /// /// *Panics if storage is empty.* - pub fn new(storage: S) -> Self { + pub fn new(storage: S) -> Self + where + S: Sized, + S::Internal: Sized, + { let internal = storage.into_internal(); assert!(S::len(&internal) > 0); Self { internal } @@ -141,7 +180,11 @@ impl Shared { } /// Returns underlying storage. - pub fn into_inner(self) -> S { + pub fn into_inner(self) -> S + where + S: Sized, + S::Internal: Sized, + { unsafe { S::from_internal(self.internal) } } } @@ -151,3 +194,5 @@ pub type Static = [MaybeUninit; N]; /// Heap storage. #[cfg(feature = "alloc")] pub type Heap = Vec>; +/// Unsized slice storage. +pub type Slice = [MaybeUninit]; diff --git a/src/tests/mod.rs b/src/tests/mod.rs index 5c03ee5..933a1ef 100644 --- a/src/tests/mod.rs +++ b/src/tests/mod.rs @@ -19,3 +19,4 @@ mod shared; #[cfg(feature = "alloc")] mod skip; mod slice; +mod unsized_; diff --git a/src/tests/unsized_.rs b/src/tests/unsized_.rs new file mode 100644 index 0000000..e448328 --- /dev/null +++ b/src/tests/unsized_.rs @@ -0,0 +1,40 @@ +use super::Rb; +use crate::{ + storage::{Slice, Static}, + traits::*, +}; +#[cfg(feature = "alloc")] +use alloc::boxed::Box; + +#[test] +fn capacity() { + const CAP: usize = 13; + let mut rb = Rb::>::default(); + let urb = &mut rb as &mut Rb>; + assert_eq!(urb.capacity().get(), CAP); +} + +#[test] +fn push_pop() { + let mut rb = Rb::>::default(); + let urb = &mut rb as &mut Rb>; + let (mut prod, mut cons) = urb.split_ref(); + + assert_eq!(prod.try_push(123), Ok(())); + assert_eq!(prod.try_push(321), Err(321)); + assert_eq!(cons.try_pop(), Some(123)); + assert_eq!(cons.try_pop(), None); +} + +#[cfg(feature = "alloc")] +#[test] +fn split() { + let rb = Rb::>::default(); + let urb = Box::new(rb) as Box>>; + let (mut prod, mut cons) = urb.split(); + + assert_eq!(prod.try_push(123), Ok(())); + assert_eq!(prod.try_push(321), Err(321)); + assert_eq!(cons.try_pop(), Some(123)); + assert_eq!(cons.try_pop(), None); +} diff --git a/src/traits/consumer.rs b/src/traits/consumer.rs index f24b5a7..80238ec 100644 --- a/src/traits/consumer.rs +++ b/src/traits/consumer.rs @@ -251,12 +251,12 @@ pub trait Consumer: Observer { } /// An iterator that removes items from the ring buffer. -pub struct PopIter + AsRef, C: Consumer> { +pub struct PopIter + AsRef, C: Consumer + ?Sized> { inner: U, _ghost: PhantomData, } -impl + AsRef, C: Consumer> PopIter { +impl + AsRef, C: Consumer + ?Sized> PopIter { pub fn new(inner: U) -> Self { Self { inner, @@ -368,7 +368,7 @@ where macro_rules! impl_consumer_traits { ($type:ident $(< $( $param:tt $( : $first_bound:tt $(+ $next_bound:tt )* )? ),+ >)?) => { - impl $(< $( $param $( : $first_bound $(+ $next_bound )* )? ),+ >)? core::iter::IntoIterator for $type $(< $( $param ),+ >)? { + impl $(< $( $param $( : $first_bound $(+ $next_bound )* )? ),+ >)? core::iter::IntoIterator for $type $(< $( $param ),+ >)? where Self: Sized { type Item = ::Item; type IntoIter = $crate::traits::consumer::PopIter; fn into_iter(self) -> Self::IntoIter { diff --git a/src/traits/observer.rs b/src/traits/observer.rs index 07e260b..68be464 100644 --- a/src/traits/observer.rs +++ b/src/traits/observer.rs @@ -4,7 +4,7 @@ use core::{mem::MaybeUninit, num::NonZeroUsize}; /// Ring buffer observer. /// /// Can observe ring buffer state but cannot safely access its data. -pub trait Observer: Sized { +pub trait Observer { type Item: Sized; /// Capacity of the ring buffer. diff --git a/src/traits/split.rs b/src/traits/split.rs index ea058a5..f466087 100644 --- a/src/traits/split.rs +++ b/src/traits/split.rs @@ -1,7 +1,7 @@ -use crate::traits::{Consumer, Producer, RingBuffer}; +use crate::traits::{Consumer, Producer}; /// Split the ring buffer onto producer and consumer. -pub trait Split: RingBuffer { +pub trait Split { /// Producer type. type Prod: Producer; /// Consumer type. @@ -12,7 +12,7 @@ pub trait Split: RingBuffer { } /// Split the ring buffer by reference onto producer and consumer. -pub trait SplitRef: RingBuffer { +pub trait SplitRef { /// Ref producer type. type RefProd<'a>: Producer + 'a where diff --git a/src/traits/utils.rs b/src/traits/utils.rs index 72a5208..5ba8f87 100644 --- a/src/traits/utils.rs +++ b/src/traits/utils.rs @@ -6,7 +6,7 @@ use core::num::NonZeroUsize; /// Used for automatically delegating methods. pub trait Based { /// Type the wrapper based on. - type Base; + type Base: ?Sized; /// Reference to base. fn base(&self) -> &Self::Base; /// Mutable reference to base. @@ -17,6 +17,6 @@ pub trait Based { /// /// Equals to `2 * capacity`. #[inline] -pub fn modulus(this: &impl Observer) -> NonZeroUsize { +pub fn modulus(this: &O) -> NonZeroUsize { unsafe { NonZeroUsize::new_unchecked(2 * this.capacity().get()) } }