#![cfg_attr(feature = "std", doc = include_str!("../README.md"))]
#![cfg_attr(not(feature = "std"), no_std)]
use crossbeam_channel as cross;
#[cfg(not(feature = "std"))]
use core as std;
use std::cmp::Ordering;
use std::fmt;
use std::hash::{Hash, Hasher};
use std::mem::ManuallyDrop;
use std::ops::{Deref, DerefMut, Drop};
use std::time::Duration;
pub struct Loan<T> {
reference: ManuallyDrop<T>,
tx: cross::Sender<T>,
}
impl<T> Loan<T> {
fn new(value: T, tx: cross::Sender<T>) -> Self {
Self {
reference: ManuallyDrop::new(value),
tx,
}
}
}
impl<T> AsRef<T> for Loan<T> {
fn as_ref(&self) -> &T {
&self.reference
}
}
impl<T> AsMut<T> for Loan<T> {
fn as_mut(&mut self) -> &mut T {
&mut self.reference
}
}
impl<T> Deref for Loan<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
&self.reference
}
}
impl<T> DerefMut for Loan<T> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.reference
}
}
impl<T> Drop for Loan<T> {
fn drop(&mut self) {
unsafe {
let _ = self.tx.send(ManuallyDrop::take(&mut self.reference));
}
}
}
impl<T: fmt::Debug> fmt::Debug for Loan<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Debug::fmt(&self, f)
}
}
impl<T: fmt::Display> fmt::Display for Loan<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Display::fmt(&self, f)
}
}
impl<T: Hash> Hash for Loan<T> {
fn hash<H: Hasher>(&self, state: &mut H) {
(**self).hash(state)
}
}
impl<T: PartialEq> PartialEq for Loan<T> {
fn eq(&self, other: &Self) -> bool {
(**self).eq(other)
}
}
impl<T: Eq> Eq for Loan<T> {}
impl<T: PartialOrd> PartialOrd for Loan<T> {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
(**self).partial_cmp(other)
}
}
impl<T: Ord> Ord for Loan<T> {
fn cmp(&self, other: &Self) -> Ordering {
(**self).cmp(other)
}
}
#[derive(Clone)]
pub struct MemoryBank<T> {
rx: cross::Receiver<T>,
tx: cross::Sender<T>,
}
impl<T> Default for MemoryBank<T> {
fn default() -> Self {
Self::new()
}
}
impl<T: Default> MemoryBank<T> {
pub fn take_loan(&self) -> Loan<T> {
let value = self.rx.try_recv().unwrap_or_default();
Loan::new(value, self.tx.clone())
}
}
impl<T> MemoryBank<T> {
pub fn new() -> Self {
let (tx, rx) = cross::unbounded();
Self { tx, rx }
}
pub fn deposit(&self, value: T) -> Loan<T> {
Loan::new(value, self.tx.clone())
}
pub fn take_old_loan(&self) -> Option<Loan<T>> {
Some(Loan::new(self.rx.try_recv().ok()?, self.tx.clone()))
}
pub fn take_old_loan_timeout(&self, timeout: Duration) -> Option<Loan<T>> {
Some(Loan::new(
self.rx.recv_timeout(timeout).ok()?,
self.tx.clone(),
))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[cfg(not(feature = "std"))]
extern crate alloc;
#[cfg(not(feature = "std"))]
use alloc::{vec, vec::Vec};
#[cfg(feature = "std")]
use std::collections::hash_map::DefaultHasher;
#[test]
fn basics() {
let bank: MemoryBank<Vec<i32>> = MemoryBank::new();
let v = vec![1, 2, 3, 4, 5];
let mut loan = bank.take_loan();
loan.clone_from(&v);
drop(loan);
let loan2 = bank.take_old_loan().unwrap();
assert!(bank.take_old_loan().is_none());
assert_eq!(*loan2, v);
}
#[test]
fn multiloan() {
let bank: MemoryBank<Vec<i32>> = MemoryBank::new();
let mut loan1 = bank.take_loan();
let mut loan2 = bank.take_loan();
loan1.clone_from(&(0..100).collect());
loan2.clone_from(&(0..500).collect());
loan1[5] = 2;
loan2[3] = 5;
let loan1_vec = loan1.clone();
let loan2_vec = loan2.clone();
drop(loan2);
drop(loan1);
let loan3 = bank.take_old_loan().unwrap();
let loan4 = bank.take_old_loan().unwrap();
assert!(bank.take_old_loan().is_none());
drop(bank);
assert_eq!(*loan3, loan2_vec);
assert_eq!(*loan4, loan1_vec);
}
#[cfg(feature = "std")]
#[test]
fn hash_equality() {
let bank: MemoryBank<String> = MemoryBank::new();
let string = "Swimming with the fishes";
let mut loan_string = bank.take_loan();
loan_string.push_str(string);
let mut string_hasher = DefaultHasher::new();
string.hash(&mut string_hasher);
let mut loan_hasher = DefaultHasher::new();
loan_string.hash(&mut loan_hasher);
assert_eq!(string_hasher.finish(), loan_hasher.finish());
}
#[test]
fn comparisons() {
let n1 = 40;
let n2 = 30;
let bank: MemoryBank<i32> = MemoryBank::new();
let mut loan1 = bank.take_loan();
*loan1 = n1;
let mut loan2 = bank.take_loan();
*loan2 = n2;
assert!(loan1 > loan2);
assert_ne!(loan1, loan2);
}
#[cfg(feature = "std")]
#[test]
fn bank_between_threads() {
let bank: MemoryBank<Vec<i32>> = MemoryBank::default();
let mut v1 = vec![0, 2, 3, 5];
let mut loan = bank.take_loan();
loan.clone_from(&v1);
loan[0] = 1;
v1[0] = 1;
assert_eq!(*loan, v1);
drop(loan);
let bank_clone = bank.clone();
let v1_clone = v1.clone();
let thread = std::thread::spawn(move || {
let mut loan = bank_clone.take_old_loan().unwrap();
assert_eq!(*loan, v1_clone);
loan[1] = 1;
});
v1[1] = 1;
let _ = thread.join();
let loan = bank.take_loan();
assert_eq!(*loan, v1);
}
#[cfg(feature = "std")]
#[test]
fn loan_between_threads() {
let bank: MemoryBank<Vec<i32>> = MemoryBank::default();
let mut v = vec![17, 23, 1, 4, 5];
let mut loan = bank.take_loan();
loan.clone_from(&v);
loan[0] = 3;
v[0] = 3;
let v_clone = v.clone();
let thread = std::thread::spawn(move || {
assert_eq!(*loan, v_clone);
loan[2] = 7;
});
v[2] = 7;
let _ = thread.join();
let loan = bank.take_loan();
assert_eq!(*loan, v);
}
}