relay_cabi/
core.rs

1use std::ffi::CStr;
2use std::os::raw::c_char;
3use std::{mem, ptr, slice, str};
4
5use uuid::Uuid;
6
7/// A length-prefixed UTF-8 string.
8///
9/// As opposed to C strings, this string is not null-terminated. If the string is owned, indicated
10/// by the `owned` flag, the owner must call the `free` function on this string. The convention is:
11///
12///  - When obtained as instance through return values, always free the string.
13///  - When obtained as pointer through field access, never free the string.
14#[repr(C)]
15pub struct RelayStr {
16    /// Pointer to the UTF-8 encoded string data.
17    pub data: *mut c_char,
18    /// The length of the string pointed to by `data`.
19    pub len: usize,
20    /// Indicates that the string is owned and must be freed.
21    pub owned: bool,
22}
23
24impl RelayStr {
25    /// Creates a new `RelayStr` by borrowing the given `&str`.
26    pub(crate) fn new(s: &str) -> RelayStr {
27        RelayStr {
28            data: s.as_ptr() as *mut c_char,
29            len: s.len(),
30            owned: false,
31        }
32    }
33
34    /// Creates a new `RelayStr` by assuming ownership over the given `String`.
35    ///
36    /// When dropping this `RelayStr` instance, the buffer is freed.
37    pub(crate) fn from_string(mut s: String) -> RelayStr {
38        s.shrink_to_fit();
39        let rv = RelayStr {
40            data: s.as_ptr() as *mut c_char,
41            len: s.len(),
42            owned: true,
43        };
44        mem::forget(s);
45        rv
46    }
47
48    /// Frees the string buffer if it is owned.
49    pub(crate) unsafe fn free(&mut self) {
50        if self.owned {
51            String::from_raw_parts(self.data as *mut _, self.len, self.len);
52            self.data = ptr::null_mut();
53            self.len = 0;
54            self.owned = false;
55        }
56    }
57
58    /// Returns a borrowed string.
59    pub(crate) unsafe fn as_str(&self) -> &str {
60        str::from_utf8_unchecked(slice::from_raw_parts(self.data as *const _, self.len))
61    }
62}
63
64// RelayStr is immutable, thus it can be Send + Sync
65unsafe impl Sync for RelayStr {}
66unsafe impl Send for RelayStr {}
67
68impl Default for RelayStr {
69    fn default() -> RelayStr {
70        RelayStr {
71            data: ptr::null_mut(),
72            len: 0,
73            owned: false,
74        }
75    }
76}
77
78impl From<String> for RelayStr {
79    fn from(string: String) -> RelayStr {
80        RelayStr::from_string(string)
81    }
82}
83
84impl From<&str> for RelayStr {
85    fn from(string: &str) -> RelayStr {
86        RelayStr::new(string)
87    }
88}
89
90/// Creates a Relay string from a c string.
91#[no_mangle]
92#[relay_ffi::catch_unwind]
93pub unsafe extern "C" fn relay_str_from_cstr(s: *const c_char) -> RelayStr {
94    let s = CStr::from_ptr(s).to_str()?;
95    RelayStr {
96        data: s.as_ptr() as *mut _,
97        len: s.len(),
98        owned: false,
99    }
100}
101
102/// Frees a Relay str.
103///
104/// If the string is marked as not owned then this function does not
105/// do anything.
106#[no_mangle]
107#[relay_ffi::catch_unwind]
108pub unsafe extern "C" fn relay_str_free(s: *mut RelayStr) {
109    if !s.is_null() {
110        (*s).free()
111    }
112}
113
114/// A 16-byte UUID.
115#[repr(C)]
116pub struct RelayUuid {
117    /// UUID bytes in network byte order (big endian).
118    pub data: [u8; 16],
119}
120
121impl RelayUuid {
122    pub(crate) fn new(uuid: Uuid) -> RelayUuid {
123        let data = *uuid.as_bytes();
124        Self { data }
125    }
126}
127
128impl From<Uuid> for RelayUuid {
129    fn from(uuid: Uuid) -> RelayUuid {
130        RelayUuid::new(uuid)
131    }
132}
133
134/// Returns true if the uuid is nil.
135#[no_mangle]
136#[relay_ffi::catch_unwind]
137pub unsafe extern "C" fn relay_uuid_is_nil(uuid: *const RelayUuid) -> bool {
138    if let Ok(uuid) = Uuid::from_slice(&(*uuid).data[..]) {
139        uuid == Uuid::nil()
140    } else {
141        false
142    }
143}
144
145/// Formats the UUID into a string.
146///
147/// The string is newly allocated and needs to be released with
148/// `relay_cstr_free`.
149#[no_mangle]
150#[relay_ffi::catch_unwind]
151pub unsafe extern "C" fn relay_uuid_to_str(uuid: *const RelayUuid) -> RelayStr {
152    let uuid = Uuid::from_slice(&(*uuid).data[..]).unwrap_or_else(|_| Uuid::nil());
153    RelayStr::from_string(uuid.as_hyphenated().to_string())
154}
155
156/// A binary buffer of known length.
157///
158/// If the buffer is owned, indicated by the `owned` flag, the owner must call the `free` function
159/// on this buffer. The convention is:
160///
161///  - When obtained as instance through return values, always free the buffer.
162///  - When obtained as pointer through field access, never free the buffer.
163#[repr(C)]
164pub struct RelayBuf {
165    /// Pointer to the raw data.
166    pub data: *mut u8,
167    /// The length of the buffer pointed to by `data`.
168    pub len: usize,
169    /// Indicates that the buffer is owned and must be freed.
170    pub owned: bool,
171}
172
173impl RelayBuf {
174    pub(crate) unsafe fn free(&mut self) {
175        if self.owned {
176            Vec::from_raw_parts(self.data, self.len, self.len);
177            self.data = ptr::null_mut();
178            self.len = 0;
179            self.owned = false;
180        }
181    }
182
183    pub(crate) unsafe fn as_bytes(&self) -> &[u8] {
184        slice::from_raw_parts(self.data as *const u8, self.len)
185    }
186}
187
188impl Default for RelayBuf {
189    fn default() -> RelayBuf {
190        RelayBuf {
191            data: ptr::null_mut(),
192            len: 0,
193            owned: false,
194        }
195    }
196}
197
198/// Frees a Relay buf.
199///
200/// If the buffer is marked as not owned then this function does not
201/// do anything.
202#[no_mangle]
203#[relay_ffi::catch_unwind]
204pub unsafe extern "C" fn relay_buf_free(b: *mut RelayBuf) {
205    if !b.is_null() {
206        (*b).free()
207    }
208}