use memmap2::{Mmap, MmapOptions};
use std::collections::VecDeque;
use std::fs::File;
use std::path::Path;
use std::sync::{Arc, Mutex};
use super::memory_opt_enabled;
use crate::error::{Result, ScipixError};
pub struct BufferPool<T> {
pool: Arc<Mutex<VecDeque<T>>>,
factory: Arc<dyn Fn() -> T + Send + Sync>,
#[allow(dead_code)]
max_size: usize,
}
impl<T: Send + 'static> BufferPool<T> {
pub fn new<F>(factory: F, initial_size: usize, max_size: usize) -> Self
where
F: Fn() -> T + Send + Sync + 'static,
{
let factory = Arc::new(factory);
let pool = Arc::new(Mutex::new(VecDeque::with_capacity(max_size)));
if memory_opt_enabled() {
let mut pool_lock = pool.lock().unwrap();
for _ in 0..initial_size {
pool_lock.push_back(factory());
}
}
Self {
pool,
factory,
max_size,
}
}
pub fn acquire(&self) -> PooledBuffer<T> {
let buffer = if memory_opt_enabled() {
self.pool
.lock()
.unwrap()
.pop_front()
.unwrap_or_else(|| (self.factory)())
} else {
(self.factory)()
};
PooledBuffer {
buffer: Some(buffer),
pool: self.pool.clone(),
}
}
pub fn size(&self) -> usize {
self.pool.lock().unwrap().len()
}
pub fn clear(&self) {
self.pool.lock().unwrap().clear();
}
}
pub struct PooledBuffer<T> {
buffer: Option<T>,
pool: Arc<Mutex<VecDeque<T>>>,
}
impl<T> PooledBuffer<T> {
pub fn get_mut(&mut self) -> &mut T {
self.buffer.as_mut().unwrap()
}
pub fn get(&self) -> &T {
self.buffer.as_ref().unwrap()
}
}
impl<T> Drop for PooledBuffer<T> {
fn drop(&mut self) {
if memory_opt_enabled() {
if let Some(buffer) = self.buffer.take() {
let mut pool = self.pool.lock().unwrap();
pool.push_back(buffer);
}
}
}
}
impl<T> std::ops::Deref for PooledBuffer<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
self.buffer.as_ref().unwrap()
}
}
impl<T> std::ops::DerefMut for PooledBuffer<T> {
fn deref_mut(&mut self) -> &mut Self::Target {
self.buffer.as_mut().unwrap()
}
}
pub struct MmapModel {
_mmap: Mmap,
data: *const u8,
len: usize,
}
unsafe impl Send for MmapModel {}
unsafe impl Sync for MmapModel {}
impl MmapModel {
pub fn from_file<P: AsRef<Path>>(path: P) -> Result<Self> {
let file = File::open(path.as_ref()).map_err(|e| ScipixError::Io(e))?;
let mmap = unsafe {
MmapOptions::new()
.map(&file)
.map_err(|e| ScipixError::Io(e))?
};
let data = mmap.as_ptr();
let len = mmap.len();
Ok(Self {
_mmap: mmap,
data,
len,
})
}
pub fn as_slice(&self) -> &[u8] {
unsafe { std::slice::from_raw_parts(self.data, self.len) }
}
pub fn len(&self) -> usize {
self.len
}
pub fn is_empty(&self) -> bool {
self.len == 0
}
}
pub struct ImageView<'a> {
data: &'a [u8],
width: u32,
height: u32,
channels: u8,
}
impl<'a> ImageView<'a> {
pub fn new(data: &'a [u8], width: u32, height: u32, channels: u8) -> Result<Self> {
let expected_len = (width * height * channels as u32) as usize;
if data.len() != expected_len {
return Err(ScipixError::InvalidInput(format!(
"Invalid data length: expected {}, got {}",
expected_len,
data.len()
)));
}
Ok(Self {
data,
width,
height,
channels,
})
}
pub fn pixel(&self, x: u32, y: u32) -> &[u8] {
let offset = ((y * self.width + x) * self.channels as u32) as usize;
&self.data[offset..offset + self.channels as usize]
}
pub fn data(&self) -> &[u8] {
self.data
}
pub fn dimensions(&self) -> (u32, u32) {
(self.width, self.height)
}
pub fn channels(&self) -> u8 {
self.channels
}
pub fn subview(&self, x: u32, y: u32, width: u32, height: u32) -> Result<Self> {
if x + width > self.width || y + height > self.height {
return Err(ScipixError::InvalidInput(
"Subview out of bounds".to_string(),
));
}
let mut subview_data = Vec::new();
for row in y..y + height {
let start = ((row * self.width + x) * self.channels as u32) as usize;
let end = start + (width * self.channels as u32) as usize;
subview_data.extend_from_slice(&self.data[start..end]);
}
let leaked = Box::leak(subview_data.into_boxed_slice());
Ok(Self {
data: leaked,
width,
height,
channels: self.channels,
})
}
}
pub struct Arena {
buffer: Vec<u8>,
offset: usize,
}
impl Arena {
pub fn with_capacity(capacity: usize) -> Self {
Self {
buffer: Vec::with_capacity(capacity),
offset: 0,
}
}
pub fn alloc(&mut self, size: usize, align: usize) -> &mut [u8] {
let padding = (align - (self.offset % align)) % align;
self.offset += padding;
let start = self.offset;
let end = start + size;
if end > self.buffer.capacity() {
self.buffer.reserve(end - self.buffer.len());
}
unsafe {
self.buffer.set_len(end);
}
self.offset = end;
&mut self.buffer[start..end]
}
pub fn reset(&mut self) {
self.offset = 0;
self.buffer.clear();
}
pub fn usage(&self) -> usize {
self.offset
}
pub fn capacity(&self) -> usize {
self.buffer.capacity()
}
}
pub struct GlobalPools {
small: BufferPool<Vec<u8>>, medium: BufferPool<Vec<u8>>, large: BufferPool<Vec<u8>>, }
impl GlobalPools {
fn new() -> Self {
Self {
small: BufferPool::new(|| Vec::with_capacity(1024), 10, 100),
medium: BufferPool::new(|| Vec::with_capacity(64 * 1024), 5, 50),
large: BufferPool::new(|| Vec::with_capacity(1024 * 1024), 2, 20),
}
}
pub fn get() -> &'static Self {
static POOLS: std::sync::OnceLock<GlobalPools> = std::sync::OnceLock::new();
POOLS.get_or_init(GlobalPools::new)
}
pub fn acquire_small(&self) -> PooledBuffer<Vec<u8>> {
self.small.acquire()
}
pub fn acquire_medium(&self) -> PooledBuffer<Vec<u8>> {
self.medium.acquire()
}
pub fn acquire_large(&self) -> PooledBuffer<Vec<u8>> {
self.large.acquire()
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::io::Write;
use tempfile::NamedTempFile;
#[test]
fn test_buffer_pool() {
let pool = BufferPool::new(|| Vec::with_capacity(1024), 2, 10);
assert_eq!(pool.size(), 2);
let mut buf1 = pool.acquire();
assert_eq!(buf1.capacity(), 1024);
buf1.extend_from_slice(b"test");
drop(buf1);
assert_eq!(pool.size(), 3); }
#[test]
fn test_mmap_model() {
let mut temp = NamedTempFile::new().unwrap();
temp.write_all(b"test model data").unwrap();
temp.flush().unwrap();
let mmap = MmapModel::from_file(temp.path()).unwrap();
assert_eq!(mmap.as_slice(), b"test model data");
assert_eq!(mmap.len(), 15);
}
#[test]
fn test_image_view() {
let data = vec![
255, 0, 0, 255, 0, 255, 0, 255, 0, 0, 255, 255, 255, 255, 255, 255, ];
let view = ImageView::new(&data, 2, 2, 4).unwrap();
assert_eq!(view.dimensions(), (2, 2));
assert_eq!(view.pixel(0, 0), &[255, 0, 0, 255]);
assert_eq!(view.pixel(1, 1), &[255, 255, 255, 255]);
}
#[test]
fn test_arena() {
let mut arena = Arena::with_capacity(1024);
let slice1 = arena.alloc(100, 8);
assert_eq!(slice1.len(), 100);
let slice2 = arena.alloc(200, 8);
assert_eq!(slice2.len(), 200);
assert!(arena.usage() >= 300);
arena.reset();
assert_eq!(arena.usage(), 0);
}
#[test]
fn test_global_pools() {
let pools = GlobalPools::get();
let small = pools.acquire_small();
assert!(small.capacity() >= 1024);
let medium = pools.acquire_medium();
assert!(medium.capacity() >= 64 * 1024);
let large = pools.acquire_large();
assert!(large.capacity() >= 1024 * 1024);
}
}