refutil 0.1.1

Various utilities for reference, lifetime and memory management
Documentation
//! # Reverting Reference
//! When giving an immutable reference to outer code, it is guaranteed that
//! after it expires, referred object will be the same. But if receiving code
//! wants to use modified data, it has to expensively clone.
//!
//! It is possible to pass a mutable reference instead, but in that case object
//! might be different than from before.
//!
//! Reverting reference (or reverting borrow) is a middle ground between
//! immutable and mutable references: it allows to mutate the borrowed object
//! but requires that it will be reverted to it's original state:
//! * Sematically, by being a distinct type
//! * Optionally with a runtime check
//!
//! The reference itself is a [`RevMut`] type and checks are done by
//! implementors of the [`RevertCheck`] trait.

use core::{
    marker::PhantomData,
    mem::ManuallyDrop,
    ops::{Deref, DerefMut},
};

/// Used to check whether an object was reverted to an older state.
pub trait RevertCheck<D: ?Sized> {
    /// Storage of "older state" for this type.
    type Snapshot;

    /// Check if current object is semantically equivalent to a given snapshot.
    fn ensure_same(from: &D, other: Self::Snapshot);

    /// Crates a snapshot of the current state for later check.
    fn snapshot(from: &D) -> Self::Snapshot;
}

/// Revert checker that uses [clones](Clone) as snapshots and [equality](Eq)
/// as revert check, [panicking](panic) on failure.
pub struct CloneEqPanicRevertCheck;

impl<D: Eq + Clone> RevertCheck<D> for CloneEqPanicRevertCheck {
    type Snapshot = D;

    fn ensure_same(from: &D, other: Self::Snapshot) {
        #[cfg(feature = "std")]
        if std::thread::panicking() {
            return;
        }
        if from != &other {
            panic!("Value was modified and not returned to its original state");
        }
    }

    fn snapshot(from: &D) -> Self::Snapshot {
        from.clone()
    }
}

/// Forwards to a given revert checker unless [`debug_assertions`](https://doc.rust-lang.org/rustc/codegen-options/index.html#debug-assertions)
/// are disabled.
pub struct ControlledRevertCheck<C>(PhantomData<C>);

impl<D, C: RevertCheck<D>> RevertCheck<D> for ControlledRevertCheck<C> {
    type Snapshot = cfg_select! {
        debug_assertions => C::Snapshot,
        _ => (),
    };

    #[cfg_attr(not(debug_assertions), allow(unused_variables))]
    fn ensure_same(from: &D, other: Self::Snapshot) {
        #[cfg(debug_assertions)]
        C::ensure_same(from, other);
    }

    #[cfg_attr(not(debug_assertions), allow(unused_variables))]
    fn snapshot(from: &D) -> Self::Snapshot {
        cfg_select! {
            debug_assertions => C::snapshot(from),
            _ => (),
        }
    }
}

type DefaultRevertCheck = ControlledRevertCheck<CloneEqPanicRevertCheck>;

/// A reverting reference - mutable borrow that must return borrowed object to
/// its initial state, which is enforced by a selected [checker](crate::revcheck::RevertCheck).
///
/// # Default validation
/// Default checker is a combination of [`ControlledRevertCheck`] and
/// [`CloneEqPanicRevertCheck`], using [Clone] and [Eq] for validation and
/// [panicking](panic) on failure unless [`debug_assertions`](https://doc.rust-lang.org/rustc/codegen-options/index.html#debug-assertions)
/// are disabled, otherwise becoming a 0-cost wrapper around `&'a mut T`.
///
/// # Safety guarantees
/// This type will always [create a snapshot of held reference](RevertCheck::snapshot)
/// when constructed and [verify it](RevertCheck::ensure_same) when destructed;
/// you can rely on this fact in unsafe code:
/// ```
/// # use refutil::revref::RevMut;
/// # use std::ops::DerefMut;
/// #
/// fn unknown_fn(mut rev: impl DerefMut<Target = *const i32>) {
///   *rev = rev.wrapping_add(12);
///   *rev = rev.wrapping_sub(12);
/// }
///
/// let data = [1, 2, 3];
/// let mut ptr = &raw const data[0];
///
/// unknown_fn(RevMut::<_>::new(&mut ptr));
///
/// // SAFETY: `ptr` was created from a valid reference - `data[0]`. Although
/// // `unknown_fn` could possibly mutate it, `CloneEqPanicRevertCheck` would
/// // panic if its value was not reverted back to `data[0]` - a valid reference.
/// assert_eq!(data[0], unsafe { ptr.read() });
/// ```
pub struct RevMut<D: DerefMut, C: RevertCheck<D::Target> = DefaultRevertCheck> {
    new: ManuallyDrop<D>,
    old: ManuallyDrop<C::Snapshot>,
}

impl<D: DerefMut, C: RevertCheck<D::Target>> RevMut<D, C> {
    /// Creates a new reverting reference.
    ///
    /// Since this type implements [`DerefMut`], this function can also be used
    /// for reborrow.
    pub fn new(value: D) -> Self {
        Self {
            old: ManuallyDrop::new(C::snapshot(value.deref())),
            new: ManuallyDrop::new(value),
        }
    }

    // SAFETY: `this` must not be used again
    unsafe fn unwrap_core(this: &mut Self) -> D {
        // SAFETY: guaranteed by the caller
        let old = unsafe { ManuallyDrop::take(&mut this.old) };
        // SAFETY: same as above
        let new = unsafe { ManuallyDrop::take(&mut this.new) };

        C::ensure_same(&new, old);
        new
    }

    /// Converts back into a mutable reference.
    pub fn unwrap(this: Self) -> D {
        let mut this = ManuallyDrop::new(this);
        // SAFETY: `this` is forgotten after and therefore never unused again
        let new = unsafe { Self::unwrap_core(&mut this) };
        new
    }
}

impl<D: DerefMut, C: RevertCheck<D::Target>> Drop for RevMut<D, C> {
    fn drop(&mut self) {
        // SAFETY: `self` is being dropped and therefore never unused again
        _ = unsafe { Self::unwrap_core(self) };
    }
}

impl<D: DerefMut, C: RevertCheck<D::Target>> Deref for RevMut<D, C> {
    type Target = D::Target;

    fn deref(&self) -> &Self::Target {
        self.new.deref()
    }
}

impl<D: DerefMut, C: RevertCheck<D::Target>> DerefMut for RevMut<D, C> {
    fn deref_mut(&mut self) -> &mut Self::Target {
        self.new.deref_mut()
    }
}