use linkrs_core::Value;
use parking_lot::Mutex;
use std::sync::LazyLock;
pub const MAX_POOLED_ROWS: usize = 64;
pub const MAX_POOLED_ROW_CAPACITY: usize = 1024;
static ROW_POOL: LazyLock<Mutex<Vec<Vec<Value>>>> = LazyLock::new(|| Mutex::new(Vec::new()));
pub struct RowBufferPool;
impl RowBufferPool {
pub fn acquire_row() -> Vec<Value> {
ROW_POOL.lock().pop().unwrap_or_default()
}
pub fn release_row(row: &mut Vec<Value>) {
row.clear();
if row.capacity() > MAX_POOLED_ROW_CAPACITY {
return;
}
let mut pool = ROW_POOL.lock();
if pool.len() < MAX_POOLED_ROWS {
pool.push(std::mem::take(row));
}
}
pub fn acquire_rows(n: usize) -> Vec<Vec<Value>> {
let mut pool = ROW_POOL.lock();
let take = n.min(pool.len());
let mut out = Vec::with_capacity(n);
for _ in 0..take {
if let Some(row) = pool.pop() {
out.push(row);
}
}
out
}
pub fn release_rows(rows: &mut Vec<Vec<Value>>) {
let mut pool = ROW_POOL.lock();
for row in rows.iter_mut() {
row.clear();
if row.capacity() > MAX_POOLED_ROW_CAPACITY {
continue;
}
if pool.len() >= MAX_POOLED_ROWS {
break;
}
pool.push(std::mem::take(row));
}
rows.clear();
}
pub fn pool_len() -> usize {
ROW_POOL.lock().len()
}
#[cfg(test)]
pub fn clear_for_test() {
ROW_POOL.lock().clear();
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn acquire_release_reuses_capacity() {
RowBufferPool::clear_for_test();
let mut row = vec![Value::Int(1), Value::Int(2)];
RowBufferPool::release_row(&mut row);
assert!(row.is_empty());
assert_eq!(RowBufferPool::pool_len(), 1);
let reused = RowBufferPool::acquire_row();
assert_eq!(RowBufferPool::pool_len(), 0);
assert!(reused.capacity() >= 2);
}
#[test]
fn pool_drops_beyond_capacity() {
RowBufferPool::clear_for_test();
let mut rows: Vec<Vec<Value>> = (0..(MAX_POOLED_ROWS + 16))
.map(|_| Vec::<Value>::new())
.collect();
RowBufferPool::release_rows(&mut rows);
assert!(rows.is_empty());
assert_eq!(RowBufferPool::pool_len(), MAX_POOLED_ROWS);
RowBufferPool::clear_for_test();
}
#[test]
fn oversized_row_is_dropped() {
RowBufferPool::clear_for_test();
let mut row = Vec::with_capacity(MAX_POOLED_ROW_CAPACITY + 1);
row.push(Value::Int(1));
RowBufferPool::release_row(&mut row);
assert_eq!(RowBufferPool::pool_len(), 0);
}
#[test]
fn acquire_rows_partial_fill() {
RowBufferPool::clear_for_test();
let got = RowBufferPool::acquire_rows(4);
assert!(got.is_empty());
let mut rows = vec![vec![Value::Int(1)], vec![Value::Int(2)]];
RowBufferPool::release_rows(&mut rows);
let got = RowBufferPool::acquire_rows(4);
assert_eq!(got.len(), 2);
RowBufferPool::clear_for_test();
}
}