summaryrefslogtreecommitdiffstats
path: root/vendor/triomphe/src/thin_arc.rs
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/triomphe/src/thin_arc.rs')
-rw-r--r--vendor/triomphe/src/thin_arc.rs329
1 files changed, 329 insertions, 0 deletions
diff --git a/vendor/triomphe/src/thin_arc.rs b/vendor/triomphe/src/thin_arc.rs
new file mode 100644
index 000000000..e048468ad
--- /dev/null
+++ b/vendor/triomphe/src/thin_arc.rs
@@ -0,0 +1,329 @@
+use core::ffi::c_void;
+use core::fmt;
+use core::hash::{Hash, Hasher};
+use core::iter::{ExactSizeIterator, Iterator};
+use core::marker::PhantomData;
+use core::mem::ManuallyDrop;
+use core::ops::Deref;
+use core::ptr;
+use core::usize;
+
+use super::{Arc, ArcInner, HeaderSliceWithLength, HeaderWithLength};
+
+/// A "thin" `Arc` containing dynamically sized data
+///
+/// This is functionally equivalent to `Arc<(H, [T])>`
+///
+/// When you create an `Arc` containing a dynamically sized type
+/// like `HeaderSlice<H, [T]>`, the `Arc` is represented on the stack
+/// as a "fat pointer", where the length of the slice is stored
+/// alongside the `Arc`'s pointer. In some situations you may wish to
+/// have a thin pointer instead, perhaps for FFI compatibility
+/// or space efficiency.
+///
+/// Note that we use `[T; 0]` in order to have the right alignment for `T`.
+///
+/// `ThinArc` solves this by storing the length in the allocation itself,
+/// via `HeaderSliceWithLength`.
+#[repr(transparent)]
+pub struct ThinArc<H, T> {
+ ptr: ptr::NonNull<ArcInner<HeaderSliceWithLength<H, [T; 0]>>>,
+ phantom: PhantomData<(H, T)>,
+}
+
+unsafe impl<H: Sync + Send, T: Sync + Send> Send for ThinArc<H, T> {}
+unsafe impl<H: Sync + Send, T: Sync + Send> Sync for ThinArc<H, T> {}
+
+// Synthesize a fat pointer from a thin pointer.
+//
+// See the comment around the analogous operation in from_header_and_iter.
+fn thin_to_thick<H, T>(
+ thin: *mut ArcInner<HeaderSliceWithLength<H, [T; 0]>>,
+) -> *mut ArcInner<HeaderSliceWithLength<H, [T]>> {
+ let len = unsafe { (*thin).data.header.length };
+ let fake_slice = ptr::slice_from_raw_parts_mut(thin as *mut T, len);
+
+ fake_slice as *mut ArcInner<HeaderSliceWithLength<H, [T]>>
+}
+
+impl<H, T> ThinArc<H, T> {
+ /// Temporarily converts |self| into a bonafide Arc and exposes it to the
+ /// provided callback. The refcount is not modified.
+ #[inline]
+ pub fn with_arc<F, U>(&self, f: F) -> U
+ where
+ F: FnOnce(&Arc<HeaderSliceWithLength<H, [T]>>) -> U,
+ {
+ // Synthesize transient Arc, which never touches the refcount of the ArcInner.
+ let transient = unsafe {
+ ManuallyDrop::new(Arc {
+ p: ptr::NonNull::new_unchecked(thin_to_thick(self.ptr.as_ptr())),
+ phantom: PhantomData,
+ })
+ };
+
+ // Expose the transient Arc to the callback, which may clone it if it wants
+ // and forward the result to the user
+ f(&transient)
+ }
+
+ /// Creates a `ThinArc` for a HeaderSlice using the given header struct and
+ /// iterator to generate the slice.
+ pub fn from_header_and_iter<I>(header: H, items: I) -> Self
+ where
+ I: Iterator<Item = T> + ExactSizeIterator,
+ {
+ let header = HeaderWithLength::new(header, items.len());
+ Arc::into_thin(Arc::from_header_and_iter(header, items))
+ }
+
+ /// Creates a `ThinArc` for a HeaderSlice using the given header struct and
+ /// a slice to copy.
+ pub fn from_header_and_slice(header: H, items: &[T]) -> Self
+ where
+ T: Copy,
+ {
+ let header = HeaderWithLength::new(header, items.len());
+ Arc::into_thin(Arc::from_header_and_slice(header, items))
+ }
+
+ /// Returns the address on the heap of the ThinArc itself -- not the T
+ /// within it -- for memory reporting.
+ #[inline]
+ pub fn ptr(&self) -> *const c_void {
+ self.ptr.as_ptr() as *const ArcInner<T> as *const c_void
+ }
+
+ /// Returns the address on the heap of the Arc itself -- not the T within it -- for memory
+ /// reporting.
+ #[inline]
+ pub fn heap_ptr(&self) -> *const c_void {
+ self.ptr()
+ }
+
+ /// # Safety
+ ///
+ /// Constructs an ThinArc from a raw pointer.
+ ///
+ /// The raw pointer must have been previously returned by a call to
+ /// ThinArc::into_raw.
+ ///
+ /// The user of from_raw has to make sure a specific value of T is only dropped once.
+ ///
+ /// This function is unsafe because improper use may lead to memory unsafety,
+ /// even if the returned ThinArc is never accessed.
+ #[inline]
+ pub unsafe fn from_raw(ptr: *const c_void) -> Self {
+ Self {
+ ptr: ptr::NonNull::new_unchecked(ptr as *mut c_void).cast(),
+ phantom: PhantomData,
+ }
+ }
+
+ /// Consume ThinArc and returned the wrapped pointer.
+ #[inline]
+ pub fn into_raw(self) -> *const c_void {
+ let this = ManuallyDrop::new(self);
+ this.ptr.cast().as_ptr()
+ }
+
+ /// Provides a raw pointer to the data.
+ /// The counts are not affected in any way and the ThinArc is not consumed.
+ /// The pointer is valid for as long as there are strong counts in the ThinArc.
+ #[inline]
+ pub fn as_ptr(&self) -> *const c_void {
+ self.ptr()
+ }
+}
+
+impl<H, T> Deref for ThinArc<H, T> {
+ type Target = HeaderSliceWithLength<H, [T]>;
+
+ #[inline]
+ fn deref(&self) -> &Self::Target {
+ unsafe { &(*thin_to_thick(self.ptr.as_ptr())).data }
+ }
+}
+
+impl<H, T> Clone for ThinArc<H, T> {
+ #[inline]
+ fn clone(&self) -> Self {
+ ThinArc::with_arc(self, |a| Arc::into_thin(a.clone()))
+ }
+}
+
+impl<H, T> Drop for ThinArc<H, T> {
+ #[inline]
+ fn drop(&mut self) {
+ let _ = Arc::from_thin(ThinArc {
+ ptr: self.ptr,
+ phantom: PhantomData,
+ });
+ }
+}
+
+impl<H, T> Arc<HeaderSliceWithLength<H, [T]>> {
+ /// Converts an `Arc` into a `ThinArc`. This consumes the `Arc`, so the refcount
+ /// is not modified.
+ #[inline]
+ pub fn into_thin(a: Self) -> ThinArc<H, T> {
+ let a = ManuallyDrop::new(a);
+ assert_eq!(
+ a.header.length,
+ a.slice.len(),
+ "Length needs to be correct for ThinArc to work"
+ );
+ let fat_ptr: *mut ArcInner<HeaderSliceWithLength<H, [T]>> = a.ptr();
+ let thin_ptr = fat_ptr as *mut [usize] as *mut usize;
+ ThinArc {
+ ptr: unsafe {
+ ptr::NonNull::new_unchecked(
+ thin_ptr as *mut ArcInner<HeaderSliceWithLength<H, [T; 0]>>,
+ )
+ },
+ phantom: PhantomData,
+ }
+ }
+
+ /// Converts a `ThinArc` into an `Arc`. This consumes the `ThinArc`, so the refcount
+ /// is not modified.
+ #[inline]
+ pub fn from_thin(a: ThinArc<H, T>) -> Self {
+ let a = ManuallyDrop::new(a);
+ let ptr = thin_to_thick(a.ptr.as_ptr());
+ unsafe {
+ Arc {
+ p: ptr::NonNull::new_unchecked(ptr),
+ phantom: PhantomData,
+ }
+ }
+ }
+}
+
+impl<H: PartialEq, T: PartialEq> PartialEq for ThinArc<H, T> {
+ #[inline]
+ fn eq(&self, other: &ThinArc<H, T>) -> bool {
+ ThinArc::with_arc(self, |a| ThinArc::with_arc(other, |b| *a == *b))
+ }
+}
+
+impl<H: Eq, T: Eq> Eq for ThinArc<H, T> {}
+
+impl<H: Hash, T: Hash> Hash for ThinArc<H, T> {
+ fn hash<HSR: Hasher>(&self, state: &mut HSR) {
+ ThinArc::with_arc(self, |a| a.hash(state))
+ }
+}
+
+impl<H: fmt::Debug, T: fmt::Debug> fmt::Debug for ThinArc<H, T> {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ fmt::Debug::fmt(&**self, f)
+ }
+}
+
+impl<H, T> fmt::Pointer for ThinArc<H, T> {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ fmt::Pointer::fmt(&self.ptr(), f)
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use crate::{Arc, HeaderWithLength, ThinArc};
+ use alloc::vec;
+ use core::clone::Clone;
+ use core::ops::Drop;
+ use core::sync::atomic;
+ use core::sync::atomic::Ordering::{Acquire, SeqCst};
+
+ #[derive(PartialEq)]
+ struct Canary(*mut atomic::AtomicUsize);
+
+ impl Drop for Canary {
+ fn drop(&mut self) {
+ unsafe {
+ (*self.0).fetch_add(1, SeqCst);
+ }
+ }
+ }
+
+ #[test]
+ fn empty_thin() {
+ let header = HeaderWithLength::new(100u32, 0);
+ let x = Arc::from_header_and_iter(header, core::iter::empty::<i32>());
+ let y = Arc::into_thin(x.clone());
+ assert_eq!(y.header.header, 100);
+ assert!(y.slice.is_empty());
+ assert_eq!(x.header.header, 100);
+ assert!(x.slice.is_empty());
+ }
+
+ #[test]
+ fn thin_assert_padding() {
+ #[derive(Clone, Default)]
+ #[repr(C)]
+ struct Padded {
+ i: u16,
+ }
+
+ // The header will have more alignment than `Padded`
+ let header = HeaderWithLength::new(0i32, 2);
+ let items = vec![Padded { i: 0xdead }, Padded { i: 0xbeef }];
+ let a = ThinArc::from_header_and_iter(header, items.into_iter());
+ assert_eq!(a.slice.len(), 2);
+ assert_eq!(a.slice[0].i, 0xdead);
+ assert_eq!(a.slice[1].i, 0xbeef);
+ }
+
+ #[test]
+ #[allow(clippy::redundant_clone, clippy::eq_op)]
+ fn slices_and_thin() {
+ let mut canary = atomic::AtomicUsize::new(0);
+ let c = Canary(&mut canary as *mut atomic::AtomicUsize);
+ let v = vec![5, 6];
+ let header = HeaderWithLength::new(c, v.len());
+ {
+ let x = Arc::into_thin(Arc::from_header_and_slice(header, &v));
+ let y = ThinArc::with_arc(&x, |q| q.clone());
+ let _ = y.clone();
+ let _ = x == x;
+ Arc::from_thin(x.clone());
+ }
+ assert_eq!(canary.load(Acquire), 1);
+ }
+
+ #[test]
+ #[allow(clippy::redundant_clone, clippy::eq_op)]
+ fn iter_and_thin() {
+ let mut canary = atomic::AtomicUsize::new(0);
+ let c = Canary(&mut canary as *mut atomic::AtomicUsize);
+ let v = vec![5, 6];
+ let header = HeaderWithLength::new(c, v.len());
+ {
+ let x = Arc::into_thin(Arc::from_header_and_iter(header, v.into_iter()));
+ let y = ThinArc::with_arc(&x, |q| q.clone());
+ let _ = y.clone();
+ let _ = x == x;
+ Arc::from_thin(x.clone());
+ }
+ assert_eq!(canary.load(Acquire), 1);
+ }
+
+ #[test]
+ fn into_raw_and_from_raw() {
+ let mut canary = atomic::AtomicUsize::new(0);
+ let c = Canary(&mut canary as *mut atomic::AtomicUsize);
+ let v = vec![5, 6];
+ let header = HeaderWithLength::new(c, v.len());
+ {
+ type ThinArcCanary = ThinArc<Canary, u32>;
+ let x: ThinArcCanary = Arc::into_thin(Arc::from_header_and_iter(header, v.into_iter()));
+ let ptr = x.as_ptr();
+
+ assert_eq!(x.into_raw(), ptr);
+
+ let _x = unsafe { ThinArcCanary::from_raw(ptr) };
+ }
+ assert_eq!(canary.load(Acquire), 1);
+ }
+}