use crate::{ByteString, Bytes, storage::Storage};
#[derive(Debug)]
pub struct StorageVTable {
pub(crate) as_ptr: unsafe fn(*const u8, usize) -> *const u8,
pub(crate) len: unsafe fn(*const u8, usize) -> usize,
pub(crate) clone: unsafe fn(*const u8, usize) -> Option<(*const u8, usize)>,
pub(crate) drop: unsafe fn(*const u8, usize),
}
impl StorageVTable {
pub const fn new(
as_ptr: unsafe fn(*const u8, usize) -> *const u8,
len: unsafe fn(*const u8, usize) -> usize,
clone: unsafe fn(*const u8, usize) -> Option<(*const u8, usize)>,
drop: unsafe fn(*const u8, usize),
) -> StorageVTable {
StorageVTable {
as_ptr,
len,
clone,
drop,
}
}
}
pub unsafe trait StorageExt: Send + Sync {
fn create(self) -> (*const u8, usize, &'static StorageVTable);
}
pub unsafe trait StorageExtStr: StorageExt + Sized {
fn create(self) -> (*const u8, usize, &'static StorageVTable) {
StorageExt::create(self)
}
}
impl Bytes {
pub fn from_ext<T: StorageExt>(val: T) -> Bytes {
let (addr, len, vtable) = val.create();
Bytes {
storage: Storage::from_stext(addr, len, vtable),
}
}
}
impl ByteString {
pub fn from_ext<T: StorageExtStr>(val: T) -> ByteString {
let (addr, len, vtable) = StorageExtStr::create(val);
unsafe {
ByteString::from_bytes_unchecked(Bytes {
storage: Storage::from_stext(addr, len, vtable),
})
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::info::Kind;
fn as_ptr(addr: *const u8, _: usize) -> *const u8 {
addr
}
fn len(_: *const u8, len: usize) -> usize {
len
}
fn clone(_: *const u8, _: usize) -> Option<(*const u8, usize)> {
None
}
fn drop(addr: *const u8, len: usize) {
let ptr = std::ptr::slice_from_raw_parts_mut(addr.cast_mut(), len);
std::mem::drop(unsafe { Box::from_raw(ptr) });
}
struct Boxed(Box<[u8]>);
unsafe impl StorageExt for Boxed {
fn create(self) -> (*const u8, usize, &'static StorageVTable) {
static VTABLE: StorageVTable = StorageVTable::new(as_ptr, len, clone, drop);
let len = self.0.len();
(Box::into_raw(self.0).cast::<u8>(), len, &VTABLE)
}
}
#[test]
fn clone_copies_data() {
let data = vec![7u8; 100].into_boxed_slice();
let b = Bytes::from_ext(Boxed(data));
assert_eq!(b.info().kind, Kind::StExt);
let b2 = b.clone();
assert_eq!(b2.info().kind, Kind::Vec);
assert_eq!(b, b2);
let vtable = StorageVTable::new(as_ptr, len, clone, drop);
assert!(format!("{vtable:?}").contains("StorageVTable"));
}
}