contain 0.4.0

A crate for defining/extending lifetimes
Documentation
#![warn(clippy::cargo)]

use std::{collections::HashMap, hash::Hash, marker::PhantomData};

use parking_lot::Mutex;

/// A trait for creating a reference from the specified value.
/// The resulting reference can live as long as the `Container` does.
pub trait Container<T> {
    /// Holds the given value.
    /// This returns a reference with the same lifetime as the [`Container`].
    fn put(&self, item: T) -> &T;
}

/// A trait for creating a mutable reference from the specified value.
/// The resulting reference can live as long as the `Container` does.
/// See [`Container<T>`] for creating immutable references.
pub trait ContainerMut<T> {
    /// Holds the given value.
    /// This returns a mutable reference with the same lifetime as the [`Container`].
    #[allow(clippy::mut_from_ref)]
    fn put_mut(&self, item: T) -> &mut T;
}

/// A basic fast implementation of [`Container`] and [`ContainerMut`] backed by [`Vec`].
///
/// # Example
/// ```
/// use contain::{Container, SimpleContainer};
///
/// fn append_thing<'a>(container: &'a impl Container<String>, s: &str) -> &'a str {
///     container.put(format!("{}thing", s))
/// }
///
/// let container = SimpleContainer::new();
/// let a = append_thing(&container, "some");
/// let b = append_thing(&container, "a ");
/// let c = append_thing(&container, "that ");
/// assert_eq!(a, "something");
/// assert_eq!(b, "a thing");
/// assert_eq!(c, "that thing");
/// assert_eq!(container.len(), 3);
/// ```
#[allow(clippy::module_name_repetitions)]
pub struct SimpleContainer<'a, T> {
    items: Mutex<Vec<*mut T>>,
    _marker: PhantomData<&'a T>,
}

impl<'a, T> SimpleContainer<'a, T> {
    #[must_use]
    pub const fn new() -> Self {
        Self {
            items: Mutex::new(Vec::new()),
            _marker: PhantomData,
        }
    }

    #[must_use]
    pub fn with_capacity(capacity: usize) -> Self {
        Self {
            items: Mutex::new(Vec::with_capacity(capacity)),
            _marker: PhantomData,
        }
    }

    pub fn len(&self) -> usize {
        self.items.lock().len()
    }

    pub fn is_empty(&self) -> bool {
        self.items.lock().is_empty()
    }
}

impl<'a, T> Container<T> for SimpleContainer<'a, T> {
    fn put(&self, item: T) -> &T {
        unsafe {
            let item = Box::new(item);
            let item_ref: *mut T = Box::into_raw(item);
            let mut items = self.items.lock();
            items.push(item_ref);
            &*item_ref
        }
    }
}

impl<'a, T> ContainerMut<T> for SimpleContainer<'a, T> {
    fn put_mut(&self, item: T) -> &mut T {
        unsafe {
            let item = Box::new(item);
            let item_ref: *mut T = Box::into_raw(item);
            let mut items = self.items.lock();
            items.push(item_ref);
            &mut *item_ref
        }
    }
}

impl<'a, T> Default for SimpleContainer<'a, T> {
    fn default() -> Self {
        Self::new()
    }
}

impl<'a, T> Drop for SimpleContainer<'a, T> {
    fn drop(&mut self) {
        for p in self.items.lock().iter().copied() {
            unsafe {
                let _ = Box::from_raw(p);
            }
        }
    }
}

unsafe impl<'a, T: Send> Send for SimpleContainer<'a, T> {}

unsafe impl<'a, T: Send> Sync for SimpleContainer<'a, T> {}

