use std::{mem::size_of, ops::Deref, ptr::copy_nonoverlapping, slice::from_raw_parts};
#[derive(Debug, Clone, Copy)]
pub struct ArgSlice {
pub ptr: *const u8,
pub length: usize,
}
impl ArgSlice {
#[inline]
pub fn new(ptr: *const u8, length: usize) -> Self {
Self { ptr, length }
}
#[inline]
pub fn as_slice<'a>(&self) -> &'a [u8] {
if self.ptr.is_null() || self.length == 0 {
&[]
} else {
unsafe { from_raw_parts(self.ptr, self.length) }
}
}
#[inline]
pub fn total_size(&self) -> usize {
self.length + size_of::<u32>()
}
pub unsafe fn serialize_to(&self, dest: *mut u8) {
unsafe {
let len_u32 = self.length as u32;
copy_nonoverlapping(&len_u32 as *const u32 as *const u8, dest, 4);
if self.length > 0 {
copy_nonoverlapping(self.ptr, dest.add(4), self.length);
}
}
}
pub unsafe fn from_length_prefixed_ptr(src: *const u8) -> Self {
unsafe {
let mut len_u32 = 0u32;
copy_nonoverlapping(src, &mut len_u32 as *mut u32 as *mut u8, 4);
Self {
ptr: src.add(4),
length: len_u32 as usize,
}
}
}
}
unsafe impl Send for ArgSlice {}
unsafe impl Sync for ArgSlice {}
impl Deref for ArgSlice {
type Target = [u8];
#[inline]
fn deref(&self) -> &Self::Target {
self.as_slice()
}
}
impl AsRef<[u8]> for ArgSlice {
#[inline]
fn as_ref(&self) -> &[u8] {
self.as_slice()
}
}
#[cfg(test)]
mod tests {
use std::ptr::null;
use super::*;
#[test]
fn test_arg_slice_roundtrip() {
let payload = b"Hello, Garnet ArgSlice!";
let slice = ArgSlice::new(payload.as_ptr(), payload.len());
assert_eq!(&*slice, payload);
assert_eq!(slice.total_size(), payload.len() + 4);
let mut buf = vec![0u8; slice.total_size()];
unsafe {
slice.serialize_to(buf.as_mut_ptr());
let deserialized = ArgSlice::from_length_prefixed_ptr(buf.as_ptr());
assert_eq!(&*deserialized, payload);
}
}
#[test]
fn test_empty_arg_slice() {
let slice = ArgSlice::new(null(), 0);
assert!(slice.is_empty());
assert_eq!(&*slice, b"");
}
}