Skip to main content

ntex_bytes/
stext_arc.rs

1use std::{mem, ptr, sync::Arc};
2
3use crate::{StorageExt, StorageExtStr, StorageVTable};
4
5// ======== Impl for Arc<str> ========
6
7fn as_ptr(addr: *const u8, _: usize) -> *const u8 {
8    addr
9}
10
11fn len(_: *const u8, len: usize) -> usize {
12    len
13}
14
15fn clone_str(addr: *const u8, len: usize) -> Option<(*const u8, usize)> {
16    let slice_ptr: *const [u8] = ptr::slice_from_raw_parts(addr, len);
17    let str_ptr = slice_ptr as *const str;
18    // SAFETY: The pair owns a raw `Arc` reference; forgetting the reconstructed handle and its
19    // clone retains both.
20    let arc = unsafe { Arc::from_raw(str_ptr) };
21    let arc2 = arc.clone();
22    mem::forget(arc);
23    mem::forget(arc2);
24
25    Some((addr, len))
26}
27
28fn drop_str(addr: *const u8, len: usize) {
29    let slice_ptr: *const [u8] = ptr::slice_from_raw_parts(addr, len);
30    let str_ptr = slice_ptr as *const str;
31    // SAFETY: The vtable pair comes from `Arc::into_raw` and owns the strong reference released
32    // here.
33    let arc = unsafe { Arc::from_raw(str_ptr) };
34    drop(arc);
35}
36
37const ARC_STR_VTABLE: StorageVTable = StorageVTable::new(as_ptr, len, clone_str, drop_str);
38
39// SAFETY: the vtable shares and releases the `Arc<str>` allocation, which
40// stays valid and immutable until the last handle is dropped
41unsafe impl StorageExt for Arc<str> {
42    fn create(self) -> (*const u8, usize, &'static StorageVTable) {
43        let ptr = Arc::into_raw(self) as *const [u8];
44
45        // Extract address and length
46        let addr = ptr.cast::<()>().cast::<u8>();
47        let len = ptr.len();
48        (addr, len, &ARC_STR_VTABLE)
49    }
50}
51
52// SAFETY: `Arc<str>` always holds valid UTF-8
53unsafe impl StorageExtStr for Arc<str> {}
54
55#[cfg(test)]
56#[allow(unused_must_use)]
57mod tests {
58    use super::*;
59    use crate::{ByteString, Bytes, buf::Buf};
60
61    /// STATEMENT: External `Arc<str>` storage tracks clones and detaches when views change their bounds.
62    ///
63    /// HOW:
64    /// - Wrap and clone `test` as `Bytes` and `ByteString`, checking contents, capacity, and strong counts through drops.
65    /// - Read, truncate, and adjust storage bounds on new wrappers, verifying substrings and release of the external reference.
66    #[test]
67    fn test_arc_str() {
68        let test: Arc<str> = Arc::from("test".to_string());
69
70        let b = Bytes::from_ext(test.clone());
71        assert_eq!(&b, b"test");
72        assert_eq!(b.storage.capacity(), 4);
73        assert_eq!(Arc::strong_count(&test), 2);
74
75        let b2 = b.clone();
76        assert_eq!(&b2, b"test");
77        assert_eq!(Arc::strong_count(&test), 3);
78
79        drop(b2);
80        assert_eq!(&b, b"test");
81        assert_eq!(Arc::strong_count(&test), 2);
82
83        drop(b);
84        assert_eq!(Arc::strong_count(&test), 1);
85
86        let b = ByteString::from_ext(test.clone());
87        assert_eq!(&b, "test");
88        assert_eq!(Arc::strong_count(&test), 2);
89
90        drop(b);
91        assert_eq!(Arc::strong_count(&test), 1);
92
93        let mut b = Bytes::from_ext(test.clone());
94        assert_eq!(Arc::strong_count(&test), 2);
95        assert_eq!(b.get_u8(), b't');
96        assert_eq!(&b, b"est");
97        assert_eq!(Arc::strong_count(&test), 1);
98
99        let mut b = Bytes::from_ext(test.clone());
100        b.truncate(2);
101        assert_eq!(&b, b"te");
102        assert_eq!(Arc::strong_count(&test), 1);
103
104        let mut b = Bytes::from_ext(test.clone());
105        // SAFETY: The initialized four-byte view contains the two bytes being consumed.
106        unsafe { b.storage.set_start(2) };
107        assert_eq!(&b, b"st");
108        assert_eq!(Arc::strong_count(&test), 1);
109
110        let mut b = Bytes::from_ext(test.clone());
111        // SAFETY: The initialized four-byte view contains the two-byte prefix being retained.
112        unsafe { b.storage.set_end(2) };
113        assert_eq!(&b, b"te");
114        assert_eq!(Arc::strong_count(&test), 1);
115    }
116}