/// A deduplicating [`Container`] backed by a [`HashMap`].
/// If two equal items are stored, the second is dropped
/// and a reference to the first is returned.
/// Whilst more resource-intensive than [`SimpleContainer`], it can be more memory efficient in scenarios where many
/// items are equal and equivalent since the duplicates will be dropped.
///
/// # Example
/// ```
/// use contain::{Container, DeduplicatingContainer};
///
/// fn append_thing<'a>(container: &'a impl Container<String>, s: &str) -> &'a str {
///     container.put(format!("{}thing", s))
/// }
///
/// let container = DeduplicatingContainer::new();
/// let a = append_thing(&container, "some");
/// let b = append_thing(&container, "a ");
/// let c = append_thing(&container, "some");
/// assert_eq!(a, "something");
/// assert_eq!(b, "a thing");
/// assert_eq!(c, "something");
/// assert_eq!(container.len(), 2);
/// ```
pub struct DeduplicatingContainer<'a, T: Eq + Hash> {
    items: Mutex<HashMap<&'a T, *mut T>>,
}

impl<'a, T: Eq + Hash> DeduplicatingContainer<'a, T> {
    #[must_use]
    pub fn new() -> Self {
        DeduplicatingContainer {
            items: Mutex::new(HashMap::new()),
        }
    }

    #[must_use]
    pub fn with_capacity(capacity: usize) -> Self {
        Self {
            items: Mutex::new(HashMap::with_capacity(capacity)),
        }
    }

    pub fn len(&self) -> usize {
        self.items.lock().len()
    }

    pub fn is_empty(&self) -> bool {
        self.items.lock().is_empty()
    }
}

impl<'a, T: Eq + Hash> Container<T> for DeduplicatingContainer<'a, T> {
    /// Manage the lifetime of a given object.
    ///
    /// # Panics
    /// May panic if invariants of Eq and Hash are not upheld.
    fn put(&self, item: T) -> &T {
        unsafe {
            let mut items = self.items.lock();
            if let Some(existing) = items.get(&item) {
                &**existing
            } else {
                let item_ref = Box::into_raw(Box::new(item));
                items.insert(&*item_ref, item_ref);
                &*item_ref
            }
        }
    }
}

impl<'a, T: Eq + Hash> Default for DeduplicatingContainer<'a, T> {
    fn default() -> Self {
        Self::new()
    }
}

impl<'a, T: Eq + Hash> Drop for DeduplicatingContainer<'a, T> {
    fn drop(&mut self) {
        for p in self.items.lock().values().copied() {
            unsafe {
                let _ = Box::from_raw(p);
            }
        }
    }
}

unsafe impl<'a, T: Eq + Hash + Send> Send for DeduplicatingContainer<'a, T> {}

unsafe impl<'a, T: Eq + Hash + Send + Sync> Sync for DeduplicatingContainer<'a, T> {}

#[cfg(test)]
mod tests {
    use std::sync::{Barrier, Arc, mpsc};

    use super::*;

    #[test]
    fn simple_test() {
        let container = SimpleContainer::new();
        let mut holder = &String::from("initial");
        println!("holder: {}", holder);
        {
            holder = container.put(String::from("next"));
        }
        println!("holder: {}", holder);
    }

    #[test]
    fn multiple_strings() {
        let container = SimpleContainer::new();
        let mut holder_1 = &String::from("initial 1");
        let mut holder_2 = &String::from("initial 2");
        assert_eq!("initial 1", holder_1);
        assert_eq!("initial 2", holder_2);
        {
            holder_1 = container.put(String::from("next 1"));
            holder_2 = container.put(String::from("next 2"));
        }

        assert_eq!("next 1", holder_1);
        assert_eq!("next 2", holder_2);
    }

    #[test]
    fn with_resize() {
        let container = SimpleContainer::with_capacity(2);
        let mut holder_1 = &String::from("initial 1");
        let mut holder_2 = &String::from("initial 2");
        let mut holder_3 = &String::from("initial 3");
        let mut holder_4 = &String::from("initial 4");
        let mut holder_5 = &String::from("initial 5");
        assert_eq!("initial 1", holder_1);
        assert_eq!("initial 2", holder_2);
        assert_eq!("initial 3", holder_3);
        assert_eq!("initial 4", holder_4);
        assert_eq!("initial 5", holder_5);
        {
            holder_1 = container.put(String::from("next 1"));
            holder_2 = container.put(String::from("next 2"));
            holder_3 = container.put(String::from("next 3"));
            holder_4 = container.put(String::from("next 4"));
            holder_5 = container.put(String::from("next 5"));
        }
        assert_eq!("next 1", holder_1);
        assert_eq!("next 2", holder_2);
        assert_eq!("next 3", holder_3);
        assert_eq!("next 4", holder_4);
        assert_eq!("next 5", holder_5);
    }

