thread_local! {
static CLASSIFY_WORK: std::cell::Cell<u32> = const { std::cell::Cell::new(0) };
static CLASSIFY_DEPTH: std::cell::Cell<u32> = const { std::cell::Cell::new(0) };
static CLASSIFY_BYTES: std::cell::Cell<u64> = const { std::cell::Cell::new(0) };
static CLASSIFY_BYTE_LIMIT: std::cell::Cell<u64> = const { std::cell::Cell::new(u64::MAX) };
}
pub(super) const CLASSIFY_BYTE_BASE: u64 = 64 * 1024;
pub(super) const CLASSIFY_BYTES_PER_INPUT_BYTE: u64 = 64;
const MAX_CLASSIFY_WORK: u32 = 512;
pub(crate) struct ClassifyGuard;
impl ClassifyGuard {
pub(crate) fn enter(input_len: usize) -> Option<Self> {
let len = input_len as u64;
if CLASSIFY_DEPTH.with(|d| d.get()) == 0 {
CLASSIFY_WORK.with(|w| w.set(0));
CLASSIFY_BYTES.with(|b| b.set(0));
CLASSIFY_BYTE_LIMIT.with(|l| l.set(CLASSIFY_BYTE_BASE.saturating_add(CLASSIFY_BYTES_PER_INPUT_BYTE.saturating_mul(len))));
}
let spent = CLASSIFY_WORK.with(|w| {
let n = w.get().saturating_add(1);
w.set(n);
n
});
let bytes = CLASSIFY_BYTES.with(|b| {
let n = b.get().saturating_add(len);
b.set(n);
n
});
if spent > MAX_CLASSIFY_WORK || bytes > CLASSIFY_BYTE_LIMIT.with(std::cell::Cell::get) {
return None;
}
CLASSIFY_DEPTH.with(|d| d.set(d.get() + 1));
Some(ClassifyGuard)
}
}
impl Drop for ClassifyGuard {
fn drop(&mut self) {
let depth = CLASSIFY_DEPTH.with(|d| {
let n = d.get().saturating_sub(1);
d.set(n);
n
});
if depth == 0 {
CLASSIFY_WORK.with(|w| w.set(0));
CLASSIFY_BYTES.with(|b| b.set(0));
}
}
}
pub(crate) fn charge_classify_work(units: u32) -> bool {
CLASSIFY_WORK.with(|w| {
let n = w.get().saturating_add(units);
w.set(n);
n <= MAX_CLASSIFY_WORK
})
}