loupe/
lib.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
//! 🔎 `loupe` is a set of tools to analyse and to profile Rust
//! code. For the moment, it only provides tools about memory
//! usage. It's mostly driven by
//! [Wasmer](https://github.com/wasmerio/wasmer)'s needs, but feel
//! free to propose new features!
//!
//! `loupe` is a French word to express _magnifying glass_, and can be
//! pronounced exactly like _loop_.
//!
//! ## Memory Usage
//!
//! `loupe` provides the [`MemoryUsage`] trait. It allows to know the
//! size of a value in bytes, _recursively_. So it traverses most of
//! the types (some are missing, feel free to contribute!), and its
//! fields or variants as deep as possible. Hopefully, it tracks
//! already visited values so that it doesn't enter an infinite
//! ~~_loupe_~~ loop. The trait looks like this:
//!
//! ```rust,ignore
//! pub trait MemoryUsage {
//!     fn size_of_val(&self, tracker: &mut dyn MemoryUsageTracker) -> usize;
//! }
//! ```
//!
//! `loupe` provides a [`size_of_val`] function that is a close sibling
//! of
//! [`std::mem::size_of_val`](https://doc.rust-lang.org/std/mem/fn.size_of_val.html). It
//! can be used the same way.
//!
//! `loupe` exports its best companion: handful procedural macros from
//! the [`loupe-derive`](../loupe_derive/index.html) crate, to
//! automatically implement the [`MemoryUsage`] trait if possible, on
//! `struct`s and `enum`s.
//!
//! Thus, one only needs to write:
//!
//! ```rust
//! use loupe::MemoryUsage;
//! use std::mem;
//!
//! #[derive(MemoryUsage)]
//! struct S {
//!     x: Vec<i32>,
//!     y: Vec<i32>,
//! }
//!
//! fn main() {
//!     let s = S {
//!         x: vec![1, 2, 3],
//!         y: vec![1, 2, 3],
//!     };
//!     
//!     assert_eq!(48, mem::size_of_val(&s));
//!     assert_eq!(72, loupe::size_of_val(&s));
//! }
//! ```
//!
//! In the example above, we see that each elements of `Vec<i32>` has
//! been counted in the size of the value `s`. In
//! [Wasmer](https://github.com/wasmerio/wasmer), it is possible to
//! get the size of an `Instance`, which traverses `Module`, `Store`,
//! `Engine`, `Compiler` etc. It's an entire tree of values that is
//! traversed and the size of each value is summed.
//!
//! ### Opinionated implementations
//!
//! Even if [`MemoryUsage`] is already implemented for common types,
//! some types are missing. We happily welcome more implementations!
//! However, implementations of `MemoryUsage`:
//!
//! * must never alter the values,
//! * must never panic of fail,
//! * must be deterministic as much as possible (ideally, everytime).
//!
//! In the same spirit, our implementation of [`MemoryUsage`] for
//! `*const T` or `*mut T` (and other pointer types, like `NonNull`,
//! `UnsafeCell` etc.) just returns the size of the pointer, but it
//! doesn't dereference the pointer as it's unsafe. It doesn't mean
//! one must not do that: It's totally possible if it's sure that the
//! pointer can be safely dereferenced.
//!
//! Remember that a user can implement [`MemoryUsage`] by hand; no
//! need to try to have a default implementation for all the standard
//! types.
//!
//! Finally, our implementations are certainly not perfect! Feel free to
//! challenge it and come to discuss!

mod memory_usage;

#[cfg(feature = "derive")]
pub use loupe_derive::*;
pub use memory_usage::*;

use std::collections::BTreeSet;

/// Returns the size of the pointer-to value in bytes. The size is
/// calculated with `MemoryUsage::size_of_val`.
///
/// # Example
///
/// ```rust
/// use loupe::MemoryUsage;
/// use std::mem;
///
/// #[derive(MemoryUsage)]
/// struct S {
///     x: Vec<i32>,
///     y: Vec<i32>,
/// }
///
/// fn main() {
///     let s = S {
///         x: vec![1, 2, 3],
///         y: vec![1, 2, 3],
///     };
///     
///     assert_eq!(48, mem::size_of_val(&s));
///     assert_eq!(72, loupe::size_of_val(&s));
/// }
/// ```
pub fn size_of_val<T: MemoryUsage>(value: &T) -> usize {
    <T as MemoryUsage>::size_of_val(value, &mut BTreeSet::new())
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_size_of_val_helper() {
        assert_eq!(size_of_val(&"abc"), 2 * POINTER_BYTE_SIZE + 1 * 3);
    }
}