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}