#![forbid(unsafe_code)]
#[cfg(feature = "count-allocations")]
pub fn measure_with_result<F, R>(f: F) -> (R, allocation_counter::AllocationInfo)
where
F: FnOnce() -> R,
{
let mut result: Option<R> = None;
let info = allocation_counter::measure(|| {
result = Some(f());
});
(result.expect("Closure should have set result"), info)
}
#[cfg(not(feature = "count-allocations"))]
pub fn measure_with_result<F, R>(f: F) -> (R, ())
where
F: FnOnce() -> R,
{
(f(), ())
}
#[cfg(test)]
mod tests {
use super::*;
#[cfg(feature = "count-allocations")]
fn non_negative(value: i64) -> u64 {
u64::try_from(value.max(0)).unwrap_or(0)
}
#[test]
fn test_measure_with_result_comprehensive() {
let expected_result = 42;
let (result, _alloc_info) = measure_with_result(|| expected_result);
assert_eq!(result, expected_result);
let (vec_result, _alloc_info) = measure_with_result(|| vec![1, 2, 3, 4, 5]);
assert_eq!(vec_result, vec![1, 2, 3, 4, 5]);
let (string_result, _alloc_info) = measure_with_result(|| {
let mut s = String::new();
s.push_str("Hello, ");
s.push_str("World!");
s
});
assert_eq!(string_result, "Hello, World!");
let (complex_result, _alloc_info) = measure_with_result(|| {
let mut data: Vec<String> = Vec::new();
for i in 0..5 {
data.push(format!("Item {i}"));
}
data.len()
});
assert_eq!(complex_result, 5);
let (tuple_result, _alloc_info) = measure_with_result(|| ("hello", 42));
assert_eq!(tuple_result, ("hello", 42));
let (option_result, _alloc_info) = measure_with_result(|| Some("value"));
assert_eq!(option_result, Some("value"));
let (result_result, _alloc_info) = measure_with_result(|| Ok::<i32, &str>(123));
assert_eq!(result_result, Ok(123));
let (sum_result, _alloc_info) = measure_with_result(|| {
let data = [1, 2, 3];
data.iter().sum::<i32>()
});
assert_eq!(sum_result, 6);
}
#[test]
fn test_measure_with_result_executes_once() {
use std::sync::atomic::{AtomicUsize, Ordering};
let calls = AtomicUsize::new(0);
let (result, _alloc_info) = measure_with_result(|| {
calls.fetch_add(1, Ordering::SeqCst);
123
});
assert_eq!(result, 123);
assert_eq!(calls.load(Ordering::SeqCst), 1);
}
#[test]
fn test_measure_with_result_panics_propagate() {
let should_panic = std::hint::black_box(true);
let result = std::panic::catch_unwind(|| {
let _ = measure_with_result(|| -> i32 {
assert!(!should_panic, "boom");
0
});
});
assert!(result.is_err());
}
#[cfg(feature = "count-allocations")]
#[test]
fn test_measure_with_result_allocation_info_structure() {
let (_result, alloc_info) = measure_with_result(|| {
vec![0u8; 1024]
});
std::hint::black_box(&alloc_info.bytes_total);
std::hint::black_box(&alloc_info.count_total);
std::hint::black_box(&alloc_info.bytes_current);
std::hint::black_box(&alloc_info.count_current);
std::hint::black_box(&alloc_info.bytes_max);
std::hint::black_box(&alloc_info.count_max);
assert!(
alloc_info.bytes_total > 0,
"Should have allocated memory for the vector"
);
let bytes_current = non_negative(alloc_info.bytes_current);
let count_current = non_negative(alloc_info.count_current);
assert!(
alloc_info.bytes_total >= bytes_current,
"Total bytes should be >= current bytes"
);
assert!(
alloc_info.bytes_max >= bytes_current,
"Max bytes should be >= current bytes"
);
assert!(
alloc_info.count_total >= count_current,
"Total allocations should be >= current allocations"
);
assert!(
alloc_info.count_max >= count_current,
"Max allocation count should be >= current allocation count"
);
}
#[cfg(feature = "count-allocations")]
#[test]
fn test_measure_with_result_no_allocation_invariants() {
let (_result, alloc_info) = measure_with_result(|| 7usize);
let bytes_current = non_negative(alloc_info.bytes_current);
let count_current = non_negative(alloc_info.count_current);
assert!(
alloc_info.bytes_total >= bytes_current,
"Total bytes should be >= current bytes"
);
assert!(
alloc_info.bytes_max >= bytes_current,
"Max bytes should be >= current bytes"
);
assert!(
alloc_info.count_total >= count_current,
"Total allocations should be >= current allocations"
);
assert!(
alloc_info.count_max >= count_current,
"Max allocation count should be >= current allocation count"
);
}
#[cfg(not(feature = "count-allocations"))]
#[test]
fn test_measure_with_result_no_allocation_feature() {
let (_result, alloc_info) = measure_with_result(|| vec![0u8; 1024]);
let _: () = alloc_info;
}
}