use std::ops::Deref;
use std::sync::Arc;
use super::pool::PacketPool;
pub enum FrameBuf {
Pooled {
pool: Arc<PacketPool>,
index: u32,
len: u32,
},
Heap(Vec<u8>),
}
impl FrameBuf {
pub fn from_pool(pool: &Arc<PacketPool>, data: &[u8]) -> Self {
if let Some(mut pkt) = pool.alloc() {
if pkt.copy_from_slice(data).is_ok() {
let index = pkt.into_index();
return Self::Pooled {
pool: Arc::clone(pool),
index,
len: data.len() as u32,
};
}
}
Self::Heap(data.to_vec())
}
#[inline]
pub fn len(&self) -> usize {
match self {
Self::Pooled { len, .. } => *len as usize,
Self::Heap(v) => v.len(),
}
}
#[inline]
pub fn is_empty(&self) -> bool {
self.len() == 0
}
#[inline]
pub fn is_pooled(&self) -> bool {
matches!(self, Self::Pooled { .. })
}
}
impl Deref for FrameBuf {
type Target = [u8];
#[inline]
fn deref(&self) -> &[u8] {
match self {
Self::Pooled { pool, index, len } => {
let buf = unsafe { pool.get(*index) };
&buf.as_full_slice()[..*len as usize]
}
Self::Heap(v) => v,
}
}
}
impl AsRef<[u8]> for FrameBuf {
#[inline]
fn as_ref(&self) -> &[u8] {
self
}
}
impl Drop for FrameBuf {
fn drop(&mut self) {
if let Self::Pooled { pool, index, .. } = self {
unsafe { pool.free_by_index(*index) };
}
}
}
impl From<Vec<u8>> for FrameBuf {
#[inline]
fn from(v: Vec<u8>) -> Self {
Self::Heap(v)
}
}
impl std::fmt::Debug for FrameBuf {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Pooled { index, len, .. } => f
.debug_struct("FrameBuf::Pooled")
.field("index", index)
.field("len", len)
.finish(),
Self::Heap(v) => f
.debug_struct("FrameBuf::Heap")
.field("len", &v.len())
.finish(),
}
}
}
const _: () = {
const fn assert_send<T: Send>() {}
assert_send::<FrameBuf>();
};
#[cfg(test)]
mod tests {
use super::*;
fn make_pool() -> Arc<PacketPool> {
Arc::new(PacketPool::new(16).unwrap())
}
#[test]
fn test_from_pool() {
let pool = make_pool();
let data = [0xAB; 100];
let buf = FrameBuf::from_pool(&pool, &data);
assert!(buf.is_pooled());
assert_eq!(buf.len(), 100);
assert_eq!(&buf[..], &data[..]);
assert_eq!(pool.free_count(), 15);
drop(buf);
assert_eq!(pool.free_count(), 16);
}
#[test]
fn test_from_vec() {
let buf = FrameBuf::from(vec![1, 2, 3]);
assert!(!buf.is_pooled());
assert_eq!(buf.len(), 3);
assert_eq!(&buf[..], &[1, 2, 3]);
}
#[test]
fn test_pool_exhaustion_fallback() {
let pool = Arc::new(PacketPool::new(1).unwrap());
let _held = FrameBuf::from_pool(&pool, &[0; 10]);
assert_eq!(pool.free_count(), 0);
let fallback = FrameBuf::from_pool(&pool, &[0xFF; 20]);
assert!(!fallback.is_pooled());
assert_eq!(fallback.len(), 20);
}
#[test]
fn test_deref_as_slice() {
let pool = make_pool();
let buf = FrameBuf::from_pool(&pool, &[10, 20, 30]);
let slice: &[u8] = &buf;
assert_eq!(slice, &[10, 20, 30]);
}
#[test]
fn test_as_ref() {
let buf = FrameBuf::from(vec![4, 5, 6]);
let r: &[u8] = buf.as_ref();
assert_eq!(r, &[4, 5, 6]);
}
#[test]
fn test_empty() {
let buf = FrameBuf::from(Vec::new());
assert!(buf.is_empty());
assert_eq!(buf.len(), 0);
}
#[test]
fn test_send() {
fn assert_send<T: Send>() {}
assert_send::<FrameBuf>();
}
#[test]
fn test_concurrent_alloc_drop() {
let pool = Arc::new(PacketPool::new(64).unwrap());
let handles: Vec<_> = (0..4)
.map(|_| {
let pool = Arc::clone(&pool);
std::thread::spawn(move || {
for i in 0..500 {
let data = vec![(i % 256) as u8; 100];
let buf = FrameBuf::from_pool(&pool, &data);
assert_eq!(buf.len(), 100);
assert_eq!(buf[0], (i % 256) as u8);
}
})
})
.collect();
for h in handles {
h.join().unwrap();
}
assert_eq!(pool.free_count(), 64);
}
}