uuid/
uuid.rs

1#![warn(unsafe_op_in_unsafe_fn)]
2#![allow(non_camel_case_types)]
3#![allow(clippy::missing_safety_doc)]
4#![allow(unused_unsafe)]
5
6use ffizz_passby::Value;
7use libc::c_char;
8use std::ffi::CStr;
9use uuid::Uuid;
10
11/// Length, in bytes, of the string representation of a UUID (without NUL terminator)
12pub const UUID_STRING_BYTES: usize = 36;
13
14/// uuid_t contains a UUID, represented as big-endian bytes.
15#[derive(Clone, Copy)]
16#[repr(C)]
17pub struct uuid_t([u8; 16]);
18
19type UuidValue = Value<Uuid, uuid_t>;
20
21impl Into<Uuid> for uuid_t {
22    fn into(self) -> Uuid {
23        // SAFETY:
24        //  - any 16-byte value is a valid Uuid
25        uuid::Uuid::from_bytes(self.0)
26    }
27}
28impl From<Uuid> for uuid_t {
29    fn from(rval: Uuid) -> uuid_t {
30        uuid_t(*rval.as_bytes())
31    }
32}
33
34/// Create a new, randomly-generated UUID.
35#[no_mangle]
36pub unsafe extern "C" fn uuid_new_v4() -> uuid_t {
37    // SAFETY:
38    // - value is not allocated
39    unsafe { UuidValue::return_val(uuid::Uuid::new_v4()) }
40}
41
42/// Create a new UUID with the nil value.
43#[no_mangle]
44pub unsafe extern "C" fn uuid_nil() -> uuid_t {
45    // SAFETY:
46    // - value is not allocated
47    unsafe { UuidValue::return_val(uuid::Uuid::nil()) }
48}
49
50/// Get the version of the given UUID.
51#[no_mangle]
52pub unsafe extern "C" fn uuid_version(uuid: uuid_t) -> usize {
53    // SAFETY:
54    //  - tcuuid is a valid uuid_t (all byte patterns are valid)
55    let uuid = unsafe { UuidValue::take(uuid) };
56    uuid.get_version_num()
57}
58
59/// Write the string representation of a uuid_t into the given buffer, which must be
60/// at least UUID_STRING_BYTES long.  No NUL terminator is added.
61///
62/// # Safety
63///
64/// * buf must point to at least UUID_STRING_BYTES of valid memory.
65#[no_mangle]
66pub unsafe extern "C" fn uuid_to_buf(tcuuid: uuid_t, buf: *mut c_char) {
67    debug_assert!(!buf.is_null());
68    // SAFETY:
69    //  - buf is valid for len bytes (by C convention)
70    //  - (no alignment requirements for a byte slice)
71    //  - content of buf will not be mutated during the lifetime of this slice (lifetime
72    //    does not outlive this function call)
73    //  - the length of the buffer is less than isize::MAX (see docstring)
74    let buf: &mut [u8] =
75        unsafe { std::slice::from_raw_parts_mut(buf as *mut u8, UUID_STRING_BYTES) };
76    // SAFETY:
77    //  - tcuuid is a valid uuid_t (all byte patterns are valid)
78    let uuid = unsafe { UuidValue::take(tcuuid) };
79    uuid.as_hyphenated().encode_lower(buf);
80}
81
82/// Parse the given string as a UUID.  Returns false on parse failure or if the given
83/// string is not valid.
84///
85/// # Safety
86///
87/// * s must be non-NULL and point to a valid NUL-terminated string.
88/// * s is not modified by this function.
89/// * uuid_out must be non-NULL and point to a valid memory location for a uuid_t.
90#[no_mangle]
91pub unsafe extern "C" fn uuid_from_str(s: *const c_char, uuid_out: *mut uuid_t) -> bool {
92    debug_assert!(!s.is_null());
93    debug_assert!(!uuid_out.is_null());
94    // SAFETY:
95    //  - s is valid (see docstring)
96    let s = unsafe { CStr::from_ptr(s) };
97    if let Ok(s) = s.to_str() {
98        if let Ok(u) = uuid::Uuid::parse_str(s) {
99            // SAFETY:
100            //  - uuid_out is not NULL (see docstring)
101            //  - alignment is not required
102            unsafe { UuidValue::to_out_param(u, uuid_out) };
103            return true;
104        }
105    }
106    false
107}
108
109fn main() {
110    let u = unsafe { uuid_new_v4() };
111    assert_eq!(unsafe { uuid_version(u) }, 4);
112
113    let u = unsafe { uuid_nil() };
114    assert_eq!(unsafe { uuid_version(u) }, 0);
115
116    let mut buf = [0u8; UUID_STRING_BYTES];
117    unsafe { uuid_to_buf(u, buf.as_mut_ptr() as *mut c_char) };
118    assert_eq!(
119        std::str::from_utf8(&buf[..]).expect("invalid utf-8"),
120        "00000000-0000-0000-0000-000000000000"
121    );
122
123    let mut u = unsafe { uuid_nil() };
124    assert!(unsafe {
125        uuid_from_str(
126            // (cheating a little, by creating a C string with explicit trailing NUL)
127            "d9c5d004-1bf4-11ed-861d-0242ac120002\0".as_ptr() as *const c_char,
128            &mut u as *mut uuid_t,
129        )
130    });
131    assert_eq!(unsafe { uuid_version(u) }, 1);
132}