    #[test]
    fn size_check() {
        let container = SimpleContainer::new();
        let mut holder_1 = &String::from("initial 1");
        let mut holder_2 = &String::from("initial 2");
        let mut holder_3 = &String::from("initial 3");
        assert_eq!("initial 1", holder_1);
        assert_eq!("initial 2", holder_2);
        assert_eq!("initial 3", holder_3);
        {
            holder_1 = container.put(String::from("next 1"));
            holder_2 = container.put(String::from("next 2"));
            holder_3 = container.put(String::from("next 1"));
        }

        assert_eq!(container.len(), 3);
        assert_eq!("next 1", holder_1);
        assert_eq!("next 2", holder_2);
        assert_eq!("next 1", holder_3);
    }

    #[test]
    fn mutable_test() {
        let container = SimpleContainer::new();
        let mut holder_1 = &mut String::from("initial 1");
        let mut holder_2 = &mut String::from("initial 2");
        let mut holder_3 = &mut String::from("initial 3");
        assert_eq!("initial 1", holder_1);
        assert_eq!("initial 2", holder_2);
        assert_eq!("initial 3", holder_3);
        {
            holder_1 = container.put_mut(String::from("next 1"));
            holder_2 = container.put_mut(String::from("next 2"));
            holder_3 = container.put_mut(String::from("next 3"));
        }

        assert_eq!("next 1", holder_1);
        assert_eq!("next 2", holder_2);
        assert_eq!("next 3", holder_3);

        *holder_1 = String::from("final 1");

        assert_eq!("final 1", holder_1);
        assert_eq!("next 2", holder_2);
        assert_eq!("next 3", holder_3);

        *holder_2 = String::from("final 2");

        assert_eq!("final 1", holder_1);
        assert_eq!("final 2", holder_2);
        assert_eq!("next 3", holder_3);

        *holder_3 = String::from("final 3");

        assert_eq!("final 1", holder_1);
        assert_eq!("final 2", holder_2);
        assert_eq!("final 3", holder_3);

    }

    #[test]
    fn threaded_test() {
        let container = Arc::new(SimpleContainer::<String>::new());
        let container_1 = Arc::clone(&container);
        let container_2 = Arc::clone(&container);
        let barrier = Arc::new(Barrier::new(2));
        let barrier_1 = Arc::clone(&barrier);
        let barrier_2 = Arc::clone(&barrier);

        let thread_1 = std::thread::spawn(|| {
            let container = container_1;
            let barrier = barrier_1;
            barrier.wait();
            let test_from_thread_1 = container.put(String::from("test from thread 1"));
            barrier.wait();
            assert_eq!(container.len(), 2);
            assert_eq!(test_from_thread_1, "test from thread 1");
            barrier.wait();
        });

        let thread_2 = std::thread::spawn(|| {
            let container = container_2;
            let barrier = barrier_2;
            barrier.wait();
            let test_from_thread_2 = container.put(String::from("test from thread 2"));
            barrier.wait();
            assert_eq!(container.len(), 2);
            assert_eq!(test_from_thread_2, "test from thread 2");
            barrier.wait();
        });

        thread_1.join().unwrap();
        thread_2.join().unwrap();

        assert_eq!(container.len(), 2);
    }

    #[test]
    fn simple_test_deduplicating() {
        let container = DeduplicatingContainer::new();
        let mut holder = &String::from("initial");
        println!("holder: {}", holder);
        {
            holder = container.put(String::from("next"));
        }
        println!("holder: {}", holder);
    }

