Skip to main content

nifty_rt/
ffi.rs

1//! Plain-data building blocks of the descriptors compiled programs carry: a slice and a
2//! string, laid out as a pointer and a length.
3
4use std::{fmt, slice, str};
5
6/// `len` items starting at `ptr`, which live for the rest of the program.
7#[repr(C)]
8pub struct Slice<T: 'static> {
9    ptr: *const T,
10    len: usize,
11}
12
13// SAFETY: a slice only reads static, immutable items, so sharing it shares `&T`s.
14unsafe impl<T: Sync> Sync for Slice<T> {}
15
16impl<T> Slice<T> {
17    /// Where the pointer and the length sit, for code that emits slices as data.
18    pub const PTR_OFFSET: usize = std::mem::offset_of!(Self, ptr);
19    pub const LEN_OFFSET: usize = std::mem::offset_of!(Self, len);
20
21    pub const EMPTY: Self = Self {
22        ptr: std::ptr::null(),
23        len: 0,
24    };
25
26    #[must_use]
27    pub const fn new(items: &'static [T]) -> Self {
28        Self {
29            ptr: items.as_ptr(),
30            len: items.len(),
31        }
32    }
33
34    #[must_use]
35    pub fn items(&self) -> &'static [T] {
36        if self.len == 0 {
37            return &[];
38        }
39        // SAFETY: a `Slice` is made from a static slice, or emitted by `nf build` with the
40        // same layout, which `nifty_rt_main`'s caller vouches for.
41        unsafe { slice::from_raw_parts(self.ptr, self.len) }
42    }
43}
44
45impl<T: fmt::Debug> fmt::Debug for Slice<T> {
46    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
47        f.debug_list().entries(self.items()).finish()
48    }
49}
50
51/// UTF-8 text that lives for the rest of the program, laid out as a `Slice<u8>`.
52#[repr(C)]
53pub struct Str(Slice<u8>);
54
55impl Str {
56    pub const EMPTY: Self = Self(Slice::EMPTY);
57
58    #[must_use]
59    pub const fn new(text: &'static str) -> Self {
60        Self(Slice::new(text.as_bytes()))
61    }
62
63    /// The text, with anything that isn't UTF-8 replaced.
64    #[must_use]
65    pub fn lossy(&self) -> String {
66        String::from_utf8_lossy(self.0.items()).into_owned()
67    }
68
69    /// # Errors
70    /// When the bytes aren't UTF-8.
71    pub fn text(&self) -> Result<&'static str, str::Utf8Error> {
72        str::from_utf8(self.0.items())
73    }
74}
75
76impl fmt::Debug for Str {
77    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
78        fmt::Debug::fmt(&self.lossy(), f)
79    }
80}