memuse/
lib.rs

1//! Measure dynamic memory usage of your types!
2//!
3//! ## About
4//!
5//! Memory-tracking is a common activity in large applications, particularly ones
6//! that receive data from a network and store it in memory. By monitoring how much
7//! memory is used by different areas of the application, memory pressure can be
8//! alleviated by ignoring new packets, or implementing random drop logic for DoS
9//! mitigation.
10//!
11//! Measuring memory use on the stack is easy, with [`std::mem::size_of`] and
12//! friends. Measuring memory allocated on the heap is more tricky. Applications can
13//! use a custom global allocator to track the memory usage of different areas. This
14//! isn't an option for reusable library code however, and the nearest alternative
15//! (using custom allocators for individual types) is currently only an experimental
16//! feature in nightly Rust ([`allocator_api`]).
17//!
18//! [`allocator_api`]: https://github.com/rust-lang/rust/issues/32838
19//!
20//! This crate takes a different approach: it provides traits that library authors
21//! can use to expose dynamic memory usage information on their types. By composing
22//! these implementations, we gain the ability to query the amount of heap-allocated
23//! memory in use by specific instances of types at any point in time, without any
24//! changes to the way in which these types are constructed.
25//!
26//! ## Minimum Supported Rust Version
27//!
28//! Requires Rust **1.51** or newer.
29//!
30//! In the future, we reserve the right to change MSRV (i.e. MSRV is out-of-scope for this
31//! crate's SemVer guarantees), however when we do it will be accompanied by a minor
32//! version bump.
33//!
34//! ## Usage
35//!
36//! ```
37//! # #[cfg(feature = "std")]
38//! # {
39//! # use std::collections::HashMap;
40//! use memuse::DynamicUsage;
41//!
42//! // Simple types don't allocate memory on the heap.
43//! assert_eq!(7u64.dynamic_usage(), 0);
44//! assert_eq!("I'm simple!".dynamic_usage(), 0);
45//!
46//! // When a type allocates memory, we can see it!
47//! assert_eq!(vec![7u64; 2].dynamic_usage(), 16);
48//!
49//! // We see the memory the type has allocated, even if it isn't being used.
50//! let empty: Vec<u32> = Vec::with_capacity(100);
51//! assert_eq!(empty.len(), 0);
52//! assert_eq!(empty.dynamic_usage(), 400);
53//!
54//! // For some types, we can't measure the exact memory usage, so we return a best
55//! // estimate. If you need precision, call `dynamic_usage_bounds` which returns a
56//! // lower bound, and (if known) an upper bound.
57//! let map: HashMap<u8, u64> = HashMap::with_capacity(27);
58//! let (lower, upper): (usize, Option<usize>) = map.dynamic_usage_bounds();
59//! assert!(upper.is_none());
60//! # }
61//! ```
62
63#![no_std]
64#![forbid(unsafe_code)]
65// Catch documentation errors caused by code changes.
66#![deny(broken_intra_doc_links)]
67
68#[cfg_attr(test, macro_use)]
69extern crate alloc;
70
71#[cfg(feature = "std")]
72extern crate std;
73
74use alloc::boxed::Box;
75use alloc::collections::{BinaryHeap, LinkedList, VecDeque};
76use alloc::string::String;
77use alloc::vec::Vec;
78use core::mem;
79
80/// Trait for measuring the dynamic memory usage of types.
81pub trait DynamicUsage {
82    /// Returns a best estimate of the amount of heap-allocated memory used by this type.
83    ///
84    /// For most types, this will return an exact value. However, for types that use a
85    /// complex allocation strategy (such as a `HashMap`), `memuse` cannot provide an
86    /// exact heap allocation value, as it does not have access to the internal details
87    /// and can only infer allocations from observable properties (such as the number of
88    /// elements in a collection, or constants extracted from the implementation of the
89    /// type). In those cases, this method returns a "best estimate" inferred from the
90    /// implemented behaviour of the type. As more crates implement this trait themselves,
91    /// the estimates will become more precise.
92    ///
93    /// The value returned by this method will always fall between the bounds returned by
94    /// [`DynamicUsage::dynamic_usage_bounds`]:
95    ///
96    /// ```
97    /// # #[cfg(feature = "std")]
98    /// # {
99    /// use std::collections::HashMap;
100    /// use memuse::DynamicUsage;
101    ///
102    /// let a: HashMap<u8, u64> = HashMap::with_capacity(27);
103    /// let usage = a.dynamic_usage();
104    /// let (lower, upper) = a.dynamic_usage_bounds();
105    ///
106    /// assert!(lower <= usage);
107    /// if let Some(upper) = upper {
108    ///     assert!(usage <= upper);
109    /// }
110    /// # }
111    /// ```
112    fn dynamic_usage(&self) -> usize;
113
114    /// Returns the lower and upper bounds on the amount of heap-allocated memory used by
115    /// this type.
116    ///
117    /// The lower bound is always precise; a type cannot allocate fewer than zero bytes,
118    /// and a collection cannot allocate fewer than the number of bytes required to store
119    /// the entries it holds.
120    ///
121    /// The upper bound is only present if some property of the type ensures that its
122    /// allocations do not exceed the bound, and is `None` otherwise (to indicate an
123    /// unlimited upper bound).
124    ///
125    /// If the type's allocated memory is precisely known, then the lower and upper bounds
126    /// will be equal.
127    fn dynamic_usage_bounds(&self) -> (usize, Option<usize>);
128}
129
130//
131// Helper macros
132//
133
134/// Helper to implement [`DynamicUsage`] for simple types that don't allocate.
135///
136/// # Examples
137///
138/// ```
139/// // Must be imported so it is accessible to the macro.
140/// use memuse::DynamicUsage;
141///
142/// struct RegisterByte(u8);
143/// struct RegisterWord(u16);
144///
145/// memuse::impl_no_dynamic_usage!(RegisterByte, RegisterWord);
146/// ```
147///
148/// The above is equivalent to:
149/// ```
150/// use memuse::DynamicUsage;
151///
152/// struct RegisterByte(u8);
153/// struct RegisterWord(u16);
154///
155/// impl DynamicUsage for RegisterByte {
156///     #[inline(always)]
157///     fn dynamic_usage(&self) -> usize {
158///         0
159///     }
160///
161///     #[inline(always)]
162///     fn dynamic_usage_bounds(&self) -> (usize, Option<usize>) {
163///         (0, Some(0))
164///     }
165/// }
166///
167/// impl DynamicUsage for RegisterWord {
168///     #[inline(always)]
169///     fn dynamic_usage(&self) -> usize {
170///         0
171///     }
172///
173///     #[inline(always)]
174///     fn dynamic_usage_bounds(&self) -> (usize, Option<usize>) {
175///         (0, Some(0))
176///     }
177/// }
178/// ```
179#[macro_export]
180macro_rules! impl_no_dynamic_usage {
181    ($($type:ty),+) => {
182        $(
183            impl DynamicUsage for $type {
184                #[inline(always)]
185                fn dynamic_usage(&self) -> usize {
186                    0
187                }
188
189                #[inline(always)]
190                fn dynamic_usage_bounds(&self) -> (usize, Option<usize>) {
191                    (0, Some(0))
192                }
193            }
194        )+
195    };
196}
197
198macro_rules! impl_iterable_dynamic_usage {
199    ($type:ty, $base_usage:expr) => {
200        impl<T: DynamicUsage> DynamicUsage for $type {
201            fn dynamic_usage(&self) -> usize {
202                $base_usage(self) + self.iter().map(DynamicUsage::dynamic_usage).sum::<usize>()
203            }
204
205            fn dynamic_usage_bounds(&self) -> (usize, Option<usize>) {
206                let base = $base_usage(self);
207                let (lower, upper) = self.iter().map(DynamicUsage::dynamic_usage_bounds).fold(
208                    (0, Some(0)),
209                    |(acc_lower, acc_upper), (lower, upper)| {
210                        (acc_lower + lower, acc_upper.zip(upper).map(|(a, b)| a + b))
211                    },
212                );
213                (base + lower, upper.map(|u| base + u))
214            }
215        }
216    };
217}
218
219//
220// Primitives
221//
222
223impl_no_dynamic_usage!(());
224impl_no_dynamic_usage!(i8, i16, i32, i64, i128, isize);
225impl_no_dynamic_usage!(u8, u16, u32, u64, u128, usize);
226impl_no_dynamic_usage!(f32, f64, bool);
227impl_no_dynamic_usage!(char, str);
228
229// Tuples are handled below (so they render more nicely in docs)
230
231impl<T: DynamicUsage, const N: usize> DynamicUsage for [T; N] {
232    fn dynamic_usage(&self) -> usize {
233        self.iter().map(DynamicUsage::dynamic_usage).sum::<usize>()
234    }
235
236    fn dynamic_usage_bounds(&self) -> (usize, Option<usize>) {
237        self.iter().map(DynamicUsage::dynamic_usage_bounds).fold(
238            (0, Some(0)),
239            |(acc_lower, acc_upper), (lower, upper)| {
240                (acc_lower + lower, acc_upper.zip(upper).map(|(a, b)| a + b))
241            },
242        )
243    }
244}
245
246impl_iterable_dynamic_usage!([T], |_| 0);
247
248//
249// Structs
250//
251
252impl DynamicUsage for String {
253    fn dynamic_usage(&self) -> usize {
254        self.capacity()
255    }
256
257    fn dynamic_usage_bounds(&self) -> (usize, Option<usize>) {
258        let usage = self.capacity();
259        (usage, Some(usage))
260    }
261}
262
263//
264// Containers
265//
266
267impl<T: DynamicUsage> DynamicUsage for Box<T> {
268    fn dynamic_usage(&self) -> usize {
269        mem::size_of::<T>() + self.as_ref().dynamic_usage()
270    }
271
272    fn dynamic_usage_bounds(&self) -> (usize, Option<usize>) {
273        let box_size = mem::size_of::<T>();
274        let (inner_lower, inner_upper) = self.as_ref().dynamic_usage_bounds();
275        (box_size + inner_lower, inner_upper.map(|u| box_size + u))
276    }
277}
278
279impl<T: DynamicUsage> DynamicUsage for Option<T> {
280    fn dynamic_usage(&self) -> usize {
281        self.as_ref().map(DynamicUsage::dynamic_usage).unwrap_or(0)
282    }
283
284    fn dynamic_usage_bounds(&self) -> (usize, Option<usize>) {
285        self.as_ref()
286            .map(DynamicUsage::dynamic_usage_bounds)
287            .unwrap_or((0, Some(0)))
288    }
289}
290
291impl<T: DynamicUsage, E: DynamicUsage> DynamicUsage for Result<T, E> {
292    fn dynamic_usage(&self) -> usize {
293        match self {
294            Ok(t) => t.dynamic_usage(),
295            Err(e) => e.dynamic_usage(),
296        }
297    }
298
299    fn dynamic_usage_bounds(&self) -> (usize, Option<usize>) {
300        match self {
301            Ok(t) => t.dynamic_usage_bounds(),
302            Err(e) => e.dynamic_usage_bounds(),
303        }
304    }
305}
306
307//
308// Collections
309//
310
311impl_iterable_dynamic_usage!(Vec<T>, |c: &Vec<T>| c.capacity() * mem::size_of::<T>());
312
313impl_iterable_dynamic_usage!(BinaryHeap<T>, |c: &BinaryHeap<T>| {
314    // BinaryHeap<T> is a wrapper around Vec<T>
315    c.capacity() * mem::size_of::<T>()
316});
317
318impl_iterable_dynamic_usage!(LinkedList<T>, |c: &LinkedList<T>| {
319    c.len() * mem::size_of::<T>()
320});
321
322impl_iterable_dynamic_usage!(VecDeque<T>, |c: &VecDeque<T>| {
323    // +1 since the ringbuffer always leaves one space empty.
324    (c.capacity() + 1) * mem::size_of::<T>()
325});
326
327#[cfg(feature = "std")]
328mod hash;
329
330//
331// External crate types (provided for helpfulness, since `DynamicUsage` can only be
332// implemented either here or in the external crate).
333//
334
335#[cfg(feature = "nonempty")]
336impl_iterable_dynamic_usage!(nonempty::NonEmpty<T>, |c: &nonempty::NonEmpty<T>| {
337    // NonEmpty<T> stores its head element separately from its tail Vec<T>.
338    (c.capacity() - 1) * mem::size_of::<T>()
339});
340
341//
342// Larger definitions (placed at the end so they render more nicely in docs).
343//
344
345mod tuple;
346
347#[cfg(test)]
348mod tests {
349    use alloc::string::ToString;
350
351    use super::*;
352
353    #[test]
354    fn standard_types() {
355        assert_eq!(129u8.dynamic_usage(), 0);
356        assert_eq!(3i128.dynamic_usage(), 0);
357        assert_eq!(7.0f32.dynamic_usage(), 0);
358        assert_eq!("foobar".dynamic_usage(), 0);
359
360        assert_eq!(129u8.dynamic_usage_bounds(), (0, Some(0)));
361        assert_eq!(3i128.dynamic_usage_bounds(), (0, Some(0)));
362        assert_eq!(7.0f32.dynamic_usage_bounds(), (0, Some(0)));
363        assert_eq!("foobar".dynamic_usage_bounds(), (0, Some(0)));
364    }
365
366    #[test]
367    fn string() {
368        assert_eq!(String::new().dynamic_usage(), 0);
369        assert_eq!("foobar".to_string().dynamic_usage(), 6);
370
371        assert_eq!(String::new().dynamic_usage_bounds(), (0, Some(0)));
372        assert_eq!("foobar".to_string().dynamic_usage_bounds(), (6, Some(6)));
373    }
374
375    #[test]
376    fn boxed() {
377        let a: u64 = 7;
378        assert_eq!(a.dynamic_usage(), 0);
379        assert_eq!(a.dynamic_usage_bounds(), (0, Some(0)));
380
381        let b: Box<u64> = Box::new(42);
382        assert_eq!(b.dynamic_usage(), 8);
383        assert_eq!(b.dynamic_usage_bounds(), (8, Some(8)));
384
385        let capacity = 7;
386        let c: Box<Vec<u16>> = Box::new(Vec::with_capacity(capacity));
387        let expected = mem::size_of::<Vec<u16>>() + capacity * mem::size_of::<u16>();
388        assert_eq!(c.dynamic_usage(), expected);
389        assert_eq!(c.dynamic_usage_bounds(), (expected, Some(expected)));
390    }
391
392    #[test]
393    fn option() {
394        let a: Option<Vec<u8>> = None;
395        let b: Option<Vec<u8>> = Some(vec![7u8; 4]);
396        assert_eq!(a.dynamic_usage(), 0);
397        assert_eq!(a.dynamic_usage_bounds(), (0, Some(0)));
398        assert_eq!(b.dynamic_usage(), 4);
399        assert_eq!(b.dynamic_usage_bounds(), (4, Some(4)));
400    }
401
402    #[test]
403    fn array() {
404        let a = [7; 42];
405        assert_eq!(a.dynamic_usage(), 0);
406        assert_eq!(a.dynamic_usage_bounds(), (0, Some(0)));
407
408        let mut b = [None, None, None, None];
409        assert_eq!(b.dynamic_usage(), 0);
410        assert_eq!(b.dynamic_usage_bounds(), (0, Some(0)));
411
412        b[0] = Some(vec![4u8; 20]);
413        assert_eq!(b.dynamic_usage(), 20);
414        assert_eq!(b.dynamic_usage_bounds(), (20, Some(20)));
415    }
416
417    #[test]
418    fn vec() {
419        let capacity = 7;
420        let mut a = Vec::with_capacity(capacity);
421        a.push(42u64);
422
423        let expected = capacity * mem::size_of::<u64>();
424        assert_eq!(a.dynamic_usage(), expected);
425        assert_eq!(a.dynamic_usage_bounds(), (expected, Some(expected)));
426    }
427
428    #[cfg(feature = "nonempty")]
429    #[test]
430    fn nonempty() {
431        let a = nonempty::NonEmpty::new(42);
432        assert_eq!(a.dynamic_usage(), 0);
433        assert_eq!(a.dynamic_usage_bounds(), (0, Some(0)));
434
435        const CAPACITY: usize = 7;
436        let b = nonempty::NonEmpty::from_slice(&[27u128; CAPACITY]).unwrap();
437
438        let expected = (CAPACITY - 1) * mem::size_of::<u128>();
439        assert_eq!(b.dynamic_usage(), expected);
440        assert_eq!(b.dynamic_usage_bounds(), (expected, Some(expected)));
441    }
442}