    #[test]
    fn multiple_strings_deduplicating() {
        let container = DeduplicatingContainer::new();
        let mut holder_1 = &String::from("initial 1");
        let mut holder_2 = &String::from("initial 2");
        assert_eq!("initial 1", holder_1);
        assert_eq!("initial 2", holder_2);
        {
            holder_1 = container.put(String::from("next 1"));
            holder_2 = container.put(String::from("next 2"));
        }

        assert_eq!("next 1", holder_1);
        assert_eq!("next 2", holder_2);
    }

    #[test]
    fn with_resize_deduplicating() {
        let container = DeduplicatingContainer::with_capacity(2);
        let mut holder_1 = &String::from("initial 1");
        let mut holder_2 = &String::from("initial 2");
        let mut holder_3 = &String::from("initial 3");
        let mut holder_4 = &String::from("initial 4");
        let mut holder_5 = &String::from("initial 5");
        assert_eq!("initial 1", holder_1);
        assert_eq!("initial 2", holder_2);
        assert_eq!("initial 3", holder_3);
        assert_eq!("initial 4", holder_4);
        assert_eq!("initial 5", holder_5);
        {
            holder_1 = container.put(String::from("next 1"));
            holder_2 = container.put(String::from("next 2"));
            holder_3 = container.put(String::from("next 3"));
            holder_4 = container.put(String::from("next 4"));
            holder_5 = container.put(String::from("next 5"));
        }
        assert_eq!("next 1", holder_1);
        assert_eq!("next 2", holder_2);
        assert_eq!("next 3", holder_3);
        assert_eq!("next 4", holder_4);
        assert_eq!("next 5", holder_5);
    }

    #[test]
    fn size_check_deduplicating() {
        let container = DeduplicatingContainer::new();
        let mut holder_1 = &String::from("initial 1");
        let mut holder_2 = &String::from("initial 2");
        let mut holder_3 = &String::from("initial 3");
        assert_eq!("initial 1", holder_1);
        assert_eq!("initial 2", holder_2);
        assert_eq!("initial 3", holder_3);
        {
            holder_1 = container.put(String::from("next 1"));
            holder_2 = container.put(String::from("next 2"));
            holder_3 = container.put(String::from("next 1"));
        }

        assert_eq!(container.len(), 2);
        assert_eq!("next 1", holder_1);
        assert_eq!("next 2", holder_2);
        assert_eq!("next 1", holder_3);
    }

    #[test]
    fn threaded_test_deduplicating() {
        let container = Arc::new(DeduplicatingContainer::<String>::new());
        let container_1 = Arc::clone(&container);
        let container_2 = Arc::clone(&container);
        let barrier = Arc::new(Barrier::new(2));
        let barrier_1 = Arc::clone(&barrier);
        let barrier_2 = Arc::clone(&barrier);
        let (send_1, recv_1) = mpsc::channel();
        let (send_2, recv_2) = std::sync::mpsc::channel();

        let thread_1 = std::thread::spawn(|| {
            let container = container_1;
            let barrier = barrier_1;
            let send = send_1;
            barrier.wait();
            let test_from_thread_1 = container.put(String::from("test from thread 1"));
            let test_from_both_threads = container.put(String::from("test from both threads"));
            barrier.wait();
            assert_eq!(container.len(), 3);
            assert_eq!(test_from_thread_1, "test from thread 1");
            assert_eq!(test_from_both_threads, "test from both threads");
            send.send(test_from_both_threads.as_ptr() as usize).unwrap();
        });

        let thread_2 = std::thread::spawn(|| {
            let container = container_2;
            let barrier = barrier_2;
            let send = send_2;
            barrier.wait();
            let test_from_thread_2 = container.put(String::from("test from thread 2"));
            let test_from_both_threads = container.put(String::from("test from both threads"));
            barrier.wait();
            assert_eq!(container.len(), 3);
            assert_eq!(test_from_thread_2, "test from thread 2");
            assert_eq!(test_from_both_threads, "test from both threads");
            send.send(test_from_both_threads.as_ptr() as usize).unwrap();
        });

        thread_1.join().unwrap();
        thread_2.join().unwrap();

        assert_eq!(container.len(), 3);
        assert_eq!(recv_1.recv().unwrap(), recv_2.recv().unwrap())
    }
}