#![cfg_attr(not(feature = "std"), no_std)]
#![allow(clippy::assertions_on_constants)]
#[cfg(feature = "fake_clock-types")]
mod fake_clock;
#[cfg(feature = "futures-types")]
mod futures;
#[cfg(feature = "smallvec-types")]
mod smallvec;
#[cfg(feature = "std")]
mod std;
#[cfg(feature = "tokio-types")]
mod tokio;
pub use datasize_derive::DataSize;
pub const fn min(a: usize, b: usize) -> usize {
[a, b][(a > b) as usize]
}
pub trait DataSize {
const IS_DYNAMIC: bool;
const STATIC_HEAP_SIZE: usize;
fn estimate_heap_size(&self) -> usize;
#[cfg(feature = "detailed")]
#[inline]
fn estimate_detailed_heap_size(&self) -> MemUsageNode {
MemUsageNode::Size(self.estimate_heap_size())
}
}
#[cfg(feature = "detailed")]
#[derive(Debug, serde::Serialize, PartialEq)]
pub enum MemUsageNode {
Size(usize),
Detailed(::std::collections::HashMap<&'static str, MemUsageNode>),
}
#[cfg(feature = "detailed")]
impl MemUsageNode {
#[inline]
pub fn total(&self) -> usize {
match self {
MemUsageNode::Size(sz) => *sz,
MemUsageNode::Detailed(members) => members.values().map(MemUsageNode::total).sum(),
}
}
}
#[inline]
pub fn data_size<T: ?Sized>(value: &T) -> usize
where
T: DataSize,
{
value.estimate_heap_size()
}
#[cfg(feature = "detailed")]
#[inline]
pub fn data_size_detailed<T: ?Sized>(value: &T) -> MemUsageNode
where
T: DataSize,
{
value.estimate_detailed_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 f32 f64, 0);
non_dynamic_const_heap_size!(core::time::Duration, 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);
impl<T0> DataSize for (T0,)
where
T0: DataSize,
{
const IS_DYNAMIC: bool = T0::IS_DYNAMIC;
const STATIC_HEAP_SIZE: usize = T0::STATIC_HEAP_SIZE;
#[inline]
fn estimate_heap_size(&self) -> usize {
self.0.estimate_heap_size()
}
}
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 Option<T>
where
T: DataSize,
{
const IS_DYNAMIC: bool = (T::IS_DYNAMIC || T::STATIC_HEAP_SIZE > 0);
const STATIC_HEAP_SIZE: usize = 0;
#[inline]
fn estimate_heap_size(&self) -> usize {
match self {
Some(val) => data_size(val),
None => 0,
}
}
}
impl<T, E> DataSize for Result<T, E>
where
T: DataSize,
E: DataSize,
{
const IS_DYNAMIC: bool =
(T::IS_DYNAMIC || E::IS_DYNAMIC || (T::STATIC_HEAP_SIZE != E::STATIC_HEAP_SIZE));
const STATIC_HEAP_SIZE: usize = min(T::STATIC_HEAP_SIZE, E::STATIC_HEAP_SIZE);
#[inline]
fn estimate_heap_size(&self) -> usize {
match self {
Ok(val) => data_size(val),
Err(err) => data_size(err),
}
}
}
impl<T> DataSize for core::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> DataSize for core::ops::Range<T> {
const IS_DYNAMIC: bool = T::IS_DYNAMIC;
const STATIC_HEAP_SIZE: usize = 2 * T::STATIC_HEAP_SIZE;
#[inline]
fn estimate_heap_size(&self) -> usize {
self.start.estimate_heap_size() + self.end.estimate_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_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_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_tuple_with_one_element() {
type Foo = (u32,);
assert!(!Foo::IS_DYNAMIC);
assert_eq!(Foo::STATIC_HEAP_SIZE, 0);
let foo: Foo = (456,);
assert_eq!(data_size(&foo), 0);
}
#[test]
fn test_result() {
assert_eq!(Result::<u8, u8>::STATIC_HEAP_SIZE, 0);
assert!(!Result::<u8, u8>::IS_DYNAMIC);
assert_eq!(Result::<u8, u16>::STATIC_HEAP_SIZE, 0);
assert!(!Result::<u8, u16>::IS_DYNAMIC);
assert_eq!(Result::<u8, Box<u16>>::STATIC_HEAP_SIZE, 0);
assert!(Result::<u8, Box<u16>>::IS_DYNAMIC);
assert_eq!(Result::<Box<u8>, u16>::STATIC_HEAP_SIZE, 0);
assert!(Result::<Box<u8>, u16>::IS_DYNAMIC);
assert_eq!(Result::<Box<u8>, Box<u16>>::STATIC_HEAP_SIZE, 1);
assert!(Result::<Box<u8>, Box<u16>>::IS_DYNAMIC);
assert_eq!(Result::<Box<u16>, Box<u16>>::STATIC_HEAP_SIZE, 2);
assert!(!Result::<Box<u16>, Box<u16>>::IS_DYNAMIC);
assert_eq!(Result::<u16, Vec<u16>>::STATIC_HEAP_SIZE, 0);
assert!(Result::<u16, Vec<u16>>::IS_DYNAMIC);
assert_eq!(Result::<Vec<u16>, u16>::STATIC_HEAP_SIZE, 0);
assert!(Result::<Vec<u16>, u16>::IS_DYNAMIC);
assert_eq!(Result::<Vec<u16>, Vec<u16>>::STATIC_HEAP_SIZE, 0);
assert!(Result::<Vec<u16>, Vec<u16>>::IS_DYNAMIC);
}
#[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),
}
}
#[test]
fn use_with_annotation() {
fn ds_for_field_b(value: &u32) -> usize {
assert_eq!(*value, 2); 1234
}
#[derive(DataSize)]
struct Foo {
field_a: u32,
#[data_size(with = ds_for_field_b)]
field_b: u32,
field_c: u32,
}
let value = Foo {
field_a: 1,
field_b: 2,
field_c: 3,
};
assert_eq!(value.estimate_heap_size(), 1234);
}
}