#![warn(clippy::cargo)]
use std::{collections::HashMap, hash::Hash, marker::PhantomData};
use parking_lot::Mutex;
pub trait Container<T> {
fn put(&self, item: T) -> &T;
}
pub trait ContainerMut<T> {
#[allow(clippy::mut_from_ref)]
fn put_mut(&self, item: T) -> &mut T;
}
#[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> {}
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> {
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())
}
}