tree_sitter_c2rust_core/
util.rs

1use std::{
2    cmp::Ordering,
3    ffi::CStr,
4    fmt,
5    io::{self, ErrorKind, Write},
6    os::{
7        self,
8        raw::{c_char, c_int, c_uint},
9    },
10    sync::atomic::{AtomicU32, AtomicUsize},
11    time::{Instant, SystemTime},
12};
13
14use crate::parser::{TSClock, TSDuration, __syscall_slong_t, __time_t};
15
16pub type size_t = usize;
17
18#[derive(Copy, Clone)]
19#[repr(C)]
20pub struct StackElement<T> {
21    pub contents: T,
22    pub size: u32,
23    pub capacity: u32,
24}
25
26#[derive(Copy, Clone)]
27#[repr(C)]
28pub union LongShortData {
29    pub long_data: *mut std::os::raw::c_char,
30    pub short_data: [std::os::raw::c_char; 24],
31}
32
33#[derive(Copy, Clone)]
34#[repr(C)]
35pub union ScannerStateWithLookahead {
36    pub c2rust_unnamed: ScannerStateLookaheadMeta,
37    pub external_scanner_state: crate::ExternalScannerState,
38    pub lookahead_char: std::os::raw::c_int,
39}
40
41#[derive(Copy, Clone)]
42#[repr(C)]
43pub struct ScannerStateLookaheadMeta {
44    pub visible_child_count: u32,
45    pub named_child_count: u32,
46    pub node_count: u32,
47    pub dynamic_precedence: std::os::raw::c_int,
48    pub repeat_depth: u16,
49    pub production_id: u16,
50    pub first_leaf: ScannerStateLookaheadFirstLeaf,
51}
52
53#[derive(Copy, Clone)]
54#[repr(C)]
55pub struct ScannerStateLookaheadFirstLeaf {
56    pub symbol: std::os::raw::c_ushort,
57    pub parse_state: std::os::raw::c_ushort,
58}
59
60pub(crate) trait WrappingOffsetFromExt<T: Sized> {
61    fn wrapping_offset_from_(self, origin: *const T) -> isize;
62}
63
64impl<T: Sized> WrappingOffsetFromExt<T> for *const T {
65    #[inline]
66    fn wrapping_offset_from_(self, origin: *const T) -> isize {
67        let pointee_size = std::mem::size_of::<T>();
68        assert!(0 < pointee_size && pointee_size <= isize::max_value() as usize);
69
70        let d = isize::wrapping_sub(self as _, origin as _);
71        d.wrapping_div(pointee_size as _)
72    }
73}
74
75impl<T: Sized> WrappingOffsetFromExt<T> for *mut T {
76    fn wrapping_offset_from_(self, origin: *const T) -> isize {
77        (self as *const T).wrapping_offset_from_(origin)
78    }
79}
80
81/// Simple replacement for libc::strcmp
82///
83/// Safety: both `a` and `b` must point to valid C strings, and no alive unique reference must
84/// point to the underlying memory.
85pub(crate) unsafe fn strcmp(s1: *const c_char, s2: *const c_char) -> c_int {
86    let s1 = CStr::from_ptr(s1);
87    let s2 = CStr::from_ptr(s2);
88
89    match s1.cmp(s2) {
90        Ordering::Less => -1,
91        Ordering::Equal => 0,
92        Ordering::Greater => 1,
93    }
94}
95
96/// Simple replacement for libc::strncmp
97///
98/// Safety: both `a` and `b` must point to valid C strings, and no alive unique reference must
99/// point to the underlying memory. Neither `a + n` or `b + n` can overflow.
100pub(crate) unsafe fn strncmp(mut s1: *const c_char, mut s2: *const c_char, mut n: usize) -> c_int {
101    while n > 0 {
102        let a = *s1;
103        let b = *s2;
104
105        match a.cmp(&b) {
106            Ordering::Less => return -1,
107            Ordering::Greater => return 1,
108            Ordering::Equal => {}
109        }
110
111        if a == 0 {
112            return 0;
113        }
114
115        s1 = s1.add(1);
116        s2 = s2.add(1);
117        n -= 1;
118    }
119
120    0
121}
122
123#[derive(Debug)]
124pub struct WriteCounter<W> {
125    inner: W,
126    count: usize,
127}
128
129impl<W: Write> WriteCounter<W> {
130    pub(crate) fn new(write: W) -> Self {
131        Self {
132            inner: write,
133            count: 0,
134        }
135    }
136}
137
138impl<W> WriteCounter<W> {
139    pub fn count(&self) -> usize {
140        self.count
141    }
142}
143
144impl<W> Write for WriteCounter<W>
145where
146    W: Write,
147{
148    fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
149        match self.inner.write(buf) {
150            Ok(count) => {
151                self.count += count;
152                Ok(count)
153            }
154            err => err,
155        }
156    }
157
158    fn flush(&mut self) -> std::io::Result<()> {
159        self.inner.flush()
160    }
161}
162
163struct VoidWriter;
164
165impl Write for VoidWriter {
166    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
167        Ok(buf.len())
168    }
169
170    fn flush(&mut self) -> io::Result<()> {
171        Ok(())
172    }
173}
174
175macro_rules! try_fmt_write_zero {
176    ($counter:expr, $args:expr) => {
177        match $counter.write_fmt($args) {
178            Ok(()) => Ok(()),
179            Err(err) if matches!(err.kind(), ErrorKind::WriteZero) => return Ok($counter.count()),
180            Err(err) => Err(err),
181        }
182    };
183}
184
185pub(crate) unsafe fn snwrite_runtime(
186    string: *mut c_char,
187    size: usize,
188    args: fmt::Arguments,
189) -> io::Result<usize> {
190    let raw = snwrite_inner(
191        WriteCounter::new(std::slice::from_raw_parts_mut(string as *mut u8, size)),
192        args,
193    )?;
194
195    // TODO(shadaj): more precise semantics
196    if size == 1 {
197        snwrite_inner(WriteCounter::new(VoidWriter), args)
198    } else {
199        Ok(raw)
200    }
201}
202
203fn snwrite_inner<W>(mut counter: WriteCounter<W>, args: fmt::Arguments) -> io::Result<usize>
204where
205    W: Write,
206{
207    try_fmt_write_zero!(counter, args)?;
208    try_fmt_write_zero!(counter, format_args!("\0"))?;
209
210    Ok(counter.count() - 1)
211}
212
213#[macro_export]
214macro_rules! snwrite {
215    ($string:expr, $size:expr, $($arg:tt)*) => {
216        crate::util::snwrite_runtime($string, $size, ::std::format_args!($($arg)*))
217    }
218}
219
220#[repr(transparent)]
221#[cfg_attr(feature = "capi", derive(Clone, Copy))]
222pub struct File(
223    #[cfg(feature = "capi")] crate::capi::CFile,
224    #[cfg(not(feature = "capi"))] Option<Box<std::io::BufWriter<std::fs::File>>>,
225);
226
227impl File {
228    pub(crate) fn empty() -> Self {
229        #[cfg(feature = "capi")]
230        {
231            Self(crate::capi::CFile::new(std::ptr::null_mut()))
232        }
233
234        #[cfg(not(feature = "capi"))]
235        {
236            Self(None)
237        }
238    }
239
240    pub(crate) fn is_valid(&self) -> bool {
241        #[cfg(feature = "capi")]
242        {
243            !self.0.is_null()
244        }
245
246        #[cfg(not(feature = "capi"))]
247        {
248            self.0.is_some()
249        }
250    }
251
252    pub(crate) fn close(&mut self) {
253        if !self.is_valid() {
254            return;
255        }
256
257        #[cfg(feature = "capi")]
258        {
259            if self.0.close() != 0 {
260                panic!("cannot close libc file stream");
261            }
262        }
263
264        #[cfg(not(feature = "capi"))]
265        {
266            self.0 = None;
267        }
268    }
269}
270
271impl Write for File {
272    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
273        #[cfg(feature = "capi")]
274        {
275            self.0.write(buf)
276        }
277
278        #[cfg(not(feature = "capi"))]
279        {
280            self.0.as_mut().unwrap().write(buf)
281        }
282    }
283
284    fn flush(&mut self) -> io::Result<()> {
285        #[cfg(feature = "capi")]
286        {
287            self.0.flush()
288        }
289
290        #[cfg(not(feature = "capi"))]
291        {
292            self.0.as_mut().unwrap().flush()
293        }
294    }
295}
296
297#[cfg(feature = "capi")]
298impl<T> From<T> for File
299where
300    T: Into<crate::capi::CFile>,
301{
302    fn from(file: T) -> Self {
303        Self(file.into())
304    }
305}
306
307#[cfg(not(feature = "capi"))]
308impl From<std::io::BufWriter<std::fs::File>> for File {
309    fn from(file: std::io::BufWriter<std::fs::File>) -> Self {
310        Self(Some(Box::new(file)))
311    }
312}
313
314// Credits: (musl libc library)[https://www.musl-libc.org/]
315pub(crate) fn iswspace(wc: c_uint) -> c_int {
316    const SPACES: [c_uint; 21] = [
317        b' ' as _, b'\t' as _, b'\n' as _, b'\r' as _, 11, 12, 0x0085, 0x2000, 0x2001, 0x2002,
318        0x2003, 0x2004, 0x2005, 0x2006, 0x2008, 0x2009, 0x200a, 0x2028, 0x2029, 0x205f, 0x3000,
319    ];
320
321    (wc != 0 && SPACES.contains(&wc)) as c_int
322}
323
324// Credits: (musl libc library)[https://www.musl-libc.org/]
325pub(crate) fn iswdigit(wc: c_uint) -> c_int {
326    (wc.wrapping_sub(b'0' as c_uint) < 10) as c_int
327}
328
329// Credits: (musl libc library)[https://www.musl-libc.org/]
330pub(crate) fn iswalpha(wc: c_uint) -> c_int {
331    const TABLE: [c_uint; 3904] = include!("./alpha.data");
332
333    if wc < 0x20000 {
334        ((TABLE[(TABLE[(wc >> 8) as usize].wrapping_mul(32) + ((wc & 255) >> 3)) as usize]
335            >> (wc & 7))
336            & 1) as c_int
337    } else if wc < 0x2fffe {
338        1
339    } else {
340        0
341    }
342}
343
344// Credits: (musl libc library)[https://www.musl-libc.org/]
345pub(crate) fn iswalnum(wc: c_uint) -> c_int {
346    (iswdigit(wc) != 0 || iswalpha(wc) != 0) as c_int
347}
348
349#[inline]
350pub(crate) unsafe extern "C" fn atomic_inc(p: *const u32) -> u32 {
351    (&*(p as *const AtomicU32))
352        .fetch_add(1, std::sync::atomic::Ordering::SeqCst)
353        .wrapping_add(1)
354}
355
356#[inline]
357pub(crate) unsafe extern "C" fn atomic_dec(mut p: *mut u32) -> u32 {
358    (&*(p as *const AtomicU32))
359        .fetch_sub(1, std::sync::atomic::Ordering::SeqCst)
360        .wrapping_sub(1)
361}
362
363#[inline]
364pub(crate) unsafe extern "C" fn clock_now() -> TSClock {
365    let now = SystemTime::now()
366        .duration_since(std::time::UNIX_EPOCH)
367        .unwrap();
368    let mut result: TSClock = TSClock {
369        tv_sec: now.as_secs() as isize,
370        tv_nsec: now.as_nanos() as isize,
371    };
372    return result;
373}
374
375#[inline]
376pub(crate) unsafe extern "C" fn clock_after(
377    mut base: TSClock,
378    mut duration: TSDuration,
379) -> TSClock {
380    let mut result: TSClock = base;
381    result.tv_sec = (result.tv_sec as usize)
382        .wrapping_add(duration.wrapping_div(1000000 as os::raw::c_int as u64) as usize)
383        as __time_t as __time_t;
384    result.tv_nsec = (result.tv_nsec as usize).wrapping_add(
385        duration
386            .wrapping_rem(1000000 as os::raw::c_int as u64)
387            .wrapping_mul(1000 as os::raw::c_int as u64) as usize,
388    ) as __syscall_slong_t as __syscall_slong_t;
389    return result;
390}
391
392#[inline]
393pub(crate) unsafe extern "C" fn atomic_load(mut p: *const usize) -> usize {
394    (&*(p as *const AtomicUsize)).load(std::sync::atomic::Ordering::SeqCst) as usize
395}