Skip to main content

libddwaf_sys/
lib.rs

1#![crate_type = "dylib"]
2#![deny(clippy::correctness, clippy::perf, clippy::style, clippy::suspicious)]
3#![allow(unused)]
4#![allow(non_camel_case_types)]
5#![allow(non_snake_case)]
6#![allow(non_upper_case_globals)]
7#![allow(unsafe_op_in_unsafe_fn)] // Bindgen generates some offending code...
8
9use std::alloc::Layout;
10use std::ptr::null;
11use std::slice;
12
13include!(concat!(env!("OUT_DIR"), "/bindings.rs"));
14
15#[cfg(feature = "dynamic")]
16mod dylib;
17#[cfg(feature = "dynamic")]
18pub use dylib::*;
19
20// Implement [Send] and [Sync] for [ddwaf_object]. There is nothing thread unsafe about these unless
21// its pointers are dereferences, which is inherently unsafe anyway.
22unsafe impl Send for ddwaf_object {}
23unsafe impl Sync for ddwaf_object {}
24
25#[warn(clippy::pedantic)]
26impl ddwaf_object {
27    /// Drops the array data associated with the receiving [`ddwaf_object`].
28    ///
29    /// # Safety
30    /// - The [`ddwaf_object`] must be a valid representation of an array.
31    /// - The array must be an [`std::alloc::alloc`]ated array of [`ddwaf_object`] of the proper size.
32    /// - The individual elements of the array must be valid [`ddwaf_object`]s that can be dropped
33    ///   with [`ddwaf_object::drop_object`].
34    #[allow(clippy::missing_panics_doc)]
35    pub unsafe fn drop_array(&mut self) {
36        debug_assert_eq!(self.obj_type(), DDWAF_OBJ_ARRAY);
37        let array = unsafe { self.via.array };
38        if array.capacity == 0 {
39            return;
40        }
41        for i in 0..array.size {
42            // We don't support 16-bit isize, so the unsigned -> signed cast is safe.
43            #[allow(clippy::cast_possible_wrap)]
44            let elem = unsafe { &mut *array.ptr.offset(i as isize) };
45            unsafe { elem.drop_object() };
46        }
47        // Could only panic if sizeof(ddwaf_object) * capacity > isize::MAX, which can't happen
48        // given that capacity is limited to u16::MAX.
49        let layout = Layout::array::<ddwaf_object>(array.capacity as usize).unwrap();
50        unsafe { std::alloc::dealloc(array.ptr.cast(), layout) };
51    }
52
53    /// Drops the map data associated with the receiving [`ddwaf_object`].
54    ///
55    /// # Safety
56    /// - The [`ddwaf_object`] must be a valid representation of a map.
57    /// - The map must be an [`std::alloc::alloc`]ated array of [`ddwaf_object`] of the proper size.
58    /// - The individual elements of the map must be valid [`ddwaf_object`]s that can be dropped with
59    ///   both [`ddwaf_object::drop_object`] and [`ddwaf_object::drop_key`].
60    #[allow(clippy::missing_panics_doc)]
61    pub unsafe fn drop_map(&mut self) {
62        debug_assert_eq!(self.obj_type(), DDWAF_OBJ_MAP);
63        let map = unsafe { self.via.map };
64        if map.capacity == 0 {
65            return;
66        }
67        for i in 0..map.size {
68            #[allow(clippy::cast_possible_wrap)]
69            let elem = unsafe { &mut *map.ptr.offset(i as isize) };
70            unsafe { elem.key.drop_object() };
71            unsafe { elem.val.drop_object() };
72        }
73        let layout = Layout::array::<_ddwaf_object_kv>(map.capacity as usize).unwrap();
74        unsafe { std::alloc::dealloc(map.ptr.cast(), layout) };
75    }
76
77    /// Drops the value associated with the receiving [`ddwaf_object`].
78    ///
79    /// # Safety
80    /// If the [`ddwaf_object`] is a string, array, or map, the respective requirements of the
81    /// [`ddwaf_object::drop_string`], [`ddwaf_object::drop_array`], or [`ddwaf_object::drop_map`]
82    /// methods apply.
83    /// The method can't be called more than once.
84    pub unsafe fn drop_object(&mut self) {
85        match self.obj_type() {
86            DDWAF_OBJ_STRING => unsafe { self.drop_string() },
87            DDWAF_OBJ_ARRAY => unsafe { self.drop_array() },
88            DDWAF_OBJ_MAP => unsafe { self.drop_map() },
89            _ => { /* nothing to do */ }
90        }
91    }
92
93    /// Drops the regular string associated with the receiving [`ddwaf_object`].
94    ///
95    /// # Safety
96    /// - The [`ddwaf_object`] must be a valid representation of a string
97    /// - The [`_ddwaf_object__bindgen_ty_1::str_`] field must have a
98    ///   [`_ddwaf_object_string::ptr`] set from an allocation of `c_char` of the
99    ///   size indicated by the [`_ddwaf_object_string::size`] field done with [`std::alloc::alloc`].
100    #[allow(clippy::missing_panics_doc)]
101    pub unsafe fn drop_string(&mut self) {
102        debug_assert_eq!(self.obj_type(), DDWAF_OBJ_STRING);
103        let sval = unsafe { self.via.str_.ptr };
104        if sval.is_null() {
105            return;
106        }
107        unsafe {
108            std::alloc::dealloc(
109                sval.cast(),
110                Layout::array::<::std::os::raw::c_char>(self.via.str_.size as usize).unwrap(),
111            );
112        }
113    }
114
115    /// Returns the type of the [`ddwaf_object`]
116    #[must_use]
117    pub fn obj_type(&self) -> DDWAF_OBJ_TYPE {
118        DDWAF_OBJ_TYPE::from(unsafe { self.type_ })
119    }
120
121    /// Returns true if the [`ddwaf_object`] is a string.
122    #[must_use]
123    pub fn is_string(&self) -> bool {
124        (self.obj_type() & DDWAF_OBJ_STRING) != 0
125    }
126
127    /// Returns a slice of the bytes from the string associated with the receiving [`ddwaf_object`].
128    ///
129    /// # Safety
130    /// - The [`ddwaf_object`] must be a valid representation of a string.
131    unsafe fn string_vec(&self) -> &[u8] {
132        debug_assert!(self.is_string());
133
134        if self.obj_type() == DDWAF_OBJ_STRING || self.obj_type() == DDWAF_OBJ_LITERAL_STRING {
135            let str = unsafe { self.via.str_ };
136            if str.size == 0 {
137                return &[];
138            }
139            unsafe { slice::from_raw_parts(str.ptr.cast(), str.size as usize) }
140        } else {
141            let sstr = unsafe { &self.via.sstr };
142            let data = &sstr.data[..sstr.size as usize];
143            // reinterpret &[i8] as &[u8]
144            unsafe { std::slice::from_raw_parts(data.as_ptr().cast(), data.len()) }
145        }
146    }
147}
148
149impl std::cmp::PartialEq<ddwaf_object> for ddwaf_object {
150    fn eq(&self, other: &ddwaf_object) -> bool {
151        if self.is_string() && other.is_string() {
152            let left = unsafe { self.string_vec() };
153            let right = unsafe { other.string_vec() };
154            return left == right;
155        }
156
157        if unsafe { self.type_ != other.type_ } {
158            return false;
159        }
160        match self.obj_type() {
161            DDWAF_OBJ_INVALID | DDWAF_OBJ_NULL => true,
162            DDWAF_OBJ_SIGNED => unsafe { self.via.i64_.val == other.via.i64_.val },
163            DDWAF_OBJ_UNSIGNED => unsafe { self.via.u64_.val == other.via.u64_.val },
164            DDWAF_OBJ_BOOL => unsafe { self.via.b8.val == other.via.b8.val },
165            DDWAF_OBJ_FLOAT => unsafe { self.via.f64_.val == other.via.f64_.val },
166            DDWAF_OBJ_ARRAY => {
167                let left = unsafe { self.via.array };
168                let right = unsafe { other.via.array };
169                if left.size != right.size {
170                    return false;
171                }
172                if left.size == 0 {
173                    return true;
174                }
175                for i in 0..left.size {
176                    let left = unsafe { &*left.ptr.offset(i as isize) };
177                    let right = unsafe { &*right.ptr.offset(i as isize) };
178                    if left != right {
179                        return false;
180                    }
181                }
182                true
183            }
184            DDWAF_OBJ_MAP => {
185                let left = unsafe { self.via.map };
186                let right = unsafe { other.via.map };
187                if left.size != right.size {
188                    return false;
189                }
190                if left.size == 0 {
191                    return true;
192                }
193                for i in 0..left.size {
194                    let left = unsafe { &*left.ptr.offset(i as isize) };
195                    let right = unsafe { &*right.ptr.offset(i as isize) };
196                    if left.key != right.key || left.val != right.val {
197                        return false;
198                    }
199                }
200                true
201            }
202
203            _ => false,
204        }
205    }
206}
207impl std::fmt::Debug for ddwaf_object {
208    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
209        let mut dbg = f.debug_struct("ddwaf_object");
210        match self.obj_type() {
211            DDWAF_OBJ_BOOL => dbg
212                .field("type", &stringify!(DDWAF_OBJ_BOOL))
213                .field("boolean", unsafe { &self.via.b8.val }),
214            DDWAF_OBJ_FLOAT => dbg
215                .field("type", &stringify!(DDWAF_OBJ_FLOAT))
216                .field("f64", unsafe { &self.via.f64_.val }),
217            DDWAF_OBJ_SIGNED => dbg
218                .field("type", &stringify!(DDWAF_OBJ_SIGNED))
219                .field("int", unsafe { &self.via.i64_.val }),
220            DDWAF_OBJ_UNSIGNED => dbg
221                .field("type", &stringify!(DDWAF_OBJ_UNSIGNED))
222                .field("uint", unsafe { &self.via.u64_.val }),
223            DDWAF_OBJ_STRING | DDWAF_OBJ_LITERAL_STRING => {
224                let sval = unsafe { self.string_vec() };
225                let sval = String::from_utf8_lossy(sval);
226                dbg.field(
227                    "type",
228                    if self.obj_type() == DDWAF_OBJ_STRING {
229                        &stringify!(DDWAF_OBJ_STRING)
230                    } else {
231                        &stringify!(DDWAF_OBJ_LITERAL_STRING)
232                    },
233                )
234                .field("string", &sval)
235            }
236            DDWAF_OBJ_SMALL_STRING => {
237                let sval = unsafe { self.string_vec() };
238                let sval = String::from_utf8_lossy(sval);
239                dbg.field("type", &stringify!(DDWAF_OBJ_SMALL_STRING))
240                    .field("string", &sval)
241            }
242            DDWAF_OBJ_ARRAY => {
243                let array = unsafe { self.via.array };
244                let array: &[ddwaf_object] =
245                    unsafe { slice::from_raw_parts(array.ptr.cast(), array.size as usize) };
246                dbg.field("type", &stringify!(DDWAF_OBJ_ARRAY))
247                    .field("array", &array)
248            }
249            DDWAF_OBJ_MAP => {
250                let map = unsafe { self.via.map };
251                let map: &[_ddwaf_object_kv] =
252                    unsafe { slice::from_raw_parts(map.ptr.cast(), map.size as usize) };
253                dbg.field("type", &stringify!(DDWAF_OBJ_MAP))
254                    .field("map", &map)
255            }
256            DDWAF_OBJ_NULL => dbg.field("type", &stringify!(DDWAF_OBJ_NULL)),
257            DDWAF_OBJ_INVALID => dbg.field("type", &stringify!(DDWAF_OBJ_INVALID)),
258            unknown => dbg.field("type", &unknown),
259        };
260
261        dbg.finish_non_exhaustive()
262    }
263}
264
265impl std::fmt::Debug for _ddwaf_object_kv {
266    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
267        let mut dbg = f.debug_struct("ddwaf_object_kv");
268        dbg.field("key", &self.key)
269            .field("val", &self.val)
270            .finish_non_exhaustive()
271    }
272}