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    NonZeroI8, NonZeroI16, NonZeroI32, NonZeroI64, NonZeroI128, NonZeroIsize, NonZeroU8,
10    NonZeroU16, NonZeroU32, NonZeroU64, NonZeroU128, NonZeroUsize,
11};
12use std::ops::{Range, RangeFrom, RangeFull, RangeInclusive, RangeTo, RangeToInclusive};
13use std::rc::{Rc, Weak as RcWeak};
14use std::sync::atomic::{
15    AtomicBool, AtomicI8, AtomicI16, AtomicI32, AtomicI64, AtomicIsize, AtomicU8, AtomicU16,
16    AtomicU32, AtomicU64, AtomicUsize, Ordering,
17};
18use std::sync::{Arc, Mutex, OnceLock, RwLock, Weak as ArcWeak};
19use std::time::{Duration, Instant, SystemTime};
20
21#[cfg(feature = "derive")]
22#[cfg_attr(docsrs, doc(cfg(feature = "derive")))]
23pub use get_size_derive2::*;
24
25mod tracker;
26pub use tracker::*;
27#[cfg(test)]
28mod test;
29
30/// Determines how many bytes the object occupies inside the heap.
31pub fn heap_size<T: GetSize>(value: &T) -> usize {
32    value.get_heap_size()
33}
34
35/// Determine the size in bytes an object occupies inside RAM.
36pub trait GetSize: Sized {
37    /// Determines how may bytes this object occupies inside the stack.
38    ///
39    /// The default implementation uses [`std::mem::size_of`] and should work for almost all types.
40    #[must_use]
41    fn get_stack_size() -> usize {
42        std::mem::size_of::<Self>()
43    }
44
45    /// Determines how many bytes this object occupies inside the heap.
46    ///
47    /// The default implementation returns 0, assuming the object is fully allocated on the stack.
48    /// It must be adjusted as appropriate for objects which hold data inside the heap.
49    fn get_heap_size(&self) -> usize {
50        0
51    }
52
53    /// Determines how many bytes this object occupies inside the heap while using a `tracker`.
54    ///
55    /// The default implementation ignores the tracker and calls [`get_heap_size`](Self::get_heap_size)
56    /// instead, returning the tracker untouched in the second argument.
57    fn get_heap_size_with_tracker<T: GetSizeTracker>(&self, tracker: T) -> (usize, T) {
58        (GetSize::get_heap_size(self), tracker)
59    }
60
61    /// Determines the total size of the object.
62    ///
63    /// The default implementation simply adds up the results of [`get_stack_size`](Self::get_stack_size)
64    /// and [`get_heap_size`](Self::get_heap_size) and is not meant to be changed.
65    fn get_size(&self) -> usize {
66        Self::get_stack_size() + GetSize::get_heap_size(self)
67    }
68
69    /// Determines the total size of the object while using a `tracker`.
70    ///
71    /// The default implementation simply adds up the results of [`get_stack_size`](Self::get_stack_size)
72    /// and [`get_heap_size_with_tracker`](Self::get_heap_size_with_tracker) and is not meant to
73    /// be changed.
74    fn get_size_with_tracker<T: GetSizeTracker>(&self, tracker: T) -> (usize, T) {
75        let stack_size = Self::get_stack_size();
76        let (heap_size, tracker) = GetSize::get_heap_size_with_tracker(self, tracker);
77
78        let total = stack_size + heap_size;
79
80        (total, tracker)
81    }
82}
83
84impl GetSize for () {}
85impl GetSize for bool {}
86impl GetSize for u8 {}
87impl GetSize for u16 {}
88impl GetSize for u32 {}
89impl GetSize for u64 {}
90impl GetSize for u128 {}
91impl GetSize for usize {}
92impl GetSize for NonZeroU8 {}
93impl GetSize for NonZeroU16 {}
94impl GetSize for NonZeroU32 {}
95impl GetSize for NonZeroU64 {}
96impl GetSize for NonZeroU128 {}
97impl GetSize for NonZeroUsize {}
98impl GetSize for i8 {}
99impl GetSize for i16 {}
100impl GetSize for i32 {}
101impl GetSize for i64 {}
102impl GetSize for i128 {}
103impl GetSize for isize {}
104impl GetSize for NonZeroI8 {}
105impl GetSize for NonZeroI16 {}
106impl GetSize for NonZeroI32 {}
107impl GetSize for NonZeroI64 {}
108impl GetSize for NonZeroI128 {}
109impl GetSize for NonZeroIsize {}
110impl GetSize for f32 {}
111impl GetSize for f64 {}
112impl GetSize for char {}
113
114impl GetSize for AtomicBool {}
115impl GetSize for AtomicI8 {}
116impl GetSize for AtomicI16 {}
117impl GetSize for AtomicI32 {}
118impl GetSize for AtomicI64 {}
119impl GetSize for AtomicIsize {}
120impl GetSize for AtomicU8 {}
121impl GetSize for AtomicU16 {}
122impl GetSize for AtomicU32 {}
123impl GetSize for AtomicU64 {}
124impl GetSize for AtomicUsize {}
125impl GetSize for Ordering {}
126
127impl GetSize for std::cmp::Ordering {}
128
129impl GetSize for Infallible {}
130impl<T> GetSize for PhantomData<T> {}
131impl GetSize for PhantomPinned {}
132
133impl GetSize for Instant {}
134impl GetSize for Duration {}
135impl GetSize for SystemTime {}
136
137/// This macro is similar to the derive macro; Generate a `GetSize` impl that
138/// adds the heap sizes of the fields that are specified. However, since we want
139/// to implement this also for third-party types where we cannot add the derive
140/// macro, we go this way.
141///
142/// # Examples
143/// ```ignore
144/// impl_sum_of_fields!(Range, start, end);
145/// impl_sum_of_fields!(Person, name, address, phone);
146/// ```
147macro_rules! impl_sum_of_fields {
148    ($name:ident, $($field:ident),+) => {
149        impl<I: GetSize> GetSize for $name<I> {
150            #[inline]
151            fn get_heap_size(&self) -> usize {
152                0 $(+ self.$field.get_heap_size())+
153            }
154        }
155    };
156}
157
158impl_sum_of_fields!(Range, start, end);
159impl_sum_of_fields!(RangeFrom, start);
160impl_sum_of_fields!(RangeTo, end);
161impl_sum_of_fields!(RangeToInclusive, end);
162impl GetSize for RangeFull {}
163
164impl<I: GetSize> GetSize for RangeInclusive<I> {
165    #[inline]
166    fn get_heap_size(&self) -> usize {
167        // Custom impl since start and end fields are not public API
168        (*self.start()).get_heap_size() + (*self.end()).get_heap_size()
169    }
170}
171
172impl<T> GetSize for Cow<'_, T>
173where
174    T: ToOwned,
175    <T as ToOwned>::Owned: GetSize,
176{
177    fn get_heap_size(&self) -> usize {
178        match self {
179            Self::Borrowed(_borrowed) => 0,
180            Self::Owned(owned) => GetSize::get_heap_size(owned),
181        }
182    }
183}
184
185macro_rules! impl_size_set {
186    ($name:ident) => {
187        impl<T> GetSize for $name<T>
188        where
189            T: GetSize,
190        {
191            fn get_heap_size(&self) -> usize {
192                let mut total = 0;
193
194                for v in self.iter() {
195                    // We assume that value are hold inside the heap.
196                    total += GetSize::get_size(v);
197                }
198
199                let additional: usize = self.capacity() - self.len();
200                total += additional * T::get_stack_size();
201
202                total
203            }
204        }
205    };
206}
207
208macro_rules! impl_size_set_no_capacity {
209    ($name:ident) => {
210        impl<T> GetSize for $name<T>
211        where
212            T: GetSize,
213        {
214            fn get_heap_size(&self) -> usize {
215                let mut total = 0;
216
217                for v in self.iter() {
218                    // We assume that value are hold inside the heap.
219                    total += GetSize::get_size(v);
220                }
221
222                total
223            }
224        }
225    };
226}
227
228impl_size_set_no_capacity!(BTreeSet);
229impl_size_set!(BinaryHeap);
230impl_size_set_no_capacity!(LinkedList);
231impl_size_set!(VecDeque);
232
233impl<K, V> GetSize for BTreeMap<K, V>
234where
235    K: GetSize,
236    V: GetSize,
237{
238    fn get_heap_size(&self) -> usize {
239        let mut total = 0;
240        for (k, v) in self {
241            total += GetSize::get_size(k);
242            total += GetSize::get_size(v);
243        }
244        total
245    }
246}
247
248impl<K, V, S: ::std::hash::BuildHasher> GetSize for HashMap<K, V, S>
249where
250    K: GetSize,
251    V: GetSize,
252{
253    fn get_heap_size(&self) -> usize {
254        let mut total = 0;
255        for (k, v) in self {
256            total += GetSize::get_heap_size(k);
257            total += GetSize::get_heap_size(v);
258        }
259        total += self.capacity() * <(K, V)>::get_stack_size();
260        total
261    }
262}
263
264impl<T, S: ::std::hash::BuildHasher> GetSize for HashSet<T, S>
265where
266    T: GetSize,
267{
268    fn get_heap_size(&self) -> usize {
269        let mut total = 0;
270        for v in self {
271            total += GetSize::get_size(v);
272        }
273        let additional: usize = self.capacity() - self.len();
274        total += additional * T::get_stack_size();
275        total
276    }
277}
278
279impl_size_set!(Vec);
280
281macro_rules! impl_size_tuple {
282    ($($t:ident, $T:ident),+) => {
283        impl<$($T,)*> GetSize for ($($T,)*)
284        where
285            $(
286                $T: GetSize,
287            )*
288        {
289            fn get_heap_size(&self) -> usize {
290                let mut total = 0;
291
292                let ($($t,)*) = self;
293                $(
294                    total += GetSize::get_heap_size($t);
295                )*
296
297                total
298            }
299        }
300    }
301}
302
303macro_rules! execute_tuple_macro_16 {
304    ($name:ident) => {
305        $name!(v1, V1);
306        $name!(v1, V1, v2, V2);
307        $name!(v1, V1, v2, V2, v3, V3);
308        $name!(v1, V1, v2, V2, v3, V3, v4, V4);
309        $name!(v1, V1, v2, V2, v3, V3, v4, V4, v5, V5);
310        $name!(v1, V1, v2, V2, v3, V3, v4, V4, v5, V5, v6, V6);
311        $name!(v1, V1, v2, V2, v3, V3, v4, V4, v5, V5, v6, V6, v7, V7);
312        $name!(
313            v1, V1, v2, V2, v3, V3, v4, V4, v5, V5, v6, V6, v7, V7, v8, V8
314        );
315        $name!(
316            v1, V1, v2, V2, v3, V3, v4, V4, v5, V5, v6, V6, v7, V7, v8, V8, v9, V9
317        );
318        $name!(
319            v1, V1, v2, V2, v3, V3, v4, V4, v5, V5, v6, V6, v7, V7, v8, V8, v9, V9, v10, V10
320        );
321        $name!(
322            v1, V1, v2, V2, v3, V3, v4, V4, v5, V5, v6, V6, v7, V7, v8, V8, v9, V9, v10, V10, v11,
323            V11
324        );
325        $name!(
326            v1, V1, v2, V2, v3, V3, v4, V4, v5, V5, v6, V6, v7, V7, v8, V8, v9, V9, v10, V10, v11,
327            V11, v12, V12
328        );
329        $name!(
330            v1, V1, v2, V2, v3, V3, v4, V4, v5, V5, v6, V6, v7, V7, v8, V8, v9, V9, v10, V10, v11,
331            V11, v12, V12, v13, V13
332        );
333        $name!(
334            v1, V1, v2, V2, v3, V3, v4, V4, v5, V5, v6, V6, v7, V7, v8, V8, v9, V9, v10, V10, v11,
335            V11, v12, V12, v13, V13, v14, V14
336        );
337        $name!(
338            v1, V1, v2, V2, v3, V3, v4, V4, v5, V5, v6, V6, v7, V7, v8, V8, v9, V9, v10, V10, v11,
339            V11, v12, V12, v13, V13, v14, V14, v15, V15
340        );
341        $name!(
342            v1, V1, v2, V2, v3, V3, v4, V4, v5, V5, v6, V6, v7, V7, v8, V8, v9, V9, v10, V10, v11,
343            V11, v12, V12, v13, V13, v14, V14, v15, V15, v16, V16
344        );
345    };
346}
347
348execute_tuple_macro_16!(impl_size_tuple);
349
350impl<T, const SIZE: usize> GetSize for [T; SIZE]
351where
352    T: GetSize,
353{
354    fn get_heap_size(&self) -> usize {
355        let mut total = 0;
356
357        for element in self {
358            // The array stack size already accounts for the stack size of the elements of the array.
359            total += GetSize::get_heap_size(element);
360        }
361
362        total
363    }
364}
365
366impl<T> GetSize for &[T] where T: GetSize {}
367
368impl<T> GetSize for &T {}
369impl<T> GetSize for &mut T {}
370impl<T> GetSize for *const T {}
371impl<T> GetSize for *mut T {}
372
373impl<T> GetSize for Box<T>
374where
375    T: GetSize,
376{
377    fn get_heap_size(&self) -> usize {
378        GetSize::get_size(&**self)
379    }
380}
381
382impl<T> GetSize for Rc<T>
383where
384    T: GetSize + 'static,
385{
386    fn get_heap_size(&self) -> usize {
387        let tracker = StandardTracker::default();
388
389        let (total, _) = GetSize::get_heap_size_with_tracker(self, tracker);
390
391        total
392    }
393
394    fn get_heap_size_with_tracker<TR: GetSizeTracker>(&self, mut tracker: TR) -> (usize, TR) {
395        let strong_ref = Self::clone(self);
396
397        let addr = Self::as_ptr(&strong_ref);
398
399        if tracker.track(addr, strong_ref) {
400            GetSize::get_size_with_tracker(&**self, tracker)
401        } else {
402            (0, tracker)
403        }
404    }
405}
406
407impl<T> GetSize for RcWeak<T> {}
408
409impl<T> GetSize for Arc<T>
410where
411    T: GetSize + 'static,
412{
413    fn get_heap_size(&self) -> usize {
414        let tracker = StandardTracker::default();
415
416        let (total, _) = GetSize::get_heap_size_with_tracker(self, tracker);
417
418        total
419    }
420
421    fn get_heap_size_with_tracker<TR: GetSizeTracker>(&self, mut tracker: TR) -> (usize, TR) {
422        let strong_ref = Self::clone(self);
423
424        let addr = Self::as_ptr(&strong_ref);
425
426        if tracker.track(addr, strong_ref) {
427            GetSize::get_size_with_tracker(&**self, tracker)
428        } else {
429            (0, tracker)
430        }
431    }
432}
433
434impl<T> GetSize for ArcWeak<T> {}
435
436impl<T> GetSize for Option<T>
437where
438    T: GetSize,
439{
440    fn get_heap_size(&self) -> usize {
441        self.as_ref().map_or(0, |t| GetSize::get_heap_size(t))
442    }
443}
444
445impl<T, E> GetSize for Result<T, E>
446where
447    T: GetSize,
448    E: GetSize,
449{
450    fn get_heap_size(&self) -> usize {
451        match self {
452            // The results stack size already accounts for the values stack size.
453            Ok(t) => GetSize::get_heap_size(t),
454            Err(e) => GetSize::get_heap_size(e),
455        }
456    }
457}
458
459impl<T> GetSize for Mutex<T>
460where
461    T: GetSize,
462{
463    fn get_heap_size(&self) -> usize {
464        // We assume that a `Mutex` holds its data at the stack.
465        GetSize::get_heap_size(&*(self.lock().expect("Mutex is poisoned")))
466    }
467}
468
469impl<T> GetSize for RwLock<T>
470where
471    T: GetSize,
472{
473    fn get_heap_size(&self) -> usize {
474        // We assume that a `RwLock` holds its data at the stack.
475        GetSize::get_heap_size(&*(self.read().expect("RwLock is poisoned")))
476    }
477}
478
479impl<T> GetSize for OnceLock<T>
480where
481    T: GetSize,
482{
483    fn get_heap_size(&self) -> usize {
484        // We assume that a `OnceLock` holds its data at the stack.
485        match self.get() {
486            None => 0,
487            Some(value) => GetSize::get_heap_size(value),
488        }
489    }
490}
491
492impl GetSize for String {
493    fn get_heap_size(&self) -> usize {
494        self.capacity()
495    }
496}
497
498impl GetSize for &str {}
499
500impl GetSize for std::ffi::CString {
501    fn get_heap_size(&self) -> usize {
502        self.as_bytes_with_nul().len()
503    }
504}
505
506impl GetSize for &std::ffi::CStr {
507    fn get_heap_size(&self) -> usize {
508        self.to_bytes_with_nul().len()
509    }
510}
511
512impl GetSize for std::ffi::OsString {
513    fn get_heap_size(&self) -> usize {
514        self.len()
515    }
516}
517
518impl GetSize for &std::ffi::OsStr {
519    fn get_heap_size(&self) -> usize {
520        self.len()
521    }
522}
523
524impl GetSize for std::fs::DirBuilder {}
525impl GetSize for std::fs::DirEntry {}
526impl GetSize for std::fs::File {}
527impl GetSize for std::fs::FileType {}
528impl GetSize for std::fs::Metadata {}
529impl GetSize for std::fs::OpenOptions {}
530impl GetSize for std::fs::Permissions {}
531impl GetSize for std::fs::ReadDir {}
532
533impl<T> GetSize for std::io::BufReader<T>
534where
535    T: GetSize,
536{
537    fn get_heap_size(&self) -> usize {
538        let mut total = GetSize::get_heap_size(self.get_ref());
539
540        total += self.capacity();
541
542        total
543    }
544}
545
546impl<T> GetSize for std::io::BufWriter<T>
547where
548    T: GetSize + std::io::Write,
549{
550    fn get_heap_size(&self) -> usize {
551        let mut total = GetSize::get_heap_size(self.get_ref());
552
553        total += self.capacity();
554
555        total
556    }
557}
558
559impl GetSize for std::path::PathBuf {
560    fn get_heap_size(&self) -> usize {
561        self.capacity()
562    }
563}
564
565impl GetSize for &std::path::Path {}
566
567impl<T> GetSize for Box<[T]>
568where
569    T: GetSize,
570{
571    fn get_heap_size(&self) -> usize {
572        self.iter().map(GetSize::get_size).sum()
573    }
574}
575
576impl GetSize for Box<str> {
577    fn get_heap_size(&self) -> usize {
578        self.len()
579    }
580}
581
582impl GetSize for Rc<str> {
583    fn get_heap_size(&self) -> usize {
584        self.len()
585    }
586}
587
588impl GetSize for Arc<str> {
589    fn get_heap_size(&self) -> usize {
590        self.len()
591    }
592}
593
594#[cfg(feature = "chrono")]
595mod chrono {
596    use crate::GetSize;
597
598    impl GetSize for chrono::NaiveDate {}
599    impl GetSize for chrono::NaiveTime {}
600    impl GetSize for chrono::NaiveDateTime {}
601    impl GetSize for chrono::Utc {}
602    impl GetSize for chrono::FixedOffset {}
603    impl GetSize for chrono::TimeDelta {}
604
605    impl<Tz: chrono::TimeZone> GetSize for chrono::DateTime<Tz>
606    where
607        Tz::Offset: GetSize,
608    {
609        fn get_heap_size(&self) -> usize {
610            GetSize::get_heap_size(self.offset())
611        }
612    }
613}
614
615#[cfg(feature = "chrono-tz")]
616impl GetSize for chrono_tz::TzOffset {}
617
618#[cfg(feature = "url")]
619impl GetSize for url::Url {
620    fn get_heap_size(&self) -> usize {
621        self.as_str().len()
622    }
623}
624
625#[cfg(feature = "bytes")]
626impl GetSize for bytes::Bytes {
627    fn get_heap_size(&self) -> usize {
628        self.len()
629    }
630}
631
632#[cfg(feature = "bytes")]
633impl GetSize for bytes::BytesMut {
634    fn get_heap_size(&self) -> usize {
635        self.len()
636    }
637}
638
639#[cfg(feature = "hashbrown")]
640impl<K, V, H> GetSize for hashbrown::HashMap<K, V, H>
641where
642    K: GetSize + Eq + std::hash::Hash,
643    V: GetSize,
644    H: std::hash::BuildHasher,
645{
646    fn get_heap_size(&self) -> usize {
647        self.allocation_size()
648            + self
649                .iter()
650                .map(|(k, v)| k.get_heap_size() + v.get_heap_size())
651                .sum::<usize>()
652    }
653}
654
655#[cfg(feature = "hashbrown")]
656impl<T, H> GetSize for hashbrown::HashSet<T, H>
657where
658    T: GetSize + Eq + std::hash::Hash,
659    H: std::hash::BuildHasher,
660{
661    fn get_heap_size(&self) -> usize {
662        self.allocation_size() + self.iter().map(GetSize::get_heap_size).sum::<usize>()
663    }
664}
665
666#[cfg(feature = "hashbrown")]
667impl<T> GetSize for hashbrown::HashTable<T>
668where
669    T: GetSize,
670{
671    fn get_heap_size(&self) -> usize {
672        self.allocation_size() + self.iter().map(GetSize::get_heap_size).sum::<usize>()
673    }
674}
675
676#[cfg(feature = "smallvec")]
677impl<A: smallvec::Array> GetSize for smallvec::SmallVec<A>
678where
679    A::Item: GetSize,
680{
681    fn get_heap_size(&self) -> usize {
682        if self.len() <= self.inline_size() {
683            return self.iter().map(GetSize::get_heap_size).sum();
684        }
685
686        self.iter().map(GetSize::get_size).sum()
687    }
688}
689
690#[cfg(feature = "compact-str")]
691impl GetSize for compact_str::CompactString {
692    fn get_heap_size(&self) -> usize {
693        if self.is_heap_allocated() {
694            self.len()
695        } else {
696            0
697        }
698    }
699}