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}