priority-lfu 0.3.0

A high-performance, concurrent, in-memory cache with W-TinyLFU eviction policy and weight-based prioritization.
Documentation
use crate::deepsize::{Context, DeepSizeOf};

/// A macro to generate an impl for types with known inner allocation sizes.
///
/// Repurposed from the `heapsize` crate
///
/// Usage:
/// ```rust
/// use priority_lfu::known_deep_size;
///
/// struct A(u32);
/// struct B(A, char);
/// struct C(Box<u32>);
///
/// known_deep_size!(0; A, B); // A and B do not have any allocation
/// known_deep_size!(4; C); // C will always have an allocation of 4 bytes
/// ```
#[macro_export]
macro_rules! known_deep_size (
    ($size:expr; $($({$($gen:tt)*})? $type:ty,)+) => (
        known_deep_size!($size; $($({$($gen)*})? $type),*);
    );
    ($size:expr; $($({$($gen:tt)*})? $type:ty),+) => (
        $(
            impl$(<$($gen)*>)? $crate::DeepSizeOf for $type {
                #[inline(always)]
                fn deep_size_of_children(&self, _: &mut $crate::Context) -> usize {
                    $size
                }
            }
        )+
    );
);

use core::num;
use core::sync::atomic;

known_deep_size!(0;
	(), bool, char, str,
	u8, u16, u32, u64, u128, usize,
	i8, i16, i32, i64, i128, isize,
	f32, f64,
);
known_deep_size!(0;
	atomic::AtomicBool,
	atomic::AtomicI8,
	atomic::AtomicI16,
	atomic::AtomicI32,
	atomic::AtomicI64,
	atomic::AtomicIsize,
	atomic::AtomicU8,
	atomic::AtomicU16,
	atomic::AtomicU32,
	atomic::AtomicU64,
	atomic::AtomicUsize,

	num::NonZeroI8,
	num::NonZeroI16,
	num::NonZeroI32,
	num::NonZeroI64,
	num::NonZeroI128,
	num::NonZeroIsize,
	num::NonZeroU8,
	num::NonZeroU16,
	num::NonZeroU32,
	num::NonZeroU64,
	num::NonZeroU128,
	num::NonZeroUsize,
);

known_deep_size!(0;
	{T} core::ops::Bound<T>
);

known_deep_size!(0;
	{T: ?Sized} core::marker::PhantomData<T>,
	{T} core::mem::MaybeUninit<T>,
	// In theory this could be incorrect, but it's unlikely
	{T: Copy} core::cell::Cell<T>,

	// Weak reference counted pointers do not own their contents
	{T} std::sync::Weak<T>,
	{T} std::rc::Weak<T>,
);

mod strings {
	use std::ffi::{CStr, CString, OsStr, OsString};
	use std::path::{Path, PathBuf};

	use super::{Context, DeepSizeOf};

	known_deep_size!(0; Path, OsStr, CStr);

	impl DeepSizeOf for PathBuf {
		fn deep_size_of_children(&self, _: &mut Context) -> usize {
			self.capacity()
		}
	}
	impl DeepSizeOf for OsString {
		fn deep_size_of_children(&self, _: &mut Context) -> usize {
			self.capacity()
		}
	}
	impl DeepSizeOf for CString {
		fn deep_size_of_children(&self, _: &mut Context) -> usize {
			// This may cause a length check at runtime, but that
			// doesn't seem avoidable.  This assumes that the allocation
			// is the exact length of the string and the added null
			// terminator.
			self.as_bytes().len() + 1
		}
	}
}

impl DeepSizeOf for std::string::String {
	fn deep_size_of_children(&self, _: &mut Context) -> usize {
		self.capacity()
	}
}

impl<T: DeepSizeOf> DeepSizeOf for core::option::Option<T> {
	fn deep_size_of_children(&self, context: &mut Context) -> usize {
		match &self {
			Some(t) => t.deep_size_of_children(context),
			None => 0,
		}
	}
}

impl<R: DeepSizeOf, E: DeepSizeOf> DeepSizeOf for core::result::Result<R, E> {
	fn deep_size_of_children(&self, context: &mut Context) -> usize {
		match &self {
			Ok(r) => r.deep_size_of_children(context),
			Err(e) => e.deep_size_of_children(context),
		}
	}
}

impl<T: DeepSizeOf> DeepSizeOf for core::cell::RefCell<T> {
	fn deep_size_of_children(&self, context: &mut Context) -> usize {
		self.borrow().deep_size_of_children(context)
	}
}

mod std_net {
	use std::net::{
		AddrParseError, IpAddr, Ipv4Addr, Ipv6Addr, Shutdown, SocketAddr, SocketAddrV4,
		SocketAddrV6, TcpListener, TcpStream, UdpSocket,
	};

	known_deep_size!(0;
		// structs
		AddrParseError, Ipv4Addr,
		Ipv6Addr, SocketAddrV4, SocketAddrV6,
		TcpListener, TcpStream, UdpSocket,
		// enums
		IpAddr, Shutdown, SocketAddr
	);
}

mod std_time {
	use std::time::{Duration, Instant, SystemTime, SystemTimeError};

	known_deep_size!(0;
		Instant, Duration, SystemTime, SystemTimeError
	);
}

mod std_sync {
	use crate::deepsize::{Context, DeepSizeOf};

	impl<T: DeepSizeOf> DeepSizeOf for std::sync::Mutex<T> {
		/// This locks the `Mutex`, so it may deadlock; If the mutex is
		/// poisoned, this returns 0
		fn deep_size_of_children(&self, context: &mut Context) -> usize {
			self.lock().map(|s| s.deep_size_of_children(context)).unwrap_or(0)
		}
	}

	impl<T: DeepSizeOf> DeepSizeOf for std::sync::RwLock<T> {
		/// This reads the `RwLock`, so it may deadlock; If the lock is
		/// poisoned, this returns 0
		fn deep_size_of_children(&self, context: &mut Context) -> usize {
			self.read().map(|s| s.deep_size_of_children(context)).unwrap_or(0)
		}
	}
}

impl<T: DeepSizeOf, const N: usize> DeepSizeOf for [T; N] {
	fn deep_size_of_children(&self, context: &mut Context) -> usize {
		self.as_ref().deep_size_of_children(context)
	}
}

macro_rules! deep_size_tuple {
    ($(($n:tt, $T:ident)),+ ) => {
        impl<$($T,)+> DeepSizeOf for ($($T,)+)
            where $($T: DeepSizeOf,)+
        {
            fn deep_size_of_children(&self, context: &mut Context) -> usize {
                0 $( + self.$n.deep_size_of_children(context))+
            }
        }
    };
}

deep_size_tuple!((0, A));
deep_size_tuple!((0, A), (1, B));
deep_size_tuple!((0, A), (1, B), (2, C));
deep_size_tuple!((0, A), (1, B), (2, C), (3, D));
deep_size_tuple!((0, A), (1, B), (2, C), (3, D), (4, E));
deep_size_tuple!((0, A), (1, B), (2, C), (3, D), (4, E), (5, F));
deep_size_tuple!((0, A), (1, B), (2, C), (3, D), (4, E), (5, F), (6, G));
deep_size_tuple!((0, A), (1, B), (2, C), (3, D), (4, E), (5, F), (6, G), (7, H));
deep_size_tuple!((0, A), (1, B), (2, C), (3, D), (4, E), (5, F), (6, G), (7, H), (8, I));
deep_size_tuple!((0, A), (1, B), (2, C), (3, D), (4, E), (5, F), (6, G), (7, H), (8, I), (9, J));