dyn_size_of/
lib.rs

1#![doc = include_str!("../README.md")]
2
3use std::sync::atomic::{AtomicBool, AtomicI8, AtomicI16, AtomicI32, AtomicI64, AtomicIsize,
4    AtomicU8, AtomicU16, AtomicU32, AtomicU64, AtomicUsize};
5
6/// Provides methods to get dynamic and total size of the variable.
7pub trait GetSize {
8    /// Returns approximate number of bytes occupied by dynamic (heap) part of `self`.
9    /// Same as `self.size_bytes() - std::mem::size_of_val(self)`.
10    #[inline] fn size_bytes_dyn(&self) -> usize { 0 }
11
12    /// Returns approximate number of bytes occupied by dynamic (heap) part of `self` content.
13    /// It usually equals to `size_bytes_dyn()`.
14    /// However, sometimes it is smaller by the amount of memory reserved but not yet used
15    /// (e.g., `size_bytes_content_dyn()` only takes into account the length of the vector and not its capacity).
16    #[inline] fn size_bytes_content_dyn(&self) -> usize { self.size_bytes_dyn() }
17
18    /// Returns approximate, total (including heap memory) number of bytes occupied by `self`.
19    #[inline] fn size_bytes(&self) -> usize {
20        std::mem::size_of_val(self) + self.size_bytes_dyn()
21    }
22
23    /// `true` if and only if the variables of this type can use dynamic (heap) memory.
24    const USES_DYN_MEM: bool = false;
25}
26
27/// Implement GetSize for one or more types that do not use heap memory.
28macro_rules! impl_nodyn_getsize_for {
29    ($x:ty) => (impl self::GetSize for $x {});
30    // `$x` followed by at least one `$y,`
31    ($x:ty, $($y:ty),+) => (
32        impl self::GetSize for $x {}
33        impl_nodyn_getsize_for!($($y),+);
34    )
35}
36
37impl_nodyn_getsize_for!(u8, u16, u32, u64, u128, usize,
38    AtomicU8, AtomicU16, AtomicU32, AtomicU64, AtomicUsize,
39    bool, AtomicBool,
40    i8, i16, i32, i64, i128, isize,
41    AtomicI8, AtomicI16, AtomicI32, AtomicI64, AtomicIsize,
42    f32, f64, char, ());
43
44//impl<T: GetSize> GetSize for [T] {    // this works also with slices, but is this sound?
45impl<T: GetSize, const N: usize> GetSize for [T; N] {
46    fn size_bytes_dyn(&self) -> usize {
47        if T::USES_DYN_MEM {
48            self.iter().map(self::GetSize::size_bytes_dyn).sum()
49        } else {
50            0
51        }
52    }
53    fn size_bytes_content_dyn(&self) -> usize {
54        if T::USES_DYN_MEM {
55            self.iter().map(self::GetSize::size_bytes_content_dyn).sum()
56        } else {
57            0
58        }
59    }
60    const USES_DYN_MEM: bool = T::USES_DYN_MEM;
61}
62
63macro_rules! impl_getsize_methods_for_pointer {
64    () => (
65        fn size_bytes_dyn(&self) -> ::std::primitive::usize {
66            ::std::ops::Deref::deref(self).size_bytes()
67        }
68        const USES_DYN_MEM: bool = true;
69    );
70}
71
72impl <T: GetSize> GetSize for Box<T> {
73    impl_getsize_methods_for_pointer!();
74}
75
76#[cfg(feature = "aligned-vec")] impl <T: GetSize> GetSize for aligned_vec::ABox<T> {
77    impl_getsize_methods_for_pointer!();
78}
79
80impl <T: GetSize> GetSize for ::std::rc::Rc<T> {
81    fn size_bytes_dyn(&self) -> ::std::primitive::usize {
82        // round((size of T + size of strong and weak reference counters) / number of strong references)
83        let c = ::std::rc::Rc::strong_count(self);
84        (::std::ops::Deref::deref(self).size_bytes() + 2*::std::mem::size_of::<usize>() + c/2) / c
85    }
86    const USES_DYN_MEM: bool = true;
87}
88
89macro_rules! impl_getsize_methods_for_dyn_arr {
90    ($T:ty) => (
91        fn size_bytes_dyn(&self) -> ::std::primitive::usize {
92            if <$T>::USES_DYN_MEM {
93                self.iter().map(self::GetSize::size_bytes).sum()
94            } else {
95                ::std::mem::size_of::<$T>() * self.len()
96            }
97        }
98        const USES_DYN_MEM: bool = true;
99    );
100}
101
102impl<T: GetSize> GetSize for Box<[T]> {
103    impl_getsize_methods_for_dyn_arr!(T);
104}
105
106#[cfg(feature = "aligned-vec")] impl <T: GetSize, A: aligned_vec::Alignment> GetSize for aligned_vec::ABox<[T], A> {
107    impl_getsize_methods_for_dyn_arr!(T);
108}
109
110macro_rules! impl_getsize_methods_for_vec {
111    ($T:ty) => (
112        fn size_bytes_dyn(&self) -> usize {
113            let c = ::std::mem::size_of::<$T>() * self.capacity();
114            if <$T>::USES_DYN_MEM {
115                c + self.iter().map(GetSize::size_bytes_dyn).sum::<usize>()
116            } else {
117                c
118            }
119        }
120        fn size_bytes_content_dyn(&self) -> usize {
121            let c = ::std::mem::size_of::<$T>() * self.len();
122            if <$T>::USES_DYN_MEM {
123                c + self.iter().map(GetSize::size_bytes_content_dyn).sum::<usize>()
124            } else {
125                c
126            }
127        }
128        const USES_DYN_MEM: bool = true;
129    );
130}
131
132impl<T: GetSize> GetSize for Vec<T> {
133    impl_getsize_methods_for_vec!(T);
134}
135
136#[cfg(feature = "aligned-vec")] impl <T: GetSize, A: aligned_vec::Alignment> GetSize for aligned_vec::AVec<T, A> {
137    impl_getsize_methods_for_vec!(T);
138}
139
140#[cfg(feature = "rsdict")] impl GetSize for rsdict::RsDict {
141    #[inline] fn size_bytes_dyn(&self) -> usize { self.heap_size() }
142    const USES_DYN_MEM: bool = true;
143}
144
145macro_rules! impl_getsize_for_tuple {
146    ($( $T:ident ),+) => {
147        impl<$( $T: self::GetSize ),+> self::GetSize for ($( $T, )+) {
148            #[allow(non_snake_case)]
149            fn size_bytes_dyn(&self) -> ::std::primitive::usize {
150                let &($( ref $T, )+) = self;
151                0 $( + $T.size_bytes_dyn() )+
152            }
153            #[allow(non_snake_case)]
154            fn size_bytes_content_dyn(&self) -> ::std::primitive::usize {
155                let &($( ref $T, )+) = self;
156                0 $( + $T.size_bytes_content_dyn() )+
157            }
158            const USES_DYN_MEM: bool = $( $T::USES_DYN_MEM )|*;
159        }
160    }
161}
162
163impl_getsize_for_tuple!(A);
164impl_getsize_for_tuple!(A, B);
165impl_getsize_for_tuple!(A, B, C);
166impl_getsize_for_tuple!(A, B, C, D);
167impl_getsize_for_tuple!(A, B, C, D, E);
168impl_getsize_for_tuple!(A, B, C, D, E, F);
169impl_getsize_for_tuple!(A, B, C, D, E, F, G);
170impl_getsize_for_tuple!(A, B, C, D, E, F, G, H);
171impl_getsize_for_tuple!(A, B, C, D, E, F, G, H, I);
172impl_getsize_for_tuple!(A, B, C, D, E, F, G, H, I, J);
173
174
175
176#[cfg(test)]
177mod tests {
178    use super::*;
179
180    fn test_primitive<T: GetSize>(v: T) {
181        assert_eq!(v.size_bytes_dyn(), 0);
182        assert_eq!(v.size_bytes_content_dyn(), 0);
183        assert_eq!(v.size_bytes(), std::mem::size_of_val(&v));
184        assert!(!T::USES_DYN_MEM);
185    }
186
187    #[test]
188    fn test_primitives() {
189        test_primitive(1u32);
190        test_primitive(1.0f32);
191    }
192
193    #[test]
194    fn test_array() {
195        assert_eq!([1u32, 2u32, 3u32].size_bytes(), 3*4);
196        assert_eq!([[1u32, 2u32], [3u32, 4u32]].size_bytes(), 4*4);
197        assert_eq!([vec![1u32, 2u32], vec![3u32, 4u32]].size_bytes_content_dyn(), 4*4);
198    }
199
200    #[test]
201    fn test_vec() {
202        assert_eq!(vec![1u32, 2u32, 3u32].size_bytes_content_dyn(), 3*4);
203        assert_eq!(vec![[1u32, 2u32], [3u32, 4u32]].size_bytes_content_dyn(), 4*4);
204        let v = vec![1u32, 2u32];
205        assert_eq!(vec![v.clone(), v.clone()].size_bytes_dyn(), 2*v.size_bytes());
206        assert_eq!(Vec::<u32>::with_capacity(3).size_bytes_dyn(), 3*4);
207        assert_eq!(Vec::<u32>::with_capacity(3).size_bytes_content_dyn(), 0);
208    }
209
210    #[test]
211    fn test_boxed_slice() {
212        let bs = vec![1u32, 2u32, 3u32].into_boxed_slice();
213        assert_eq!(bs.size_bytes_dyn(), 3*4);
214        assert_eq!(bs.size_bytes(), 3*4 + std::mem::size_of_val(&bs));
215    }
216
217    #[test]
218    fn test_tuple() {
219        assert_eq!((1u32, 2u32).size_bytes_dyn(), 0);
220        assert_eq!((1u32, vec![3u32, 4u32]).size_bytes_dyn(), 2*4);
221        assert_eq!((vec![1u32, 2u32], vec![3u32, 4u32]).size_bytes_dyn(), 4*4);
222    }
223
224    #[test]
225    #[allow(unused_allocation)]
226    fn test_box() {
227        assert_eq!(Box::new(1u32).size_bytes_dyn(), 4);
228        assert_eq!(Box::new([1u32, 2u32]).size_bytes_dyn(), 2*4);
229    }
230}