Skip to main content

gc_alloc/
cstring.rs

1use std::{ffi::CStr, fmt::Write, ptr::NonNull};
2
3use crate::gc;
4
5pub fn from_str(s: &str) -> Result<&'static CStr, NulError> {
6    if let Some(pos) = s.find('\0') {
7        return Err(NulError(pos));
8    }
9    let ptr = alloc(s.len() + 1);
10    unsafe { std::ptr::copy_nonoverlapping(s.as_ptr(), ptr.as_ptr(), s.len()) };
11    unsafe { std::ptr::write(ptr.as_ptr().add(s.len()), 0) };
12    Ok(unsafe { CStr::from_ptr(ptr.as_ptr() as *const i8) })
13}
14
15pub fn from_cstr(s: &CStr) -> &'static CStr {
16    let bytes = s.to_bytes_with_nul();
17    let ptr = alloc(bytes.len());
18    unsafe { std::ptr::copy_nonoverlapping(bytes.as_ptr(), ptr.as_ptr(), bytes.len()) };
19    unsafe { CStr::from_ptr(ptr.as_ptr() as *const i8) }
20}
21
22pub fn from_iter<I: IntoIterator<Item = char>>(iter: I) -> Result<&'static CStr, I::IntoIter> {
23    let mut iter = iter.into_iter();
24    let (lower, _) = iter.size_hint();
25
26    let mut formatter = Formatter::with_capacity(lower);
27    while let Some(c) = iter.next() {
28        if formatter.write_char(c).is_err() {
29            return Err(iter);
30        }
31    }
32    Ok(formatter.finish())
33}
34
35pub struct Formatter {
36    buf: NonNull<u8>,
37    len: usize,
38    cap: usize,
39}
40
41impl Formatter {
42    pub fn new() -> Self {
43        Self {
44            buf: NonNull::dangling(),
45            len: 0,
46            cap: 0,
47        }
48    }
49
50    pub fn with_capacity(cap: usize) -> Self {
51        let buf = if cap == 0 {
52            NonNull::dangling()
53        } else {
54            alloc(cap)
55        };
56        Self { buf, len: 0, cap }
57    }
58}
59
60impl Write for Formatter {
61    #[inline]
62    fn write_str(&mut self, s: &str) -> std::fmt::Result {
63        if s.find('\0').is_some() {
64            return Err(std::fmt::Error);
65        }
66
67        let mut cap = self.cap;
68        while self.len + s.len() > cap {
69            cap = cap.max(1).checked_mul(2).expect("Capacity overflow");
70        }
71        if cap != self.cap {
72            let new_buf = alloc(cap + 1);
73            unsafe { std::ptr::copy_nonoverlapping(self.buf.as_ptr(), new_buf.as_ptr(), self.len) };
74            self.buf = new_buf;
75            self.cap = cap;
76        }
77        unsafe {
78            std::ptr::copy_nonoverlapping(s.as_ptr(), self.buf.add(self.len).as_ptr(), s.len())
79        };
80        self.len += s.len();
81        Ok(())
82    }
83}
84
85impl Formatter {
86    pub fn finish(self) -> &'static CStr {
87        if self.len == 0 {
88            return c"";
89        }
90        unsafe {
91            std::ptr::write(self.buf.add(self.len).as_ptr(), 0);
92            CStr::from_ptr(self.buf.as_ptr() as *const i8)
93        }
94    }
95}
96
97impl Default for Formatter {
98    fn default() -> Self {
99        Self::new()
100    }
101}
102
103#[macro_export]
104#[doc(hidden)]
105macro_rules! cformat {
106    ($($arg:tt)*) => {{
107        let mut formatter = $crate::cstring::Formatter::new();
108        std::fmt::write(&mut formatter, format_args!($($arg)*)).expect("Formatting failed");
109        formatter.finish()
110    }};
111}
112
113pub use cformat as format;
114
115#[derive(Clone, Copy, PartialEq, Eq, Debug)]
116pub struct NulError(usize);
117
118impl NulError {
119    pub fn nul_position(&self) -> usize {
120        self.0
121    }
122}
123
124impl std::fmt::Display for NulError {
125    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
126        write!(f, "nul byte found in provided data at position: {}", self.0)
127    }
128}
129
130impl std::error::Error for NulError {}
131
132fn alloc(cap: usize) -> NonNull<u8> {
133    let ptr = unsafe { gc::GC_malloc_atomic(cap) as *mut u8 };
134    std::ptr::NonNull::new(ptr).expect("GC_malloc_atomic failed")
135}