slice_arena/
lib.rs

1use std::ffi::OsStr;
2use std::path::Path;
3use std::sync::Mutex;
4
5/// Thread-safe memory arena.
6pub struct SliceArena<T = u8> {
7    arenas: Mutex<Vec<Vec<T>>>,
8}
9
10impl<T: Clone> SliceArena<T> {
11    /// New arena with ~64KB of initial capacity (which will grow exponentially as needed).
12    ///
13    /// It works with any cloneable element type, but `SliceArena<u8>` is the default (it's like `Vec<u8>`).
14    #[inline]
15    pub fn new() -> Self {
16        Self::with_capacity(((1<<16) / std::mem::size_of::<T>()).next_power_of_two())
17    }
18
19    /// New arena with memory reserved for the initial number of elements.
20    ///
21    /// Arena grows expomnentially, so once this capacity is exhausted,
22    /// a double of that size will be allocated.
23    ///
24    /// Capacity should be much much larger than sizes of slices pushed.
25    /// Pushing a slice larger than the capacity leaves previous capacity unused.
26    #[inline]
27    pub fn with_capacity(capacity: usize) -> Self {
28        let mut arenas = Vec::with_capacity(1.max(8.min(capacity)));
29        arenas.push(Vec::with_capacity(capacity));
30        Self {
31            arenas: Mutex::new(arenas),
32        }
33    }
34
35    /// Copy a slice into the arena. Returns arena-allocated slice.
36    pub fn push<'arena>(&'arena self, slice: &[T]) -> &'arena mut [T] {
37        let mut a = self.arenas.lock().unwrap();
38        let slice_len = slice.len();
39        let latest = &mut a[0];
40        if latest.capacity() >= latest.len() + slice_len {
41            let start = latest.len();
42            let _start_ptr = latest.as_ptr();
43            latest.extend_from_slice(slice);
44            debug_assert_eq!(_start_ptr, latest.as_ptr(), "No realloc");
45            unsafe {
46                std::mem::transmute::<&mut [T], &'arena mut [T]>(&mut latest[start..start + slice_len])
47            }
48        } else {
49            let new_capacity = slice_len.max(latest.capacity()*2);
50            a.push(Vec::with_capacity(new_capacity));
51            let last_idx = a.len()-1;
52            a.swap(0, last_idx); // for convenience, 0th one should be the latest
53            let latest = &mut a[0];
54            let _start_ptr = latest.as_ptr();
55            debug_assert!(latest.len() == 0);
56            latest.extend_from_slice(slice);
57            debug_assert!(latest.len() == slice_len);
58            debug_assert_eq!(_start_ptr, latest.as_ptr(), "No realloc");
59            unsafe {
60                std::mem::transmute::<&mut [T], &'arena mut [T]>(&mut latest[0..slice_len])
61            }
62        }
63    }
64}
65
66impl SliceArena<u8> {
67    /// Store a string slice in the arena.
68    #[inline]
69    pub fn push_str<'arena>(&'arena self, s: impl AsRef<str>) -> &'arena str {
70        let bytes = self.push(s.as_ref().as_bytes());
71        unsafe {
72            std::str::from_utf8_unchecked(bytes)
73        }
74    }
75
76    /// Store a Unix `OsStr` in the arena. This function does not exist on Windows.
77    #[inline]
78    #[cfg(unix)]
79    pub fn push_os_str<'arena>(&'arena self, s: &OsStr) -> &'arena OsStr {
80        use std::os::unix::ffi::OsStrExt;
81
82        let bytes = self.push(s.as_bytes());
83        OsStr::from_bytes(bytes)
84    }
85
86    /// Store a Unix `Path` in the arena. This function does not exist on Windows.
87    #[inline]
88    #[cfg(unix)]
89    pub fn push_path<'arena>(&'arena self, str: impl AsRef<Path>) -> &'arena Path {
90        let bytes = self.push_os_str(str.as_ref().as_os_str());
91        Path::new(bytes)
92    }
93}
94
95
96#[test]
97fn test_non_copy() {
98    let arena = SliceArena::with_capacity(0);
99    let a = arena.push(&[String::from("a")]);
100    let b = arena.push(&[String::from("b"), String::from("c")]);
101    a[0].push_str("1");
102    b[0].push_str("2");
103    b[1].push_str("3");
104    assert_eq!(a[0], "a1");
105    assert_eq!(b[0], "b2");
106    assert_eq!(b[1], "c3");
107}
108
109#[test]
110fn test_send_and_sync() {
111    fn is_sync<S: Sync>(_: S) {}
112
113    let arena = SliceArena::new();
114    arena.push_str("hi");
115    std::thread::spawn(move || {
116        arena.push_str("bye");
117        is_sync(arena);
118    }).join().unwrap();
119}
120
121#[test]
122#[cfg(unix)]
123fn test_path() {
124    let arena = SliceArena::with_capacity(9999);
125    let p = Path::new("some path");
126    assert_eq!(arena.push(&[p]), &[p]);
127    assert_eq!(arena.push(&[p]), &[p]);
128    let p = Path::new("some other path");
129    assert_eq!(arena.push(&[p]), &[p]);
130}
131
132#[test]
133fn kick_tires() {
134    let arena = SliceArena::with_capacity(4);
135    let h1 = arena.push(b"hello world");
136    let h2 = arena.push(b"hello world again");
137    assert_eq!(b"hello world", h1);
138    assert_eq!(b"hello world again", h2);
139
140    let arena = SliceArena::with_capacity(0);
141    assert_eq!(&[0u8; 0], arena.push(&[]));
142    assert_eq!(&[0u8; 0], arena.push(&[]));
143    let mut grows = String::new();
144    let mut in_arena = Vec::new();
145    for i in 0..1000 {
146        grows.push_str(&format!("and {} ", i));
147        let res = arena.push_str(&grows);
148        assert_eq!(res, grows);
149        in_arena.push(res);
150    }
151    let h1 = arena.push(b"hello world");
152    let h2 = arena.push(b"hello world again");
153    assert_eq!(b"hello world", h1);
154    assert_eq!(b"hello world again", h2);
155
156    let mut grows = String::new();
157    for i in 0..1000 {
158        grows.push_str(&format!("and {} ", i));
159        assert_eq!(in_arena[i], grows);
160    }
161}
162