fayalite 0.2.0

Hardware Description Language embedded in Rust, using FIRRTL's semantics
Documentation
// SPDX-License-Identifier: LGPL-3.0-or-later
// See Notices.txt for copyright information
mod safety_boundary {
    use std::{cell::Cell, ptr::NonNull};

    pub(super) struct Impl<T: ?Sized>(Cell<Option<NonNull<T>>>);

    impl<T: ?Sized> Impl<T> {
        #[inline]
        pub(super) const fn new() -> Self {
            Self(Cell::new(None))
        }
        #[inline]
        pub(super) const fn opt_static_ref(v: Option<&'static T>) -> Self {
            Self(Cell::new(if let Some(v) = v {
                // SAFETY: v is a valid reference for lifetime 'static
                unsafe { Some(NonNull::new_unchecked(v as *const T as *mut T)) }
            } else {
                None
            }))
        }
        /// set `self` to `value` for the duration of the `f()` call.
        #[inline]
        pub(super) fn set_opt<F: FnOnce() -> R, R>(&self, value: Option<&T>, f: F) -> R {
            struct ResetOnDrop<'a, T: ?Sized> {
                this: &'a Impl<T>,
                old: Option<NonNull<T>>,
            }
            impl<T: ?Sized> Drop for ResetOnDrop<'_, T> {
                fn drop(&mut self) {
                    self.this.0.set(self.old);
                }
            }
            // reset to old value before exiting this function ensuring `self`
            // is not set to `value` when its lifetime is expired
            let _reset_on_drop = ResetOnDrop {
                this: self,
                old: self.0.replace(value.map(NonNull::from)),
            };
            f()
        }
        #[inline]
        pub(super) fn get_ptr(&self) -> Option<NonNull<T>> {
            self.0.get()
        }
        /// get the reference in `self` for the duration of the `f(...)` call.
        #[inline]
        pub(super) fn with_opt<F: for<'a> FnOnce(Option<&'a T>) -> R, R>(&self, f: F) -> R {
            // SAFETY:
            // `self.0` is only `Some` when inside some `set_opt` call or when set
            // to some `&'static T`, which ensures that the pointer is live and valid.
            //
            // the reference we give away has its lifetime scoped to this
            // function call which ensures that it won't escape
            unsafe { f(self.0.get().map(|v| &*v.as_ptr())) }
        }
    }
}

/// holds a `Cell<Option<&'scoped T>>` where `'scoped` is erased. This is useful for holding references in TLS.
pub struct ScopedRef<T: ?Sized>(safety_boundary::Impl<T>);

impl<T: ?Sized> ScopedRef<T> {
    /// create a new empty [`ScopedRef`]
    #[inline]
    pub const fn new() -> Self {
        Self(safety_boundary::Impl::new())
    }
    #[inline]
    pub const fn opt_static_ref(v: Option<&'static T>) -> Self {
        Self(safety_boundary::Impl::opt_static_ref(v))
    }
    #[inline]
    pub const fn static_ref(v: &'static T) -> Self {
        Self::opt_static_ref(Some(v))
    }
    /// set `self` to `value` for the duration of the `f()` call.
    #[inline]
    pub fn set_opt<F: FnOnce() -> R, R>(&self, value: Option<&T>, f: F) -> R {
        self.0.set_opt(value, f)
    }
    /// set `self` to `value` for the duration of the `f()` call.
    #[inline]
    pub fn set<F: FnOnce() -> R, R>(&self, value: &T, f: F) -> R {
        self.0.set_opt(Some(value), f)
    }
    #[inline]
    pub fn is_some(&self) -> bool {
        self.0.get_ptr().is_some()
    }
    #[inline]
    pub fn is_none(&self) -> bool {
        self.0.get_ptr().is_none()
    }
    /// get the reference in `self` for the duration of the `f(...)` call. panics if no reference is set.
    #[inline]
    pub fn with<F: FnOnce(&T) -> R, R>(&self, f: F) -> R {
        self.0.with_opt(
            #[inline]
            |v| f(v.expect("called ScopedRef::with on an empty ScopedRef")),
        )
    }
    /// get the reference in `self` for the duration of the `f(...)` call.
    #[inline]
    pub fn with_opt<F: FnOnce(Option<&T>) -> R, R>(&self, f: F) -> R {
        self.0.with_opt(f)
    }
}

impl<T: ?Sized> Default for ScopedRef<T> {
    fn default() -> Self {
        Self::new()
    }
}