datasize/lib.rs
1//! Heap data estimator.
2//!
3//! The `datasize` crate allows estimating the amount of heap memory used by a value. It does so by
4//! providing or deriving an implementation of the `DataSize` trait, which knows how to calculate
5//! the size for many `std` types and primitives.
6//!
7//! The aim is to get a reasonable approximation of memory usage, especially with variably sized
8//! types like `Vec`s. While it is acceptable to be a few bytes off in some cases, any user should
9//! be able to easily tell whether their memory is growing linearly or logarithmically by glancing
10//! at the reported numbers.
11//!
12//! The crate does not take alignment or memory layouts into account, or unusual behavior or
13//! optimizations of allocators. It is depending entirely on the data inside the type, thus the name
14//! of the crate.
15//!
16//! # General usage
17//!
18//! For any type that implements `DataSize`, the `data_size` convenience function can be used to
19//! guess the size of its heap allocation:
20//!
21//! ```rust
22//! use datasize::data_size;
23//!
24//! let data: Vec<u64> = vec![1, 2, 3];
25//! #[cfg(feature = "std")]
26//! assert_eq!(data_size(&data), 24);
27//! ```
28//!
29//! Types implementing the trait also provide two additional constants, `IS_DYNAMIC` and
30//! `STATIC_HEAP_SIZE`.
31//!
32//! `IS_DYNAMIC` indicates whether a value's size can change over time:
33//!
34//! ```rust
35//! use datasize::DataSize;
36//!
37//! #[cfg(feature = "std")]
38//! // A `Vec` of any kind may have elements added or removed, so it changes size.
39//! assert!(Vec::<u64>::IS_DYNAMIC);
40//!
41//! // The elements of type `u64` in it are not dynamic. This allows the implementation to
42//! // simply estimate the size as number_of_elements * size_of::<u64>.
43//! assert!(!u64::IS_DYNAMIC);
44//! ```
45//!
46//! Additionally, `STATIC_HEAP_SIZE` indicates the amount of heap memory a type will always use. A
47//! good example is a `Box<u64>` -- it will always use 8 bytes of heap memory, but not change in
48//! size:
49//!
50//!
51//! ```rust
52//! use datasize::DataSize;
53//!
54//! #[cfg(feature = "std")]
55//! assert_eq!(Box::<u64>::STATIC_HEAP_SIZE, 8);
56//! #[cfg(feature = "std")]
57//! assert!(!Box::<u64>::IS_DYNAMIC);
58//! ```
59//!
60//! # Overriding derived data size calculation for single fields.
61//!
62//! On structs (but not enums!) the calculation for heap size can be overriden for single fields,
63//! which is useful when dealing with third-party crates whose fields do not implement `DataSize` by
64//! simply annotating it with `#[data_size(with = ...)]` and pointing to a `Fn(T) -> usize`
65//! function:
66//!
67//! ```rust
68//! use datasize::DataSize;
69//!
70//! // Let's pretend this type is from a foreign crate.
71//! struct ThirdPartyType;
72//!
73//! fn estimate_third_party_type(value: &Vec<ThirdPartyType>) -> usize {
74//! // We assume every item is 512 bytes in heap size.
75//! value.len() * 512
76//! }
77//!
78//! #[cfg(feature = "std")]
79//! #[derive(DataSize)]
80//! struct MyStruct {
81//! items: Vec<u32>,
82//! #[data_size(with = estimate_third_party_type)]
83//! other_stuff: Vec<ThirdPartyType>,
84//! }
85//! ```
86//!
87//! This automatically marks the whole struct as always dynamic, so the custom estimation function
88//! is called every time `MyStruct` is sized.
89//!
90//! # Implementing `DataSize` for custom types
91//!
92//! The `DataSize` trait can be implemented for custom types manually:
93//!
94//! ```rust
95//! # use datasize::{DataSize, data_size};
96//! struct MyType {
97//! items: Vec<i64>,
98//! flag: bool,
99//! counter: Box<u64>,
100//! }
101//!
102//! #[cfg(feature = "std")]
103//! impl DataSize for MyType {
104//! // `MyType` contains a `Vec`, so `IS_DYNAMIC` is set to true.
105//! const IS_DYNAMIC: bool = true;
106//!
107//! // The only always present heap item is the `counter` value, which is 8 bytes.
108//! const STATIC_HEAP_SIZE: usize = 8;
109//!
110//! #[inline]
111//! fn estimate_heap_size(&self) -> usize {
112//! // We can be lazy here and delegate to all the existing implementations:
113//! data_size(&self.items) + data_size(&self.flag) + data_size(&self.counter)
114//! }
115//! }
116//!
117//! let my_data = MyType {
118//! items: vec![1, 2, 3],
119//! flag: true,
120//! counter: Box::new(42),
121//! };
122//!
123//! #[cfg(feature = "std")]
124//! // Three i64 and one u64 on the heap sum up to 32 bytes:
125//! assert_eq!(data_size(&my_data), 32);
126//! ```
127//!
128//! Since implementing this for `struct` types is cumbersome and repetitive, the crate provides a
129//! `DataSize` macro for convenience:
130//!
131//! ```
132//! # use datasize::{DataSize, data_size};
133//! // Equivalent to the manual implementation above:
134//! #[cfg(feature = "std")]
135//! #[derive(DataSize)]
136//! struct MyType {
137//! items: Vec<i64>,
138//! flag: bool,
139//! counter: Box<u64>,
140//! }
141//! # #[cfg(feature = "std")]
142//! # let my_data = MyType {
143//! # items: vec![1, 2, 3],
144//! # flag: true,
145//! # counter: Box::new(42),
146//! # };
147//! # #[cfg(feature = "std")]
148//! # assert_eq!(data_size(&my_data), 32);
149//! ```
150//!
151//! See the `DataSize` macro documentation in the `datasize_derive` crate for details.
152//!
153//! ## Performance considerations
154//!
155//! Determining the full size of data can be quite expensive, especially if multiple nested levels
156//! of dynamic types are used. The crate uses `IS_DYNAMIC` and `STATIC_HEAP_SIZE` to optimize when
157//! it can, so in many cases not every element of a vector needs to be checked individually.
158//!
159//! However, if the contained types are dynamic, every element must (and will) be checked, so keep
160//! this in mind when performance is an issue.
161//!
162//! ## Handlings references, `Arc`s and similar types
163//!
164//! Any reference will be counted as having a data size of 0, as it does not own the value. There
165//! are some special reference-like types like `Arc`, which are discussed below.
166//!
167//! ### `Arc` and `Rc`
168//!
169//! Currently `Arc`s are not supported. A planned development is to allow users to mark an instance
170//! of an `Arc` as "primary" and have its heap memory usage counted, but currently this is not
171//! implemented.
172//!
173//! Any `Arc` will be estimated to have a heap size of `0`, to avoid cycles resulting in infinite
174//! loops.
175//!
176//! The `Rc` type is handled in the same manner.
177//!
178//! ## Additional types
179//!
180//! Some additional types from external crates are available behind feature flags.
181//!
182//! * `fake_clock-types`: Support for the `fake_instant::FakeClock` type.
183//! * `futures-types`: Some types from the `futures` crate.
184//! * `smallvec-types`: Support for the `smallvec::SmallVec` type.
185//! * `tokio-types`: Some types from the `tokio` crate.
186//!
187//! ## `no_std` support
188//!
189//! Although slightly paradoxical due to the fact that without `std` or at least `alloc` there won't
190//! be any heap in most cases, the crate supports a `no_std` environment. Disabling the "std"
191//! feature (by disabling default features) will produce a version of the crate that does not rely
192//! on the standard library. This can be used to derive the `DataSize` trait for types without
193//! boilerplate, even though their heap size will usually be 0.
194//!
195//! ## Arrays and const generics
196//!
197//! By default, this crate requires at least Rust version 1.51.0, in order to implement DataSize
198//! for [T; N] arrays generically. This implementation is provided by the "const-generics"
199//! feature flag, which is enabled by default. In order to use an older Rust version,
200//! you can specify [`default-features = false`](https://doc.rust-lang.org/cargo/reference/features.html#dependency-features) and `features = ["std"]` for `datasize` in your Cargo.toml.
201//!
202//! When the `const-generics` feature flag is disabled, a DataSize implementation will be provided
203//! for arrays of small sizes, and for some larger sizes related to powers of 2.
204//!
205//! ## Known issues
206//!
207//! The derive macro currently does not support generic structs with inline type bounds, e.g.
208//!
209//! ```ignore
210//! struct Foo<T: Copy> { ... }
211//! ```
212//!
213//! This can be worked around by using an equivalent `where` clause:
214//!
215//! ```ignore
216//! struct Foo<T>
217//! where T: Copy
218//! { ... }
219//! ```
220
221#![cfg_attr(not(feature = "std"), no_std)]
222#![allow(clippy::assertions_on_constants)]
223
224#[cfg(feature = "fake_clock-types")]
225mod fake_clock;
226#[cfg(feature = "futures-types")]
227mod futures;
228#[cfg(feature = "smallvec-types")]
229mod smallvec;
230#[cfg(feature = "std")]
231mod std;
232#[cfg(feature = "tokio-types")]
233mod tokio;
234
235pub use datasize_derive::DataSize;
236
237/// A `const fn` variant of the `min` function.
238pub const fn min(a: usize, b: usize) -> usize {
239 [a, b][(a > b) as usize]
240}
241
242/// Indicates that a type knows how to approximate its memory usage.
243pub trait DataSize {
244 /// If `true`, the type has a heap size that can vary at runtime, depending on the actual value.
245 const IS_DYNAMIC: bool;
246
247 /// The amount of space a value of the type _always_ occupies. If `IS_DYNAMIC` is false, this is
248 /// the total amount of heap memory occupied by the value. Otherwise this is a lower bound.
249 const STATIC_HEAP_SIZE: usize;
250
251 /// Estimates the size of heap memory taken up by this value.
252 ///
253 /// Does not include data on the stack, which is usually determined using `mem::size_of`.
254 fn estimate_heap_size(&self) -> usize;
255
256 #[cfg(feature = "detailed")]
257 /// Create a tree of memory estimations.
258 ///
259 /// Similar to `estimate_heap_size`, but the returned value is a tree that typically reports
260 /// memory used by structs individually.
261 ///
262 /// Requires the `detailed` feature to be enabled.
263 #[inline]
264 fn estimate_detailed_heap_size(&self) -> MemUsageNode {
265 MemUsageNode::Size(self.estimate_heap_size())
266 }
267}
268
269#[cfg(feature = "detailed")]
270/// A node in a memory reporting tree.
271#[derive(Debug, serde::Serialize, PartialEq)]
272pub enum MemUsageNode {
273 Size(usize),
274 Detailed(::std::collections::HashMap<&'static str, MemUsageNode>),
275}
276
277#[cfg(feature = "detailed")]
278impl MemUsageNode {
279 /// Calculate the total memory usage given by detailed estimate
280 #[inline]
281 pub fn total(&self) -> usize {
282 match self {
283 MemUsageNode::Size(sz) => *sz,
284 MemUsageNode::Detailed(members) => members.values().map(MemUsageNode::total).sum(),
285 }
286 }
287}
288
289/// Estimates allocated heap data from data of value.
290///
291/// Checks if `T` is dynamic; if it is not, returns `T::STATIC_HEAP_SIZE`. Otherwise delegates to
292/// `T::estimate_heap_size`.
293#[inline]
294pub fn data_size<T: ?Sized>(value: &T) -> usize
295where
296 T: DataSize,
297{
298 value.estimate_heap_size()
299}
300
301#[cfg(feature = "detailed")]
302/// Estimates allocated heap data from data of value.
303#[inline]
304pub fn data_size_detailed<T: ?Sized>(value: &T) -> MemUsageNode
305where
306 T: DataSize,
307{
308 value.estimate_detailed_heap_size()
309}
310
311/// Helper macro to define a heap size for one or more non-dynamic types.
312#[macro_export]
313macro_rules! non_dynamic_const_heap_size {
314 ($($ty:ty)*, $sz:expr) => {
315 $(impl DataSize for $ty {
316 const IS_DYNAMIC: bool = false;
317 const STATIC_HEAP_SIZE: usize = $sz;
318
319 #[inline]
320 fn estimate_heap_size(&self) -> usize {
321 $sz
322 }
323 })*
324 };
325}
326
327// Hack to allow `+` to be used to join macro arguments.
328macro_rules! strip_plus {
329 (+ $($rest: tt)*) => {
330 $($rest)*
331 }
332}
333
334macro_rules! tuple_heap_size {
335 ($($n:tt $name:ident);+) => {
336 impl<$($name),*> DataSize for ($($name),*)
337 where $($name: DataSize),*
338 {
339 const IS_DYNAMIC: bool = $($name::IS_DYNAMIC)|*;
340
341 const STATIC_HEAP_SIZE: usize =
342 strip_plus!($(+ $name::STATIC_HEAP_SIZE)+);
343
344 #[inline]
345 fn estimate_heap_size(&self) -> usize {
346 strip_plus!($(+ self.$n.estimate_heap_size())+)
347 }
348 }
349 };
350}
351
352#[cfg(not(feature = "const-generics"))]
353macro_rules! array_heap_size {
354 ($($n:tt)+) => {
355 $(
356 impl<T> DataSize for [T; $n]
357 where
358 T: DataSize,
359 {
360 const IS_DYNAMIC: bool = T::IS_DYNAMIC;
361
362 const STATIC_HEAP_SIZE: usize = T::STATIC_HEAP_SIZE * $n;
363
364 #[inline]
365 fn estimate_heap_size(&self) -> usize {
366 if T::IS_DYNAMIC {
367 (&self[..]).iter().map(DataSize::estimate_heap_size).sum()
368 } else {
369 T::STATIC_HEAP_SIZE * $n
370 }
371 }
372 }
373 )*
374 };
375}
376
377// Primitives
378non_dynamic_const_heap_size!(() u8 u16 u32 u64 u128 usize i8 i16 i32 i64 i128 isize bool char f32 f64, 0);
379
380// Assorted heapless `core` types
381non_dynamic_const_heap_size!(core::time::Duration, 0);
382
383tuple_heap_size!(0 T0; 1 T1);
384tuple_heap_size!(0 T0; 1 T1; 2 T2);
385tuple_heap_size!(0 T0; 1 T1; 2 T2; 3 T3);
386tuple_heap_size!(0 T0; 1 T1; 2 T2; 3 T3; 4 T4);
387tuple_heap_size!(0 T0; 1 T1; 2 T2; 3 T3; 4 T4; 5 T5);
388tuple_heap_size!(0 T0; 1 T1; 2 T2; 3 T3; 4 T4; 5 T5; 6 T6);
389tuple_heap_size!(0 T0; 1 T1; 2 T2; 3 T3; 4 T4; 5 T5; 6 T6; 7 T7);
390tuple_heap_size!(0 T0; 1 T1; 2 T2; 3 T3; 4 T4; 5 T5; 6 T6; 7 T7; 8 T8);
391tuple_heap_size!(0 T0; 1 T1; 2 T2; 3 T3; 4 T4; 5 T5; 6 T6; 7 T7; 8 T8; 9 T9);
392tuple_heap_size!(0 T0; 1 T1; 2 T2; 3 T3; 4 T4; 5 T5; 6 T6; 7 T7; 8 T8; 9 T9; 10 T10);
393tuple_heap_size!(0 T0; 1 T1; 2 T2; 3 T3; 4 T4; 5 T5; 6 T6; 7 T7; 8 T8; 9 T9; 10 T10; 11 T11);
394tuple_heap_size!(0 T0; 1 T1; 2 T2; 3 T3; 4 T4; 5 T5; 6 T6; 7 T7; 8 T8; 9 T9; 10 T10; 11 T11; 12 T12);
395tuple_heap_size!(0 T0; 1 T1; 2 T2; 3 T3; 4 T4; 5 T5; 6 T6; 7 T7; 8 T8; 9 T9; 10 T10; 11 T11; 12 T12; 13 T13);
396tuple_heap_size!(0 T0; 1 T1; 2 T2; 3 T3; 4 T4; 5 T5; 6 T6; 7 T7; 8 T8; 9 T9; 10 T10; 11 T11; 12 T12; 13 T13; 14 T14);
397tuple_heap_size!(0 T0; 1 T1; 2 T2; 3 T3; 4 T4; 5 T5; 6 T6; 7 T7; 8 T8; 9 T9; 10 T10; 11 T11; 12 T12; 13 T13; 14 T14; 15 T15);
398
399impl<T0> DataSize for (T0,)
400where
401 T0: DataSize,
402{
403 const IS_DYNAMIC: bool = T0::IS_DYNAMIC;
404 const STATIC_HEAP_SIZE: usize = T0::STATIC_HEAP_SIZE;
405
406 #[inline]
407 fn estimate_heap_size(&self) -> usize {
408 self.0.estimate_heap_size()
409 }
410}
411
412#[cfg(not(feature = "const-generics"))]
413array_heap_size!(0 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 128 192 256 384 512 1024 2048 4096 8192 16384 1048576 2097152 3145728 4194304);
414
415#[cfg(feature = "const-generics")]
416impl<T, const N: usize> DataSize for [T; N]
417where
418 T: DataSize,
419{
420 const IS_DYNAMIC: bool = T::IS_DYNAMIC;
421
422 const STATIC_HEAP_SIZE: usize = T::STATIC_HEAP_SIZE * N;
423
424 #[inline]
425 fn estimate_heap_size(&self) -> usize {
426 if T::IS_DYNAMIC {
427 self[..].iter().map(DataSize::estimate_heap_size).sum()
428 } else {
429 T::STATIC_HEAP_SIZE * N
430 }
431 }
432}
433
434// REFERENCES
435
436impl<T> DataSize for &T {
437 const IS_DYNAMIC: bool = false;
438
439 const STATIC_HEAP_SIZE: usize = 0;
440
441 #[inline]
442 fn estimate_heap_size(&self) -> usize {
443 0
444 }
445}
446
447impl<T> DataSize for &mut T {
448 const IS_DYNAMIC: bool = false;
449
450 const STATIC_HEAP_SIZE: usize = 0;
451
452 #[inline]
453 fn estimate_heap_size(&self) -> usize {
454 0
455 }
456}
457
458// COMMONLY USED NON-PRIMITIVE TYPES
459
460impl<T> DataSize for Option<T>
461where
462 T: DataSize,
463{
464 // Options are only not dynamic if their type has no heap data at all and is not dynamic.
465 const IS_DYNAMIC: bool = (T::IS_DYNAMIC || T::STATIC_HEAP_SIZE > 0);
466
467 const STATIC_HEAP_SIZE: usize = 0;
468
469 #[inline]
470 fn estimate_heap_size(&self) -> usize {
471 match self {
472 Some(val) => data_size(val),
473 None => 0,
474 }
475 }
476}
477
478impl<T, E> DataSize for Result<T, E>
479where
480 T: DataSize,
481 E: DataSize,
482{
483 // Results are only not dynamic if their types have no heap data at all and are not dynamic.
484 const IS_DYNAMIC: bool =
485 (T::IS_DYNAMIC || E::IS_DYNAMIC || (T::STATIC_HEAP_SIZE != E::STATIC_HEAP_SIZE));
486
487 const STATIC_HEAP_SIZE: usize = min(T::STATIC_HEAP_SIZE, E::STATIC_HEAP_SIZE);
488
489 #[inline]
490 fn estimate_heap_size(&self) -> usize {
491 match self {
492 Ok(val) => data_size(val),
493 Err(err) => data_size(err),
494 }
495 }
496}
497
498impl<T> DataSize for core::marker::PhantomData<T> {
499 const IS_DYNAMIC: bool = false;
500 const STATIC_HEAP_SIZE: usize = 0;
501
502 #[inline]
503 fn estimate_heap_size(&self) -> usize {
504 0
505 }
506}
507
508impl<T: DataSize> DataSize for core::ops::Range<T> {
509 const IS_DYNAMIC: bool = T::IS_DYNAMIC;
510 const STATIC_HEAP_SIZE: usize = 2 * T::STATIC_HEAP_SIZE;
511
512 #[inline]
513 fn estimate_heap_size(&self) -> usize {
514 self.start.estimate_heap_size() + self.end.estimate_heap_size()
515 }
516}
517
518#[cfg(test)]
519mod tests {
520 use crate as datasize; // Required for the derive macro.
521 use crate::{data_size, DataSize};
522
523 #[test]
524 fn test_for_simple_builtin_types() {
525 // We only sample some, as they are all macro generated.
526 assert_eq!(1u8.estimate_heap_size(), 0);
527 assert_eq!(1u16.estimate_heap_size(), 0);
528 }
529
530 #[test]
531 fn test_newtype_struct() {
532 #[derive(DataSize)]
533 struct Foo(u32);
534
535 assert!(!Foo::IS_DYNAMIC);
536 assert_eq!(Foo::STATIC_HEAP_SIZE, 0);
537 assert_eq!(data_size(&Foo(123)), 0);
538 }
539
540 #[test]
541 fn test_tuple_struct() {
542 #[derive(DataSize)]
543 struct Foo(u32, u8);
544
545 assert!(!Foo::IS_DYNAMIC);
546 assert_eq!(Foo::STATIC_HEAP_SIZE, 0);
547 assert_eq!(data_size(&Foo(123, 45)), 0);
548 }
549
550 #[test]
551 fn test_tuple_with_one_element() {
552 type Foo = (u32,);
553 assert!(!Foo::IS_DYNAMIC);
554 assert_eq!(Foo::STATIC_HEAP_SIZE, 0);
555
556 let foo: Foo = (456,);
557 assert_eq!(data_size(&foo), 0);
558 }
559
560 #[test]
561 fn test_result() {
562 assert_eq!(Result::<u8, u8>::STATIC_HEAP_SIZE, 0);
563 assert!(!Result::<u8, u8>::IS_DYNAMIC);
564
565 assert_eq!(Result::<u8, u16>::STATIC_HEAP_SIZE, 0);
566 assert!(!Result::<u8, u16>::IS_DYNAMIC);
567
568 #[cfg(feature = "std")]
569 assert_eq!(Result::<u8, Box<u16>>::STATIC_HEAP_SIZE, 0);
570 #[cfg(feature = "std")]
571 assert!(Result::<u8, Box<u16>>::IS_DYNAMIC);
572
573 #[cfg(feature = "std")]
574 assert_eq!(Result::<Box<u8>, u16>::STATIC_HEAP_SIZE, 0);
575 #[cfg(feature = "std")]
576 assert!(Result::<Box<u8>, u16>::IS_DYNAMIC);
577
578 #[cfg(feature = "std")]
579 assert_eq!(Result::<Box<u8>, Box<u16>>::STATIC_HEAP_SIZE, 1);
580 #[cfg(feature = "std")]
581 assert!(Result::<Box<u8>, Box<u16>>::IS_DYNAMIC);
582
583 #[cfg(feature = "std")]
584 assert_eq!(Result::<Box<u16>, Box<u16>>::STATIC_HEAP_SIZE, 2);
585 #[cfg(feature = "std")]
586 assert!(!Result::<Box<u16>, Box<u16>>::IS_DYNAMIC);
587
588 #[cfg(feature = "std")]
589 assert_eq!(Result::<u16, Vec<u16>>::STATIC_HEAP_SIZE, 0);
590 #[cfg(feature = "std")]
591 assert!(Result::<u16, Vec<u16>>::IS_DYNAMIC);
592
593 #[cfg(feature = "std")]
594 assert_eq!(Result::<Vec<u16>, u16>::STATIC_HEAP_SIZE, 0);
595 #[cfg(feature = "std")]
596 assert!(Result::<Vec<u16>, u16>::IS_DYNAMIC);
597
598 #[cfg(feature = "std")]
599 assert_eq!(Result::<Vec<u16>, Vec<u16>>::STATIC_HEAP_SIZE, 0);
600 #[cfg(feature = "std")]
601 assert!(Result::<Vec<u16>, Vec<u16>>::IS_DYNAMIC);
602 }
603
604 #[test]
605 fn test_empty_struct() {
606 #[derive(DataSize)]
607 struct Foo {}
608
609 #[derive(DataSize)]
610 struct Bar;
611
612 assert!(!Foo::IS_DYNAMIC);
613 assert!(!Bar::IS_DYNAMIC);
614
615 assert_eq!(Foo::STATIC_HEAP_SIZE, 0);
616 assert_eq!(Bar::STATIC_HEAP_SIZE, 0);
617
618 assert_eq!(data_size(&Foo {}), 0);
619 assert_eq!(data_size(&Bar), 0);
620 }
621
622 #[test]
623 fn test_empty_enum() {
624 #[derive(DataSize)]
625 enum Foo {}
626
627 assert!(!Foo::IS_DYNAMIC);
628 assert_eq!(Foo::STATIC_HEAP_SIZE, 0);
629
630 // We cannot instantiate empty enums.
631 }
632
633 #[test]
634 fn macro_does_not_panic_on_foreign_attributes() {
635 #[derive(DataSize)]
636 /// This docstring shows up as `#[doc = ""]`...
637 struct Foo {
638 /// This docstring shows up as `#[doc = ""]`...
639 dummy: u8,
640 }
641 }
642
643 // TODO: This does not work, the equivalent should be constructed using `trybuild`.
644 // #[test]
645 // #[should_panic = "unexpected datasize attribute"]
646 // fn macro_panics_on_invalid_data_size_attribute() {
647 // #[derive(DataSize)]
648 // /// This docstring shows up as `#[doc = ""]`...
649 // struct Foo {
650 // #[data_size(invalid)]
651 // /// This docstring shows up as `#[doc = ""]`...
652 // dummy: u8,
653 // }
654 // }
655
656 #[test]
657 fn keeps_where_clauses_on_structs() {
658 #[allow(dead_code)]
659 #[derive(DataSize)]
660 struct Foo<T>
661 where
662 T: Copy,
663 {
664 field: T,
665 }
666 }
667
668 #[test]
669 fn keeps_where_clauses_on_enums() {
670 #[allow(dead_code)]
671 #[derive(DataSize)]
672 enum Foo<T>
673 where
674 T: Copy,
675 {
676 Value(T),
677 }
678 }
679
680 #[test]
681 fn use_with_annotation() {
682 fn ds_for_field_b(value: &u32) -> usize {
683 assert_eq!(*value, 2); // set in the example
684 1234
685 }
686
687 #[derive(DataSize)]
688 struct Foo {
689 field_a: u32,
690 #[data_size(with = ds_for_field_b)]
691 field_b: u32,
692 field_c: u32,
693 }
694
695 let value = Foo {
696 field_a: 1,
697 field_b: 2,
698 field_c: 3,
699 };
700 assert_eq!(value.estimate_heap_size(), 1234);
701 }
702}