#![allow(unsafe_code)]
use std::mem::ManuallyDrop;
use std::sync::Arc;
pub struct SelfRef<T: 'static> {
value: ManuallyDrop<T>,
backing: ManuallyDrop<Backing>,
}
pub trait SharedBacking: Send + Sync + 'static {
fn as_bytes(&self) -> &[u8];
}
pub enum Backing {
Boxed(Box<[u8]>),
Shared(Arc<dyn SharedBacking>),
}
impl Backing {
pub fn shared(shared: Arc<dyn SharedBacking>) -> Self {
Self::Shared(shared)
}
pub fn as_bytes(&self) -> &[u8] {
match self {
Backing::Boxed(b) => b,
Backing::Shared(s) => s.as_bytes(),
}
}
}
impl<T: 'static> Drop for SelfRef<T> {
fn drop(&mut self) {
unsafe {
ManuallyDrop::drop(&mut self.value);
ManuallyDrop::drop(&mut self.backing);
}
}
}
impl<T: 'static> SelfRef<T> {
pub fn try_new<E>(
backing: Backing,
builder: impl FnOnce(&'static [u8]) -> Result<T, E>,
) -> Result<Self, E> {
let bytes: &'static [u8] = unsafe {
let b = backing.as_bytes();
std::slice::from_raw_parts(b.as_ptr(), b.len())
};
let value = builder(bytes)?;
Ok(Self {
value: ManuallyDrop::new(value),
backing: ManuallyDrop::new(backing),
})
}
pub fn new(backing: Backing, builder: impl FnOnce(&'static [u8]) -> T) -> Self {
Self::try_new(backing, |bytes| {
Ok::<_, std::convert::Infallible>(builder(bytes))
})
.unwrap_or_else(|e: std::convert::Infallible| match e {})
}
pub fn owning(backing: Backing, value: T) -> Self {
Self {
value: ManuallyDrop::new(value),
backing: ManuallyDrop::new(backing),
}
}
pub fn try_repack<U: 'static, E>(
mut self,
f: impl FnOnce(T, &'static [u8]) -> Result<U, E>,
) -> Result<SelfRef<U>, E> {
let value = unsafe { ManuallyDrop::take(&mut self.value) };
let backing = unsafe { ManuallyDrop::take(&mut self.backing) };
core::mem::forget(self);
let bytes: &'static [u8] = unsafe {
let b = backing.as_bytes();
std::slice::from_raw_parts(b.as_ptr(), b.len())
};
match f(value, bytes) {
Ok(u) => Ok(SelfRef {
value: ManuallyDrop::new(u),
backing: ManuallyDrop::new(backing),
}),
Err(e) => Err(e),
}
}
pub fn try_map<U: 'static, E>(
mut self,
f: impl FnOnce(T) -> Result<U, E>,
) -> Result<SelfRef<U>, E> {
let value = unsafe { ManuallyDrop::take(&mut self.value) };
let backing = unsafe { ManuallyDrop::take(&mut self.backing) };
core::mem::forget(self);
match f(value) {
Ok(u) => Ok(SelfRef {
value: ManuallyDrop::new(u),
backing: ManuallyDrop::new(backing),
}),
Err(e) => Err(e),
}
}
pub fn map<U: 'static>(mut self, f: impl FnOnce(T) -> U) -> SelfRef<U> {
let value = unsafe { ManuallyDrop::take(&mut self.value) };
let backing = unsafe { ManuallyDrop::take(&mut self.backing) };
core::mem::forget(self);
SelfRef {
value: ManuallyDrop::new(f(value)),
backing: ManuallyDrop::new(backing),
}
}
}
pub unsafe trait Reborrow: 'static {
type Ref<'a>;
}
impl<T: Reborrow> SelfRef<T> {
pub fn get(&self) -> &T::Ref<'_> {
unsafe { core::mem::transmute::<&T, &T::Ref<'_>>(&self.value) }
}
}
unsafe impl Reborrow for u32 {
type Ref<'a> = u32;
}
unsafe impl Reborrow for usize {
type Ref<'a> = usize;
}
unsafe impl Reborrow for i8 {
type Ref<'a> = i8;
}
unsafe impl Reborrow for i16 {
type Ref<'a> = i16;
}
unsafe impl Reborrow for i32 {
type Ref<'a> = i32;
}
unsafe impl Reborrow for i64 {
type Ref<'a> = i64;
}
unsafe impl Reborrow for i128 {
type Ref<'a> = i128;
}
unsafe impl Reborrow for u8 {
type Ref<'a> = u8;
}
unsafe impl Reborrow for u16 {
type Ref<'a> = u16;
}
unsafe impl Reborrow for u64 {
type Ref<'a> = u64;
}
unsafe impl Reborrow for u128 {
type Ref<'a> = u128;
}
unsafe impl Reborrow for f32 {
type Ref<'a> = f32;
}
unsafe impl Reborrow for f64 {
type Ref<'a> = f64;
}
unsafe impl Reborrow for bool {
type Ref<'a> = bool;
}
unsafe impl Reborrow for char {
type Ref<'a> = char;
}
unsafe impl Reborrow for String {
type Ref<'a> = String;
}
unsafe impl Reborrow for &'static str {
type Ref<'a> = &'a str;
}
#[macro_export]
macro_rules! impl_reborrow {
($($ty:ident),* $(,)?) => {
$(
unsafe impl $crate::Reborrow for $ty<'static> {
type Ref<'a> = $ty<'a>;
}
)*
};
}
#[macro_export]
macro_rules! selfref_match {
(
$selfref:expr, $field:ident {
$( $first:ident $(:: $rest:ident)* ($binding:tt) => $body:block )*
}
) => {{
let __sref = $selfref;
$(
if ::core::matches!(&__sref.get().$field, $first$(::$rest)*(_)) {
#[allow(unused_variables)]
let $binding = __sref.map(|__v| match __v.$field {
$first$(::$rest)*(__inner) => __inner,
_ => unreachable!(),
});
$body
} else
)*
{
let _ = __sref;
}
}};
}
#[cfg(test)]
mod tests {
use super::*;
use std::sync::atomic::{AtomicBool, Ordering};
unsafe impl Reborrow for &'static [u8] {
type Ref<'a> = &'a [u8];
}
unsafe impl Reborrow for (u32, u8, u8) {
type Ref<'a> = (u32, u8, u8);
}
struct TestSharedBacking {
bytes: Vec<u8>,
dropped: Arc<AtomicBool>,
}
impl SharedBacking for TestSharedBacking {
fn as_bytes(&self) -> &[u8] {
&self.bytes
}
}
impl Drop for TestSharedBacking {
fn drop(&mut self) {
self.dropped.store(true, Ordering::Release);
}
}
struct DropOrderValue {
backing_dropped: Arc<AtomicBool>,
value_dropped_before_backing: Arc<AtomicBool>,
}
impl Drop for DropOrderValue {
fn drop(&mut self) {
let backing_is_dropped = self.backing_dropped.load(Ordering::Acquire);
self.value_dropped_before_backing
.store(!backing_is_dropped, Ordering::Release);
}
}
#[test]
fn try_new_builds_borrowing_value_from_backing() {
let backing = Backing::Boxed(Box::from([1_u8, 2, 3, 4]));
let sref = SelfRef::try_new(backing, |bytes| Ok::<_, ()>(&bytes[1..3]))
.expect("try_new should succeed");
assert_eq!(sref.get(), &[2_u8, 3]);
}
#[test]
fn try_new_propagates_builder_error() {
let backing = Backing::Boxed(Box::from([9_u8, 8, 7]));
let err = match SelfRef::<u32>::try_new(backing, |_| Err::<u32, _>("boom")) {
Ok(_) => panic!("try_new should return builder error"),
Err(err) => err,
};
assert_eq!(err, "boom");
}
#[test]
fn try_map_and_try_repack_preserve_backing_and_transform_value() {
let backing = Backing::Boxed(Box::from(*b"hello"));
let sref = SelfRef::new(backing, |bytes| bytes);
let len_ref = sref
.try_map(|bytes| Ok::<_, ()>(bytes.len()))
.expect("try_map should succeed");
assert_eq!(*len_ref.get(), 5);
let backing = Backing::Boxed(Box::from(*b"abcdef"));
let sref = SelfRef::new(backing, |_| 10_u32);
let repacked = sref
.try_repack(|value, bytes| Ok::<_, ()>((value + 1, bytes[0], bytes[5])))
.expect("try_repack should succeed");
assert_eq!(*repacked.get(), (11_u32, b'a', b'f'));
}
#[test]
fn try_map_and_try_repack_propagate_errors() {
let backing = Backing::Boxed(Box::from([1_u8, 2, 3]));
let sref = SelfRef::new(backing, |_| 7_u8);
let err = match sref.try_map::<u8, _>(|_| Err::<u8, _>("nope")) {
Ok(_) => panic!("try_map error should propagate"),
Err(err) => err,
};
assert_eq!(err, "nope");
let backing = Backing::Boxed(Box::from([4_u8, 5, 6]));
let sref = SelfRef::new(backing, |_| 9_u8);
let err = match sref.try_repack::<u8, _>(|_, _| Err::<u8, _>("bad")) {
Ok(_) => panic!("try_repack error should propagate"),
Err(err) => err,
};
assert_eq!(err, "bad");
}
#[test]
fn drop_order_drops_value_before_backing() {
let backing_dropped = Arc::new(AtomicBool::new(false));
let value_dropped_before_backing = Arc::new(AtomicBool::new(false));
let shared = Arc::new(TestSharedBacking {
bytes: vec![1, 2, 3],
dropped: Arc::clone(&backing_dropped),
});
let value = DropOrderValue {
backing_dropped: Arc::clone(&backing_dropped),
value_dropped_before_backing: Arc::clone(&value_dropped_before_backing),
};
let sref = SelfRef::owning(Backing::shared(shared), value);
drop(sref);
assert!(
value_dropped_before_backing.load(Ordering::Acquire),
"value should drop before backing"
);
assert!(
backing_dropped.load(Ordering::Acquire),
"backing should eventually be dropped"
);
}
}