rune/core/object/
string.rs
1use super::{CloneIn, IntoObject};
2use crate::core::gc::{AllocState, Block, GcHeap, GcMoveable, GcState, Trace};
3use std::cell::Cell;
4use std::fmt::{Debug, Display};
5use std::ops::Deref;
6use std::ptr::NonNull;
7
8pub(crate) type GcString<'a> = bumpalo::collections::String<'a>;
9pub(crate) struct LispString(GcHeap<LispStringInner>);
10
11struct LispStringInner(Cell<*mut str>);
19
20impl GcMoveable for LispString {
21 type Value = std::ptr::NonNull<LispString>;
22
23 fn move_value(&self, to_space: &bumpalo::Bump) -> Option<(Self::Value, bool)> {
24 match self.0.allocation_state() {
25 AllocState::Forwarded(f) => Some((f.cast::<Self>(), false)),
26 AllocState::Global => None,
27 AllocState::Unmoved => {
28 let ptr = {
29 let mut new = GcString::from_str_in(self, to_space);
30 let lisp_str = unsafe { LispString::new(new.as_mut_str(), false) };
31 std::mem::forget(new);
32 let alloc = to_space.alloc(lisp_str);
33 NonNull::from(alloc)
34 };
35 self.0.forward(ptr.cast::<u8>());
36 Some((ptr, true))
37 }
38 }
39 }
40}
41
42impl Trace for LispString {
43 fn trace(&self, _state: &mut GcState) {}
44}
45
46impl Debug for LispString {
47 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
48 Debug::fmt(self.inner(), f)
49 }
50}
51
52impl Display for LispString {
53 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
54 let mut output = String::new();
55 for c in self.inner().chars() {
56 match c {
57 '\\' => output.push_str("\\\\"),
58 '"' => output.push_str("\\\""),
59 c => output.push(c),
60 }
61 }
62 Display::fmt(&output, f)
63 }
64}
65
66impl PartialEq for LispString {
67 fn eq(&self, other: &Self) -> bool {
68 self.inner() == other.inner()
69 }
70}
71
72impl Eq for LispString {}
73
74impl PartialEq<str> for LispString {
75 fn eq(&self, other: &str) -> bool {
76 self.inner() == other
77 }
78}
79
80impl Deref for LispString {
81 type Target = str;
82
83 fn deref(&self) -> &Self::Target {
84 self.inner()
85 }
86}
87
88impl LispString {
89 pub(in crate::core) unsafe fn new(string: *mut str, constant: bool) -> Self {
90 Self(GcHeap::new(LispStringInner(Cell::new(string)), constant))
91 }
92
93 pub(crate) fn inner(&self) -> &str {
94 unsafe { &*self.0.0.get() }
95 }
96}
97
98impl LispString {
99 pub(crate) fn len(&self) -> usize {
100 self.chars().count()
101 }
102
103 pub(crate) fn clear(&self) {
104 let inner_mut_str = unsafe { &mut *self.0.0.get() };
105 for byte in unsafe { inner_mut_str.as_bytes_mut().iter_mut() } {
106 *byte = b'\0';
107 }
108 }
109}
110
111impl<'new> CloneIn<'new, &'new Self> for LispString {
112 fn clone_in<const C: bool>(&self, bk: &'new Block<C>) -> super::Gc<&'new Self> {
113 GcString::from_str_in(self.inner(), &bk.objects).into_obj(bk)
114 }
115}
116
117impl AsRef<str> for LispString {
118 fn as_ref(&self) -> &str {
119 self
120 }
121}
122
123impl<'a> From<&'a LispString> for &'a str {
124 fn from(value: &'a LispString) -> Self {
125 value
126 }
127}
128
129impl<'a> From<&'a LispString> for &'a [u8] {
130 fn from(value: &'a LispString) -> Self {
131 value.as_bytes()
132 }
133}
134
135pub(crate) struct ByteString(GcHeap<*mut [u8]>);
136type ByteVec<'a> = bumpalo::collections::Vec<'a, u8>;
137
138impl Deref for ByteString {
139 type Target = [u8];
140
141 fn deref(&self) -> &Self::Target {
142 self.inner()
143 }
144}
145
146impl GcMoveable for ByteString {
147 type Value = std::ptr::NonNull<ByteString>;
148
149 fn move_value(&self, to_space: &bumpalo::Bump) -> Option<(Self::Value, bool)> {
150 match self.0.allocation_state() {
151 AllocState::Forwarded(f) => Some((f.cast::<Self>(), false)),
152 AllocState::Global => None,
153 AllocState::Unmoved => {
154 let ptr = {
155 let mut new = ByteVec::new_in(to_space);
156 new.extend_from_slice(self.inner());
157 let byte_string = ByteString::new(new.as_mut_slice(), false);
158 std::mem::forget(new);
159 let alloc = to_space.alloc(byte_string);
160 NonNull::from(alloc)
161 };
162 self.0.forward(ptr.cast::<u8>());
163 Some((ptr, true))
164 }
165 }
166 }
167}
168
169impl PartialEq for ByteString {
170 fn eq(&self, other: &Self) -> bool {
171 self.inner() == other.inner()
172 }
173}
174
175impl Eq for ByteString {}
176
177impl Trace for ByteString {
178 fn trace(&self, _state: &mut GcState) {}
179}
180
181impl ByteString {
182 pub(in crate::core) fn new(string: *mut [u8], constant: bool) -> Self {
183 Self(GcHeap::new(string, constant))
184 }
185
186 pub(crate) fn inner(&self) -> &[u8] {
187 unsafe { &**self.0 }
188 }
189}
190
191impl<'new> CloneIn<'new, &'new Self> for ByteString {
192 fn clone_in<const C: bool>(&self, bk: &'new Block<C>) -> super::Gc<&'new Self> {
193 (**self).to_vec().into_obj(bk)
195 }
196}
197
198impl Display for ByteString {
199 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
200 for byte in &**self {
201 if byte.is_ascii() {
202 write!(f, "{}", *byte as char)?;
203 } else {
204 write!(f, "\\{byte:03o}")?;
205 }
206 }
207 Ok(())
208 }
209}
210
211impl Debug for ByteString {
212 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
213 write!(f, "{self}")
214 }
215}
216
217#[cfg(test)]
218mod test {
219 use crate::core::gc::{Context, RootSet};
220 use rune_core::macros::root;
221
222 #[test]
223 fn test_string_aliasing() {
224 let roots = &RootSet::default();
225 let cx = &mut Context::new(roots);
226 let s1 = cx.add(String::from("hello"));
227 let mut s2 = cx.string_with_capacity(5);
228 s2.push_str("hello");
229 let s2 = cx.add(s2);
230 assert_eq!(s1, s2);
231 root!(s1, cx);
232 root!(s2, cx);
233 cx.garbage_collect(true);
234 assert_eq!(s1, s2);
235 }
236
237 #[test]
238 fn test_byte_string_aliasing() {
239 let roots = &RootSet::default();
240 let cx = &mut Context::new(roots);
241 let s1 = cx.add(vec![1u8, 2, 3]);
242 let s2 = cx.add(vec![1u8, 2, 3]);
243 let s2 = cx.add(s2);
244 assert_eq!(s1, s2);
245 root!(s1, cx);
246 root!(s2, cx);
247 cx.garbage_collect(true);
248 assert_eq!(s1, s2);
249 }
250}