Skip to main content

gc_alloc/
string.rs

1use std::{fmt::Write, ptr::NonNull};
2
3use crate::{GcToken, gc};
4
5pub fn from_str(_token: &impl GcToken, s: &str) -> &'static mut str {
6    let ptr = alloc(s.len());
7    unsafe { std::ptr::copy_nonoverlapping(s.as_ptr(), ptr.as_ptr(), s.len()) };
8    unsafe {
9        std::str::from_utf8_unchecked_mut(std::slice::from_raw_parts_mut(ptr.as_ptr(), s.len()))
10    }
11}
12
13pub fn from_iter<I: IntoIterator<Item = char>>(token: &impl GcToken, iter: I) -> &'static str {
14    let iter = iter.into_iter();
15    let (lower, _) = iter.size_hint();
16
17    let mut formatter = Formatter::with_capacity(token, lower);
18    for c in iter {
19        let _ = formatter.write_char(c);
20    }
21    formatter.finish()
22}
23
24pub struct Formatter {
25    buf: NonNull<u8>,
26    len: usize,
27    cap: usize,
28}
29
30impl Formatter {
31    pub fn new(_token: &impl GcToken) -> Self {
32        Self {
33            buf: NonNull::dangling(),
34            len: 0,
35            cap: 0,
36        }
37    }
38
39    pub fn with_capacity(_token: &impl GcToken, cap: usize) -> Self {
40        let buf = if cap == 0 {
41            NonNull::dangling()
42        } else {
43            alloc(cap)
44        };
45        Self { buf, len: 0, cap }
46    }
47}
48
49impl Write for Formatter {
50    #[inline]
51    fn write_str(&mut self, s: &str) -> std::fmt::Result {
52        let mut cap = self.cap;
53        while self.len + s.len() > cap {
54            cap = cap.max(1).checked_mul(2).expect("Capacity overflow");
55        }
56        if cap != self.cap {
57            let new_buf = alloc(cap);
58            unsafe { std::ptr::copy_nonoverlapping(self.buf.as_ptr(), new_buf.as_ptr(), self.len) };
59            self.buf = new_buf;
60            self.cap = cap;
61        }
62        unsafe {
63            std::ptr::copy_nonoverlapping(s.as_ptr(), self.buf.add(self.len).as_ptr(), s.len())
64        };
65        self.len += s.len();
66        Ok(())
67    }
68}
69
70impl Formatter {
71    pub fn finish(self) -> &'static str {
72        if self.len == 0 {
73            return "";
74        }
75        unsafe {
76            std::str::from_utf8_unchecked(std::slice::from_raw_parts(self.buf.as_ptr(), self.len))
77        }
78    }
79}
80
81#[macro_export]
82#[doc(hidden)]
83macro_rules! format {
84    ($token:expr, $($arg:tt)*) => {{
85        let mut formatter = $crate::string::Formatter::new($token);
86        let _ = std::fmt::write(&mut formatter, format_args!($($arg)*));
87        formatter.finish()
88    }};
89}
90
91pub use crate::format;
92
93fn alloc(cap: usize) -> NonNull<u8> {
94    let ptr = unsafe { gc::GC_malloc_atomic(cap) as *mut u8 };
95    std::ptr::NonNull::new(ptr).expect("GC_malloc_atomic failed")
96}