#![no_std]
#![forbid(unsafe_code)]
#![deny(broken_intra_doc_links)]
#[cfg_attr(test, macro_use)]
extern crate alloc;
#[cfg(feature = "std")]
extern crate std;
use alloc::boxed::Box;
use alloc::collections::{BinaryHeap, LinkedList, VecDeque};
use alloc::string::String;
use alloc::vec::Vec;
use core::mem;
pub trait DynamicUsage {
fn dynamic_usage(&self) -> usize;
fn dynamic_usage_bounds(&self) -> (usize, Option<usize>);
}
#[macro_export]
macro_rules! impl_no_dynamic_usage {
($($type:ty),+) => {
$(
impl DynamicUsage for $type {
#[inline(always)]
fn dynamic_usage(&self) -> usize {
0
}
#[inline(always)]
fn dynamic_usage_bounds(&self) -> (usize, Option<usize>) {
(0, Some(0))
}
}
)+
};
}
macro_rules! impl_iterable_dynamic_usage {
($type:ty, $base_usage:expr) => {
impl<T: DynamicUsage> DynamicUsage for $type {
fn dynamic_usage(&self) -> usize {
$base_usage(self) + self.iter().map(DynamicUsage::dynamic_usage).sum::<usize>()
}
fn dynamic_usage_bounds(&self) -> (usize, Option<usize>) {
let base = $base_usage(self);
let (lower, upper) = self.iter().map(DynamicUsage::dynamic_usage_bounds).fold(
(0, Some(0)),
|(acc_lower, acc_upper), (lower, upper)| {
(acc_lower + lower, acc_upper.zip(upper).map(|(a, b)| a + b))
},
);
(base + lower, upper.map(|u| base + u))
}
}
};
}
impl_no_dynamic_usage!(());
impl_no_dynamic_usage!(i8, i16, i32, i64, i128, isize);
impl_no_dynamic_usage!(u8, u16, u32, u64, u128, usize);
impl_no_dynamic_usage!(f32, f64, bool);
impl_no_dynamic_usage!(char, str);
impl<T: DynamicUsage, const N: usize> DynamicUsage for [T; N] {
fn dynamic_usage(&self) -> usize {
self.iter().map(DynamicUsage::dynamic_usage).sum::<usize>()
}
fn dynamic_usage_bounds(&self) -> (usize, Option<usize>) {
self.iter().map(DynamicUsage::dynamic_usage_bounds).fold(
(0, Some(0)),
|(acc_lower, acc_upper), (lower, upper)| {
(acc_lower + lower, acc_upper.zip(upper).map(|(a, b)| a + b))
},
)
}
}
impl_iterable_dynamic_usage!([T], |_| 0);
impl DynamicUsage for String {
fn dynamic_usage(&self) -> usize {
self.capacity()
}
fn dynamic_usage_bounds(&self) -> (usize, Option<usize>) {
let usage = self.capacity();
(usage, Some(usage))
}
}
impl<T: DynamicUsage> DynamicUsage for Box<T> {
fn dynamic_usage(&self) -> usize {
mem::size_of::<T>() + self.as_ref().dynamic_usage()
}
fn dynamic_usage_bounds(&self) -> (usize, Option<usize>) {
let box_size = mem::size_of::<T>();
let (inner_lower, inner_upper) = self.as_ref().dynamic_usage_bounds();
(box_size + inner_lower, inner_upper.map(|u| box_size + u))
}
}
impl<T: DynamicUsage> DynamicUsage for Option<T> {
fn dynamic_usage(&self) -> usize {
self.as_ref().map(DynamicUsage::dynamic_usage).unwrap_or(0)
}
fn dynamic_usage_bounds(&self) -> (usize, Option<usize>) {
self.as_ref()
.map(DynamicUsage::dynamic_usage_bounds)
.unwrap_or((0, Some(0)))
}
}
impl<T: DynamicUsage, E: DynamicUsage> DynamicUsage for Result<T, E> {
fn dynamic_usage(&self) -> usize {
match self {
Ok(t) => t.dynamic_usage(),
Err(e) => e.dynamic_usage(),
}
}
fn dynamic_usage_bounds(&self) -> (usize, Option<usize>) {
match self {
Ok(t) => t.dynamic_usage_bounds(),
Err(e) => e.dynamic_usage_bounds(),
}
}
}
impl_iterable_dynamic_usage!(Vec<T>, |c: &Vec<T>| c.capacity() * mem::size_of::<T>());
impl_iterable_dynamic_usage!(BinaryHeap<T>, |c: &BinaryHeap<T>| {
c.capacity() * mem::size_of::<T>()
});
impl_iterable_dynamic_usage!(LinkedList<T>, |c: &LinkedList<T>| {
c.len() * mem::size_of::<T>()
});
impl_iterable_dynamic_usage!(VecDeque<T>, |c: &VecDeque<T>| {
(c.capacity() + 1) * mem::size_of::<T>()
});
#[cfg(feature = "std")]
mod hash;
#[cfg(feature = "nonempty")]
impl_iterable_dynamic_usage!(nonempty::NonEmpty<T>, |c: &nonempty::NonEmpty<T>| {
(c.capacity() - 1) * mem::size_of::<T>()
});
mod tuple;
#[cfg(test)]
mod tests {
use alloc::string::ToString;
use super::*;
#[test]
fn standard_types() {
assert_eq!(129u8.dynamic_usage(), 0);
assert_eq!(3i128.dynamic_usage(), 0);
assert_eq!(7.0f32.dynamic_usage(), 0);
assert_eq!("foobar".dynamic_usage(), 0);
assert_eq!(129u8.dynamic_usage_bounds(), (0, Some(0)));
assert_eq!(3i128.dynamic_usage_bounds(), (0, Some(0)));
assert_eq!(7.0f32.dynamic_usage_bounds(), (0, Some(0)));
assert_eq!("foobar".dynamic_usage_bounds(), (0, Some(0)));
}
#[test]
fn string() {
assert_eq!(String::new().dynamic_usage(), 0);
assert_eq!("foobar".to_string().dynamic_usage(), 6);
assert_eq!(String::new().dynamic_usage_bounds(), (0, Some(0)));
assert_eq!("foobar".to_string().dynamic_usage_bounds(), (6, Some(6)));
}
#[test]
fn boxed() {
let a: u64 = 7;
assert_eq!(a.dynamic_usage(), 0);
assert_eq!(a.dynamic_usage_bounds(), (0, Some(0)));
let b: Box<u64> = Box::new(42);
assert_eq!(b.dynamic_usage(), 8);
assert_eq!(b.dynamic_usage_bounds(), (8, Some(8)));
let capacity = 7;
let c: Box<Vec<u16>> = Box::new(Vec::with_capacity(capacity));
let expected = mem::size_of::<Vec<u16>>() + capacity * mem::size_of::<u16>();
assert_eq!(c.dynamic_usage(), expected);
assert_eq!(c.dynamic_usage_bounds(), (expected, Some(expected)));
}
#[test]
fn option() {
let a: Option<Vec<u8>> = None;
let b: Option<Vec<u8>> = Some(vec![7u8; 4]);
assert_eq!(a.dynamic_usage(), 0);
assert_eq!(a.dynamic_usage_bounds(), (0, Some(0)));
assert_eq!(b.dynamic_usage(), 4);
assert_eq!(b.dynamic_usage_bounds(), (4, Some(4)));
}
#[test]
fn array() {
let a = [7; 42];
assert_eq!(a.dynamic_usage(), 0);
assert_eq!(a.dynamic_usage_bounds(), (0, Some(0)));
let mut b = [None, None, None, None];
assert_eq!(b.dynamic_usage(), 0);
assert_eq!(b.dynamic_usage_bounds(), (0, Some(0)));
b[0] = Some(vec![4u8; 20]);
assert_eq!(b.dynamic_usage(), 20);
assert_eq!(b.dynamic_usage_bounds(), (20, Some(20)));
}
#[test]
fn vec() {
let capacity = 7;
let mut a = Vec::with_capacity(capacity);
a.push(42u64);
let expected = capacity * mem::size_of::<u64>();
assert_eq!(a.dynamic_usage(), expected);
assert_eq!(a.dynamic_usage_bounds(), (expected, Some(expected)));
}
#[cfg(feature = "nonempty")]
#[test]
fn nonempty() {
let a = nonempty::NonEmpty::new(42);
assert_eq!(a.dynamic_usage(), 0);
assert_eq!(a.dynamic_usage_bounds(), (0, Some(0)));
const CAPACITY: usize = 7;
let b = nonempty::NonEmpty::from_slice(&[27u128; CAPACITY]).unwrap();
let expected = (CAPACITY - 1) * mem::size_of::<u128>();
assert_eq!(b.dynamic_usage(), expected);
assert_eq!(b.dynamic_usage_bounds(), (expected, Some(expected)));
}
}