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 + ?Sized,
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,
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        if tracker.track(Rc::as_ptr(self)) {
396            GetSize::get_size_with_tracker(&**self, tracker)
397        } else {
398            (0, tracker)
399        }
400    }
401}
402
403impl<T> GetSize for RcWeak<T> {}
404
405impl<T> GetSize for Arc<T>
406where
407    T: GetSize,
408{
409    fn get_heap_size(&self) -> usize {
410        let tracker = StandardTracker::default();
411
412        let (total, _) = GetSize::get_heap_size_with_tracker(self, tracker);
413
414        total
415    }
416
417    fn get_heap_size_with_tracker<TR: GetSizeTracker>(&self, mut tracker: TR) -> (usize, TR) {
418        if tracker.track(Arc::as_ptr(self)) {
419            GetSize::get_size_with_tracker(&**self, tracker)
420        } else {
421            (0, tracker)
422        }
423    }
424}
425
426impl<T> GetSize for ArcWeak<T> {}
427
428impl<T> GetSize for Option<T>
429where
430    T: GetSize,
431{
432    fn get_heap_size(&self) -> usize {
433        self.as_ref().map_or(0, |t| GetSize::get_heap_size(t))
434    }
435}
436
437impl<T, E> GetSize for Result<T, E>
438where
439    T: GetSize,
440    E: GetSize,
441{
442    fn get_heap_size(&self) -> usize {
443        match self {
444            // The results stack size already accounts for the values stack size.
445            Ok(t) => GetSize::get_heap_size(t),
446            Err(e) => GetSize::get_heap_size(e),
447        }
448    }
449}
450
451impl<T> GetSize for Mutex<T>
452where
453    T: GetSize,
454{
455    fn get_heap_size(&self) -> usize {
456        // We assume that a `Mutex` holds its data at the stack.
457        GetSize::get_heap_size(&*(self.lock().expect("Mutex is poisoned")))
458    }
459}
460
461impl<T> GetSize for RwLock<T>
462where
463    T: GetSize,
464{
465    fn get_heap_size(&self) -> usize {
466        // We assume that a `RwLock` holds its data at the stack.
467        GetSize::get_heap_size(&*(self.read().expect("RwLock is poisoned")))
468    }
469}
470
471impl<T> GetSize for OnceLock<T>
472where
473    T: GetSize,
474{
475    fn get_heap_size(&self) -> usize {
476        // We assume that a `OnceLock` holds its data at the stack.
477        match self.get() {
478            None => 0,
479            Some(value) => GetSize::get_heap_size(value),
480        }
481    }
482}
483
484impl GetSize for String {
485    fn get_heap_size(&self) -> usize {
486        self.capacity()
487    }
488}
489
490impl GetSize for &str {}
491
492impl GetSize for std::ffi::CString {
493    fn get_heap_size(&self) -> usize {
494        self.as_bytes_with_nul().len()
495    }
496}
497
498impl GetSize for &std::ffi::CStr {
499    fn get_heap_size(&self) -> usize {
500        self.to_bytes_with_nul().len()
501    }
502}
503
504impl GetSize for std::ffi::OsString {
505    fn get_heap_size(&self) -> usize {
506        self.len()
507    }
508}
509
510impl GetSize for &std::ffi::OsStr {
511    fn get_heap_size(&self) -> usize {
512        self.len()
513    }
514}
515
516impl GetSize for std::fs::DirBuilder {}
517impl GetSize for std::fs::DirEntry {}
518impl GetSize for std::fs::File {}
519impl GetSize for std::fs::FileType {}
520impl GetSize for std::fs::Metadata {}
521impl GetSize for std::fs::OpenOptions {}
522impl GetSize for std::fs::Permissions {}
523impl GetSize for std::fs::ReadDir {}
524
525impl<T> GetSize for std::io::BufReader<T>
526where
527    T: GetSize,
528{
529    fn get_heap_size(&self) -> usize {
530        let mut total = GetSize::get_heap_size(self.get_ref());
531
532        total += self.capacity();
533
534        total
535    }
536}
537
538impl<T> GetSize for std::io::BufWriter<T>
539where
540    T: GetSize + std::io::Write,
541{
542    fn get_heap_size(&self) -> usize {
543        let mut total = GetSize::get_heap_size(self.get_ref());
544
545        total += self.capacity();
546
547        total
548    }
549}
550
551impl GetSize for std::path::PathBuf {
552    fn get_heap_size(&self) -> usize {
553        self.capacity()
554    }
555}
556
557impl GetSize for &std::path::Path {}
558
559impl<T> GetSize for Box<[T]>
560where
561    T: GetSize,
562{
563    fn get_heap_size(&self) -> usize {
564        self.iter().map(GetSize::get_size).sum()
565    }
566}
567
568impl GetSize for Box<str> {
569    fn get_heap_size(&self) -> usize {
570        self.len()
571    }
572}
573
574impl<T> GetSize for Rc<[T]>
575where
576    T: GetSize,
577{
578    fn get_heap_size(&self) -> usize {
579        self.iter().map(GetSize::get_size).sum()
580    }
581}
582
583impl GetSize for Rc<str> {
584    fn get_heap_size(&self) -> usize {
585        self.len()
586    }
587}
588
589impl<T> GetSize for Arc<[T]>
590where
591    T: GetSize,
592{
593    fn get_heap_size(&self) -> usize {
594        self.iter().map(GetSize::get_size).sum()
595    }
596}
597
598impl GetSize for Arc<str> {
599    fn get_heap_size(&self) -> usize {
600        self.len()
601    }
602}
603
604#[cfg(feature = "chrono")]
605mod chrono {
606    use crate::GetSize;
607
608    impl GetSize for chrono::NaiveDate {}
609    impl GetSize for chrono::NaiveTime {}
610    impl GetSize for chrono::NaiveDateTime {}
611    impl GetSize for chrono::Utc {}
612    impl GetSize for chrono::FixedOffset {}
613    impl GetSize for chrono::TimeDelta {}
614
615    impl<Tz: chrono::TimeZone> GetSize for chrono::DateTime<Tz>
616    where
617        Tz::Offset: GetSize,
618    {
619        fn get_heap_size(&self) -> usize {
620            GetSize::get_heap_size(self.offset())
621        }
622    }
623}
624
625#[cfg(feature = "chrono-tz")]
626impl GetSize for chrono_tz::TzOffset {}
627
628#[cfg(feature = "url")]
629impl GetSize for url::Url {
630    fn get_heap_size(&self) -> usize {
631        self.as_str().len()
632    }
633}
634
635#[cfg(feature = "bytes")]
636impl GetSize for bytes::Bytes {
637    fn get_heap_size(&self) -> usize {
638        self.len()
639    }
640}
641
642#[cfg(feature = "bytes")]
643impl GetSize for bytes::BytesMut {
644    fn get_heap_size(&self) -> usize {
645        self.len()
646    }
647}
648
649#[cfg(feature = "hashbrown")]
650impl<K, V, H> GetSize for hashbrown::HashMap<K, V, H>
651where
652    K: GetSize + Eq + std::hash::Hash,
653    V: GetSize,
654    H: std::hash::BuildHasher,
655{
656    fn get_heap_size(&self) -> usize {
657        self.allocation_size()
658            + self
659                .iter()
660                .map(|(k, v)| k.get_heap_size() + v.get_heap_size())
661                .sum::<usize>()
662    }
663}
664
665#[cfg(feature = "hashbrown")]
666impl<T, H> GetSize for hashbrown::HashSet<T, H>
667where
668    T: GetSize + Eq + std::hash::Hash,
669    H: std::hash::BuildHasher,
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 = "hashbrown")]
677impl<T> GetSize for hashbrown::HashTable<T>
678where
679    T: GetSize,
680{
681    fn get_heap_size(&self) -> usize {
682        self.allocation_size() + self.iter().map(GetSize::get_heap_size).sum::<usize>()
683    }
684}
685
686#[cfg(feature = "smallvec")]
687impl<A: smallvec::Array> GetSize for smallvec::SmallVec<A>
688where
689    A::Item: GetSize,
690{
691    fn get_heap_size(&self) -> usize {
692        if self.len() <= self.inline_size() {
693            return self.iter().map(GetSize::get_heap_size).sum();
694        }
695
696        let mut total = self.iter().map(GetSize::get_size).sum();
697        let additional: usize = self.capacity() - self.len();
698        total += additional * A::Item::get_stack_size();
699        total
700    }
701}
702
703#[cfg(feature = "thin-vec")]
704impl<T> GetSize for thin_vec::ThinVec<T>
705where
706    T: GetSize,
707{
708    fn get_heap_size(&self) -> usize {
709        if self.capacity() == 0 {
710            // If it's the singleton we might not be a heap pointer.
711            return 0;
712        }
713
714        // The capacity and length are stored on the heap.
715        let mut total = std::mem::size_of::<usize>() * 2;
716
717        for v in self {
718            // We assume that value are hold inside the heap.
719            total += GetSize::get_size(v);
720        }
721
722        let additional: usize = self.capacity() - self.len();
723        total += additional * T::get_stack_size();
724
725        total
726    }
727}
728
729#[cfg(feature = "compact-str")]
730impl GetSize for compact_str::CompactString {
731    fn get_heap_size(&self) -> usize {
732        if self.is_heap_allocated() {
733            self.capacity()
734        } else {
735            0
736        }
737    }
738}
739
740#[cfg(feature = "indexmap")]
741impl<K, V, S> GetSize for indexmap::IndexMap<K, V, S>
742where
743    K: GetSize,
744    V: GetSize,
745    S: std::hash::BuildHasher,
746{
747    fn get_heap_size(&self) -> usize {
748        self.capacity() * <(K, V)>::get_stack_size()
749            + self
750                .iter()
751                .map(|(k, v)| k.get_heap_size() + v.get_heap_size())
752                .sum::<usize>()
753    }
754}
755
756#[cfg(feature = "indexmap")]
757impl<T, S> GetSize for indexmap::IndexSet<T, S>
758where
759    T: GetSize,
760{
761    fn get_heap_size(&self) -> usize {
762        self.capacity() * <T>::get_stack_size()
763            + self.iter().map(GetSize::get_heap_size).sum::<usize>()
764    }
765}