shape_value/v2/
string_obj.rs1use super::heap_header::{HeapHeader, HEAP_KIND_V2_STRING};
15
16#[repr(C)]
19pub struct StringObj {
20 pub header: HeapHeader,
21 pub data: *const u8,
23 pub len: u32,
25 pub _pad: u32,
26}
27
28impl StringObj {
29 pub fn new(s: &str) -> *mut Self {
31 let layout = std::alloc::Layout::new::<Self>();
32 let ptr = unsafe { std::alloc::alloc(layout) as *mut Self };
33
34 let data = if s.is_empty() {
36 std::ptr::null()
37 } else {
38 let data_layout = std::alloc::Layout::from_size_align(s.len(), 1).unwrap();
39 let data_ptr = unsafe { std::alloc::alloc(data_layout) };
40 unsafe { std::ptr::copy_nonoverlapping(s.as_ptr(), data_ptr, s.len()) };
41 data_ptr as *const u8
42 };
43
44 unsafe {
45 (*ptr).header = HeapHeader::new(HEAP_KIND_V2_STRING);
46 (*ptr).data = data;
47 (*ptr).len = s.len() as u32;
48 (*ptr)._pad = 0;
49 }
50 ptr
51 }
52
53 pub unsafe fn as_str(ptr: *const Self) -> &'static str {
58 unsafe {
59 if (*ptr).len == 0 {
60 ""
61 } else {
62 let bytes = std::slice::from_raw_parts((*ptr).data, (*ptr).len as usize);
63 std::str::from_utf8_unchecked(bytes)
64 }
65 }
66 }
67
68 pub unsafe fn len(ptr: *const Self) -> u32 {
73 unsafe { (*ptr).len }
74 }
75
76 pub unsafe fn is_empty(ptr: *const Self) -> bool {
81 unsafe { (*ptr).len == 0 }
82 }
83
84 pub unsafe fn drop(ptr: *mut Self) {
90 unsafe {
91 if (*ptr).len > 0 && !(*ptr).data.is_null() {
92 let data_layout =
93 std::alloc::Layout::from_size_align((*ptr).len as usize, 1).unwrap();
94 std::alloc::dealloc((*ptr).data as *mut u8, data_layout);
95 }
96 let layout = std::alloc::Layout::new::<Self>();
97 std::alloc::dealloc(ptr as *mut u8, layout);
98 }
99 }
100
101 pub const OFFSET_DATA: usize = 8;
103 pub const OFFSET_LEN: usize = 16;
104}
105
106const _: () = {
108 assert!(std::mem::size_of::<StringObj>() == 24);
109};
110
111unsafe impl super::heap_element::HeapElement for StringObj {
117 unsafe fn release_elem(ptr: *const Self) {
118 if unsafe { super::refcount::v2_release(&(*ptr).header) } {
119 unsafe { Self::drop(ptr as *mut Self) };
120 }
121 }
122}
123
124#[cfg(test)]
125mod tests {
126 use super::*;
127
128 #[test]
129 fn test_size_of_string_obj() {
130 assert_eq!(std::mem::size_of::<StringObj>(), 24);
131 }
132
133 #[test]
134 fn test_create_and_read_hello() {
135 unsafe {
136 let ptr = StringObj::new("hello");
137 assert_eq!(StringObj::as_str(ptr), "hello");
138 assert_eq!(StringObj::len(ptr), 5);
139 assert!(!StringObj::is_empty(ptr));
140 assert_eq!((*ptr).header.kind(), HEAP_KIND_V2_STRING);
141 assert_eq!((*ptr).header.get_refcount(), 1);
142 StringObj::drop(ptr);
143 }
144 }
145
146 #[test]
147 fn test_unicode_string() {
148 unsafe {
149 let s = "日本語";
150 let ptr = StringObj::new(s);
151 assert_eq!(StringObj::as_str(ptr), "日本語");
152 assert_eq!(StringObj::len(ptr), 9);
154 assert!(!StringObj::is_empty(ptr));
155 StringObj::drop(ptr);
156 }
157 }
158
159 #[test]
160 fn test_empty_string() {
161 unsafe {
162 let ptr = StringObj::new("");
163 assert_eq!(StringObj::as_str(ptr), "");
164 assert_eq!(StringObj::len(ptr), 0);
165 assert!(StringObj::is_empty(ptr));
166 StringObj::drop(ptr);
167 }
168 }
169
170 #[test]
171 fn test_drop_does_not_leak() {
172 unsafe {
174 for _ in 0..100 {
175 let ptr = StringObj::new("leak test string with some content");
176 StringObj::drop(ptr);
177 }
178 for _ in 0..100 {
180 let ptr = StringObj::new("");
181 StringObj::drop(ptr);
182 }
183 }
184 }
185
186 #[test]
187 fn test_field_offsets() {
188 let ptr = StringObj::new("test");
189 let base = ptr as usize;
190 unsafe {
191 let data_offset = &(*ptr).data as *const _ as usize - base;
192 let len_offset = &(*ptr).len as *const _ as usize - base;
193
194 assert_eq!(data_offset, StringObj::OFFSET_DATA, "data must be at offset 8");
195 assert_eq!(len_offset, StringObj::OFFSET_LEN, "len must be at offset 16");
196
197 StringObj::drop(ptr);
198 }
199 }
200
201 #[test]
202 fn test_refcount_starts_at_one() {
203 unsafe {
204 let ptr = StringObj::new("refcount test");
205 assert_eq!((*ptr).header.get_refcount(), 1);
206 StringObj::drop(ptr);
207 }
208 }
209
210 #[test]
211 fn test_emoji_string() {
212 unsafe {
213 let s = "hello 🌍🚀✨";
214 let ptr = StringObj::new(s);
215 assert_eq!(StringObj::as_str(ptr), s);
216 assert_eq!(StringObj::len(ptr), s.len() as u32);
217 StringObj::drop(ptr);
218 }
219 }
220
221 #[test]
222 fn test_long_string_1mb() {
223 unsafe {
224 let s = "x".repeat(1_000_000);
225 let ptr = StringObj::new(&s);
226 assert_eq!(StringObj::len(ptr), 1_000_000);
227 assert_eq!(StringObj::as_str(ptr), s.as_str());
228 StringObj::drop(ptr);
229 }
230 }
231}