#![allow(dead_code)]
#[derive(Debug, Clone)]
pub struct IoVec {
pub data: Vec<u8>,
pub offset: u64,
}
impl IoVec {
#[must_use]
pub fn new(data: Vec<u8>, offset: u64) -> Self {
Self { data, offset }
}
#[must_use]
pub fn len(&self) -> usize {
self.data.len()
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.data.is_empty()
}
}
#[derive(Debug, Default)]
pub struct ScatterGatherList {
pub iovec: Vec<IoVec>,
pub total_bytes: u64,
}
impl ScatterGatherList {
#[must_use]
pub fn new() -> Self {
Self::default()
}
pub fn add(&mut self, data: Vec<u8>, offset: u64) {
self.total_bytes += data.len() as u64;
self.iovec.push(IoVec::new(data, offset));
}
#[must_use]
pub fn total_len(&self) -> u64 {
self.total_bytes
}
#[must_use]
#[allow(clippy::cast_possible_truncation)]
pub fn consolidate(&self) -> Vec<u8> {
let mut out = Vec::with_capacity(self.total_bytes as usize);
for v in &self.iovec {
out.extend_from_slice(&v.data);
}
out
}
}
#[derive(Debug, Default)]
pub struct ReadVec {
pub buffers: Vec<Vec<u8>>,
}
impl ReadVec {
#[must_use]
pub fn new(buffers: Vec<Vec<u8>>) -> Self {
Self { buffers }
}
pub fn total_capacity(&self) -> usize {
self.buffers.iter().map(Vec::len).sum()
}
pub fn fill_from(&mut self, src: &[u8]) -> usize {
let mut pos = 0usize;
for buf in &mut self.buffers {
if pos >= src.len() {
break;
}
let space = buf.len();
let available = src.len() - pos;
let to_copy = space.min(available);
buf[..to_copy].copy_from_slice(&src[pos..pos + to_copy]);
pos += to_copy;
}
pos
}
}
#[derive(Debug, Default)]
pub struct WriteVec {
pub buffers: Vec<IoVec>,
}
impl WriteVec {
#[must_use]
pub fn new() -> Self {
Self::default()
}
pub fn push(&mut self, iov: IoVec) {
self.buffers.push(iov);
}
#[must_use]
pub fn coalesce_sequential(&self) -> Vec<IoVec> {
let mut result: Vec<IoVec> = Vec::new();
for iov in &self.buffers {
if let Some(last) = result.last_mut() {
let last_end = last.offset + last.data.len() as u64;
if last_end == iov.offset {
last.data.extend_from_slice(&iov.data);
continue;
}
}
result.push(iov.clone());
}
result
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_iovec_len() {
let v = IoVec::new(vec![1, 2, 3], 0);
assert_eq!(v.len(), 3);
}
#[test]
fn test_iovec_is_empty_true() {
let v = IoVec::new(vec![], 0);
assert!(v.is_empty());
}
#[test]
fn test_iovec_is_empty_false() {
let v = IoVec::new(vec![0], 0);
assert!(!v.is_empty());
}
#[test]
fn test_sgl_add_updates_total() {
let mut sgl = ScatterGatherList::new();
sgl.add(vec![1, 2, 3], 0);
sgl.add(vec![4, 5], 3);
assert_eq!(sgl.total_len(), 5);
}
#[test]
fn test_sgl_consolidate_order() {
let mut sgl = ScatterGatherList::new();
sgl.add(vec![1, 2], 0);
sgl.add(vec![3, 4], 2);
assert_eq!(sgl.consolidate(), vec![1u8, 2, 3, 4]);
}
#[test]
fn test_sgl_consolidate_empty() {
let sgl = ScatterGatherList::new();
assert!(sgl.consolidate().is_empty());
}
#[test]
fn test_sgl_total_len_empty() {
let sgl = ScatterGatherList::new();
assert_eq!(sgl.total_len(), 0);
}
#[test]
fn test_readvec_total_capacity() {
let rv = ReadVec::new(vec![vec![0u8; 10], vec![0u8; 20]]);
assert_eq!(rv.total_capacity(), 30);
}
#[test]
fn test_readvec_fill_from_fits() {
let mut rv = ReadVec::new(vec![vec![0u8; 4], vec![0u8; 4]]);
let copied = rv.fill_from(&[1, 2, 3, 4, 5, 6, 7, 8]);
assert_eq!(copied, 8);
assert_eq!(rv.buffers[0], vec![1, 2, 3, 4]);
assert_eq!(rv.buffers[1], vec![5, 6, 7, 8]);
}
#[test]
fn test_readvec_fill_from_partial() {
let mut rv = ReadVec::new(vec![vec![0u8; 8]]);
let copied = rv.fill_from(&[1, 2, 3]);
assert_eq!(copied, 3);
assert_eq!(&rv.buffers[0][..3], &[1u8, 2, 3]);
}
#[test]
fn test_readvec_fill_from_empty_src() {
let mut rv = ReadVec::new(vec![vec![0u8; 4]]);
let copied = rv.fill_from(&[]);
assert_eq!(copied, 0);
}
#[test]
fn test_writevec_coalesce_adjacent() {
let mut wv = WriteVec::new();
wv.push(IoVec::new(vec![1, 2], 0));
wv.push(IoVec::new(vec![3, 4], 2));
let merged = wv.coalesce_sequential();
assert_eq!(merged.len(), 1);
assert_eq!(merged[0].data, vec![1u8, 2, 3, 4]);
assert_eq!(merged[0].offset, 0);
}
#[test]
fn test_writevec_coalesce_non_adjacent() {
let mut wv = WriteVec::new();
wv.push(IoVec::new(vec![1, 2], 0));
wv.push(IoVec::new(vec![3, 4], 10)); let merged = wv.coalesce_sequential();
assert_eq!(merged.len(), 2);
}
#[test]
fn test_writevec_coalesce_empty() {
let wv = WriteVec::new();
assert!(wv.coalesce_sequential().is_empty());
}
#[test]
fn test_writevec_coalesce_three_adjacent() {
let mut wv = WriteVec::new();
wv.push(IoVec::new(vec![1], 0));
wv.push(IoVec::new(vec![2], 1));
wv.push(IoVec::new(vec![3], 2));
let merged = wv.coalesce_sequential();
assert_eq!(merged.len(), 1);
assert_eq!(merged[0].data, vec![1u8, 2, 3]);
}
}