atc_router/ffi/router.rs
1use crate::context::Context;
2use crate::ffi::ERR_BUF_MAX_LEN;
3use crate::router::Router;
4use crate::schema::Schema;
5use std::cmp::min;
6use std::ffi;
7use std::os::raw::c_char;
8use std::slice::from_raw_parts_mut;
9use uuid::Uuid;
10
11/// Create a new router object associated with the schema.
12///
13/// # Arguments
14///
15/// - `schema`: a valid pointer to the [`Schema`] object returned by [`schema_new`].
16///
17/// # Errors
18///
19/// This function never fails.
20///
21/// # Safety
22///
23/// Violating any of the following constraints will result in undefined behavior:
24///
25/// - `schema` must be a valid pointer returned by [`schema_new`].
26#[no_mangle]
27pub unsafe extern "C" fn router_new(schema: &Schema) -> *mut Router {
28 Box::into_raw(Box::new(Router::new(schema)))
29}
30
31/// Deallocate the router object.
32///
33/// # Errors
34///
35/// This function never fails.
36///
37/// # Safety
38///
39/// Violating any of the following constraints will result in undefined behavior:
40///
41/// - `router` must be a valid pointer returned by [`router_new`].
42#[no_mangle]
43pub unsafe extern "C" fn router_free(router: *mut Router) {
44 drop(Box::from_raw(router));
45}
46
47/// Add a new matcher to the router.
48///
49/// # Arguments
50///
51/// - `router`: a pointer to the [`Router`] object returned by [`router_new`].
52/// - `priority`: the priority of the matcher, higher value means higher priority,
53/// and the matcher with the highest priority will be executed first.
54/// - `uuid`: the C-style string representing the UUID of the matcher.
55/// - `atc`: the C-style string representing the ATC expression.
56/// - `errbuf`: a buffer to store the error message.
57/// - `errbuf_len`: a pointer to the length of the error message buffer.
58///
59/// # Returns
60///
61/// Returns `true` if the matcher was added successfully, otherwise `false`,
62/// and the error message will be stored in the `errbuf`,
63/// and the length of the error message will be stored in `errbuf_len`.
64///
65/// # Errors
66///
67/// This function will return `false` if the matcher could not be added to the router,
68/// such as duplicate UUID, and invalid ATC expression.
69///
70/// # Panics
71///
72/// This function will panic when:
73///
74/// - `uuid` doesn't point to a ASCII sequence representing a valid 128-bit UUID.
75/// - `atc` doesn't point to a valid C-style string.
76///
77/// # Safety
78///
79/// Violating any of the following constraints will result in undefined behavior:
80///
81/// - `router` must be a valid pointer returned by [`router_new`].
82/// - `uuid` must be a valid pointer to a C-style string, must be properly aligned,
83/// and must not have '\0' in the middle.
84/// - `atc` must be a valid pointer to a C-style string, must be properly aligned,
85/// and must not have '\0' in the middle.
86/// - `errbuf` must be valid to read and write for `errbuf_len * size_of::<u8>()` bytes,
87/// and it must be properly aligned.
88/// - `errbuf_len` must be valid to read and write for `size_of::<usize>()` bytes,
89/// and it must be properly aligned.
90#[no_mangle]
91pub unsafe extern "C" fn router_add_matcher(
92 router: &mut Router,
93 priority: usize,
94 uuid: *const i8,
95 atc: *const i8,
96 errbuf: *mut u8,
97 errbuf_len: *mut usize,
98) -> bool {
99 let uuid = ffi::CStr::from_ptr(uuid as *const c_char).to_str().unwrap();
100 let atc = ffi::CStr::from_ptr(atc as *const c_char).to_str().unwrap();
101 let errbuf = from_raw_parts_mut(errbuf, ERR_BUF_MAX_LEN);
102
103 let uuid = Uuid::try_parse(uuid).expect("invalid UUID format");
104
105 if let Err(e) = router.add_matcher(priority, uuid, atc) {
106 let errlen = min(e.len(), *errbuf_len);
107 errbuf[..errlen].copy_from_slice(&e.as_bytes()[..errlen]);
108 *errbuf_len = errlen;
109 return false;
110 }
111
112 true
113}
114
115/// Remove a matcher from the router.
116///
117/// # Arguments
118/// - `router`: a pointer to the [`Router`] object returned by [`router_new`].
119/// - `priority`: the priority of the matcher to be removed.
120/// - `uuid`: the C-style string representing the UUID of the matcher to be removed.
121///
122/// # Returns
123///
124/// Returns `true` if the matcher was removed successfully, otherwise `false`,
125/// such as when the matcher with the specified UUID doesn't exist or
126/// the priority doesn't match the UUID.
127///
128/// # Panics
129///
130/// This function will panic when `uuid` doesn't point to a ASCII sequence
131///
132/// # Safety
133///
134/// Violating any of the following constraints will result in undefined behavior:
135///
136/// - `router` must be a valid pointer returned by [`router_new`].
137/// - `uuid` must be a valid pointer to a C-style string, must be properly aligned,
138/// and must not have '\0' in the middle.
139#[no_mangle]
140pub unsafe extern "C" fn router_remove_matcher(
141 router: &mut Router,
142 priority: usize,
143 uuid: *const i8,
144) -> bool {
145 let uuid = ffi::CStr::from_ptr(uuid as *const c_char).to_str().unwrap();
146 let uuid = Uuid::try_parse(uuid).expect("invalid UUID format");
147
148 router.remove_matcher(priority, uuid)
149}
150
151/// Execute the router with the context.
152///
153/// # Arguments
154///
155/// - `router`: a pointer to the [`Router`] object returned by [`router_new`].
156/// - `context`: a pointer to the [`Context`] object.
157///
158/// # Returns
159///
160/// Returns `true` if found a match, `false` means no match found.
161///
162/// # Safety
163///
164/// Violating any of the following constraints will result in undefined behavior:
165///
166/// - `router` must be a valid pointer returned by [`router_new`].
167/// - `context` must be a valid pointer returned by [`context_new`],
168/// and must be reset by [`context_reset`] before calling this function
169/// if you want to reuse the same context for multiple matches.
170#[no_mangle]
171pub unsafe extern "C" fn router_execute(router: &Router, context: &mut Context) -> bool {
172 router.execute(context)
173}
174
175/// Get the de-duplicated fields that are actually used in the router.
176/// This is useful when you want to know what fields are actually used in the router,
177/// so you can generate their values on-demand.
178///
179/// # Arguments
180///
181/// - `router`: a pointer to the [`Router`] object returned by [`router_new`].
182/// - `fields`: a pointer to an array of pointers to the field names
183/// (NOT C-style strings) that are actually used in the router, which will be filled in.
184/// if `fields` is `NULL`, this function will only return the number of fields used
185/// in the router.
186/// - `fields_len`: a pointer to an array of the length of each field name.
187///
188/// # Lifetimes
189///
190/// The string pointers stored in `fields` might be invalidated if any of the following
191/// operations are happened:
192///
193/// - The `router` was deallocated.
194/// - A new matcher was added to the `router`.
195/// - A matcher was removed from the `router`.
196///
197/// # Returns
198///
199/// Returns the number of fields that are actually used in the router.
200///
201/// # Errors
202///
203/// This function never fails.
204///
205/// # Safety
206///
207/// Violating any of the following constraints will result in undefined behavior:
208///
209/// - `router` must be a valid pointer returned by [`router_new`].
210/// - If `fields` is not `NULL`, `fields` must be valid to read and write for
211/// `fields_len * size_of::<*const u8>()` bytes, and it must be properly aligned.
212/// - If `fields` is not `NULL`, `fields_len` must be valid to read and write for
213/// `size_of::<usize>()` bytes, and it must be properly aligned.
214/// - DO NOT write the memory pointed by the elements of `fields`.
215/// - DO NOT access the memory pointed by the elements of `fields`
216/// after it becomes invalid, see the `Lifetimes` section.
217#[no_mangle]
218pub unsafe extern "C" fn router_get_fields(
219 router: &Router,
220 fields: *mut *const u8,
221 fields_len: *mut usize,
222) -> usize {
223 if !fields.is_null() {
224 assert!(!fields_len.is_null());
225 assert!(*fields_len >= router.fields.len());
226
227 let fields = from_raw_parts_mut(fields, *fields_len);
228 let fields_len = from_raw_parts_mut(fields_len, *fields_len);
229
230 for (i, k) in router.fields.keys().enumerate() {
231 fields[i] = k.as_bytes().as_ptr();
232 fields_len[i] = k.len()
233 }
234 }
235
236 router.fields.len()
237}
238
239#[cfg(test)]
240mod tests {
241 use super::*;
242
243 #[test]
244 fn test_long_error_message() {
245 unsafe {
246 let schema = Schema::default();
247 let mut router = Router::new(&schema);
248 let uuid = ffi::CString::new("a921a9aa-ec0e-4cf3-a6cc-1aa5583d150c").unwrap();
249 let junk = ffi::CString::new(vec![b'a'; ERR_BUF_MAX_LEN * 2]).unwrap();
250 let mut errbuf = vec![b'X'; ERR_BUF_MAX_LEN];
251 let mut errbuf_len = ERR_BUF_MAX_LEN;
252
253 let result = router_add_matcher(
254 &mut router,
255 1,
256 uuid.as_ptr() as *const i8,
257 junk.as_ptr() as *const i8,
258 errbuf.as_mut_ptr(),
259 &mut errbuf_len,
260 );
261 assert_eq!(result, false);
262 assert_eq!(errbuf_len, ERR_BUF_MAX_LEN);
263 }
264 }
265
266 #[test]
267 fn test_short_error_message() {
268 unsafe {
269 let schema = Schema::default();
270 let mut router = Router::new(&schema);
271 let uuid = ffi::CString::new("a921a9aa-ec0e-4cf3-a6cc-1aa5583d150c").unwrap();
272 let junk = ffi::CString::new("aaaa").unwrap();
273 let mut errbuf = vec![b'X'; ERR_BUF_MAX_LEN];
274 let mut errbuf_len = ERR_BUF_MAX_LEN;
275
276 let result = router_add_matcher(
277 &mut router,
278 1,
279 uuid.as_ptr() as *const i8,
280 junk.as_ptr() as *const i8,
281 errbuf.as_mut_ptr(),
282 &mut errbuf_len,
283 );
284 assert_eq!(result, false);
285 assert!(errbuf_len < ERR_BUF_MAX_LEN);
286 }
287 }
288}