1use crate::arena;
11use std::fmt;
12
13pub struct SeqString {
21 ptr: *const u8,
22 len: usize,
23 capacity: usize, global: bool,
25}
26
27impl PartialEq for SeqString {
29 fn eq(&self, other: &Self) -> bool {
30 self.as_str() == other.as_str()
31 }
32}
33
34impl Eq for SeqString {}
35
36unsafe impl Send for SeqString {}
41
42impl SeqString {
43 pub fn as_str(&self) -> &str {
48 unsafe { std::str::from_utf8_unchecked(std::slice::from_raw_parts(self.ptr, self.len)) }
49 }
50
51 #[allow(dead_code)]
53 pub fn is_global(&self) -> bool {
54 self.global
55 }
56
57 pub fn len(&self) -> usize {
59 self.len
60 }
61
62 #[allow(dead_code)]
64 pub fn is_empty(&self) -> bool {
65 self.len == 0
66 }
67
68 pub fn into_raw_parts(self) -> (*const u8, usize, usize, bool) {
76 let parts = (self.ptr, self.len, self.capacity, self.global);
77 std::mem::forget(self); parts
79 }
80
81 pub unsafe fn from_raw_parts(
87 ptr: *const u8,
88 len: usize,
89 capacity: usize,
90 global: bool,
91 ) -> Self {
92 SeqString {
93 ptr,
94 len,
95 capacity,
96 global,
97 }
98 }
99}
100
101impl Clone for SeqString {
102 fn clone(&self) -> Self {
108 let s = self.as_str().to_string();
109 global_string(s)
110 }
111}
112
113impl Drop for SeqString {
114 fn drop(&mut self) {
115 if self.global {
116 unsafe {
120 let _s = String::from_raw_parts(
121 self.ptr as *mut u8,
122 self.len,
123 self.capacity, );
125 }
127 }
128 }
130}
131
132impl fmt::Debug for SeqString {
133 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
134 write!(f, "SeqString({:?}, global={})", self.as_str(), self.global)
135 }
136}
137
138impl fmt::Display for SeqString {
139 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
140 write!(f, "{}", self.as_str())
141 }
142}
143
144pub fn arena_string(s: &str) -> SeqString {
152 arena::with_arena(|arena| {
153 let arena_str = arena.alloc_str(s);
154 SeqString {
155 ptr: arena_str.as_ptr(),
156 len: arena_str.len(),
157 capacity: 0, global: false,
159 }
160 })
161}
162
163pub fn global_string(s: String) -> SeqString {
171 let len = s.len();
172 let capacity = s.capacity();
173 let ptr = s.as_ptr();
174 std::mem::forget(s); SeqString {
177 ptr,
178 len,
179 capacity, global: true,
181 }
182}
183
184impl From<&str> for SeqString {
186 fn from(s: &str) -> Self {
187 arena_string(s)
188 }
189}
190
191impl From<String> for SeqString {
193 fn from(s: String) -> Self {
194 global_string(s)
195 }
196}
197
198#[cfg(test)]
199mod tests {
200 use super::*;
201
202 #[test]
203 fn test_arena_string() {
204 let s = arena_string("Hello, arena!");
205 assert_eq!(s.as_str(), "Hello, arena!");
206 assert_eq!(s.len(), 13);
207 assert!(!s.is_global());
208 }
209
210 #[test]
211 fn test_global_string() {
212 let s = global_string("Hello, global!".to_string());
213 assert_eq!(s.as_str(), "Hello, global!");
214 assert_eq!(s.len(), 14);
215 assert!(s.is_global());
216 }
217
218 #[test]
219 fn test_clone_creates_global() {
220 let s1 = arena_string("test");
222 let s2 = s1.clone();
223
224 assert_eq!(s1.as_str(), s2.as_str());
225 assert!(!s1.is_global());
226 assert!(s2.is_global()); }
228
229 #[test]
230 fn test_clone_global() {
231 let s1 = global_string("test".to_string());
232 let s2 = s1.clone();
233
234 assert_eq!(s1.as_str(), s2.as_str());
235 assert!(s1.is_global());
236 assert!(s2.is_global());
237 }
238
239 #[test]
240 fn test_drop_global() {
241 {
243 let s = global_string("Will be dropped".to_string());
244 assert_eq!(s.as_str(), "Will be dropped");
245 }
246 }
248
249 #[test]
250 fn test_drop_arena() {
251 {
253 let s = arena_string("Will be dropped (no-op)");
254 assert_eq!(s.as_str(), "Will be dropped (no-op)");
255 }
256 }
258
259 #[test]
260 fn test_equality() {
261 let s1 = arena_string("test");
262 let s2 = arena_string("test");
263 let s3 = global_string("test".to_string());
264 let s4 = arena_string("different");
265
266 assert_eq!(s1, s2); assert_eq!(s1, s3); assert_ne!(s1, s4); }
270
271 #[test]
272 fn test_from_str() {
273 let s: SeqString = "test".into();
274 assert_eq!(s.as_str(), "test");
275 assert!(!s.is_global()); }
277
278 #[test]
279 fn test_from_string() {
280 let s: SeqString = "test".to_string().into();
281 assert_eq!(s.as_str(), "test");
282 assert!(s.is_global()); }
284
285 #[test]
286 fn test_debug_format() {
287 let s = arena_string("debug");
288 let debug_str = format!("{:?}", s);
289 assert!(debug_str.contains("debug"));
290 assert!(debug_str.contains("global=false"));
291 }
292
293 #[test]
294 fn test_display_format() {
295 let s = global_string("display".to_string());
296 let display_str = format!("{}", s);
297 assert_eq!(display_str, "display");
298 }
299
300 #[test]
301 fn test_empty_string() {
302 let s = arena_string("");
303 assert_eq!(s.len(), 0);
304 assert!(s.is_empty());
305 assert_eq!(s.as_str(), "");
306 }
307
308 #[test]
309 fn test_unicode() {
310 let s = arena_string("Hello, δΈη! π¦");
311 assert_eq!(s.as_str(), "Hello, δΈη! π¦");
312 assert!(s.len() > 10); }
314
315 #[test]
316 fn test_global_string_preserves_capacity() {
317 let mut s = String::with_capacity(100);
319 s.push_str("hi");
320
321 assert_eq!(s.len(), 2);
322 assert_eq!(s.capacity(), 100);
323
324 let cem = global_string(s);
325
326 assert_eq!(cem.len(), 2);
328 assert_eq!(cem.capacity, 100); assert_eq!(cem.as_str(), "hi");
330 assert!(cem.is_global());
331
332 drop(cem);
334
335 }
337
338 #[test]
339 fn test_arena_string_capacity_zero() {
340 let s = arena_string("test");
342 assert_eq!(s.capacity, 0); assert!(!s.is_global());
344 }
345}