rexsgdata 0.12.0

Scatter-Gather Data Descriptors
Documentation
//
// Copyright (c) 2021 RepliXio Ltd. All rights reserved.
// Use is subject to license terms.
//

#![allow(clippy::missing_safety_doc)]

use std::ops::Range;

use libc::{c_void, iovec};
use serde::de::{self, Error};
use serde::{ser::SerializeSeq, Serialize, Serializer};

use crate::utils::{intersection, iovec_as_slice, iovec_as_slice_mut, iovec_as_static_slice};

/// Wrapper for a C-style scatter gather list
#[derive(Clone, Debug, PartialEq)]
pub struct SgList {
    /// Pointer to the `iovec` array
    pub(crate) iovec: *const iovec,
    /// Number of `iovec` elements in the array
    pub(crate) count: usize,
}

impl SgList {
    /// Constructs new `SgList` object from raw arguments
    pub fn new(iovec: *const iovec, count: usize) -> Self {
        Self { iovec, count }
    }

    /// Deconstructs the `SgList` object into original building blocks.
    pub fn into_inner(self) -> (*const iovec, usize) {
        (self.iovec, self.count)
    }

    /// Returns a reference to a `idx`' element of this Scatter Gather List
    pub unsafe fn index_ref_unchecked(&self, idx: isize) -> &iovec {
        &(*self.iovec.offset(idx))
    }

    pub unsafe fn index_unchecked(&self, idx: isize) -> iovec {
        *self.iovec.offset(idx)
    }

    /// Iterates over `&iovec` stored in this `SgList`
    #[allow(clippy::cast_sign_loss, clippy::cast_possible_wrap)]
    pub unsafe fn iter(&self) -> impl Iterator<Item = &iovec> {
        (0..self.count as isize).map(move |idx| self.index_ref_unchecked(idx))
    }

    #[allow(clippy::cast_sign_loss, clippy::cast_possible_wrap)]
    pub unsafe fn into_iter(self) -> impl Iterator<Item = iovec> {
        (0..self.count as isize).map(move |idx| self.index_unchecked(idx))
    }

    /// Iterates over byte slices &[u8]
    pub unsafe fn iter_slices(&self) -> impl Iterator<Item = &[u8]> {
        self.iter().map(|iov| iovec_as_slice(iov))
    }

    pub unsafe fn iter_static_slices(self) -> impl Iterator<Item = &'static [u8]> {
        self.into_iter().map(|iov| iovec_as_static_slice(iov))
    }

    /// Iterates over byte slices &[u8]
    pub unsafe fn iter_slices_mut(&mut self) -> impl Iterator<Item = &mut [u8]> {
        self.iter().map(|iov| iovec_as_slice_mut(iov))
    }

    /// Iterates over tuples of `&iovec` and associated `Range` describing this `iovec` position in
    /// the whole `SgList`
    pub unsafe fn ranges(&self) -> impl Iterator<Item = Range<usize>> + '_ {
        let mut offset = 0;
        self.iter().map(move |iov| {
            let start = offset;
            offset += iov.iov_len;
            start..offset
        })
    }

    pub unsafe fn iter_range(&self) -> impl Iterator<Item = (&iovec, Range<usize>)> {
        self.iter().zip(self.ranges())
    }

    /// Iterates over tuples of `&[u8]` slices and associated `Range` describing this `slice`
    /// position in the whole `SgList`
    pub unsafe fn iter_slices_range(&self) -> impl Iterator<Item = (&[u8], Range<usize>)> {
        self.iter_slices().zip(self.ranges())
    }

    /// Iterates over tuples of `&mut [u8]` slices and associated `Range` describing this `slice`
    /// position in the whole `SgList`.
    pub unsafe fn iter_slices_range_mut(
        &mut self,
    ) -> impl Iterator<Item = (&mut [u8], Range<usize>)> {
        let ranges = self.ranges().collect::<tinyvec::TinyVec<[_; 8]>>();
        self.iter_slices_mut().zip(ranges)
    }

    /// Given a `mask` iterates over `iovec` that a visible after masking
    pub unsafe fn iter_masked(&self, mask: Range<usize>) -> impl Iterator<Item = iovec> + '_ {
        self.iter_range().filter_map(move |(iov, range)| {
            intersection(&range, &mask).map(|overlap| {
                let adjust_start = overlap.start - range.start;
                let adjust_end = range.end - overlap.end;
                adjusted(iov, adjust_start, adjust_end)
            })
        })
    }

    /// Returns number of elements in the underlying object, NOT the total number of bytes.
    pub fn size(&self) -> usize {
        self.count
    }

    /// Does this sg list has any element?
    pub fn is_empty(&self) -> bool {
        self.size() == 0
    }
    /// Calculates the total amount of bytes in this `SgList`
    pub unsafe fn capacity(&self) -> usize {
        self.iter().map(|iov| iov.iov_len).sum()
    }
}

/// Creates a new `iovec` adjusted by `start` at the beginning and by `end` at the end.
fn adjusted(iov: &iovec, start: usize, end: usize) -> iovec {
    let iov_base = (iov.iov_base as usize + start) as *mut c_void;
    let iov_len = iov.iov_len - end;
    iovec { iov_base, iov_len }
}

impl Serialize for SgList {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: Serializer,
    {
        let length = unsafe { self.capacity() };
        let mut seq = serializer.serialize_seq(Some(length))?;
        for slice in unsafe { self.iter_slices() } {
            for byte in slice {
                seq.serialize_element(byte)?
            }
        }
        seq.end()
    }
}

impl<'de> de::Deserialize<'de> for SgList {
    fn deserialize<D>(_deserializer: D) -> Result<Self, D::Error>
    where
        D: de::Deserializer<'de>,
    {
        Err(D::Error::custom("Cannot deserialize SgList"))
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    fn assert_overlap(r1: Range<usize>, r2: Range<usize>, r3: &Option<Range<usize>>) {
        assert_eq!(&intersection(&r1, &r2), r3);
    }

    #[test]
    fn overlapping() {
        assert_overlap(1..3, 4..6, &None);
        assert_overlap(1..4, 4..6, &None);
        assert_overlap(1..5, 4..6, &Some(4..5));
        assert_overlap(1..6, 4..6, &Some(4..6));
        assert_overlap(1..7, 4..6, &Some(4..6));

        assert_overlap(4..5, 4..6, &Some(4..5));
        assert_overlap(4..6, 4..6, &Some(4..6));
        assert_overlap(4..7, 4..6, &Some(4..6));

        assert_overlap(5..6, 4..6, &Some(5..6));
        assert_overlap(5..7, 4..6, &Some(5..6));

        assert_overlap(6..7, 4..6, &None);
    }
}