get_size2/
lib.rs

1#![doc = include_str!("./lib.md")]
2#![cfg_attr(docsrs, feature(doc_cfg))]
3
4use std::borrow::Cow;
5use std::collections::{BTreeMap, BTreeSet, BinaryHeap, HashMap, HashSet, LinkedList, VecDeque};
6use std::convert::Infallible;
7use std::marker::{PhantomData, PhantomPinned};
8use std::num::{
9    NonZeroI128, NonZeroI16, NonZeroI32, NonZeroI64, NonZeroI8, NonZeroIsize, NonZeroU128,
10    NonZeroU16, NonZeroU32, NonZeroU64, NonZeroU8, NonZeroUsize,
11};
12use std::rc::{Rc, Weak as RcWeak};
13use std::sync::atomic::{
14    AtomicBool, AtomicI16, AtomicI32, AtomicI64, AtomicI8, AtomicIsize, AtomicU16, AtomicU32,
15    AtomicU64, AtomicU8, AtomicUsize, Ordering,
16};
17use std::sync::{Arc, Mutex, RwLock, Weak as ArcWeak};
18use std::time::{Duration, Instant, SystemTime};
19
20#[cfg(feature = "derive")]
21#[cfg_attr(docsrs, doc(cfg(feature = "derive")))]
22pub use get_size_derive2::*;
23
24mod tracker;
25pub use tracker::*;
26#[cfg(test)]
27mod test;
28
29/// Determine the size in bytes an object occupies inside RAM.
30pub trait GetSize: Sized {
31    /// Determines how may bytes this object occupies inside the stack.
32    ///
33    /// The default implementation uses [`std::mem::size_of`] and should work for almost all types.
34    #[must_use]
35    fn get_stack_size() -> usize {
36        std::mem::size_of::<Self>()
37    }
38
39    /// Determines how many bytes this object occupies inside the heap.
40    ///
41    /// The default implementation returns 0, assuming the object is fully allocated on the stack.
42    /// It must be adjusted as appropriate for objects which hold data inside the heap.
43    fn get_heap_size(&self) -> usize {
44        0
45    }
46
47    /// Determines how many bytes this object occupies inside the heap while using a `tracker`.
48    ///
49    /// The default implementation ignores the tracker and calls [`get_heap_size`](Self::get_heap_size)
50    /// instead, returning the tracker untouched in the second argument.
51    fn get_heap_size_with_tracker<T: GetSizeTracker>(&self, tracker: T) -> (usize, T) {
52        (GetSize::get_heap_size(self), tracker)
53    }
54
55    /// Determines the total size of the object.
56    ///
57    /// The default implementation simply adds up the results of [`get_stack_size`](Self::get_stack_size)
58    /// and [`get_heap_size`](Self::get_heap_size) and is not meant to be changed.
59    fn get_size(&self) -> usize {
60        Self::get_stack_size() + GetSize::get_heap_size(self)
61    }
62
63    /// Determines the total size of the object while using a `tracker`.
64    ///
65    /// The default implementation simply adds up the results of [`get_stack_size`](Self::get_stack_size)
66    /// and [`get_heap_size_with_tracker`](Self::get_heap_size_with_tracker) and is not meant to
67    /// be changed.
68    fn get_size_with_tracker<T: GetSizeTracker>(&self, tracker: T) -> (usize, T) {
69        let stack_size = Self::get_stack_size();
70        let (heap_size, tracker) = GetSize::get_heap_size_with_tracker(self, tracker);
71
72        let total = stack_size + heap_size;
73
74        (total, tracker)
75    }
76}
77
78impl GetSize for () {}
79impl GetSize for bool {}
80impl GetSize for u8 {}
81impl GetSize for u16 {}
82impl GetSize for u32 {}
83impl GetSize for u64 {}
84impl GetSize for u128 {}
85impl GetSize for usize {}
86impl GetSize for NonZeroU8 {}
87impl GetSize for NonZeroU16 {}
88impl GetSize for NonZeroU32 {}
89impl GetSize for NonZeroU64 {}
90impl GetSize for NonZeroU128 {}
91impl GetSize for NonZeroUsize {}
92impl GetSize for i8 {}
93impl GetSize for i16 {}
94impl GetSize for i32 {}
95impl GetSize for i64 {}
96impl GetSize for i128 {}
97impl GetSize for isize {}
98impl GetSize for NonZeroI8 {}
99impl GetSize for NonZeroI16 {}
100impl GetSize for NonZeroI32 {}
101impl GetSize for NonZeroI64 {}
102impl GetSize for NonZeroI128 {}
103impl GetSize for NonZeroIsize {}
104impl GetSize for f32 {}
105impl GetSize for f64 {}
106impl GetSize for char {}
107
108impl GetSize for AtomicBool {}
109impl GetSize for AtomicI8 {}
110impl GetSize for AtomicI16 {}
111impl GetSize for AtomicI32 {}
112impl GetSize for AtomicI64 {}
113impl GetSize for AtomicIsize {}
114impl GetSize for AtomicU8 {}
115impl GetSize for AtomicU16 {}
116impl GetSize for AtomicU32 {}
117impl GetSize for AtomicU64 {}
118impl GetSize for AtomicUsize {}
119impl GetSize for Ordering {}
120
121impl GetSize for std::cmp::Ordering {}
122
123impl GetSize for Infallible {}
124impl<T> GetSize for PhantomData<T> {}
125impl GetSize for PhantomPinned {}
126
127impl GetSize for Instant {}
128impl GetSize for Duration {}
129impl GetSize for SystemTime {}
130
131#[expect(clippy::needless_lifetimes, reason = "ok here")]
132impl<'a, T> GetSize for Cow<'a, T>
133where
134    T: ToOwned,
135    <T as ToOwned>::Owned: GetSize,
136{
137    fn get_heap_size(&self) -> usize {
138        match self {
139            Self::Borrowed(_borrowed) => 0,
140            Self::Owned(owned) => GetSize::get_heap_size(owned),
141        }
142    }
143}
144
145macro_rules! impl_size_set {
146    ($name:ident) => {
147        impl<T> GetSize for $name<T>
148        where
149            T: GetSize,
150        {
151            fn get_heap_size(&self) -> usize {
152                let mut total = 0;
153
154                for v in self.iter() {
155                    // We assume that value are hold inside the heap.
156                    total += GetSize::get_size(v);
157                }
158
159                let additional: usize = self.capacity() - self.len();
160                total += additional * T::get_stack_size();
161
162                total
163            }
164        }
165    };
166}
167
168macro_rules! impl_size_set_no_capacity {
169    ($name:ident) => {
170        impl<T> GetSize for $name<T>
171        where
172            T: GetSize,
173        {
174            fn get_heap_size(&self) -> usize {
175                let mut total = 0;
176
177                for v in self.iter() {
178                    // We assume that value are hold inside the heap.
179                    total += GetSize::get_size(v);
180                }
181
182                total
183            }
184        }
185    };
186}
187
188macro_rules! impl_size_map {
189    ($name:ident) => {
190        impl<K, V> GetSize for $name<K, V>
191        where
192            K: GetSize,
193            V: GetSize,
194        {
195            fn get_heap_size(&self) -> usize {
196                let mut total = 0;
197
198                for (k, v) in self.iter() {
199                    // We assume that keys and value are hold inside the heap.
200                    total += GetSize::get_size(k);
201                    total += GetSize::get_size(v);
202                }
203
204                let additional: usize = self.capacity() - self.len();
205                total += additional * K::get_stack_size();
206                total += additional * V::get_stack_size();
207
208                total
209            }
210        }
211    };
212}
213
214macro_rules! impl_size_map_no_capacity {
215    ($name:ident) => {
216        impl<K, V> GetSize for $name<K, V>
217        where
218            K: GetSize,
219            V: GetSize,
220        {
221            fn get_heap_size(&self) -> usize {
222                let mut total = 0;
223
224                for (k, v) in self.iter() {
225                    // We assume that keys and value are hold inside the heap.
226                    total += GetSize::get_size(k);
227                    total += GetSize::get_size(v);
228                }
229
230                total
231            }
232        }
233    };
234}
235
236impl_size_map_no_capacity!(BTreeMap);
237impl_size_set_no_capacity!(BTreeSet);
238impl_size_set!(BinaryHeap);
239impl_size_map!(HashMap);
240impl_size_set!(HashSet);
241impl_size_set_no_capacity!(LinkedList);
242impl_size_set!(VecDeque);
243
244impl_size_set!(Vec);
245
246macro_rules! impl_size_tuple {
247    ($($t:ident, $T:ident),+) => {
248        impl<$($T,)*> GetSize for ($($T,)*)
249        where
250            $(
251                $T: GetSize,
252            )*
253        {
254            fn get_heap_size(&self) -> usize {
255                let mut total = 0;
256
257                let ($($t,)*) = self;
258                $(
259                    total += GetSize::get_heap_size($t);
260                )*
261
262                total
263            }
264        }
265    }
266}
267
268macro_rules! execute_tuple_macro_16 {
269    ($name:ident) => {
270        $name!(v1, V1);
271        $name!(v1, V1, v2, V2);
272        $name!(v1, V1, v2, V2, v3, V3);
273        $name!(v1, V1, v2, V2, v3, V3, v4, V4);
274        $name!(v1, V1, v2, V2, v3, V3, v4, V4, v5, V5);
275        $name!(v1, V1, v2, V2, v3, V3, v4, V4, v5, V5, v6, V6);
276        $name!(v1, V1, v2, V2, v3, V3, v4, V4, v5, V5, v6, V6, v7, V7);
277        $name!(v1, V1, v2, V2, v3, V3, v4, V4, v5, V5, v6, V6, v7, V7, v8, V8);
278        $name!(v1, V1, v2, V2, v3, V3, v4, V4, v5, V5, v6, V6, v7, V7, v8, V8, v9, V9);
279        $name!(v1, V1, v2, V2, v3, V3, v4, V4, v5, V5, v6, V6, v7, V7, v8, V8, v9, V9, v10, V10);
280        $name!(
281            v1, V1, v2, V2, v3, V3, v4, V4, v5, V5, v6, V6, v7, V7, v8, V8, v9, V9, v10, V10, v11,
282            V11
283        );
284        $name!(
285            v1, V1, v2, V2, v3, V3, v4, V4, v5, V5, v6, V6, v7, V7, v8, V8, v9, V9, v10, V10, v11,
286            V11, v12, V12
287        );
288        $name!(
289            v1, V1, v2, V2, v3, V3, v4, V4, v5, V5, v6, V6, v7, V7, v8, V8, v9, V9, v10, V10, v11,
290            V11, v12, V12, v13, V13
291        );
292        $name!(
293            v1, V1, v2, V2, v3, V3, v4, V4, v5, V5, v6, V6, v7, V7, v8, V8, v9, V9, v10, V10, v11,
294            V11, v12, V12, v13, V13, v14, V14
295        );
296        $name!(
297            v1, V1, v2, V2, v3, V3, v4, V4, v5, V5, v6, V6, v7, V7, v8, V8, v9, V9, v10, V10, v11,
298            V11, v12, V12, v13, V13, v14, V14, v15, V15
299        );
300        $name!(
301            v1, V1, v2, V2, v3, V3, v4, V4, v5, V5, v6, V6, v7, V7, v8, V8, v9, V9, v10, V10, v11,
302            V11, v12, V12, v13, V13, v14, V14, v15, V15, v16, V16
303        );
304    };
305}
306
307execute_tuple_macro_16!(impl_size_tuple);
308
309impl<T, const SIZE: usize> GetSize for [T; SIZE]
310where
311    T: GetSize,
312{
313    fn get_heap_size(&self) -> usize {
314        let mut total = 0;
315
316        for element in self {
317            // The array stack size already accounts for the stack size of the elements of the array.
318            total += GetSize::get_heap_size(element);
319        }
320
321        total
322    }
323}
324
325impl<T> GetSize for &[T] where T: GetSize {}
326
327impl<T> GetSize for &T {}
328impl<T> GetSize for &mut T {}
329impl<T> GetSize for *const T {}
330impl<T> GetSize for *mut T {}
331
332impl<T> GetSize for Box<T>
333where
334    T: GetSize,
335{
336    fn get_heap_size(&self) -> usize {
337        GetSize::get_size(&**self)
338    }
339}
340
341impl<T> GetSize for Rc<T>
342where
343    T: GetSize + 'static,
344{
345    fn get_heap_size(&self) -> usize {
346        let tracker = StandardTracker::default();
347
348        let (total, _) = GetSize::get_heap_size_with_tracker(self, tracker);
349
350        total
351    }
352
353    fn get_heap_size_with_tracker<TR: GetSizeTracker>(&self, mut tracker: TR) -> (usize, TR) {
354        let strong_ref = Self::clone(self);
355
356        let addr = Self::as_ptr(&strong_ref);
357
358        if tracker.track(addr, strong_ref) {
359            GetSize::get_size_with_tracker(&**self, tracker)
360        } else {
361            (0, tracker)
362        }
363    }
364}
365
366impl<T> GetSize for RcWeak<T> {}
367
368impl<T> GetSize for Arc<T>
369where
370    T: GetSize + 'static,
371{
372    fn get_heap_size(&self) -> usize {
373        let tracker = StandardTracker::default();
374
375        let (total, _) = GetSize::get_heap_size_with_tracker(self, tracker);
376
377        total
378    }
379
380    fn get_heap_size_with_tracker<TR: GetSizeTracker>(&self, mut tracker: TR) -> (usize, TR) {
381        let strong_ref = Self::clone(self);
382
383        let addr = Self::as_ptr(&strong_ref);
384
385        if tracker.track(addr, strong_ref) {
386            GetSize::get_size_with_tracker(&**self, tracker)
387        } else {
388            (0, tracker)
389        }
390    }
391}
392
393impl<T> GetSize for ArcWeak<T> {}
394
395impl<T> GetSize for Option<T>
396where
397    T: GetSize,
398{
399    fn get_heap_size(&self) -> usize {
400        self.as_ref().map_or(0, |t| GetSize::get_heap_size(t))
401    }
402}
403
404impl<T, E> GetSize for Result<T, E>
405where
406    T: GetSize,
407    E: GetSize,
408{
409    fn get_heap_size(&self) -> usize {
410        match self {
411            // The results stack size already accounts for the values stack size.
412            Ok(t) => GetSize::get_heap_size(t),
413            Err(e) => GetSize::get_heap_size(e),
414        }
415    }
416}
417
418impl<T> GetSize for Mutex<T>
419where
420    T: GetSize,
421{
422    fn get_heap_size(&self) -> usize {
423        // We assume that a Mutex does hold its data at the stack.
424        GetSize::get_heap_size(&*(self.lock().expect("Mutex is poisoned")))
425    }
426}
427
428impl<T> GetSize for RwLock<T>
429where
430    T: GetSize,
431{
432    fn get_heap_size(&self) -> usize {
433        // We assume that a RwLock does hold its data at the stack.
434        GetSize::get_heap_size(&*(self.read().expect("RwLock is poisoned")))
435    }
436}
437
438impl GetSize for String {
439    fn get_heap_size(&self) -> usize {
440        self.capacity()
441    }
442}
443
444impl GetSize for &str {}
445
446impl GetSize for std::ffi::CString {
447    fn get_heap_size(&self) -> usize {
448        self.as_bytes_with_nul().len()
449    }
450}
451
452impl GetSize for &std::ffi::CStr {
453    fn get_heap_size(&self) -> usize {
454        self.to_bytes_with_nul().len()
455    }
456}
457
458impl GetSize for std::ffi::OsString {
459    fn get_heap_size(&self) -> usize {
460        self.len()
461    }
462}
463
464impl GetSize for &std::ffi::OsStr {
465    fn get_heap_size(&self) -> usize {
466        self.len()
467    }
468}
469
470impl GetSize for std::fs::DirBuilder {}
471impl GetSize for std::fs::DirEntry {}
472impl GetSize for std::fs::File {}
473impl GetSize for std::fs::FileType {}
474impl GetSize for std::fs::Metadata {}
475impl GetSize for std::fs::OpenOptions {}
476impl GetSize for std::fs::Permissions {}
477impl GetSize for std::fs::ReadDir {}
478
479impl<T> GetSize for std::io::BufReader<T>
480where
481    T: GetSize,
482{
483    fn get_heap_size(&self) -> usize {
484        let mut total = GetSize::get_heap_size(self.get_ref());
485
486        total += self.capacity();
487
488        total
489    }
490}
491
492impl<T> GetSize for std::io::BufWriter<T>
493where
494    T: GetSize + std::io::Write,
495{
496    fn get_heap_size(&self) -> usize {
497        let mut total = GetSize::get_heap_size(self.get_ref());
498
499        total += self.capacity();
500
501        total
502    }
503}
504
505impl GetSize for std::path::PathBuf {
506    fn get_heap_size(&self) -> usize {
507        self.capacity()
508    }
509}
510
511impl GetSize for &std::path::Path {}
512
513impl<T> GetSize for Box<[T]>
514where
515    T: GetSize,
516{
517    fn get_heap_size(&self) -> usize {
518        self.iter().map(GetSize::get_size).sum()
519    }
520}
521
522#[cfg(feature = "chrono")]
523mod chrono {
524    use crate::GetSize;
525
526    impl GetSize for chrono::NaiveDate {}
527    impl GetSize for chrono::NaiveTime {}
528    impl GetSize for chrono::NaiveDateTime {}
529    impl GetSize for chrono::Utc {}
530    impl GetSize for chrono::FixedOffset {}
531    impl GetSize for chrono::TimeDelta {}
532
533    impl<Tz: chrono::TimeZone> GetSize for chrono::DateTime<Tz>
534    where
535        Tz::Offset: GetSize,
536    {
537        fn get_heap_size(&self) -> usize {
538            GetSize::get_heap_size(self.offset())
539        }
540    }
541}
542
543#[cfg(feature = "chrono-tz")]
544impl GetSize for chrono_tz::TzOffset {}
545
546#[cfg(feature = "url")]
547impl GetSize for url::Url {
548    fn get_heap_size(&self) -> usize {
549        self.as_str().len()
550    }
551}