use super::OwnedRecord;
const DEFAULT_POOL_SIZE: usize = 1024;
#[derive(Debug)]
pub struct ReadPool {
pool: Vec<OwnedRecord>,
capacity_hint: usize,
max_pool_size: usize,
}
impl ReadPool {
#[inline]
pub fn new(capacity_hint: usize) -> Self {
Self {
pool: Vec::with_capacity(DEFAULT_POOL_SIZE),
capacity_hint,
max_pool_size: DEFAULT_POOL_SIZE,
}
}
#[inline]
pub fn with_max_size(capacity_hint: usize, max_pool_size: usize) -> Self {
Self {
pool: Vec::with_capacity(max_pool_size),
capacity_hint,
max_pool_size,
}
}
#[inline]
pub fn acquire(&mut self) -> OwnedRecord {
self.pool
.pop()
.unwrap_or_else(|| OwnedRecord::with_capacity(self.capacity_hint))
}
#[inline]
pub fn release(&mut self, mut record: OwnedRecord) {
if self.pool.len() < self.max_pool_size {
record.clear();
self.pool.push(record);
}
}
#[inline]
pub fn release_batch(&mut self, records: Vec<OwnedRecord>) {
let available_space = self.max_pool_size.saturating_sub(self.pool.len());
let to_keep = records.len().min(available_space);
for mut record in records.into_iter().take(to_keep) {
record.clear();
self.pool.push(record);
}
}
#[inline]
pub fn len(&self) -> usize {
self.pool.len()
}
#[inline]
pub fn is_empty(&self) -> bool {
self.pool.is_empty()
}
#[inline]
pub fn capacity_hint(&self) -> usize {
self.capacity_hint
}
#[inline]
pub fn clear(&mut self) {
self.pool.clear();
}
pub fn prefill(&mut self, count: usize) {
let to_add = count.min(self.max_pool_size.saturating_sub(self.pool.len()));
self.pool.reserve(to_add);
for _ in 0..to_add {
self.pool.push(OwnedRecord::with_capacity(self.capacity_hint));
}
}
}
impl Default for ReadPool {
fn default() -> Self {
Self::new(256) }
}
#[derive(Debug)]
pub struct FixedBatch {
records: Vec<OwnedRecord>,
len: usize,
}
impl FixedBatch {
pub fn new(capacity: usize, read_capacity: usize) -> Self {
let records = (0..capacity)
.map(|_| OwnedRecord::with_capacity(read_capacity))
.collect();
Self { records, len: 0 }
}
#[inline]
pub fn get_mut(&mut self, idx: usize) -> &mut OwnedRecord {
&mut self.records[idx]
}
#[inline]
pub fn get(&self, idx: usize) -> &OwnedRecord {
debug_assert!(idx < self.len, "index out of bounds");
&self.records[idx]
}
#[inline]
pub fn set_len(&mut self, len: usize) {
debug_assert!(len <= self.records.len(), "len exceeds capacity");
self.len = len;
}
#[inline]
pub fn len(&self) -> usize {
self.len
}
#[inline]
pub fn is_empty(&self) -> bool {
self.len == 0
}
#[inline]
pub fn capacity(&self) -> usize {
self.records.len()
}
#[inline]
pub fn clear(&mut self) {
for record in &mut self.records[..self.len] {
record.clear();
}
self.len = 0;
}
#[inline]
pub fn iter(&self) -> impl Iterator<Item = &OwnedRecord> {
self.records[..self.len].iter()
}
#[inline]
pub fn iter_mut(&mut self) -> impl Iterator<Item = &mut OwnedRecord> {
self.records[..self.len].iter_mut()
}
pub fn into_vec(self) -> Vec<OwnedRecord> {
let mut v = self.records;
v.truncate(self.len);
v
}
#[inline]
pub fn as_slice(&self) -> &[OwnedRecord] {
&self.records[..self.len]
}
}
#[derive(Debug)]
pub struct BatchPool {
batches: Vec<FixedBatch>,
batch_capacity: usize,
read_capacity: usize,
}
impl BatchPool {
pub fn new(batch_capacity: usize, read_capacity: usize) -> Self {
Self {
batches: Vec::new(),
batch_capacity,
read_capacity,
}
}
pub fn acquire(&mut self) -> FixedBatch {
self.batches.pop().unwrap_or_else(|| {
FixedBatch::new(self.batch_capacity, self.read_capacity)
})
}
pub fn release(&mut self, mut batch: FixedBatch) {
batch.clear();
self.batches.push(batch);
}
pub fn len(&self) -> usize {
self.batches.len()
}
pub fn is_empty(&self) -> bool {
self.batches.is_empty()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_pool_new() {
let pool = ReadPool::new(150);
assert!(pool.is_empty());
assert_eq!(pool.capacity_hint(), 150);
}
#[test]
fn test_pool_acquire_creates_new() {
let mut pool = ReadPool::new(150);
let record = pool.acquire();
assert!(record.seq.capacity() >= 150);
assert!(record.qual.capacity() >= 150);
assert!(record.is_empty());
}
#[test]
fn test_pool_release_and_acquire() {
let mut pool = ReadPool::new(150);
let mut record = pool.acquire();
record.set_from(b"read1", b"ACGT", b"IIII");
pool.release(record);
assert_eq!(pool.len(), 1);
let record = pool.acquire();
assert!(pool.is_empty());
assert!(record.is_empty()); assert!(record.seq.capacity() >= 4); }
#[test]
fn test_pool_max_size() {
let mut pool = ReadPool::with_max_size(100, 2);
pool.release(OwnedRecord::with_capacity(100));
pool.release(OwnedRecord::with_capacity(100));
assert_eq!(pool.len(), 2);
pool.release(OwnedRecord::with_capacity(100));
assert_eq!(pool.len(), 2); }
#[test]
fn test_pool_release_batch() {
let mut pool = ReadPool::with_max_size(100, 5);
let records: Vec<_> = (0..10)
.map(|_| OwnedRecord::with_capacity(100))
.collect();
pool.release_batch(records);
assert_eq!(pool.len(), 5); }
#[test]
fn test_pool_prefill() {
let mut pool = ReadPool::with_max_size(100, 10);
pool.prefill(5);
assert_eq!(pool.len(), 5);
pool.prefill(10);
assert_eq!(pool.len(), 10); }
#[test]
fn test_pool_clear() {
let mut pool = ReadPool::new(100);
pool.prefill(5);
assert_eq!(pool.len(), 5);
pool.clear();
assert!(pool.is_empty());
}
#[test]
fn test_pool_default() {
let pool = ReadPool::default();
assert_eq!(pool.capacity_hint(), 256);
}
#[test]
fn test_record_reuse_preserves_capacity() {
let mut pool = ReadPool::new(256);
let mut record = pool.acquire();
let long_seq = vec![b'A'; 500];
let long_qual = vec![b'I'; 500];
record.set_from(b"read1", &long_seq, &long_qual);
let seq_cap = record.seq.capacity();
let qual_cap = record.qual.capacity();
pool.release(record);
let record = pool.acquire();
assert!(record.seq.capacity() >= seq_cap);
assert!(record.qual.capacity() >= qual_cap);
assert!(record.is_empty()); }
}
#[cfg(test)]
mod fixed_batch_tests {
use super::*;
#[test]
fn test_fixed_batch_creation() {
let batch = FixedBatch::new(10, 256);
assert_eq!(batch.capacity(), 10);
assert_eq!(batch.len(), 0);
assert!(batch.is_empty());
}
#[test]
fn test_fixed_batch_usage() {
let mut batch = FixedBatch::new(3, 64);
batch.get_mut(0).set_from(b"read1", b"ACGT", b"IIII");
batch.get_mut(1).set_from(b"read2", b"TGCA", b"HHHH");
batch.set_len(2);
assert_eq!(batch.len(), 2);
assert_eq!(batch.get(0).name, b"read1");
assert_eq!(batch.get(1).seq, b"TGCA");
}
#[test]
fn test_fixed_batch_clear() {
let mut batch = FixedBatch::new(2, 64);
batch.get_mut(0).set_from(b"read1", b"ACGT", b"IIII");
batch.set_len(1);
batch.clear();
assert!(batch.is_empty());
assert!(batch.get_mut(0).seq.capacity() >= 64);
}
#[test]
fn test_fixed_batch_iter() {
let mut batch = FixedBatch::new(3, 64);
batch.get_mut(0).set_from(b"a", b"A", b"I");
batch.get_mut(1).set_from(b"b", b"C", b"H");
batch.set_len(2);
let names: Vec<_> = batch.iter().map(|r| &r.name).collect();
assert_eq!(names.len(), 2);
}
#[test]
fn test_batch_pool() {
let mut pool = BatchPool::new(10, 256);
assert!(pool.is_empty());
let batch1 = pool.acquire();
assert_eq!(batch1.capacity(), 10);
pool.release(batch1);
assert_eq!(pool.len(), 1);
let batch2 = pool.acquire();
assert!(pool.is_empty());
assert_eq!(batch2.capacity(), 10);
}
}