use std::cell::RefCell;
use std::ops::{Deref, DerefMut};
const MAX_BUFFERS: usize = 8;
const MAX_RETAINED_BYTES: usize = 256 * 1024;
#[derive(Default)]
struct Pool {
buffers: Vec<Vec<bool>>,
bytes: usize,
}
thread_local! {
static FLAGS: RefCell<Pool> = const { RefCell::new(Pool { buffers: Vec::new(), bytes: 0 }) };
}
pub(super) struct LineFlags(Vec<bool>);
impl LineFlags {
pub(super) fn new(len: usize) -> Self {
let mut flags = FLAGS
.try_with(|pool| {
let mut pool = pool.try_borrow_mut().ok()?;
let flags = pool.buffers.pop()?;
pool.bytes -= flags.capacity();
Some(flags)
})
.ok()
.flatten()
.unwrap_or_default();
flags.resize(len, false);
Self(flags)
}
}
impl Deref for LineFlags {
type Target = [bool];
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl DerefMut for LineFlags {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
impl Drop for LineFlags {
fn drop(&mut self) {
let mut flags = std::mem::take(&mut self.0);
flags.clear();
let capacity = flags.capacity();
if capacity == 0 || capacity > MAX_RETAINED_BYTES {
return;
}
let _ = FLAGS.try_with(|pool| {
if let Ok(mut pool) = pool.try_borrow_mut()
&& pool.buffers.len() < MAX_BUFFERS
&& pool.bytes + capacity <= MAX_RETAINED_BYTES
{
pool.bytes += capacity;
pool.buffers.push(flags);
}
});
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn nested_leases_reuse_storage_and_reset_every_flag() {
FLAGS.with(|pool| *pool.borrow_mut() = Pool::default());
let mut outer = LineFlags::new(64);
outer.fill(true);
let pointer = {
let mut nested = LineFlags::new(32);
assert!(nested.iter().all(|flag| !flag));
nested.fill(true);
nested.as_ptr()
};
let reused = LineFlags::new(16);
assert_eq!(reused.as_ptr(), pointer);
assert!(reused.iter().all(|flag| !flag));
assert!(outer.iter().all(|flag| *flag));
}
#[test]
fn oversized_documents_and_many_nested_parses_bound_retained_storage() {
FLAGS.with(|pool| *pool.borrow_mut() = Pool::default());
drop(LineFlags::new(MAX_RETAINED_BYTES + 1));
FLAGS.with(|pool| assert_eq!(pool.borrow().bytes, 0));
let flags: Vec<_> = (0..MAX_BUFFERS + 2).map(|_| LineFlags::new(64)).collect();
drop(flags);
FLAGS.with(|pool| {
let pool = pool.borrow();
assert_eq!(pool.buffers.len(), MAX_BUFFERS);
assert!(pool.bytes <= MAX_RETAINED_BYTES);
});
}
}