diff --git a/Cargo.toml b/Cargo.toml index 3d9ec43..ad4eed0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -41,6 +41,7 @@ codecov = { repository = "orium/archery", branch = "master", service = "github" [dependencies] static_assertions = "1.1.0" +triomphe = { version = "0.1.9", optional = true } [dev-dependencies] criterion = { version = "0.5.1", features = ["html_reports"] } @@ -49,6 +50,7 @@ compiletest_rs = "0.10.2" [features] fatal-warnings = [] +triomphe = ["dep:triomphe"] [[bench]] name = "std_rc" diff --git a/benches/archery_shared_pointer_arc.rs b/benches/archery_shared_pointer_arc.rs index e8adb64..37be4c5 100644 --- a/benches/archery_shared_pointer_arc.rs +++ b/benches/archery_shared_pointer_arc.rs @@ -41,5 +41,41 @@ fn archery_shared_pointer_arc_clone(c: &mut Criterion) { }); } -criterion_group!(benches, archery_shared_pointer_arc_deref, archery_shared_pointer_arc_clone); +fn archery_shared_pointer_arct_deref(c: &mut Criterion) { + let limit = 200_000; + + c.bench_function("archery shared pointer arct deref", move |b| { + b.iter(|| { + let rc: SharedPointer<_, ArcTK> = SharedPointer::new(42); + + for _ in 0..limit { + black_box(rc.deref()); + } + + rc + }) + }); +} + +fn archery_shared_pointer_arct_clone(c: &mut Criterion) { + let limit = 100_000; + + c.bench_function("archery shared pointer arct clone and drop", move |b| { + b.iter_with_setup( + || Vec::with_capacity(limit), + |mut vec| { + vec.resize(limit, SharedPointer::<_, ArcTK>::new(42)); + vec + }, + ) + }); +} + +criterion_group!( + benches, + archery_shared_pointer_arc_deref, + archery_shared_pointer_arc_clone, + archery_shared_pointer_arct_deref, + archery_shared_pointer_arct_clone +); criterion_main!(benches); diff --git a/benches/archery_shared_pointer_arct.rs b/benches/archery_shared_pointer_arct.rs new file mode 100644 index 0000000..9e0b056 --- /dev/null +++ b/benches/archery_shared_pointer_arct.rs @@ -0,0 +1,45 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#![cfg_attr(feature = "fatal-warnings", deny(warnings))] + +use archery::*; +use std::ops::Deref; + +use criterion::{criterion_group, criterion_main, Criterion}; +use std::hint::black_box; + +fn archery_shared_pointer_arct_deref(c: &mut Criterion) { + let limit = 200_000; + + c.bench_function("archery shared pointer arct deref", move |b| { + b.iter(|| { + let rc: SharedPointer<_, ArcTK> = SharedPointer::new(42); + + for _ in 0..limit { + black_box(rc.deref()); + } + + rc + }) + }); +} + +fn archery_shared_pointer_arct_clone(c: &mut Criterion) { + let limit = 100_000; + + c.bench_function("archery shared pointer arct clone and drop", move |b| { + b.iter_with_setup( + || Vec::with_capacity(limit), + |mut vec| { + vec.resize(limit, SharedPointer::<_, ArcTK>::new(42)); + vec + }, + ) + }); +} + +criterion_group!(benches, archery_shared_pointer_arct_deref, archery_shared_pointer_arct_clone); +criterion_main!(benches); diff --git a/src/lib.rs b/src/lib.rs index 7a45e1e..89044a8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -133,5 +133,8 @@ pub use shared_pointer::kind::SharedPointerKind; #[doc(no_inline)] pub use shared_pointer::kind::ArcK; +#[cfg(feature = "triomphe")] +#[doc(no_inline)] +pub use shared_pointer::kind::ArcTK; #[doc(no_inline)] pub use shared_pointer::kind::RcK; diff --git a/src/shared_pointer/kind/arct/mod.rs b/src/shared_pointer/kind/arct/mod.rs new file mode 100644 index 0000000..148e175 --- /dev/null +++ b/src/shared_pointer/kind/arct/mod.rs @@ -0,0 +1,124 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +use crate::shared_pointer::kind::SharedPointerKind; +use alloc::boxed::Box; +use core::fmt; +use core::fmt::Debug; +use core::fmt::Formatter; +use core::mem; +use core::mem::ManuallyDrop; +use core::ops::Deref; +use core::ops::DerefMut; +use core::ptr; +use triomphe::Arc; + +type UntypedArc = Arc<()>; + +/// [Type constructors](https://en.wikipedia.org/wiki/Type_constructor) for +/// [`Arc`] pointers. +pub struct ArcTK { + /// We use [`ManuallyDrop`] here, so that we can drop it explicitly as + /// [`Arc`](triomphe::Arc). Not sure if it can be dropped as [`UntypedArc`], but it + /// seems to be playing with fire (even more than we already are). + inner: ManuallyDrop, +} + +impl ArcTK { + #[inline(always)] + fn new_from_inner(arc: Arc) -> ArcTK { + ArcTK { inner: ManuallyDrop::new(unsafe { mem::transmute(arc) }) } + } + + #[inline(always)] + unsafe fn take_inner(self) -> Arc { + let arc: UntypedArc = ManuallyDrop::into_inner(self.inner); + + mem::transmute(arc) + } + + #[inline(always)] + unsafe fn as_inner_ref(&self) -> &Arc { + let arc_t: *const Arc = (self.inner.deref() as *const UntypedArc).cast::>(); + + // Static check to make sure we are not messing up the sizes. + // This could happen if we allowed for `T` to be unsized, because it would need to be + // represented as a wide pointer inside `Arc`. + // TODO Use static_assertion when https://github.com/nvzqz/static-assertions-rs/issues/21 + // gets fixed + let _ = mem::transmute::>; + + &*arc_t + } + + #[inline(always)] + unsafe fn as_inner_mut(&mut self) -> &mut Arc { + let arc_t: *mut Arc = (self.inner.deref_mut() as *mut UntypedArc).cast::>(); + + &mut *arc_t + } +} + +unsafe impl SharedPointerKind for ArcTK { + #[inline(always)] + fn new(v: T) -> ArcTK { + ArcTK::new_from_inner(Arc::new(v)) + } + + #[inline(always)] + fn from_box(v: Box) -> ArcTK { + ArcTK::new_from_inner::(Arc::from(v)) + } + + #[inline(always)] + unsafe fn as_ptr(&self) -> *const T { + Arc::as_ptr(self.as_inner_ref()) + } + + #[inline(always)] + unsafe fn deref(&self) -> &T { + self.as_inner_ref::().as_ref() + } + + #[inline(always)] + unsafe fn try_unwrap(self) -> Result { + Arc::try_unwrap(self.take_inner()).map_err(ArcTK::new_from_inner) + } + + #[inline(always)] + unsafe fn get_mut(&mut self) -> Option<&mut T> { + Arc::get_mut(self.as_inner_mut()) + } + + #[inline(always)] + unsafe fn make_mut(&mut self) -> &mut T { + Arc::make_mut(self.as_inner_mut()) + } + + #[inline(always)] + unsafe fn strong_count(&self) -> usize { + Arc::count(self.as_inner_ref::()) + } + + #[inline(always)] + unsafe fn clone(&self) -> ArcTK { + ArcTK { inner: ManuallyDrop::new(Arc::clone(self.as_inner_ref())) } + } + + #[inline(always)] + unsafe fn drop(&mut self) { + ptr::drop_in_place::>(self.as_inner_mut()); + } +} + +impl Debug for ArcTK { + #[inline(always)] + fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> { + f.write_str("ArcTK") + } +} + +#[cfg(test)] +mod test; diff --git a/src/shared_pointer/kind/arct/test.rs b/src/shared_pointer/kind/arct/test.rs new file mode 100644 index 0000000..5ce8c2e --- /dev/null +++ b/src/shared_pointer/kind/arct/test.rs @@ -0,0 +1,193 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +use super::*; +use pretty_assertions::assert_eq; +use static_assertions::assert_impl_all; +use std::cell::Cell; +use std::string::ToString; + +type PointerKind = ArcTK; + +assert_impl_all!(ArcTK: Send, Sync); + +#[test] +fn test_from_box_t() { + let mut ptr = PointerKind::from_box(Box::new(42)); + + unsafe { + assert_eq!(ptr.deref::(), &42); + + ptr.drop::(); + } +} + +#[test] +fn test_as_ptr() { + let mut x = PointerKind::new::<&'static str>("hello"); + + unsafe { + let mut y = PointerKind::clone::<&'static str>(&x); + let x_ptr: *const &'static str = PointerKind::as_ptr(&x); + + assert_eq!(x_ptr, PointerKind::as_ptr(&y)); + assert_eq!(*x_ptr, "hello"); + + x.drop::<&'static str>(); + y.drop::<&'static str>(); + } +} + +#[test] +fn test_deref() { + let mut ptr_42 = PointerKind::new::(42); + let mut ptr_box_dyn_hello = PointerKind::new::>(Box::new("hello")); + + unsafe { + assert_eq!(ptr_42.deref::(), &42); + assert_eq!(ptr_box_dyn_hello.deref::>().to_string(), "hello"); + + ptr_42.drop::(); + ptr_box_dyn_hello.drop::>(); + } +} + +#[test] +fn test_try_unwrap() { + let ptr = PointerKind::new::(42); + + unsafe { + assert_eq!(ptr.try_unwrap::().unwrap(), 42); + } + + let ptr = PointerKind::new::(42); + + unsafe { + let ptr_clone = ptr.clone::(); + + let mut ptr_clone = ptr_clone.try_unwrap::().unwrap_err(); + let mut ptr = ptr.try_unwrap::().unwrap_err(); + + assert_eq!(ptr.deref::(), &42); + assert_eq!(ptr_clone.deref::(), &42); + + ptr.drop::(); + ptr_clone.drop::(); + } +} + +#[test] +fn test_get_mut() { + let mut ptr = PointerKind::new::(42); + + unsafe { + assert_eq!(ptr.deref::(), &42); + + *ptr.get_mut::().unwrap() += 1; + + assert_eq!(ptr.deref::(), &43); + + let mut ptr_clone = ptr.clone::(); + + assert_eq!(ptr.get_mut::(), None); + assert_eq!(ptr_clone.get_mut::(), None); + + ptr.drop::(); + + *ptr_clone.get_mut::().unwrap() += 1; + + assert_eq!(ptr_clone.deref::(), &44); + + ptr_clone.drop::(); + } +} + +#[test] +fn test_make_mut() { + let mut ptr = PointerKind::new::(42); + + unsafe { + assert_eq!(ptr.deref::(), &42); + + *ptr.make_mut::() += 1; + + assert_eq!(ptr.deref::(), &43); + + // Clone to force make_mut to clone the data. + let mut ptr_clone = ptr.clone::(); + + assert_eq!(ptr_clone.deref::(), &43); + + *ptr_clone.make_mut::() += 1; + + assert_eq!(ptr.deref::(), &43); + assert_eq!(ptr_clone.deref::(), &44); + + *ptr.make_mut::() *= 2; + + assert_eq!(ptr.deref::(), &(2 * 43)); + assert_eq!(ptr_clone.deref::(), &44); + + ptr.drop::(); + + assert_eq!(ptr_clone.deref::(), &44); + + ptr_clone.drop::(); + } +} + +#[test] +fn test_strong_count() { + let mut ptr = PointerKind::new::(42); + + unsafe { + assert_eq!(ptr.strong_count::(), 1); + + let mut ptr_clone = ptr.clone::(); + + assert_eq!(ptr.strong_count::(), 2); + assert_eq!(ptr_clone.strong_count::(), 2); + + ptr.drop::(); + + assert_eq!(ptr_clone.strong_count::(), 1); + + ptr_clone.drop::(); + } +} + +#[test] +fn test_clone() { + let mut ptr = PointerKind::new::>(Cell::new(42)); + + unsafe { + let mut ptr_clone = ptr.clone::>(); + + assert_eq!(ptr.deref::>().get(), 42); + assert_eq!(ptr_clone.deref::>().get(), 42); + + ptr_clone.deref::>().set(3); + + assert_eq!(ptr.deref::>().get(), 3); + assert_eq!(ptr_clone.deref::>().get(), 3); + + ptr.drop::>(); + + assert_eq!(ptr_clone.deref::>().get(), 3); + + ptr_clone.drop::>(); + } +} + +#[test] +fn test_debug() { + let mut ptr = PointerKind::new::(42); + + assert_eq!(format!("{:?}", ptr), "ArcTK"); + + unsafe { + ptr.drop::(); + } +} diff --git a/src/shared_pointer/kind/mod.rs b/src/shared_pointer/kind/mod.rs index ad501ec..defe19f 100644 --- a/src/shared_pointer/kind/mod.rs +++ b/src/shared_pointer/kind/mod.rs @@ -53,10 +53,15 @@ pub unsafe trait SharedPointerKind: Sized + Debug { } mod arc; +#[cfg(feature = "triomphe")] +mod arct; mod rc; use alloc::boxed::Box; #[doc(inline)] pub use arc::ArcK; +#[cfg(feature = "triomphe")] +#[doc(inline)] +pub use arct::ArcTK; #[doc(inline)] pub use rc::RcK; diff --git a/tests/tests.rs b/tests/tests.rs index 15ca900..887ece5 100644 --- a/tests/tests.rs +++ b/tests/tests.rs @@ -26,7 +26,8 @@ fn rustc_flags(dependency_path: &str, dependencies: &[&str]) -> String { let mut flags = format!("--edition=2021 -L dependency={} ", dependency_path); for dep in dependencies { - let rlib_path = find_rlib(dependency_path, dep).expect("io error").expect("rlib not found"); + let rlib_path = + find_rlib(dependency_path, dbg!(dep)).expect("io error").expect("rlib not found"); flags.push_str(&format!("--extern {}={} ", dep, rlib_path.display())); }