taskchampion_lib/
kv.rs

1use crate::traits::*;
2use crate::types::*;
3
4#[ffizz_header::item]
5#[ffizz(order = 600)]
6/// ***** TCKV *****
7///
8/// TCKV contains a key/value pair that is part of a task.
9///
10/// Neither key nor value are ever NULL.  They remain owned by the TCKV and
11/// will be freed when it is freed with tc_kv_list_free.
12///
13/// ```c
14/// typedef struct TCKV {
15///   struct TCString key;
16///   struct TCString value;
17/// } TCKV;
18/// ```
19#[repr(C)]
20#[derive(Debug)]
21pub struct TCKV {
22    pub key: TCString,
23    pub value: TCString,
24}
25
26impl PassByValue for TCKV {
27    type RustType = (RustString<'static>, RustString<'static>);
28
29    unsafe fn from_ctype(self) -> Self::RustType {
30        // SAFETY:
31        //  - self.key is not NULL (field docstring)
32        //  - self.key came from return_ptr in as_ctype
33        //  - self is owned, so we can take ownership of this TCString
34        let key = unsafe { TCString::val_from_arg(self.key) };
35        // SAFETY: (same)
36        let value = unsafe { TCString::val_from_arg(self.value) };
37        (key, value)
38    }
39
40    fn as_ctype((key, value): Self::RustType) -> Self {
41        TCKV {
42            // SAFETY:
43            //  - ownership of the TCString tied to ownership of Self
44            key: unsafe { TCString::return_val(key) },
45            // SAFETY:
46            //  - ownership of the TCString tied to ownership of Self
47            value: unsafe { TCString::return_val(value) },
48        }
49    }
50}
51
52#[ffizz_header::item]
53#[ffizz(order = 610)]
54/// ***** TCKVList *****
55///
56/// TCKVList represents a list of key/value pairs.
57///
58/// The content of this struct must be treated as read-only.
59///
60/// ```c
61/// typedef struct TCKVList {
62///   // number of key/value pairs in items
63///   size_t len;
64///   // reserved
65///   size_t _u1;
66///   // Array of TCKV's. These remain owned by the TCKVList instance and will be freed by
67///   // tc_kv_list_free.  This pointer is never NULL for a valid TCKVList.
68///   struct TCKV *items;
69/// } TCKVList;
70/// ```
71#[repr(C)]
72#[derive(Debug)]
73pub struct TCKVList {
74    pub len: libc::size_t,
75    /// total size of items (internal use only)
76    pub _capacity: libc::size_t,
77    pub items: *mut TCKV,
78}
79
80impl CList for TCKVList {
81    type Element = TCKV;
82
83    unsafe fn from_raw_parts(items: *mut Self::Element, len: usize, cap: usize) -> Self {
84        TCKVList {
85            len,
86            _capacity: cap,
87            items,
88        }
89    }
90
91    fn slice(&mut self) -> &mut [Self::Element] {
92        // SAFETY:
93        //  - because we have &mut self, we have read/write access to items[0..len]
94        //  - all items are properly initialized Element's
95        //  - return value lifetime is equal to &mmut self's, so access is exclusive
96        //  - items and len came from Vec, so total size is < isize::MAX
97        unsafe { std::slice::from_raw_parts_mut(self.items, self.len) }
98    }
99
100    fn into_raw_parts(self) -> (*mut Self::Element, usize, usize) {
101        (self.items, self.len, self._capacity)
102    }
103}
104
105impl Default for TCKVList {
106    fn default() -> Self {
107        // SAFETY:
108        //  - caller will free this list
109        unsafe { TCKVList::return_val(Vec::new()) }
110    }
111}
112
113// NOTE: callers never have a TCKV that is not in a list, so there is no tc_kv_free.
114
115#[ffizz_header::item]
116#[ffizz(order = 611)]
117/// Free a TCKVList instance.  The instance, and all TCKVs it contains, must not be used after
118/// this call.
119///
120/// When this call returns, the `items` pointer will be NULL, signalling an invalid TCKVList.
121///
122/// ```c
123/// EXTERN_C void tc_kv_list_free(struct TCKVList *tckvs);
124/// ```
125#[no_mangle]
126pub unsafe extern "C" fn tc_kv_list_free(tckvs: *mut TCKVList) {
127    // SAFETY:
128    //  - tckvs is not NULL and points to a valid TCKVList (caller is not allowed to
129    //    modify the list)
130    //  - caller promises not to use the value after return
131    unsafe { drop_value_list(tckvs) }
132}
133
134#[cfg(test)]
135mod test {
136    use super::*;
137
138    #[test]
139    fn empty_list_has_non_null_pointer() {
140        let tckvs = unsafe { TCKVList::return_val(Vec::new()) };
141        assert!(!tckvs.items.is_null());
142        assert_eq!(tckvs.len, 0);
143        assert_eq!(tckvs._capacity, 0);
144    }
145
146    #[test]
147    fn free_sets_null_pointer() {
148        let mut tckvs = unsafe { TCKVList::return_val(Vec::new()) };
149        // SAFETY: testing expected behavior
150        unsafe { tc_kv_list_free(&mut tckvs) };
151        assert!(tckvs.items.is_null());
152        assert_eq!(tckvs.len, 0);
153        assert_eq!(tckvs._capacity, 0);
154    }
155}