#[cfg(feature = "fake_clock-types")]
mod fake_clock;
#[cfg(feature = "futures-types")]
mod futures;
#[cfg(feature = "smallvec-types")]
mod smallvec;
#[cfg(feature = "tokio-types")]
mod tokio;
pub use datasize_derive::DataSize;
use std::mem::size_of;
pub trait DataSize {
const IS_DYNAMIC: bool;
const STATIC_HEAP_SIZE: usize;
fn estimate_heap_size(&self) -> usize;
}
#[inline]
pub fn data_size<T>(value: &T) -> usize
where
T: DataSize,
{
if T::IS_DYNAMIC {
value.estimate_heap_size()
} else {
T::STATIC_HEAP_SIZE
}
}
#[macro_export]
macro_rules! non_dynamic_const_heap_size {
($($ty:ty)*, $sz:expr) => {
$(impl DataSize for $ty {
const IS_DYNAMIC: bool = false;
const STATIC_HEAP_SIZE: usize = $sz;
#[inline]
fn estimate_heap_size(&self) -> usize {
$sz
}
})*
};
}
macro_rules! strip_plus {
(+ $($rest: tt)*) => {
$($rest)*
}
}
macro_rules! tuple_heap_size {
($($n:tt $name:ident);+) => {
impl<$($name),*> DataSize for ($($name),*)
where $($name: DataSize),*
{
const IS_DYNAMIC: bool = $($name::IS_DYNAMIC)|*;
const STATIC_HEAP_SIZE: usize =
strip_plus!($(+ $name::STATIC_HEAP_SIZE)+);
#[inline]
fn estimate_heap_size(&self) -> usize {
strip_plus!($(+ self.$n.estimate_heap_size())+)
}
}
};
}
macro_rules! array_heap_size {
($($n:tt)+) => {
$(
impl<T> DataSize for [T; $n]
where
T: DataSize,
{
const IS_DYNAMIC: bool = T::IS_DYNAMIC;
const STATIC_HEAP_SIZE: usize = T::STATIC_HEAP_SIZE * $n;
#[inline]
fn estimate_heap_size(&self) -> usize {
if T::IS_DYNAMIC {
(&self[..]).iter().map(DataSize::estimate_heap_size).sum()
} else {
T::STATIC_HEAP_SIZE * $n
}
}
}
)*
};
}
non_dynamic_const_heap_size!(() u8 u16 u32 u64 u128 usize i8 i16 i32 i64 i128 isize bool char, 0);
non_dynamic_const_heap_size!(
std::net::Ipv4Addr
std::net::Ipv6Addr
std::net::SocketAddrV4
std::net::SocketAddrV6
std::net::IpAddr
std::net::SocketAddr
std::time::Duration
std::time::Instant
std::time::SystemTime,
0
);
tuple_heap_size!(0 T0; 1 T1);
tuple_heap_size!(0 T0; 1 T1; 2 T2);
tuple_heap_size!(0 T0; 1 T1; 2 T2; 3 T3);
tuple_heap_size!(0 T0; 1 T1; 2 T2; 3 T3; 4 T4);
tuple_heap_size!(0 T0; 1 T1; 2 T2; 3 T3; 4 T4; 5 T5);
tuple_heap_size!(0 T0; 1 T1; 2 T2; 3 T3; 4 T4; 5 T5; 6 T6);
tuple_heap_size!(0 T0; 1 T1; 2 T2; 3 T3; 4 T4; 5 T5; 6 T6; 7 T7);
tuple_heap_size!(0 T0; 1 T1; 2 T2; 3 T3; 4 T4; 5 T5; 6 T6; 7 T7; 8 T8);
tuple_heap_size!(0 T0; 1 T1; 2 T2; 3 T3; 4 T4; 5 T5; 6 T6; 7 T7; 8 T8; 9 T9);
tuple_heap_size!(0 T0; 1 T1; 2 T2; 3 T3; 4 T4; 5 T5; 6 T6; 7 T7; 8 T8; 9 T9; 10 T10);
tuple_heap_size!(0 T0; 1 T1; 2 T2; 3 T3; 4 T4; 5 T5; 6 T6; 7 T7; 8 T8; 9 T9; 10 T10; 11 T11);
tuple_heap_size!(0 T0; 1 T1; 2 T2; 3 T3; 4 T4; 5 T5; 6 T6; 7 T7; 8 T8; 9 T9; 10 T10; 11 T11; 12 T12);
tuple_heap_size!(0 T0; 1 T1; 2 T2; 3 T3; 4 T4; 5 T5; 6 T6; 7 T7; 8 T8; 9 T9; 10 T10; 11 T11; 12 T12; 13 T13);
tuple_heap_size!(0 T0; 1 T1; 2 T2; 3 T3; 4 T4; 5 T5; 6 T6; 7 T7; 8 T8; 9 T9; 10 T10; 11 T11; 12 T12; 13 T13; 14 T14);
tuple_heap_size!(0 T0; 1 T1; 2 T2; 3 T3; 4 T4; 5 T5; 6 T6; 7 T7; 8 T8; 9 T9; 10 T10; 11 T11; 12 T12; 13 T13; 14 T14; 15 T15);
array_heap_size!(0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 128 192 256 384 512 1024 2048 4096 8192 16384 1048576 2097152 3145728 4194304);
impl<T> DataSize for &T {
const IS_DYNAMIC: bool = false;
const STATIC_HEAP_SIZE: usize = 0;
#[inline]
fn estimate_heap_size(&self) -> usize {
0
}
}
impl<T> DataSize for &mut T {
const IS_DYNAMIC: bool = false;
const STATIC_HEAP_SIZE: usize = 0;
#[inline]
fn estimate_heap_size(&self) -> usize {
0
}
}
impl<T> DataSize for Box<T>
where
T: DataSize,
{
const IS_DYNAMIC: bool = T::IS_DYNAMIC;
const STATIC_HEAP_SIZE: usize = size_of::<T>();
#[inline]
fn estimate_heap_size(&self) -> usize {
size_of::<T>() + data_size::<T>(self)
}
}
impl<T> DataSize for Option<T>
where
T: DataSize,
{
const IS_DYNAMIC: bool = (T::IS_DYNAMIC || T::STATIC_HEAP_SIZE > 0);
const STATIC_HEAP_SIZE: usize = 0;
fn estimate_heap_size(&self) -> usize {
match self {
Some(val) => data_size(val),
None => 0,
}
}
}
impl<T> DataSize for std::sync::Arc<T> {
const IS_DYNAMIC: bool = false;
const STATIC_HEAP_SIZE: usize = 0;
#[inline]
fn estimate_heap_size(&self) -> usize {
0
}
}
impl<T> DataSize for std::rc::Rc<T> {
const IS_DYNAMIC: bool = false;
const STATIC_HEAP_SIZE: usize = 0;
#[inline]
fn estimate_heap_size(&self) -> usize {
0
}
}
impl<T> DataSize for std::marker::PhantomData<T> {
const IS_DYNAMIC: bool = false;
const STATIC_HEAP_SIZE: usize = 0;
#[inline]
fn estimate_heap_size(&self) -> usize {
0
}
}
impl<T> DataSize for Vec<T>
where
T: DataSize,
{
const IS_DYNAMIC: bool = true;
const STATIC_HEAP_SIZE: usize = 0;
#[inline]
fn estimate_heap_size(&self) -> usize {
let sz_base = self.capacity() * size_of::<T>();
let sz_used = if T::IS_DYNAMIC {
self.iter().map(DataSize::estimate_heap_size).sum()
} else {
self.len() * T::STATIC_HEAP_SIZE
};
sz_base + sz_used
}
}
impl<T> DataSize for std::collections::VecDeque<T>
where
T: DataSize,
{
const IS_DYNAMIC: bool = true;
const STATIC_HEAP_SIZE: usize = 0;
#[inline]
fn estimate_heap_size(&self) -> usize {
let sz_base = self.capacity() * size_of::<T>();
let sz_used = if T::IS_DYNAMIC {
self.iter().map(DataSize::estimate_heap_size).sum()
} else {
self.len() * T::STATIC_HEAP_SIZE
};
sz_base + sz_used
}
}
impl DataSize for String {
const IS_DYNAMIC: bool = true;
const STATIC_HEAP_SIZE: usize = 0;
fn estimate_heap_size(&self) -> usize {
self.capacity()
}
}
impl DataSize for std::path::PathBuf {
const IS_DYNAMIC: bool = true;
const STATIC_HEAP_SIZE: usize = 0;
fn estimate_heap_size(&self) -> usize {
self.capacity()
}
}
impl DataSize for std::ffi::OsString {
const IS_DYNAMIC: bool = true;
const STATIC_HEAP_SIZE: usize = 0;
fn estimate_heap_size(&self) -> usize {
self.capacity()
}
}
impl<K, V> DataSize for std::collections::BTreeMap<K, V>
where
K: DataSize,
V: DataSize,
{
const IS_DYNAMIC: bool = true;
const STATIC_HEAP_SIZE: usize = 0;
fn estimate_heap_size(&self) -> usize {
let mut size = 0;
if K::IS_DYNAMIC || V::IS_DYNAMIC {
for (key, value) in self.iter() {
size += size_of::<(K, V)>() + key.estimate_heap_size() + value.estimate_heap_size();
}
} else {
size += self.len() * (size_of::<(K, V)>() + K::STATIC_HEAP_SIZE + V::STATIC_HEAP_SIZE);
}
size
}
}
impl<K, V, S> DataSize for std::collections::HashMap<K, V, S>
where
K: DataSize,
V: DataSize,
{
const IS_DYNAMIC: bool = true;
const STATIC_HEAP_SIZE: usize = 0;
#[inline]
fn estimate_heap_size(&self) -> usize {
let size = self.capacity() * (size_of::<V>() + size_of::<K>() + size_of::<usize>());
if K::IS_DYNAMIC || V::IS_DYNAMIC {
self.iter().fold(size, |n, (key, value)| {
n + key.estimate_heap_size() + value.estimate_heap_size()
})
} else {
size + self.capacity() * (K::STATIC_HEAP_SIZE + V::STATIC_HEAP_SIZE)
}
}
}
impl<T, S> DataSize for std::collections::HashSet<T, S>
where
T: DataSize,
{
const IS_DYNAMIC: bool = true;
const STATIC_HEAP_SIZE: usize = 0;
#[inline]
fn estimate_heap_size(&self) -> usize {
let size = self.capacity() * (size_of::<T>() + size_of::<usize>());
if T::IS_DYNAMIC {
self.iter()
.fold(size, |n, value| n + value.estimate_heap_size())
} else {
size + self.capacity() * T::STATIC_HEAP_SIZE
}
}
}
#[cfg(test)]
mod tests {
use crate as datasize;
use crate::{data_size, DataSize};
#[test]
fn test_for_simple_builtin_types() {
assert_eq!(1u8.estimate_heap_size(), 0);
assert_eq!(1u16.estimate_heap_size(), 0);
}
#[test]
fn test_box() {
let value: Box<u64> = Box::new(1234);
assert_eq!(data_size::<Box<u64>>(&value), 8);
assert_eq!(data_size(&value), 8);
}
#[test]
fn test_option_box() {
let value_none: Option<Box<u64>> = None;
let value_some: Option<Box<u64>> = Some(Box::new(12345));
assert_eq!(data_size::<Option<Box<u64>>>(&value_none), 0);
assert_eq!(data_size::<Option<Box<u64>>>(&value_some), 8);
}
#[test]
fn test_struct() {
#[derive(DataSize)]
struct Example {
count: usize,
my_data: Vec<MyStruct>,
warning: Option<Box<u32>>,
#[data_size(skip)]
#[allow(dead_code)]
skipped: Box<char>,
}
#[derive(DataSize)]
struct MyStruct {
count: u64,
}
let mut ex = Example {
count: 99,
my_data: vec![],
warning: None,
skipped: Default::default(),
};
assert_eq!(data_size(&ex), 0);
ex.warning = Some(Box::new(12345));
assert_eq!(data_size(&ex), 4);
ex.my_data.reserve_exact(10);
assert_eq!(data_size(&ex), 4 + 10 * 8)
}
#[test]
fn test_enum() {
#[derive(Debug, DataSize)]
enum Foo {
Bar,
Baz {
boxed: Box<u32>,
nonheap: u8,
#[data_size(skip)]
extra: Box<u128>,
},
Bert(Vec<u32>, #[data_size(skip)] Vec<u8>),
#[data_size(skip)]
Skipped(Vec<i32>),
}
let bar = Foo::Bar;
assert_eq!(data_size(&bar), 0);
let baz = Foo::Baz {
boxed: Box::new(123),
nonheap: 99,
extra: Box::new(456),
};
assert_eq!(data_size(&baz), 4);
let bert = Foo::Bert(vec![5, 6, 7, 8, 9], vec![1, 2, 3, 4, 5]);
assert_eq!(data_size(&bert), 5 * 4);
let skipped = Foo::Skipped(vec![-1, 1, 99, 100]);
assert_eq!(data_size(&skipped), 0);
}
#[test]
fn test_generic_struct() {
#[derive(DataSize)]
struct Example<A, B> {
a: Option<A>,
b: Option<B>,
c: u8,
}
let none: Example<Box<u32>, Box<u8>> = Example {
a: None,
b: None,
c: 123,
};
assert_eq!(data_size(&none), 0);
let a: Example<Box<u32>, Box<u8>> = Example {
a: Some(Box::new(0)),
b: None,
c: 123,
};
assert_eq!(data_size(&a), 4);
let both: Example<Box<u32>, Box<u8>> = Example {
a: Some(Box::new(0)),
b: Some(Box::new(0)),
c: 123,
};
assert_eq!(data_size(&both), 5);
}
#[test]
fn test_generic_enum() {
#[derive(DataSize)]
enum Foo<A, B, C, D> {
Baz {
boxed: Box<A>,
#[data_size(skip)]
#[allow(dead_code)]
extra: Box<B>,
},
Bert(Vec<A>, #[data_size(skip)] Vec<D>, Box<A>),
#[data_size(skip)]
Skipped(Vec<C>),
}
let baz: Foo<u8, u16, u32, u64> = Foo::Baz {
boxed: Box::new(123),
extra: Box::new(456),
};
assert_eq!(data_size(&baz), 1);
let bert: Foo<u8, u16, u32, u64> =
Foo::Bert(vec![5, 6, 7, 8, 9], vec![1, 2, 3, 4, 5], Box::new(1));
assert_eq!(data_size(&bert), 5 + 1);
let skipped: Foo<u8, u16, u32, u64> = Foo::Skipped(vec![1, 1, 99, 100]);
assert_eq!(data_size(&skipped), 0);
}
#[test]
fn test_newtype_struct() {
#[derive(DataSize)]
struct Foo(u32);
assert!(!Foo::IS_DYNAMIC);
assert_eq!(Foo::STATIC_HEAP_SIZE, 0);
assert_eq!(data_size(&Foo(123)), 0);
}
#[test]
fn test_generic_newtype_struct() {
#[derive(DataSize)]
struct Foo<T>(T);
assert!(!Foo::<Box<u32>>::IS_DYNAMIC);
assert_eq!(Foo::<Box<u32>>::STATIC_HEAP_SIZE, 4);
assert_eq!(data_size(&Foo(Box::new(123u32))), 4);
}
#[test]
fn test_tuple_struct() {
#[derive(DataSize)]
struct Foo(u32, u8);
assert!(!Foo::IS_DYNAMIC);
assert_eq!(Foo::STATIC_HEAP_SIZE, 0);
assert_eq!(data_size(&Foo(123, 45)), 0);
}
#[test]
fn test_generic_tuple_struct() {
#[derive(DataSize)]
struct Foo<T>(T, Box<u8>, #[data_size(skip)] Box<u32>);
assert!(!Foo::<Box<u32>>::IS_DYNAMIC);
assert_eq!(Foo::<Box<u32>>::STATIC_HEAP_SIZE, 5);
assert_eq!(
data_size(&Foo(Box::new(123u32), Box::new(45), Box::new(0))),
5
);
}
#[test]
fn test_empty_struct() {
#[derive(DataSize)]
struct Foo {}
#[derive(DataSize)]
struct Bar;
assert!(!Foo::IS_DYNAMIC);
assert!(!Bar::IS_DYNAMIC);
assert_eq!(Foo::STATIC_HEAP_SIZE, 0);
assert_eq!(Bar::STATIC_HEAP_SIZE, 0);
assert_eq!(data_size(&Foo {}), 0);
assert_eq!(data_size(&Bar), 0);
}
#[test]
fn test_empty_enum() {
#[derive(DataSize)]
enum Foo {}
assert!(!Foo::IS_DYNAMIC);
assert_eq!(Foo::STATIC_HEAP_SIZE, 0);
}
#[test]
fn macro_does_not_panic_on_foreign_attributes() {
#[derive(DataSize)]
struct Foo {
dummy: u8,
}
}
#[test]
fn keeps_where_clauses_on_structs() {
#[allow(dead_code)]
#[derive(DataSize)]
struct Foo<T>
where
T: Copy,
{
field: T,
}
}
#[test]
fn keeps_where_clauses_on_enums() {
#[allow(dead_code)]
#[derive(DataSize)]
enum Foo<T>
where
T: Copy,
{
Value(T),
}
}
}