#![doc = include_str!("../README.md")]
use std::alloc::{System,GlobalAlloc,Layout};
use std::cell::Cell;
#[cfg(all(feature = "disable_release", feature = "warn_release"))]
compile_error!("disable_release cannot be active at the same time with warn_release");
#[cfg(not(all(feature = "disable_release", not(debug_assertions))))] thread_local! {
static ALLOC_FORBID_COUNT: Cell<u32> = Cell::new(0);
static ALLOC_PERMIT_COUNT: Cell<u32> = Cell::new(0);
#[cfg(any( all(feature="warn_debug", debug_assertions), all(feature="warn_release", not(debug_assertions)) ))]
static ALLOC_VIOLATION_COUNT: Cell<u32> = Cell::new(0);
}
#[cfg(all(feature = "disable_release", not(debug_assertions)))] pub fn assert_no_alloc<T, F: FnOnce() -> T> (func: F) -> T { func()
}
#[cfg(all(feature = "disable_release", not(debug_assertions)))] pub fn permit_alloc<T, F: FnOnce() -> T> (func: F) -> T { func()
}
#[cfg(not(all(feature = "disable_release", not(debug_assertions))))] pub fn assert_no_alloc<T, F: FnOnce() -> T> (func: F) -> T {
struct Guard;
impl Guard {
fn new() -> Guard {
ALLOC_FORBID_COUNT.with(|c| c.set(c.get()+1));
Guard
}
}
impl Drop for Guard {
fn drop(&mut self) {
ALLOC_FORBID_COUNT.with(|c| c.set(c.get()-1));
}
}
#[cfg(any( all(feature="warn_debug", debug_assertions), all(feature="warn_release", not(debug_assertions)) ))] let old_violation_count = violation_count();
let guard = Guard::new(); let ret = func();
std::mem::drop(guard);
#[cfg(any( all(feature="warn_debug", debug_assertions), all(feature="warn_release", not(debug_assertions)) ))] if violation_count() > old_violation_count {
eprintln!("Tried to (de)allocate memory in a thread that forbids allocator calls!");
}
return ret;
}
#[cfg(not(all(feature = "disable_release", not(debug_assertions))))] pub fn permit_alloc<T, F: FnOnce() -> T> (func: F) -> T {
struct Guard;
impl Guard {
fn new() -> Guard {
ALLOC_PERMIT_COUNT.with(|c| c.set(c.get()+1));
Guard
}
}
impl Drop for Guard {
fn drop(&mut self) {
ALLOC_PERMIT_COUNT.with(|c| c.set(c.get()-1));
}
}
let guard = Guard::new(); let ret = func();
std::mem::drop(guard);
return ret;
}
#[cfg(any( all(feature="warn_debug", debug_assertions), all(feature="warn_release", not(debug_assertions)) ))] pub fn violation_count() -> u32 {
ALLOC_VIOLATION_COUNT.with(|c| c.get())
}
#[cfg(any( all(feature="warn_debug", debug_assertions), all(feature="warn_release", not(debug_assertions)) ))] pub fn reset_violation_count() {
ALLOC_VIOLATION_COUNT.with(|c| c.set(0));
}
#[cfg(not(all(feature = "disable_release", not(debug_assertions))))] pub struct AllocDisabler;
#[cfg(not(all(feature = "disable_release", not(debug_assertions))))] impl AllocDisabler {
fn check(&self, _layout: Layout) {
let forbid_count = ALLOC_FORBID_COUNT.with(|f| f.get());
let permit_count = ALLOC_PERMIT_COUNT.with(|p| p.get());
if forbid_count > 0 && permit_count == 0 {
#[cfg(any( all(feature="warn_debug", debug_assertions), all(feature="warn_release", not(debug_assertions)) ))] ALLOC_VIOLATION_COUNT.with(|c| c.set(c.get()+1));
#[cfg(any( all(not(feature="warn_debug"), debug_assertions), all(not(feature="warn_release"), not(debug_assertions)) ))] std::alloc::handle_alloc_error(_layout);
}
}
}
#[cfg(not(all(feature = "disable_release", not(debug_assertions))))] unsafe impl GlobalAlloc for AllocDisabler {
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
self.check(layout);
System.alloc(layout)
}
unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
self.check(layout);
System.dealloc(ptr, layout)
}
}
pub struct PermitDrop<T>(Option<T>);
impl<T> PermitDrop<T> {
pub fn new(t: T) -> PermitDrop<T> {
permit_alloc(|| {
PermitDrop(Some(t))
})
}
}
impl<T> std::ops::Deref for PermitDrop<T> {
type Target = T;
fn deref(&self) -> &T { self.0.as_ref().unwrap() }
}
impl<T> std::ops::DerefMut for PermitDrop<T> {
fn deref_mut(&mut self) -> &mut T { self.0.as_mut().unwrap() }
}
impl<I: Iterator> Iterator for PermitDrop<I> {
type Item = I::Item;
fn next(&mut self) -> Option<Self::Item> {
(**self).next()
}
}
impl<T> Drop for PermitDrop<T> {
fn drop(&mut self) {
let mut tmp = None;
std::mem::swap(&mut tmp, &mut self.0);
permit_alloc(|| {
std::mem::drop(tmp);
});
}
}