use crate::error::{DaosError, Result};
use daos::{
d_iov_t, d_sg_list_t, daos_iod_t, daos_iod_type_t_DAOS_IOD_ARRAY,
daos_iod_type_t_DAOS_IOD_SINGLE, daos_key_t, daos_recx_t,
};
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct DKey(Vec<u8>);
impl DKey {
pub fn new(bytes: impl Into<Vec<u8>>) -> Result<Self> {
let bytes = bytes.into();
if bytes.is_empty() {
return Err(DaosError::InvalidArg);
}
Ok(Self(bytes))
}
#[inline]
pub fn as_bytes(&self) -> &[u8] {
&self.0
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct AKey(Vec<u8>);
impl AKey {
pub fn new(bytes: impl Into<Vec<u8>>) -> Result<Self> {
let bytes = bytes.into();
if bytes.is_empty() {
return Err(DaosError::InvalidArg);
}
Ok(Self(bytes))
}
#[inline]
pub fn as_bytes(&self) -> &[u8] {
&self.0
}
}
#[derive(Clone)]
pub struct IoBuffer<'a> {
bytes: IoBufferBytes<'a>,
}
#[derive(Debug)]
enum IoBufferBytes<'a> {
Owned(Vec<u8>),
Borrowed(&'a [u8]),
BorrowedMut(&'a mut [u8]),
}
impl std::fmt::Debug for IoBuffer<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("IoBuffer")
.field("bytes", &self.as_slice())
.finish()
}
}
impl PartialEq for IoBuffer<'_> {
#[inline]
fn eq(&self, other: &Self) -> bool {
self.as_slice() == other.as_slice()
}
}
impl Eq for IoBuffer<'_> {}
impl<'a> Clone for IoBufferBytes<'a> {
fn clone(&self) -> Self {
match self {
Self::Owned(bytes) => Self::Owned(bytes.clone()),
Self::Borrowed(bytes) => Self::Borrowed(bytes),
Self::BorrowedMut(bytes) => Self::Owned(bytes.to_vec()),
}
}
}
impl<'a> IoBuffer<'a> {
#[inline]
pub fn from_vec(bytes: Vec<u8>) -> Self {
Self {
bytes: IoBufferBytes::Owned(bytes),
}
}
#[inline]
pub fn from_slice(bytes: &'a [u8]) -> Self {
Self {
bytes: IoBufferBytes::Borrowed(bytes),
}
}
#[inline]
pub fn from_mut_slice(bytes: &'a mut [u8]) -> Self {
Self {
bytes: IoBufferBytes::BorrowedMut(bytes),
}
}
#[inline]
pub fn as_slice(&self) -> &[u8] {
match &self.bytes {
IoBufferBytes::Owned(bytes) => bytes.as_slice(),
IoBufferBytes::Borrowed(bytes) => bytes,
IoBufferBytes::BorrowedMut(bytes) => bytes,
}
}
#[inline]
pub fn as_mut_slice(&mut self) -> &mut [u8] {
if let IoBufferBytes::Borrowed(bytes) = &self.bytes {
self.bytes = IoBufferBytes::Owned(bytes.to_vec());
}
match &mut self.bytes {
IoBufferBytes::Owned(bytes) => bytes.as_mut_slice(),
IoBufferBytes::BorrowedMut(bytes) => bytes,
IoBufferBytes::Borrowed(_) => unreachable!("borrowed bytes converted to owned"),
}
}
#[inline]
fn as_mut_slice_if_writable(&mut self) -> Option<&mut [u8]> {
match &mut self.bytes {
IoBufferBytes::Owned(bytes) => Some(bytes.as_mut_slice()),
IoBufferBytes::BorrowedMut(bytes) => Some(bytes),
IoBufferBytes::Borrowed(_) => None,
}
}
#[inline]
pub fn len(&self) -> usize {
self.as_slice().len()
}
#[inline]
pub fn is_empty(&self) -> bool {
self.as_slice().is_empty()
}
}
impl<'a> From<Vec<u8>> for IoBuffer<'a> {
#[inline]
fn from(bytes: Vec<u8>) -> Self {
Self::from_vec(bytes)
}
}
impl<'a> From<&'a [u8]> for IoBuffer<'a> {
#[inline]
fn from(bytes: &'a [u8]) -> Self {
Self::from_slice(bytes)
}
}
impl<'a, const N: usize> From<&'a [u8; N]> for IoBuffer<'a> {
#[inline]
fn from(bytes: &'a [u8; N]) -> Self {
Self::from_slice(bytes)
}
}
impl<'a> From<&'a mut [u8]> for IoBuffer<'a> {
#[inline]
fn from(bytes: &'a mut [u8]) -> Self {
Self::from_mut_slice(bytes)
}
}
impl<'a, const N: usize> From<&'a mut [u8; N]> for IoBuffer<'a> {
#[inline]
fn from(bytes: &'a mut [u8; N]) -> Self {
Self::from_mut_slice(bytes)
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Sgl<'a> {
buffers: Vec<IoBuffer<'a>>,
}
impl<'a> Sgl<'a> {
#[inline]
pub fn builder() -> SglBuilder<'a> {
SglBuilder::new()
}
#[inline]
pub fn buffers(&self) -> &[IoBuffer<'a>] {
&self.buffers
}
#[inline]
pub fn total_len(&self) -> usize {
self.buffers.iter().map(IoBuffer::len).sum()
}
#[inline]
pub fn is_empty(&self) -> bool {
self.buffers.is_empty()
}
pub fn to_raw(&self) -> Result<RawSgl> {
if self.buffers.is_empty() {
return Err(DaosError::InvalidArg);
}
let mut iovs = Vec::with_capacity(self.buffers.len());
for buffer in &self.buffers {
iovs.push(d_iov_t {
iov_buf: buffer.as_slice().as_ptr() as *mut std::ffi::c_void,
iov_buf_len: buffer.len(),
iov_len: buffer.len(),
});
}
let mut sgl = d_sg_list_t {
sg_nr: iovs.len() as u32,
sg_nr_out: iovs.len() as u32,
sg_iovs: std::ptr::null_mut(),
};
sgl.sg_iovs = iovs.as_mut_ptr();
Ok(RawSgl { iovs, sgl })
}
pub fn to_raw_mut(&mut self) -> Result<RawSgl> {
if self.buffers.is_empty() {
return Err(DaosError::InvalidArg);
}
let mut iovs = Vec::with_capacity(self.buffers.len());
for buffer in &mut self.buffers {
let bytes = buffer
.as_mut_slice_if_writable()
.ok_or(DaosError::InvalidArg)?;
iovs.push(d_iov_t {
iov_buf: bytes.as_mut_ptr() as *mut std::ffi::c_void,
iov_buf_len: bytes.len(),
iov_len: bytes.len(),
});
}
let mut sgl = d_sg_list_t {
sg_nr: iovs.len() as u32,
sg_nr_out: iovs.len() as u32,
sg_iovs: std::ptr::null_mut(),
};
sgl.sg_iovs = iovs.as_mut_ptr();
Ok(RawSgl { iovs, sgl })
}
}
#[derive(Debug, Default)]
pub struct SglBuilder<'a> {
buffers: Vec<IoBuffer<'a>>,
}
impl<'a> SglBuilder<'a> {
#[inline]
pub fn new() -> Self {
Self::default()
}
#[inline]
pub fn push(mut self, buffer: impl Into<IoBuffer<'a>>) -> Self {
self.buffers.push(buffer.into());
self
}
#[inline]
pub fn build(self) -> Result<Sgl<'a>> {
if self.buffers.is_empty() {
return Err(DaosError::InvalidArg);
}
Ok(Sgl {
buffers: self.buffers,
})
}
}
pub struct RawSgl {
pub iovs: Vec<d_iov_t>,
pub sgl: d_sg_list_t,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Recx {
pub idx: u64,
pub nr: u64,
}
impl Recx {
#[inline]
pub fn new(idx: u64, nr: u64) -> Result<Self> {
if nr == 0 {
return Err(DaosError::InvalidArg);
}
Ok(Self { idx, nr })
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct IodSingle {
pub akey: AKey,
pub value_len: usize,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct IodArray {
pub akey: AKey,
pub record_len: usize,
pub recxs: Vec<Recx>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Iod {
Single(IodSingle),
Array(IodArray),
}
#[derive(Debug)]
pub struct IodSingleBuilder {
akey: AKey,
value_len: Option<usize>,
}
impl IodSingleBuilder {
pub fn new(akey: AKey) -> Self {
Self {
akey,
value_len: None,
}
}
pub fn value_len(mut self, value_len: usize) -> Self {
self.value_len = Some(value_len);
self
}
pub fn build(self) -> Result<IodSingle> {
let value_len = self.value_len.ok_or(DaosError::InvalidArg)?;
if value_len == 0 {
return Err(DaosError::InvalidArg);
}
Ok(IodSingle {
akey: self.akey,
value_len,
})
}
}
#[derive(Debug)]
pub struct IodArrayBuilder {
akey: AKey,
record_len: Option<usize>,
recxs: Vec<Recx>,
}
impl IodArrayBuilder {
pub fn new(akey: AKey) -> Self {
Self {
akey,
record_len: None,
recxs: Vec::new(),
}
}
pub fn record_len(mut self, record_len: usize) -> Self {
self.record_len = Some(record_len);
self
}
pub fn add_recx(mut self, recx: Recx) -> Self {
self.recxs.push(recx);
self
}
pub fn build(self) -> Result<IodArray> {
let record_len = self.record_len.ok_or(DaosError::InvalidArg)?;
if record_len == 0 || self.recxs.is_empty() {
return Err(DaosError::InvalidArg);
}
let mut total_records: u64 = 0;
for recx in &self.recxs {
total_records = total_records
.checked_add(recx.nr)
.ok_or(DaosError::InvalidArg)?;
}
let _total_bytes = (record_len as u128)
.checked_mul(total_records as u128)
.ok_or(DaosError::InvalidArg)?;
Ok(IodArray {
akey: self.akey,
record_len,
recxs: self.recxs,
})
}
}
pub struct RawIod {
pub akey_buf: Vec<u8>,
pub recxs: Vec<daos_recx_t>,
pub iod: daos_iod_t,
}
impl Iod {
pub fn to_raw(&self) -> Result<RawIod> {
match self {
Iod::Single(single) => {
if single.value_len == 0 {
return Err(DaosError::InvalidArg);
}
let mut akey_buf = single.akey.as_bytes().to_vec();
let key = daos_key_t {
iov_buf: akey_buf.as_mut_ptr() as *mut std::ffi::c_void,
iov_buf_len: akey_buf.len(),
iov_len: akey_buf.len(),
};
Ok(RawIod {
akey_buf,
recxs: Vec::new(),
iod: daos_iod_t {
iod_name: key,
iod_type: daos_iod_type_t_DAOS_IOD_SINGLE,
iod_size: single.value_len as u64,
iod_flags: 0,
iod_nr: 1,
iod_recxs: std::ptr::null_mut(),
},
})
}
Iod::Array(array) => {
if array.record_len == 0 || array.recxs.is_empty() {
return Err(DaosError::InvalidArg);
}
let mut akey_buf = array.akey.as_bytes().to_vec();
let key = daos_key_t {
iov_buf: akey_buf.as_mut_ptr() as *mut std::ffi::c_void,
iov_buf_len: akey_buf.len(),
iov_len: akey_buf.len(),
};
let mut recxs: Vec<daos_recx_t> = array
.recxs
.iter()
.map(|r| daos_recx_t {
rx_idx: r.idx,
rx_nr: r.nr,
})
.collect();
Ok(RawIod {
akey_buf,
iod: daos_iod_t {
iod_name: key,
iod_type: daos_iod_type_t_DAOS_IOD_ARRAY,
iod_size: array.record_len as u64,
iod_flags: 0,
iod_nr: recxs.len() as u32,
iod_recxs: recxs.as_mut_ptr(),
},
recxs,
})
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_keys_reject_empty() {
assert!(DKey::new(Vec::<u8>::new()).is_err());
assert!(AKey::new(Vec::<u8>::new()).is_err());
}
#[test]
fn test_keys_accept_non_empty() {
let dkey = DKey::new(b"dkey".to_vec()).unwrap();
let akey = AKey::new(b"akey".to_vec()).unwrap();
assert_eq!(dkey.as_bytes(), b"dkey");
assert_eq!(akey.as_bytes(), b"akey");
}
#[test]
fn test_iobuffer_from_slice_borrows_without_copy() {
let bytes = [1u8, 2, 3, 4];
let buffer = IoBuffer::from_slice(&bytes);
assert_eq!(buffer.as_slice(), &bytes);
assert_eq!(buffer.as_slice().as_ptr(), bytes.as_ptr());
}
#[test]
fn test_iobuffer_as_mut_slice_materializes_owned_copy() {
let bytes = [1u8, 2, 3];
let mut buffer = IoBuffer::from_slice(&bytes);
buffer.as_mut_slice()[0] = 9;
assert_eq!(buffer.as_slice(), &[9, 2, 3]);
assert_eq!(bytes, [1, 2, 3]);
}
#[test]
fn test_iobuffer_from_mut_slice_writes_through_without_copy() {
let mut bytes = [1u8, 2, 3, 4];
let ptr = bytes.as_ptr();
let mut buffer = IoBuffer::from_mut_slice(&mut bytes);
assert_eq!(buffer.as_slice().as_ptr(), ptr);
buffer.as_mut_slice()[1] = 9;
drop(buffer);
assert_eq!(bytes, [1, 9, 3, 4]);
}
#[test]
fn test_sgl_builder_rejects_empty() {
assert!(Sgl::builder().build().is_err());
}
#[test]
fn test_sgl_builder_accepts_buffers() {
let sgl = Sgl::builder()
.push(IoBuffer::from_vec(vec![1, 2, 3]))
.push(IoBuffer::from_vec(vec![4, 5]))
.build()
.unwrap();
assert_eq!(sgl.buffers().len(), 2);
assert_eq!(sgl.total_len(), 5);
}
#[test]
fn test_sgl_to_raw() {
let sgl = Sgl::builder()
.push(IoBuffer::from_vec(vec![1, 2, 3]))
.push(IoBuffer::from_vec(vec![4]))
.build()
.unwrap();
let raw = sgl.to_raw().unwrap();
assert_eq!(raw.sgl.sg_nr, 2);
assert_eq!(raw.sgl.sg_nr_out, 2);
assert!(!raw.sgl.sg_iovs.is_null());
assert_eq!(raw.iovs.len(), 2);
}
#[test]
fn test_sgl_to_raw_mut_accepts_owned_buffers() {
let mut sgl = Sgl::builder()
.push(IoBuffer::from_vec(vec![1, 2, 3]))
.push(IoBuffer::from_vec(vec![4]))
.build()
.unwrap();
let raw = sgl.to_raw_mut().unwrap();
assert_eq!(raw.sgl.sg_nr, 2);
assert_eq!(raw.sgl.sg_nr_out, 2);
assert!(!raw.sgl.sg_iovs.is_null());
assert_eq!(raw.iovs.len(), 2);
}
#[test]
fn test_sgl_to_raw_mut_accepts_mut_borrowed_buffers() {
let mut bytes = [1u8, 2, 3];
let mut sgl = Sgl::builder()
.push(IoBuffer::from_mut_slice(&mut bytes))
.build()
.unwrap();
let raw = sgl.to_raw_mut().unwrap();
assert_eq!(raw.sgl.sg_nr, 1);
assert_eq!(raw.sgl.sg_nr_out, 1);
assert!(!raw.sgl.sg_iovs.is_null());
assert_eq!(raw.iovs.len(), 1);
}
#[test]
fn test_sgl_to_raw_mut_rejects_readonly_borrowed_buffers() {
let bytes = [1u8, 2, 3];
let mut sgl = Sgl::builder()
.push(IoBuffer::from_slice(&bytes))
.build()
.unwrap();
assert!(matches!(sgl.to_raw_mut(), Err(DaosError::InvalidArg)));
}
#[test]
fn test_recx_validation() {
assert!(Recx::new(0, 0).is_err());
let recx = Recx::new(42, 7).unwrap();
assert_eq!(recx.idx, 42);
assert_eq!(recx.nr, 7);
}
#[test]
fn test_iod_single_builder_validation() {
let akey = AKey::new(b"a".to_vec()).unwrap();
assert!(IodSingleBuilder::new(akey.clone()).build().is_err());
assert!(IodSingleBuilder::new(akey).value_len(0).build().is_err());
}
#[test]
fn test_iod_single_builder_success() {
let akey = AKey::new(b"a".to_vec()).unwrap();
let single = IodSingleBuilder::new(akey).value_len(16).build().unwrap();
assert_eq!(single.value_len, 16);
}
#[test]
fn test_iod_array_builder_validation() {
let akey = AKey::new(b"a".to_vec()).unwrap();
assert!(IodArrayBuilder::new(akey.clone()).build().is_err());
assert!(
IodArrayBuilder::new(akey.clone())
.record_len(0)
.build()
.is_err()
);
let recx = Recx::new(0, 1).unwrap();
let ok = IodArrayBuilder::new(akey)
.record_len(8)
.add_recx(recx)
.build()
.unwrap();
assert_eq!(ok.recxs.len(), 1);
}
#[test]
fn test_iod_to_raw_single() {
let akey = AKey::new(b"akey".to_vec()).unwrap();
let single = Iod::Single(IodSingleBuilder::new(akey).value_len(32).build().unwrap());
let raw = single.to_raw().unwrap();
assert_eq!(raw.iod.iod_type, daos_iod_type_t_DAOS_IOD_SINGLE);
assert_eq!(raw.iod.iod_size, 32);
assert_eq!(raw.iod.iod_nr, 1);
assert!(raw.iod.iod_recxs.is_null());
}
#[test]
fn test_iod_to_raw_array() {
let akey = AKey::new(b"akey".to_vec()).unwrap();
let recx1 = Recx::new(0, 2).unwrap();
let recx2 = Recx::new(10, 3).unwrap();
let array = Iod::Array(
IodArrayBuilder::new(akey)
.record_len(8)
.add_recx(recx1)
.add_recx(recx2)
.build()
.unwrap(),
);
let raw = array.to_raw().unwrap();
assert_eq!(raw.iod.iod_type, daos_iod_type_t_DAOS_IOD_ARRAY);
assert_eq!(raw.iod.iod_size, 8);
assert_eq!(raw.iod.iod_nr, 2);
assert!(!raw.iod.iod_recxs.is_null());
assert_eq!(raw.recxs.len(), 2);
assert_eq!(raw.recxs[0].rx_idx, 0);
assert_eq!(raw.recxs[0].rx_nr, 2);
}
}