use core::hash::Hash;
use std::collections::{BinaryHeap, HashMap};
use super::error::{Allocation, AllocationSite, Error, Result};
fn refused<T>(site: AllocationSite, additional: usize) -> Error {
Error::Allocation(Allocation::new(
site,
additional.saturating_mul(size_of::<T>()),
))
}
pub(crate) fn reserve<T>(
buffer: &mut Vec<T>,
additional: usize,
site: AllocationSite,
) -> Result<()> {
if inject::refuses(site) {
return Err(refused::<T>(site, additional));
}
buffer
.try_reserve(additional)
.map_err(|_| refused::<T>(site, additional))
}
pub(crate) fn reserve_exact<T>(
buffer: &mut Vec<T>,
additional: usize,
site: AllocationSite,
) -> Result<()> {
if inject::refuses(site) {
return Err(refused::<T>(site, additional));
}
buffer
.try_reserve_exact(additional)
.map_err(|_| refused::<T>(site, additional))
}
pub(crate) fn reserve_heap<T: Ord>(
heap: &mut BinaryHeap<T>,
additional: usize,
site: AllocationSite,
) -> Result<()> {
if inject::refuses(site) {
return Err(refused::<T>(site, additional));
}
heap
.try_reserve(additional)
.map_err(|_| refused::<T>(site, additional))
}
pub(crate) fn reserve_map<K: Eq + Hash, V>(
map: &mut HashMap<K, V>,
additional: usize,
site: AllocationSite,
) -> Result<()> {
if inject::refuses(site) {
return Err(refused::<(K, V)>(site, additional));
}
map
.try_reserve(additional)
.map_err(|_| refused::<(K, V)>(site, additional))
}
pub(crate) fn reserve_str(
text: &mut String,
additional: usize,
site: AllocationSite,
) -> Result<()> {
if inject::refuses(site) {
return Err(refused::<u8>(site, additional));
}
text
.try_reserve(additional)
.map_err(|_| refused::<u8>(site, additional))
}
pub(crate) fn with_capacity<T>(len: usize, site: AllocationSite) -> Result<Vec<T>> {
let mut list = Vec::new();
reserve_exact(&mut list, len, site)?;
Ok(list)
}
pub(crate) fn collect<T>(
items: impl ExactSizeIterator<Item = T>,
site: AllocationSite,
) -> Result<Vec<T>> {
let mut list = with_capacity(items.len(), site)?;
list.extend(items);
Ok(list)
}
#[cfg(test)]
pub(crate) mod inject {
use core::cell::Cell;
use super::AllocationSite;
std::thread_local! {
static REFUSE: Cell<Option<(AllocationSite, usize)>> = const { Cell::new(None) };
}
#[must_use = "the seam clears when the guard drops"]
pub(crate) fn refuse(site: AllocationSite, skip: usize) -> Guard {
REFUSE.with(|cell| cell.set(Some((site, skip))));
Guard(())
}
pub(crate) struct Guard(());
impl Drop for Guard {
fn drop(&mut self) {
REFUSE.with(|cell| cell.set(None));
}
}
pub(crate) fn refuses(site: AllocationSite) -> bool {
REFUSE.with(|cell| match cell.get() {
Some((at, 0)) if at == site => true,
Some((at, skip)) if at == site => {
cell.set(Some((at, skip - 1)));
false
}
_ => false,
})
}
}
#[cfg(not(test))]
mod inject {
use super::AllocationSite;
#[inline(always)]
pub(super) const fn refuses(_: AllocationSite) -> bool {
false
}
}
#[cfg(test)]
mod tests;