use std::alloc::{GlobalAlloc, Layout, System};
use std::cell::RefCell;
pub fn count<F: FnOnce()>(run_while_counting: F) -> u64 {
let initial_count = ALLOCATIONS.with(|f| *f.borrow());
run_while_counting();
ALLOCATIONS.with(|f| *f.borrow()) - initial_count
}
pub fn assert_no_allocations<F: FnOnce()>(run_while_counting: F) {
assert_max_allocations(0, run_while_counting);
}
pub fn assert_max_allocations<F: FnOnce()>(max_allocations: u64, run_while_counting: F) {
let initial_count = ALLOCATIONS.with(|f| *f.borrow());
run_while_counting();
let num_allocations = ALLOCATIONS.with(|f| *f.borrow()) - initial_count;
assert!(
num_allocations <= max_allocations,
"Unexpected memory allocations (more than {}): {}",
max_allocations,
num_allocations
);
}
pub fn assert_num_allocations<F: FnOnce()>(
allowed_allocations: std::ops::Range<u64>,
run_while_counting: F,
) {
let initial_count = ALLOCATIONS.with(|f| *f.borrow());
run_while_counting();
let num_allocations = ALLOCATIONS.with(|f| *f.borrow()) - initial_count;
assert!(
allowed_allocations.contains(&num_allocations),
"Unexpected memory allocations (outside of {:?}): {}",
allowed_allocations,
num_allocations
);
}
pub fn avoid_counting<F: FnOnce()>(run_while_not_counting: F) {
DO_COUNT.with(|b| {
*b.borrow_mut() += 1;
run_while_not_counting();
*b.borrow_mut() -= 1;
});
}
thread_local! {
static ALLOCATIONS: RefCell<u64> = RefCell::new(0);
}
thread_local! {
static DO_COUNT: RefCell<u32> = RefCell::new(0);
}
struct CountingAllocator;
unsafe impl GlobalAlloc for CountingAllocator {
unsafe fn alloc(&self, l: Layout) -> *mut u8 {
DO_COUNT.with(|b| {
if *b.borrow() == 0 {
ALLOCATIONS.with(|f| {
*f.borrow_mut() += 1;
});
}
});
System.alloc(l)
}
unsafe fn dealloc(&self, ptr: *mut u8, l: Layout) {
System.dealloc(ptr, l);
}
}
#[global_allocator]
static GLOBAL: CountingAllocator = CountingAllocator {};
#[test]
fn test_basic() {
let allocations = count(|| {
});
assert_eq!(allocations, 0);
let allocations = count(|| {
let v: Vec<u32> = vec![12];
assert_eq!(v.len(), 1);
});
assert_eq!(allocations, 1);
let allocations = count(|| {
let v: Vec<u32> = vec![12];
assert_eq!(v.len(), 1);
});
assert_eq!(allocations, 1);
let allocations = count(|| {
let v: Vec<u32> = vec![12];
assert_eq!(v.len(), 1);
let v: Vec<u32> = vec![12];
assert_eq!(v.len(), 1);
});
assert_eq!(allocations, 2);
assert_no_allocations(|| {
});
assert_max_allocations(2, || {
});
assert_max_allocations(2, || {
let v: Vec<u32> = vec![12];
assert_eq!(v.len(), 1);
let v: Vec<u32> = vec![12];
assert_eq!(v.len(), 1);
});
assert_num_allocations(1..3, || {
let v: Vec<u32> = vec![12];
assert_eq!(v.len(), 1);
let v: Vec<u32> = vec![12];
assert_eq!(v.len(), 1);
});
assert_num_allocations(2..3, || {
let v: Vec<u32> = vec![12];
assert_eq!(v.len(), 1);
let v: Vec<u32> = vec![12];
assert_eq!(v.len(), 1);
});
}
#[test]
#[should_panic(expected = "Unexpected memory allocations (more than 0): 1")]
fn test_assert_no_allocations_panic() {
assert_no_allocations(|| {
let v: Vec<u32> = vec![12];
assert_eq!(v.len(), 1);
});
}
#[test]
#[should_panic(expected = "Unexpected memory allocations (more than 1): 2")]
fn test_assert_max_allocations_panic() {
assert_max_allocations(1, || {
let v: Vec<u32> = vec![12];
assert_eq!(v.len(), 1);
let v: Vec<u32> = vec![12];
assert_eq!(v.len(), 1);
});
}
#[test]
#[should_panic(expected = "Unexpected memory allocations (outside of 10..12): 2")]
fn test_assert_num_allocations_panic() {
assert_num_allocations(10..12, || {
let v: Vec<u32> = vec![12];
assert_eq!(v.len(), 1);
let v: Vec<u32> = vec![12];
assert_eq!(v.len(), 1);
});
}
#[test]
fn test_avoid_counting() {
let allocations = count(|| {
});
assert_eq!(allocations, 0);
let allocations = count(|| {
let v: Vec<u32> = vec![12];
assert_eq!(v.len(), 1);
avoid_counting(|| {
let v: Vec<u32> = vec![12];
assert_eq!(v.len(), 1);
avoid_counting(|| {
let v: Vec<u32> = vec![12];
assert_eq!(v.len(), 1);
});
});
let v: Vec<u32> = vec![12];
assert_eq!(v.len(), 1);
let v: Vec<u32> = vec![12];
assert_eq!(v.len(), 1);
});
assert_eq!(allocations, 3);
assert_no_allocations(|| {
avoid_counting(|| {
let v: Vec<u32> = vec![12];
assert_eq!(v.len(), 1);
});
});
}