use crate::error::{DaosError, Result};
use std::mem::MaybeUninit;
use std::ptr::NonNull;
pub const MAX_BUFFER_SIZE: usize = 256 * 1024 * 1024;
#[derive(Debug)]
pub struct Buffer<T> {
ptr: NonNull<T>,
len: usize,
}
impl<T> Buffer<T> {
#[inline]
pub unsafe fn new(ptr: NonNull<T>, len: usize) -> Result<Self> {
if len > MAX_BUFFER_SIZE {
return Err(DaosError::InvalidArg);
}
Ok(Self { ptr, len })
}
#[inline]
pub fn from_slice(slice: &[T]) -> Result<Self>
where
T: Clone,
{
if slice.len() > MAX_BUFFER_SIZE {
return Err(DaosError::InvalidArg);
}
let mut buffer = Vec::with_capacity(slice.len());
buffer.extend_from_slice(slice);
let ptr = NonNull::from(&buffer[0]);
let len = slice.len();
std::mem::forget(buffer);
unsafe { Self::new(ptr, len) }
}
#[inline]
pub fn len(&self) -> usize {
self.len
}
#[inline]
pub fn is_empty(&self) -> bool {
self.len == 0
}
#[inline]
pub fn as_ptr(&self) -> *const T {
self.ptr.as_ptr()
}
#[inline]
pub fn as_mut_ptr(&self) -> *mut T {
self.ptr.as_ptr()
}
#[inline]
pub unsafe fn as_slice(&self) -> &[T] {
unsafe { std::slice::from_raw_parts(self.ptr.as_ptr(), self.len) }
}
#[inline]
pub unsafe fn as_mut_slice(&mut self) -> &mut [T] {
unsafe { std::slice::from_raw_parts_mut(self.ptr.as_ptr(), self.len) }
}
}
#[inline]
pub fn validate_buffer_size(size: usize) -> Result<()> {
if size > MAX_BUFFER_SIZE {
Err(DaosError::InvalidArg)
} else {
Ok(())
}
}
#[inline]
pub fn validate_buffer_size_nonzero(size: usize) -> Result<()> {
if size == 0 {
Err(DaosError::InvalidArg)
} else {
Ok(())
}
}
#[inline]
pub fn validate_ffi_buffer<T>(ptr: *const T, size: usize) -> Result<Buffer<T>> {
if ptr.is_null() {
return Err(DaosError::InvalidArg);
}
if size > MAX_BUFFER_SIZE {
return Err(DaosError::InvalidArg);
}
unsafe { Buffer::new(NonNull::new_unchecked(ptr as *mut T), size) }
}
pub struct OutputBuffer<'a> {
buffer: &'a mut [MaybeUninit<u8>],
filled: usize,
}
impl<'a> OutputBuffer<'a> {
#[inline]
pub fn new(buffer: &'a mut [MaybeUninit<u8>]) -> Self {
Self { buffer, filled: 0 }
}
#[inline]
pub fn capacity(&self) -> usize {
self.buffer.len()
}
#[inline]
pub fn filled(&self) -> usize {
self.filled
}
#[inline]
pub fn as_ptr(&mut self) -> *mut std::ffi::c_void {
self.buffer.as_mut_ptr() as *mut std::ffi::c_void
}
#[inline]
pub fn as_bytes(&mut self) -> &mut [MaybeUninit<u8>] {
self.buffer
}
#[inline]
pub fn set_filled(&mut self, filled: usize) -> Result<()> {
if filled > self.buffer.len() {
return Err(DaosError::Internal(
"Filled size exceeds buffer capacity".to_string(),
));
}
self.filled = filled;
Ok(())
}
#[inline]
pub fn freeze(self) -> &'a [u8] {
unsafe { std::slice::from_raw_parts(self.buffer.as_mut_ptr() as *const u8, self.filled) }
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_validate_buffer_size_accepts_valid() {
assert!(validate_buffer_size(1024).is_ok());
}
#[test]
fn test_validate_buffer_size_rejects_excessive() {
let result = validate_buffer_size(MAX_BUFFER_SIZE + 1);
assert!(result.is_err());
assert!(matches!(result.unwrap_err(), DaosError::InvalidArg));
}
#[test]
fn test_validate_buffer_size_nonzero_rejects_zero() {
assert!(validate_buffer_size_nonzero(0).is_err());
}
#[test]
fn test_validate_buffer_size_nonzero_accepts_nonzero() {
assert!(validate_buffer_size_nonzero(1).is_ok());
}
#[test]
fn test_validate_ffi_buffer_rejects_null() {
let null_ptr: *const u8 = std::ptr::null();
let result = validate_ffi_buffer(null_ptr, 100);
assert!(result.is_err());
}
#[test]
fn test_validate_ffi_buffer_rejects_excessive_size() {
let data = [0u8; 64];
let result = validate_ffi_buffer(data.as_ptr(), MAX_BUFFER_SIZE + 1);
assert!(result.is_err());
}
#[test]
fn test_output_buffer_filled_tracking() {
let mut buffer = vec![MaybeUninit::new(0u8); 100].into_boxed_slice();
let output = OutputBuffer::new(&mut buffer);
assert_eq!(output.capacity(), 100);
assert_eq!(output.filled(), 0);
}
#[test]
fn test_output_buffer_set_filled() {
let mut buffer = vec![MaybeUninit::new(0u8); 100].into_boxed_slice();
let mut output = OutputBuffer::new(&mut buffer);
output.set_filled(50).unwrap();
assert_eq!(output.filled(), 50);
}
#[test]
fn test_output_buffer_set_filled_rejects_overflow() {
let mut buffer = vec![MaybeUninit::new(0u8); 100].into_boxed_slice();
let mut output = OutputBuffer::new(&mut buffer);
let result = output.set_filled(101);
assert!(result.is_err());
}
}