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::rc::{Rc, Weak as RcWeak};
13use std::sync::atomic::{
14    AtomicBool, AtomicI8, AtomicI16, AtomicI32, AtomicI64, AtomicIsize, AtomicU8, AtomicU16,
15    AtomicU32, AtomicU64, 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!(
278            v1, V1, v2, V2, v3, V3, v4, V4, v5, V5, v6, V6, v7, V7, v8, V8
279        );
280        $name!(
281            v1, V1, v2, V2, v3, V3, v4, V4, v5, V5, v6, V6, v7, V7, v8, V8, v9, V9
282        );
283        $name!(
284            v1, V1, v2, V2, v3, V3, v4, V4, v5, V5, v6, V6, v7, V7, v8, V8, v9, V9, v10, V10
285        );
286        $name!(
287            v1, V1, v2, V2, v3, V3, v4, V4, v5, V5, v6, V6, v7, V7, v8, V8, v9, V9, v10, V10, v11,
288            V11
289        );
290        $name!(
291            v1, V1, v2, V2, v3, V3, v4, V4, v5, V5, v6, V6, v7, V7, v8, V8, v9, V9, v10, V10, v11,
292            V11, v12, V12
293        );
294        $name!(
295            v1, V1, v2, V2, v3, V3, v4, V4, v5, V5, v6, V6, v7, V7, v8, V8, v9, V9, v10, V10, v11,
296            V11, v12, V12, v13, V13
297        );
298        $name!(
299            v1, V1, v2, V2, v3, V3, v4, V4, v5, V5, v6, V6, v7, V7, v8, V8, v9, V9, v10, V10, v11,
300            V11, v12, V12, v13, V13, v14, V14
301        );
302        $name!(
303            v1, V1, v2, V2, v3, V3, v4, V4, v5, V5, v6, V6, v7, V7, v8, V8, v9, V9, v10, V10, v11,
304            V11, v12, V12, v13, V13, v14, V14, v15, V15
305        );
306        $name!(
307            v1, V1, v2, V2, v3, V3, v4, V4, v5, V5, v6, V6, v7, V7, v8, V8, v9, V9, v10, V10, v11,
308            V11, v12, V12, v13, V13, v14, V14, v15, V15, v16, V16
309        );
310    };
311}
312
313execute_tuple_macro_16!(impl_size_tuple);
314
315impl<T, const SIZE: usize> GetSize for [T; SIZE]
316where
317    T: GetSize,
318{
319    fn get_heap_size(&self) -> usize {
320        let mut total = 0;
321
322        for element in self {
323            // The array stack size already accounts for the stack size of the elements of the array.
324            total += GetSize::get_heap_size(element);
325        }
326
327        total
328    }
329}
330
331impl<T> GetSize for &[T] where T: GetSize {}
332
333impl<T> GetSize for &T {}
334impl<T> GetSize for &mut T {}
335impl<T> GetSize for *const T {}
336impl<T> GetSize for *mut T {}
337
338impl<T> GetSize for Box<T>
339where
340    T: GetSize,
341{
342    fn get_heap_size(&self) -> usize {
343        GetSize::get_size(&**self)
344    }
345}
346
347impl<T> GetSize for Rc<T>
348where
349    T: GetSize + 'static,
350{
351    fn get_heap_size(&self) -> usize {
352        let tracker = StandardTracker::default();
353
354        let (total, _) = GetSize::get_heap_size_with_tracker(self, tracker);
355
356        total
357    }
358
359    fn get_heap_size_with_tracker<TR: GetSizeTracker>(&self, mut tracker: TR) -> (usize, TR) {
360        let strong_ref = Self::clone(self);
361
362        let addr = Self::as_ptr(&strong_ref);
363
364        if tracker.track(addr, strong_ref) {
365            GetSize::get_size_with_tracker(&**self, tracker)
366        } else {
367            (0, tracker)
368        }
369    }
370}
371
372impl<T> GetSize for RcWeak<T> {}
373
374impl<T> GetSize for Arc<T>
375where
376    T: GetSize + 'static,
377{
378    fn get_heap_size(&self) -> usize {
379        let tracker = StandardTracker::default();
380
381        let (total, _) = GetSize::get_heap_size_with_tracker(self, tracker);
382
383        total
384    }
385
386    fn get_heap_size_with_tracker<TR: GetSizeTracker>(&self, mut tracker: TR) -> (usize, TR) {
387        let strong_ref = Self::clone(self);
388
389        let addr = Self::as_ptr(&strong_ref);
390
391        if tracker.track(addr, strong_ref) {
392            GetSize::get_size_with_tracker(&**self, tracker)
393        } else {
394            (0, tracker)
395        }
396    }
397}
398
399impl<T> GetSize for ArcWeak<T> {}
400
401impl<T> GetSize for Option<T>
402where
403    T: GetSize,
404{
405    fn get_heap_size(&self) -> usize {
406        self.as_ref().map_or(0, |t| GetSize::get_heap_size(t))
407    }
408}
409
410impl<T, E> GetSize for Result<T, E>
411where
412    T: GetSize,
413    E: GetSize,
414{
415    fn get_heap_size(&self) -> usize {
416        match self {
417            // The results stack size already accounts for the values stack size.
418            Ok(t) => GetSize::get_heap_size(t),
419            Err(e) => GetSize::get_heap_size(e),
420        }
421    }
422}
423
424impl<T> GetSize for Mutex<T>
425where
426    T: GetSize,
427{
428    fn get_heap_size(&self) -> usize {
429        // We assume that a Mutex does hold its data at the stack.
430        GetSize::get_heap_size(&*(self.lock().expect("Mutex is poisoned")))
431    }
432}
433
434impl<T> GetSize for RwLock<T>
435where
436    T: GetSize,
437{
438    fn get_heap_size(&self) -> usize {
439        // We assume that a RwLock does hold its data at the stack.
440        GetSize::get_heap_size(&*(self.read().expect("RwLock is poisoned")))
441    }
442}
443
444impl GetSize for String {
445    fn get_heap_size(&self) -> usize {
446        self.capacity()
447    }
448}
449
450impl GetSize for &str {}
451
452impl GetSize for std::ffi::CString {
453    fn get_heap_size(&self) -> usize {
454        self.as_bytes_with_nul().len()
455    }
456}
457
458impl GetSize for &std::ffi::CStr {
459    fn get_heap_size(&self) -> usize {
460        self.to_bytes_with_nul().len()
461    }
462}
463
464impl GetSize for std::ffi::OsString {
465    fn get_heap_size(&self) -> usize {
466        self.len()
467    }
468}
469
470impl GetSize for &std::ffi::OsStr {
471    fn get_heap_size(&self) -> usize {
472        self.len()
473    }
474}
475
476impl GetSize for std::fs::DirBuilder {}
477impl GetSize for std::fs::DirEntry {}
478impl GetSize for std::fs::File {}
479impl GetSize for std::fs::FileType {}
480impl GetSize for std::fs::Metadata {}
481impl GetSize for std::fs::OpenOptions {}
482impl GetSize for std::fs::Permissions {}
483impl GetSize for std::fs::ReadDir {}
484
485impl<T> GetSize for std::io::BufReader<T>
486where
487    T: GetSize,
488{
489    fn get_heap_size(&self) -> usize {
490        let mut total = GetSize::get_heap_size(self.get_ref());
491
492        total += self.capacity();
493
494        total
495    }
496}
497
498impl<T> GetSize for std::io::BufWriter<T>
499where
500    T: GetSize + std::io::Write,
501{
502    fn get_heap_size(&self) -> usize {
503        let mut total = GetSize::get_heap_size(self.get_ref());
504
505        total += self.capacity();
506
507        total
508    }
509}
510
511impl GetSize for std::path::PathBuf {
512    fn get_heap_size(&self) -> usize {
513        self.capacity()
514    }
515}
516
517impl GetSize for &std::path::Path {}
518
519impl<T> GetSize for Box<[T]>
520where
521    T: GetSize,
522{
523    fn get_heap_size(&self) -> usize {
524        self.iter().map(GetSize::get_size).sum()
525    }
526}
527
528#[cfg(feature = "chrono")]
529mod chrono {
530    use crate::GetSize;
531
532    impl GetSize for chrono::NaiveDate {}
533    impl GetSize for chrono::NaiveTime {}
534    impl GetSize for chrono::NaiveDateTime {}
535    impl GetSize for chrono::Utc {}
536    impl GetSize for chrono::FixedOffset {}
537    impl GetSize for chrono::TimeDelta {}
538
539    impl<Tz: chrono::TimeZone> GetSize for chrono::DateTime<Tz>
540    where
541        Tz::Offset: GetSize,
542    {
543        fn get_heap_size(&self) -> usize {
544            GetSize::get_heap_size(self.offset())
545        }
546    }
547}
548
549#[cfg(feature = "chrono-tz")]
550impl GetSize for chrono_tz::TzOffset {}
551
552#[cfg(feature = "url")]
553impl GetSize for url::Url {
554    fn get_heap_size(&self) -> usize {
555        self.as_str().len()
556    }
557}
558
559#[cfg(feature = "bytes")]
560impl GetSize for bytes::Bytes {
561    fn get_heap_size(&self) -> usize {
562        self.len()
563    }
564}
565
566#[cfg(feature = "bytes")]
567impl GetSize for bytes::BytesMut {
568    fn get_heap_size(&self) -> usize {
569        self.len()
570    }
571}