1#![doc = include_str!("../README.md")]
3#![cfg_attr(docsrs, feature(doc_cfg))]
4#![cfg_attr(not(feature = "std"), no_std)]
5
6extern crate alloc;
7
8#[macro_use]
9mod macros;
10mod collections;
11mod core_impls;
12mod human_bytes;
13mod pointers;
14mod std_impls;
15mod support;
16mod tests;
17
18pub use human_bytes::HumanBytes;
19#[cfg(feature = "derive")]
20pub use size_of_derive::SizeOf;
21
22use alloc::{collections::BTreeSet, rc::Rc, sync::Arc};
23use core::{
24 iter::Sum,
25 mem::{replace, size_of_val},
26 ops::{Add, AddAssign, Sub, SubAssign},
27};
28
29#[inline]
55pub fn size_of_values<'a, I>(values: I) -> TotalSize
56where
57 I: IntoIterator<Item = &'a dyn SizeOf> + 'a,
58{
59 let mut context = Context::new();
60 values
61 .into_iter()
62 .for_each(|value| value.size_of_with_context(&mut context));
63 context.total_size()
64}
65
66pub trait SizeOf {
68 #[inline]
70 fn size_of(&self) -> TotalSize {
71 let mut context = Context::new();
72 self.size_of_with_context(&mut context);
73 context.total_size()
74 }
75
76 #[inline]
79 fn size_of_with_context(&self, context: &mut Context) {
80 context.add(size_of_val(self));
81 self.size_of_children(context);
82 }
83
84 fn size_of_children(&self, context: &mut Context);
90}
91
92#[derive(Debug, Clone, Default)]
95pub struct Context {
96 total_bytes: usize,
98 excess_bytes: usize,
100 shared_bytes: usize,
102 distinct_allocations: usize,
104 is_shared: bool,
106 pointers: BTreeSet<usize>,
108}
109
110impl Context {
111 #[inline]
113 pub fn new() -> Self {
114 Self::default()
115 }
116
117 #[inline]
119 pub const fn is_shared(&self) -> bool {
120 self.is_shared
121 }
122
123 #[inline]
125 pub fn shared<F>(&mut self, with_shared: F) -> &mut Self
126 where
127 F: FnOnce(&mut Self),
128 {
129 let prev = replace(&mut self.is_shared, true);
130 with_shared(self);
131 self.is_shared = prev;
132 self
133 }
134
135 #[inline]
137 pub fn add_distinct_allocation(&mut self) -> &mut Self {
138 self.add_distinct_allocations(1)
139 }
140
141 #[inline]
143 pub fn add_distinct_allocations(&mut self, allocations: usize) -> &mut Self {
144 self.distinct_allocations += allocations;
145 self
146 }
147
148 #[inline]
152 pub fn add(&mut self, size: usize) -> &mut Self {
153 self.total_bytes += size;
154 if self.is_shared {
155 self.shared_bytes += size;
156 }
157
158 self
159 }
160
161 #[inline]
163 pub fn add_shared(&mut self, size: usize) -> &mut Self {
164 self.shared_bytes += size;
165 self
166 }
167
168 #[inline]
172 pub fn add_excess(&mut self, size: usize) -> &mut Self {
173 self.total_bytes += size;
174 self.excess_bytes += size;
175 if self.is_shared {
176 self.shared_bytes += size;
177 }
178
179 self
180 }
181
182 #[inline]
188 pub fn add_arraylike(&mut self, len: usize, element_size: usize) -> &mut Self {
189 let bytes = len * element_size;
190 self.total_bytes += bytes;
191 if self.is_shared {
192 self.shared_bytes += bytes;
193 }
194
195 self
196 }
197
198 #[inline]
205 pub fn add_vectorlike(
206 &mut self,
207 len: usize,
208 capacity: usize,
209 element_size: usize,
210 ) -> &mut Self {
211 let used = len * element_size;
212 let allocated = capacity * element_size;
213 self.total_bytes += allocated;
214 self.excess_bytes += allocated - used;
215
216 if self.is_shared {
217 self.shared_bytes += allocated;
218 }
219
220 self
221 }
222
223 #[inline]
227 pub fn insert_ptr<T: ?Sized>(&mut self, ptr: *const T) -> bool {
228 self.pointers.insert(ptr as *const T as *const u8 as usize)
230 }
231
232 #[inline]
235 pub fn add_ptr<T: ?Sized>(&mut self, ptr: *const T) -> &mut Self {
236 self.insert_ptr(ptr);
237 self
238 }
239
240 #[inline]
242 pub fn contains_ptr<T: ?Sized>(&self, ptr: *const T) -> bool {
243 self.pointers
245 .contains(&(ptr as *const T as *const u8 as usize))
246 }
247
248 #[inline]
253 fn insert_rc<T: ?Sized>(&mut self, rc: &Rc<T>) -> bool {
254 self.insert_ptr(Rc::as_ptr(rc))
255 }
256
257 #[inline]
258 fn insert_arc<T: ?Sized>(&mut self, arc: &Arc<T>) -> bool {
259 self.insert_ptr(Arc::as_ptr(arc))
260 }
261
262 #[inline]
264 pub const fn total_size(&self) -> TotalSize {
265 TotalSize::new(
266 self.total_bytes,
267 self.excess_bytes,
268 self.shared_bytes,
269 self.distinct_allocations,
270 )
271 }
272}
273
274impl SizeOf for Context {
275 fn size_of_children(&self, context: &mut Context) {
276 self.pointers.size_of_children(context);
277 }
278}
279
280#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
283pub struct TotalSize {
284 total_bytes: usize,
286 excess_bytes: usize,
288 shared_bytes: usize,
290 distinct_allocations: usize,
292}
293
294impl TotalSize {
295 #[inline]
297 pub const fn new(
298 total_bytes: usize,
299 excess_bytes: usize,
300 shared_bytes: usize,
301 distinct_allocations: usize,
302 ) -> Self {
303 Self {
304 total_bytes,
305 excess_bytes,
306 shared_bytes,
307 distinct_allocations,
308 }
309 }
310
311 #[inline]
313 pub const fn zero() -> Self {
314 Self::new(0, 0, 0, 0)
315 }
316
317 #[inline]
319 pub const fn total(total: usize) -> Self {
320 Self::new(total, 0, 0, 0)
321 }
322
323 #[inline]
325 pub const fn total_bytes(&self) -> usize {
326 self.total_bytes
327 }
328
329 #[inline]
332 pub const fn excess_bytes(&self) -> usize {
333 self.excess_bytes
334 }
335
336 #[inline]
339 pub const fn shared_bytes(&self) -> usize {
340 self.shared_bytes
341 }
342
343 #[inline]
348 pub const fn distinct_allocations(&self) -> usize {
349 self.distinct_allocations
350 }
351
352 #[inline]
354 pub const fn used_bytes(&self) -> usize {
355 self.total_bytes - self.excess_bytes
356 }
357}
358
359impl Add for TotalSize {
360 type Output = Self;
361
362 #[inline]
363 fn add(self, rhs: Self) -> Self::Output {
364 Self {
365 total_bytes: self.total_bytes + rhs.total_bytes,
366 excess_bytes: self.excess_bytes + rhs.excess_bytes,
367 shared_bytes: self.shared_bytes + rhs.shared_bytes,
368 distinct_allocations: self.distinct_allocations + rhs.distinct_allocations,
369 }
370 }
371}
372
373impl AddAssign for TotalSize {
374 #[inline]
375 fn add_assign(&mut self, rhs: Self) {
376 *self = *self + rhs;
377 }
378}
379
380impl Sub for TotalSize {
381 type Output = Self;
382
383 #[inline]
384 fn sub(self, rhs: Self) -> Self::Output {
385 Self {
386 total_bytes: self.total_bytes - rhs.total_bytes,
387 excess_bytes: self.excess_bytes - rhs.excess_bytes,
388 shared_bytes: self.shared_bytes - rhs.shared_bytes,
389 distinct_allocations: self.distinct_allocations - rhs.distinct_allocations,
390 }
391 }
392}
393
394impl SubAssign for TotalSize {
395 #[inline]
396 fn sub_assign(&mut self, rhs: Self) {
397 *self = *self - rhs;
398 }
399}
400
401impl Sum for TotalSize {
402 #[inline]
403 fn sum<I>(iter: I) -> Self
404 where
405 I: Iterator<Item = Self>,
406 {
407 iter.fold(Self::zero(), |acc, size| acc + size)
408 }
409}
410
411impl SizeOf for TotalSize {
412 fn size_of_children(&self, _context: &mut Context) {}
413}