1use std::ffi::OsStr;
2use std::path::Path;
3use std::sync::Mutex;
4
5pub struct SliceArena<T = u8> {
7 arenas: Mutex<Vec<Vec<T>>>,
8}
9
10impl<T: Clone> SliceArena<T> {
11 #[inline]
15 pub fn new() -> Self {
16 Self::with_capacity(((1<<16) / std::mem::size_of::<T>()).next_power_of_two())
17 }
18
19 #[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 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); 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 #[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 #[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 #[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