taskchampion_lib/
uda.rs

1use crate::traits::*;
2use crate::types::*;
3
4#[ffizz_header::item]
5#[ffizz(order = 500)]
6/// ***** TCUda *****
7///
8/// TCUda contains the details of a UDA.
9///
10/// ```c
11/// typedef struct TCUda {
12///   // Namespace of the UDA.  For legacy UDAs, this may have a NULL ptr field.
13///   struct TCString ns;
14///   // UDA key.  Must not be NULL.
15///   struct TCString key;
16///   // Content of the UDA.  Must not be NULL.
17///   struct TCString value;
18/// } TCUda;
19/// ```
20#[repr(C)]
21#[derive(Default)]
22pub struct TCUda {
23    pub ns: TCString,
24    pub key: TCString,
25    pub value: TCString,
26}
27
28pub(crate) struct Uda {
29    pub ns: Option<RustString<'static>>,
30    pub key: RustString<'static>,
31    pub value: RustString<'static>,
32}
33
34impl PassByValue for TCUda {
35    type RustType = Uda;
36
37    unsafe fn from_ctype(self) -> Self::RustType {
38        Uda {
39            ns: if self.ns.is_null() {
40                None
41            } else {
42                // SAFETY:
43                //  - self is owned, so we can take ownership of this TCString
44                //  - self.ns is a valid, non-null TCString (NULL just checked)
45                Some(unsafe { TCString::val_from_arg(self.ns) })
46            },
47            // SAFETY:
48            //  - self is owned, so we can take ownership of this TCString
49            //  - self.key is a valid, non-null TCString (see type docstring)
50            key: unsafe { TCString::val_from_arg(self.key) },
51            // SAFETY:
52            //  - self is owned, so we can take ownership of this TCString
53            //  - self.value is a valid, non-null TCString (see type docstring)
54            value: unsafe { TCString::val_from_arg(self.value) },
55        }
56    }
57
58    fn as_ctype(uda: Uda) -> Self {
59        TCUda {
60            // SAFETY: caller assumes ownership of this value
61            ns: if let Some(ns) = uda.ns {
62                unsafe { TCString::return_val(ns) }
63            } else {
64                TCString::default()
65            },
66            // SAFETY: caller assumes ownership of this value
67            key: unsafe { TCString::return_val(uda.key) },
68            // SAFETY: caller assumes ownership of this value
69            value: unsafe { TCString::return_val(uda.value) },
70        }
71    }
72}
73
74#[ffizz_header::item]
75#[ffizz(order = 510)]
76/// ***** TCUdaList *****
77///
78/// TCUdaList represents a list of UDAs.
79///
80/// The content of this struct must be treated as read-only.
81///
82/// ```c
83/// typedef struct TCUdaList {
84///   // number of UDAs in items
85///   size_t len;
86///   // reserved
87///   size_t _u1;
88///   // Array of UDAs. These remain owned by the TCUdaList instance and will be freed by
89///   // tc_uda_list_free.  This pointer is never NULL for a valid TCUdaList.
90///   struct TCUda *items;
91/// } TCUdaList;
92/// ```
93#[repr(C)]
94pub struct TCUdaList {
95    /// number of UDAs in items
96    len: libc::size_t,
97
98    /// total size of items (internal use only)
99    _capacity: libc::size_t,
100
101    /// Array of UDAs. These remain owned by the TCUdaList instance and will be freed by
102    /// tc_uda_list_free.  This pointer is never NULL for a valid TCUdaList.
103    items: *mut TCUda,
104}
105
106impl CList for TCUdaList {
107    type Element = TCUda;
108
109    unsafe fn from_raw_parts(items: *mut Self::Element, len: usize, cap: usize) -> Self {
110        TCUdaList {
111            len,
112            _capacity: cap,
113            items,
114        }
115    }
116
117    fn slice(&mut self) -> &mut [Self::Element] {
118        // SAFETY:
119        //  - because we have &mut self, we have read/write access to items[0..len]
120        //  - all items are properly initialized Element's
121        //  - return value lifetime is equal to &mmut self's, so access is exclusive
122        //  - items and len came from Vec, so total size is < isize::MAX
123        unsafe { std::slice::from_raw_parts_mut(self.items, self.len) }
124    }
125
126    fn into_raw_parts(self) -> (*mut Self::Element, usize, usize) {
127        (self.items, self.len, self._capacity)
128    }
129}
130
131#[ffizz_header::item]
132#[ffizz(order = 501)]
133/// Free a TCUda instance.  The instance, and the TCStrings it contains, must not be used
134/// after this call.
135///
136/// ```c
137/// EXTERN_C void tc_uda_free(struct TCUda *tcuda);
138/// ```
139#[no_mangle]
140pub unsafe extern "C" fn tc_uda_free(tcuda: *mut TCUda) {
141    debug_assert!(!tcuda.is_null());
142    // SAFETY:
143    //  - *tcuda is a valid TCUda (caller promises to treat it as read-only)
144    let uda = unsafe { TCUda::take_val_from_arg(tcuda, TCUda::default()) };
145    drop(uda);
146}
147
148#[ffizz_header::item]
149#[ffizz(order = 511)]
150/// Free a TCUdaList instance.  The instance, and all TCUdas it contains, must not be used after
151/// this call.
152///
153/// When this call returns, the `items` pointer will be NULL, signalling an invalid TCUdaList.
154///
155/// ```c
156/// EXTERN_C void tc_uda_list_free(struct TCUdaList *tcudas);
157/// ```
158#[no_mangle]
159pub unsafe extern "C" fn tc_uda_list_free(tcudas: *mut TCUdaList) {
160    // SAFETY:
161    //  - tcudas is not NULL and points to a valid TCUdaList (caller is not allowed to
162    //    modify the list)
163    //  - caller promises not to use the value after return
164    unsafe { drop_value_list(tcudas) }
165}
166
167#[cfg(test)]
168mod test {
169    use super::*;
170
171    #[test]
172    fn empty_list_has_non_null_pointer() {
173        let tcudas = unsafe { TCUdaList::return_val(Vec::new()) };
174        assert!(!tcudas.items.is_null());
175        assert_eq!(tcudas.len, 0);
176        assert_eq!(tcudas._capacity, 0);
177    }
178
179    #[test]
180    fn free_sets_null_pointer() {
181        let mut tcudas = unsafe { TCUdaList::return_val(Vec::new()) };
182        // SAFETY: testing expected behavior
183        unsafe { tc_uda_list_free(&mut tcudas) };
184        assert!(tcudas.items.is_null());
185        assert_eq!(tcudas.len, 0);
186        assert_eq!(tcudas._capacity, 0);
187    }
188}