use super::ring::{ChunkQueue, RingBuffer};
use std::ops::{Deref, DerefMut};
#[derive(Copy, Clone)]
pub struct SendPtr(*const u8);
unsafe impl Send for SendPtr {}
unsafe impl Sync for SendPtr {}
impl SendPtr {
pub fn new(ptr: *const u8) -> Self { Self(ptr) }
pub fn as_ptr(&self) -> *const u8 { self.0 }
}
pub struct Chunk<'a> {
pub ring_nr: u8,
pub index: u64,
pub data: &'a mut [u8],
}
impl<'a> Deref for Chunk<'a> { type Target = [u8]; fn deref(&self) -> &[u8] { self.data } }
impl<'a> DerefMut for Chunk<'a> { fn deref_mut(&mut self) -> &mut [u8] { self.data } }
pub struct ChunkRevolver {
chunk_size: usize,
rings: Vec<RingBuffer>,
memory_blocks: Vec<Box<[u8]>>,
next_ring: usize,
}
impl ChunkRevolver {
pub fn new(chunk_size: usize, total_chunks: usize, num_workers: usize) -> Self {
let safe_workers = num_workers.min(total_chunks);
let chunks_per_ring = total_chunks / safe_workers;
let mut rings = Vec::with_capacity(safe_workers);
let mut memory_blocks = Vec::with_capacity(safe_workers);
for _ in 0..safe_workers {
let mut ring = RingBuffer::new(chunks_per_ring);
for i in 0..chunks_per_ring {
ring.push(i as u64).expect("init ring overflow");
}
rings.push(ring);
memory_blocks.push(vec![0u8; chunks_per_ring * chunk_size].into_boxed_slice());
}
Self { chunk_size, rings, memory_blocks, next_ring: 0 }
}
pub fn base_ptrs(&self) -> Vec<SendPtr> {
self.memory_blocks.iter()
.map(|b| SendPtr::new(b.as_ptr()))
.collect()
}
pub fn base_ptr(&self, ring_nr: usize) -> *const u8 {
self.memory_blocks[ring_nr].as_ptr()
}
pub fn chunk_size(&self) -> usize { self.chunk_size }
pub fn num_rings(&self) -> usize { self.rings.len() }
pub fn try_get_chunk(&mut self) -> Option<Chunk<'_>> {
let n = self.rings.len();
for i in 0..n {
let rn = (self.next_ring + i) % n;
if let Some(index) = self.rings[rn].pop() {
let offset = index as usize * self.chunk_size;
let data = &mut self.memory_blocks[rn][offset..offset + self.chunk_size];
self.next_ring = (rn + 1) % n;
return Some(Chunk { ring_nr: rn as u8, index, data });
}
}
None
}
pub fn return_chunk(&mut self, ring_nr: u8, index: u64) {
self.rings[ring_nr as usize]
.push(index)
.expect("ring overflow on return — double return?");
}
}
pub unsafe fn get_chunk_slice<'a>(
base_ptr: *const u8,
chunk_size: usize,
index: u32,
used: usize,
) -> &'a [u8] {
let offset = index as usize * chunk_size;
unsafe { std::slice::from_raw_parts(base_ptr.add(offset), used) }
}