Skip to main content

ntex_bytes/
stext.rs

1use crate::{ByteString, Bytes, storage::Storage};
2
3/// Functions that give [`Bytes`] access to externally owned data.
4///
5/// Every function receives the `(addr, len)` pair returned by
6/// [`StorageExt::create`], or by a previous `clone`.
7#[derive(Debug)]
8pub struct StorageVTable {
9    pub(crate) as_ptr: unsafe fn(*const u8, usize) -> *const u8,
10    pub(crate) len: unsafe fn(*const u8, usize) -> usize,
11    pub(crate) clone: unsafe fn(*const u8, usize) -> Option<(*const u8, usize)>,
12    pub(crate) drop: unsafe fn(*const u8, usize),
13}
14
15impl StorageVTable {
16    /// Creates a vtable from its functions.
17    ///
18    /// - `as_ptr` returns a pointer to the start of the data.
19    /// - `len` returns the length of the data in bytes.
20    /// - `clone` returns the `(addr, len)` pair of a new handle to the same
21    ///   data, or `None` to make the clone copy the data instead.
22    /// - `drop` releases the handle.
23    ///
24    /// The requirements these functions must meet are listed on
25    /// [`StorageExt`].
26    pub const fn new(
27        as_ptr: unsafe fn(*const u8, usize) -> *const u8,
28        len: unsafe fn(*const u8, usize) -> usize,
29        clone: unsafe fn(*const u8, usize) -> Option<(*const u8, usize)>,
30        drop: unsafe fn(*const u8, usize),
31    ) -> StorageVTable {
32        StorageVTable {
33            as_ptr,
34            len,
35            clone,
36            drop,
37        }
38    }
39}
40
41/// Types that can be used as external storage for [`Bytes`], see
42/// [`Bytes::from_ext`].
43///
44/// # Safety
45///
46/// `Bytes` trusts the values returned by `create` without checking them:
47///
48/// - For the returned `(addr, len)` pair, and for every pair returned by the
49///   vtable's `clone`, `as_ptr` and `len` must describe memory that is
50///   readable for `len` bytes and stays valid and unchanged until `drop` is
51///   called for that pair.
52/// - The vtable functions can be called from any thread.
53///
54/// `Bytes` calls `drop` exactly once for each pair and does not use the pair
55/// afterwards.
56pub unsafe trait StorageExt: Send + Sync {
57    /// Converts the value into an `(addr, len)` pair and the vtable that
58    /// operates on it.
59    fn create(self) -> (*const u8, usize, &'static StorageVTable);
60}
61
62/// External storage that holds valid UTF-8, see [`ByteString::from_ext`].
63///
64/// # Safety
65///
66/// The data exposed through the vtable must be valid UTF-8.
67pub unsafe trait StorageExtStr: StorageExt + Sized {
68    /// Same as [`StorageExt::create`].
69    fn create(self) -> (*const u8, usize, &'static StorageVTable) {
70        StorageExt::create(self)
71    }
72}
73
74impl Bytes {
75    /// Creates a `Bytes` that shares externally owned data without copying.
76    ///
77    /// Cloning and dropping are delegated to the value's vtable.
78    pub fn from_ext<T: StorageExt>(val: T) -> Bytes {
79        let (addr, len, vtable) = val.create();
80
81        Bytes {
82            storage: Storage::from_stext(addr, len, vtable),
83        }
84    }
85}
86
87impl ByteString {
88    /// Creates a `ByteString` that shares externally owned UTF-8 data without
89    /// copying.
90    ///
91    /// Cloning and dropping are delegated to the value's vtable.
92    pub fn from_ext<T: StorageExtStr>(val: T) -> ByteString {
93        let (addr, len, vtable) = StorageExtStr::create(val);
94
95        unsafe {
96            ByteString::from_bytes_unchecked(Bytes {
97                storage: Storage::from_stext(addr, len, vtable),
98            })
99        }
100    }
101}
102
103#[cfg(test)]
104mod tests {
105    use super::*;
106    use crate::info::Kind;
107
108    fn as_ptr(addr: *const u8, _: usize) -> *const u8 {
109        addr
110    }
111
112    fn len(_: *const u8, len: usize) -> usize {
113        len
114    }
115
116    fn clone(_: *const u8, _: usize) -> Option<(*const u8, usize)> {
117        None
118    }
119
120    fn drop(addr: *const u8, len: usize) {
121        let ptr = std::ptr::slice_from_raw_parts_mut(addr.cast_mut(), len);
122        std::mem::drop(unsafe { Box::from_raw(ptr) });
123    }
124
125    struct Boxed(Box<[u8]>);
126
127    // SAFETY: the vtable releases the leaked box exactly once
128    unsafe impl StorageExt for Boxed {
129        fn create(self) -> (*const u8, usize, &'static StorageVTable) {
130            static VTABLE: StorageVTable = StorageVTable::new(as_ptr, len, clone, drop);
131            let len = self.0.len();
132            (Box::into_raw(self.0).cast::<u8>(), len, &VTABLE)
133        }
134    }
135
136    #[test]
137    fn clone_copies_data() {
138        let data = vec![7u8; 100].into_boxed_slice();
139        let b = Bytes::from_ext(Boxed(data));
140        assert_eq!(b.info().kind, Kind::StExt);
141
142        let b2 = b.clone();
143        assert_eq!(b2.info().kind, Kind::Vec);
144        assert_eq!(b, b2);
145
146        let vtable = StorageVTable::new(as_ptr, len, clone, drop);
147        assert!(format!("{vtable:?}").contains("StorageVTable"));
148    }
149}