From 7aaff559fad6d572191ebe359029f54de130f7b3 Mon Sep 17 00:00:00 2001 From: Thomas Korrison Date: Mon, 23 Feb 2026 10:32:37 +0000 Subject: [PATCH] Fix `approx_bytes` to prevent double-counting of `SlotArena` inline size - Adjusted the `approx_bytes` method in `IntrusiveList` to correctly account for the size of the inline `SlotArena`, preventing double-counting. - Added a regression test to verify that `approx_bytes` reports the correct size for both empty and populated lists, ensuring accurate memory usage reporting. - Updated documentation to clarify the changes and the rationale behind the adjustments, improving understanding of memory footprint calculations. --- src/ds/intrusive_list.rs | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/src/ds/intrusive_list.rs b/src/ds/intrusive_list.rs index 5fbd3eb..108a199 100644 --- a/src/ds/intrusive_list.rs +++ b/src/ds/intrusive_list.rs @@ -793,7 +793,11 @@ impl IntrusiveList { /// assert!(bytes > 0); /// ``` pub fn approx_bytes(&self) -> usize { + // arena.approx_bytes() includes size_of::>>() which + // is already part of size_of::(), so subtract it to avoid + // double-counting the arena's inline footprint. std::mem::size_of::() + self.arena.approx_bytes() + - std::mem::size_of::>>() } #[cfg(any(test, debug_assertions))] @@ -1770,6 +1774,41 @@ mod tests { list.remove(c); list.debug_validate_invariants(); } + + /// Regression: approx_bytes used to double-count the SlotArena inline size. + /// + /// Both `IntrusiveList::approx_bytes` and `SlotArena::approx_bytes` included + /// `size_of::()`. Because the arena is an inline field of the list, + /// its inline size was counted twice — once inside `size_of::()` + /// and again inside `SlotArena::approx_bytes()`. + #[test] + fn approx_bytes_no_double_count() { + let empty: IntrusiveList = IntrusiveList::new(); + let struct_size = std::mem::size_of::>(); + let reported = empty.approx_bytes(); + + assert_eq!( + reported, + struct_size, + "empty list: approx_bytes ({reported}) != size_of ({struct_size}), \ + delta = {} — inline arena size is double-counted", + reported as isize - struct_size as isize + ); + + let mut list: IntrusiveList = IntrusiveList::new(); + for i in 0..10 { + list.push_back(i); + } + let reported_full = list.approx_bytes(); + assert!( + reported_full >= struct_size, + "non-empty list: approx_bytes ({reported_full}) < struct size ({struct_size})" + ); + assert!( + reported_full > struct_size, + "non-empty list should have non-zero heap allocation" + ); + } } #[cfg(test)]