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#![allow(clippy::missing_safety_doc)] // Bindgen generates undocumented unsafe bitfield accessors.
9#![allow(clippy::ptr_offset_with_cast)] // Bindgen uses offset when accessing bitfield storage.
10#![allow(clippy::unnecessary_cast)] // Bindgen casts bitfield values to their existing type.
11#![allow(clippy::useless_transmute)] // Bindgen emits identity transmutes for unsigned bitfields.
12
13#[cfg(any(feature = "source-static", feature = "source-shared"))]
14extern crate libddwaf_src;
15
16use std::alloc::Layout;
17use std::ptr::null;
18use std::slice;
19
20include!(concat!(env!("OUT_DIR"), "/bindings.rs"));
21
22#[cfg(libddwaf_windows_dll)]
23#[no_mangle]
24pub unsafe extern "C" fn ddwaf_object_set_string_nocopy(
25    object: *mut ddwaf_object,
26    string: *const std::os::raw::c_char,
27    length: u32,
28) -> *mut ddwaf_object {
29    if object.is_null() || string.is_null() {
30        return std::ptr::null_mut();
31    }
32
33    unsafe {
34        (*object).via.str_ = _ddwaf_object_string {
35            type_: DDWAF_OBJ_STRING as u8,
36            size: length,
37            ptr: string.cast_mut(),
38        };
39    }
40    object
41}
42
43#[cfg(feature = "dynamic")]
44mod dylib;
45#[cfg(feature = "dynamic")]
46pub use dylib::*;
47
48// Implement [Send] and [Sync] for [ddwaf_object]. There is nothing thread unsafe about these unless
49// its pointers are dereferences, which is inherently unsafe anyway.
50unsafe impl Send for ddwaf_object {}
51unsafe impl Sync for ddwaf_object {}
52
53#[warn(clippy::pedantic)]
54impl ddwaf_object {
55    /// Drops the array data associated with the receiving [`ddwaf_object`].
56    ///
57    /// # Safety
58    /// - The [`ddwaf_object`] must be a valid representation of an array.
59    /// - The array must be an [`std::alloc::alloc`]ated array of [`ddwaf_object`] of the proper size.
60    /// - The individual elements of the array must be valid [`ddwaf_object`]s that can be dropped
61    ///   with [`ddwaf_object::drop_object`].
62    ///
63    /// # Panics
64    /// Panics if the capacity is too large to construct a valid allocation layout. This is only
65    /// possible on 32-bit targets because the capacity is limited to 28 bits.
66    pub unsafe fn drop_array(&mut self) {
67        debug_assert!(self.is_array());
68        let size = self.array_len();
69        let capacity = self.array_capacity();
70        if capacity == 0 {
71            return;
72        }
73        let ptr = self.array_ptr();
74        for i in 0..size {
75            let elem = unsafe { &mut *ptr.add(i) };
76            unsafe { elem.drop_object() };
77        }
78        let layout = Layout::array::<ddwaf_object>(capacity).unwrap();
79        unsafe { std::alloc::dealloc(ptr.cast(), layout) };
80    }
81
82    /// Drops the map data associated with the receiving [`ddwaf_object`].
83    ///
84    /// # Safety
85    /// - The [`ddwaf_object`] must be a valid representation of a map.
86    /// - The map must be an [`std::alloc::alloc`]ated array of [`ddwaf_object`] of the proper size.
87    /// - The individual elements of the map must be valid [`ddwaf_object`]s that can be dropped with
88    ///   both [`ddwaf_object::drop_object`] and [`ddwaf_object::drop_key`].
89    ///
90    /// # Panics
91    /// Panics if the capacity is too large to construct a valid allocation layout. This is only
92    /// possible on 32-bit targets because the capacity is limited to 28 bits.
93    pub unsafe fn drop_map(&mut self) {
94        debug_assert!(self.is_map());
95        let size = self.map_len();
96        let capacity = self.map_capacity();
97        if capacity == 0 {
98            return;
99        }
100        let ptr = self.map_ptr();
101        for i in 0..size {
102            let elem = unsafe { &mut *ptr.add(i) };
103            unsafe { elem.key.drop_object() };
104            unsafe { elem.val.drop_object() };
105        }
106        let layout = Layout::array::<_ddwaf_object_kv>(capacity).unwrap();
107        unsafe { std::alloc::dealloc(ptr.cast(), layout) };
108    }
109
110    /// Drops the value associated with the receiving [`ddwaf_object`].
111    ///
112    /// # Safety
113    /// If the [`ddwaf_object`] is a string, array, or map, the respective requirements of the
114    /// [`ddwaf_object::drop_string`], [`ddwaf_object::drop_array`], or [`ddwaf_object::drop_map`]
115    /// methods apply.
116    /// The method can't be called more than once.
117    pub unsafe fn drop_object(&mut self) {
118        match self.obj_type() {
119            DDWAF_OBJ_STRING => unsafe { self.drop_string() },
120            DDWAF_OBJ_ARRAY | DDWAF_OBJ_LARGE_ARRAY => unsafe { self.drop_array() },
121            DDWAF_OBJ_MAP | DDWAF_OBJ_LARGE_MAP => unsafe { self.drop_map() },
122            _ => { /* nothing to do */ }
123        }
124    }
125
126    /// Drops the regular string associated with the receiving [`ddwaf_object`].
127    ///
128    /// # Safety
129    /// - The [`ddwaf_object`] must be a valid representation of a string
130    /// - The [`_ddwaf_object__bindgen_ty_1::str_`] field must have a
131    ///   [`_ddwaf_object_string::ptr`] set from an allocation of `c_char` of the
132    ///   size indicated by the [`_ddwaf_object_string::size`] field done with [`std::alloc::alloc`].
133    #[allow(clippy::missing_panics_doc)]
134    pub unsafe fn drop_string(&mut self) {
135        debug_assert_eq!(self.obj_type(), DDWAF_OBJ_STRING);
136        let sval = unsafe { self.via.str_.ptr };
137        if sval.is_null() {
138            return;
139        }
140        unsafe {
141            std::alloc::dealloc(
142                sval.cast(),
143                Layout::array::<::std::os::raw::c_char>(self.via.str_.size as usize).unwrap(),
144            );
145        }
146    }
147
148    /// Returns the type of the [`ddwaf_object`]
149    #[must_use]
150    pub fn obj_type(&self) -> DDWAF_OBJ_TYPE {
151        DDWAF_OBJ_TYPE::from(unsafe { self.type_ })
152    }
153
154    /// Returns true if the [`ddwaf_object`] is a string.
155    #[must_use]
156    pub fn is_string(&self) -> bool {
157        (self.obj_type() & DDWAF_OBJ_STRING) != 0
158    }
159
160    /// Returns true if the [`ddwaf_object`] is either array representation.
161    #[must_use]
162    pub fn is_array(&self) -> bool {
163        matches!(self.obj_type(), DDWAF_OBJ_ARRAY | DDWAF_OBJ_LARGE_ARRAY)
164    }
165
166    /// Returns true if the [`ddwaf_object`] is either map representation.
167    #[must_use]
168    pub fn is_map(&self) -> bool {
169        matches!(self.obj_type(), DDWAF_OBJ_MAP | DDWAF_OBJ_LARGE_MAP)
170    }
171
172    /// Returns the length of the array associated with the receiving [`ddwaf_object`].
173    ///
174    /// # Panics
175    /// Panics if the object is not an array.
176    #[must_use]
177    #[allow(clippy::cast_possible_truncation)]
178    pub fn array_len(&self) -> usize {
179        match self.obj_type() {
180            DDWAF_OBJ_ARRAY => usize::from(unsafe { self.via.array.size }),
181            DDWAF_OBJ_LARGE_ARRAY => unsafe { self.via.large_array.size() as usize },
182            object_type => panic!("object of type {object_type} is not an array"),
183        }
184    }
185
186    /// Returns the capacity of the array associated with the receiving [`ddwaf_object`].
187    ///
188    /// # Panics
189    /// Panics if the object is not an array.
190    #[must_use]
191    #[allow(clippy::cast_possible_truncation)]
192    pub fn array_capacity(&self) -> usize {
193        match self.obj_type() {
194            DDWAF_OBJ_ARRAY => usize::from(unsafe { self.via.array.capacity }),
195            DDWAF_OBJ_LARGE_ARRAY => unsafe { self.via.large_array.capacity() as usize },
196            object_type => panic!("object of type {object_type} is not an array"),
197        }
198    }
199
200    /// Returns the element pointer of the array associated with the receiving [`ddwaf_object`].
201    ///
202    /// # Panics
203    /// Panics if the object is not an array.
204    #[must_use]
205    pub fn array_ptr(&self) -> *mut ddwaf_object {
206        match self.obj_type() {
207            DDWAF_OBJ_ARRAY => unsafe { self.via.array.ptr },
208            DDWAF_OBJ_LARGE_ARRAY => unsafe { self.via.large_array.ptr },
209            object_type => panic!("object of type {object_type} is not an array"),
210        }
211    }
212
213    /// Changes the length of the array associated with the receiving [`ddwaf_object`].
214    ///
215    /// # Safety
216    /// Every element before `new_len` must be initialized and valid to drop.
217    ///
218    /// # Panics
219    /// Panics if `new_len` exceeds the array's capacity.
220    pub unsafe fn set_array_len(&mut self, new_len: usize) {
221        assert!(new_len <= self.array_capacity());
222        match self.obj_type() {
223            DDWAF_OBJ_ARRAY => {
224                self.via.array.size = new_len.try_into().expect("compact array length overflow");
225            }
226            DDWAF_OBJ_LARGE_ARRAY => unsafe {
227                self.via.large_array.set_size(new_len as u64);
228            },
229            _ => unreachable!(),
230        }
231    }
232
233    /// Returns the length of the map associated with the receiving [`ddwaf_object`].
234    ///
235    /// # Panics
236    /// Panics if the object is not a map.
237    #[must_use]
238    #[allow(clippy::cast_possible_truncation)]
239    pub fn map_len(&self) -> usize {
240        match self.obj_type() {
241            DDWAF_OBJ_MAP => usize::from(unsafe { self.via.map.size }),
242            DDWAF_OBJ_LARGE_MAP => unsafe { self.via.large_map.size() as usize },
243            object_type => panic!("object of type {object_type} is not a map"),
244        }
245    }
246
247    /// Returns the capacity of the map associated with the receiving [`ddwaf_object`].
248    ///
249    /// # Panics
250    /// Panics if the object is not a map.
251    #[must_use]
252    #[allow(clippy::cast_possible_truncation)]
253    pub fn map_capacity(&self) -> usize {
254        match self.obj_type() {
255            DDWAF_OBJ_MAP => usize::from(unsafe { self.via.map.capacity }),
256            DDWAF_OBJ_LARGE_MAP => unsafe { self.via.large_map.capacity() as usize },
257            object_type => panic!("object of type {object_type} is not a map"),
258        }
259    }
260
261    /// Returns the entry pointer of the map associated with the receiving [`ddwaf_object`].
262    ///
263    /// # Panics
264    /// Panics if the object is not a map.
265    #[must_use]
266    pub fn map_ptr(&self) -> *mut _ddwaf_object_kv {
267        match self.obj_type() {
268            DDWAF_OBJ_MAP => unsafe { self.via.map.ptr },
269            DDWAF_OBJ_LARGE_MAP => unsafe { self.via.large_map.ptr },
270            object_type => panic!("object of type {object_type} is not a map"),
271        }
272    }
273
274    /// Changes the length of the map associated with the receiving [`ddwaf_object`].
275    ///
276    /// # Safety
277    /// Every entry before `new_len` must be initialized and valid to drop.
278    ///
279    /// # Panics
280    /// Panics if `new_len` exceeds the map's capacity.
281    pub unsafe fn set_map_len(&mut self, new_len: usize) {
282        assert!(new_len <= self.map_capacity());
283        match self.obj_type() {
284            DDWAF_OBJ_MAP => {
285                self.via.map.size = new_len.try_into().expect("compact map length overflow");
286            }
287            DDWAF_OBJ_LARGE_MAP => unsafe {
288                self.via.large_map.set_size(new_len as u64);
289            },
290            _ => unreachable!(),
291        }
292    }
293
294    /// Returns a slice of the bytes from the string associated with the receiving [`ddwaf_object`].
295    ///
296    /// # Safety
297    /// - The [`ddwaf_object`] must be a valid representation of a string.
298    unsafe fn string_vec(&self) -> &[u8] {
299        debug_assert!(self.is_string());
300
301        if self.obj_type() == DDWAF_OBJ_STRING || self.obj_type() == DDWAF_OBJ_LITERAL_STRING {
302            let str = unsafe { self.via.str_ };
303            if str.size == 0 {
304                return &[];
305            }
306            unsafe { slice::from_raw_parts(str.ptr.cast(), str.size as usize) }
307        } else {
308            let sstr = unsafe { &self.via.sstr };
309            let data = &sstr.data[..sstr.size as usize];
310            // reinterpret &[i8] as &[u8]
311            unsafe { std::slice::from_raw_parts(data.as_ptr().cast(), data.len()) }
312        }
313    }
314}
315
316impl std::cmp::PartialEq<ddwaf_object> for ddwaf_object {
317    fn eq(&self, other: &ddwaf_object) -> bool {
318        if self.is_string() && other.is_string() {
319            let left = unsafe { self.string_vec() };
320            let right = unsafe { other.string_vec() };
321            return left == right;
322        }
323
324        if self.is_array() && other.is_array() {
325            if self.array_len() != other.array_len() {
326                return false;
327            }
328            for i in 0..self.array_len() {
329                let left = unsafe { &*self.array_ptr().add(i) };
330                let right = unsafe { &*other.array_ptr().add(i) };
331                if left != right {
332                    return false;
333                }
334            }
335            return true;
336        }
337
338        if self.is_map() && other.is_map() {
339            if self.map_len() != other.map_len() {
340                return false;
341            }
342            for i in 0..self.map_len() {
343                let left = unsafe { &*self.map_ptr().add(i) };
344                let right = unsafe { &*other.map_ptr().add(i) };
345                if left.key != right.key || left.val != right.val {
346                    return false;
347                }
348            }
349            return true;
350        }
351
352        if unsafe { self.type_ != other.type_ } {
353            return false;
354        }
355        match self.obj_type() {
356            DDWAF_OBJ_INVALID | DDWAF_OBJ_NULL => true,
357            DDWAF_OBJ_SIGNED => unsafe { self.via.i64_.val == other.via.i64_.val },
358            DDWAF_OBJ_UNSIGNED => unsafe { self.via.u64_.val == other.via.u64_.val },
359            DDWAF_OBJ_BOOL => unsafe { self.via.b8.val == other.via.b8.val },
360            DDWAF_OBJ_FLOAT => unsafe { self.via.f64_.val == other.via.f64_.val },
361            _ => false,
362        }
363    }
364}
365impl std::fmt::Debug for ddwaf_object {
366    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
367        let mut dbg = f.debug_struct("ddwaf_object");
368        match self.obj_type() {
369            DDWAF_OBJ_BOOL => dbg
370                .field("type", &stringify!(DDWAF_OBJ_BOOL))
371                .field("boolean", unsafe { &self.via.b8.val }),
372            DDWAF_OBJ_FLOAT => dbg
373                .field("type", &stringify!(DDWAF_OBJ_FLOAT))
374                .field("f64", unsafe { &self.via.f64_.val }),
375            DDWAF_OBJ_SIGNED => dbg
376                .field("type", &stringify!(DDWAF_OBJ_SIGNED))
377                .field("int", unsafe { &self.via.i64_.val }),
378            DDWAF_OBJ_UNSIGNED => dbg
379                .field("type", &stringify!(DDWAF_OBJ_UNSIGNED))
380                .field("uint", unsafe { &self.via.u64_.val }),
381            DDWAF_OBJ_STRING | DDWAF_OBJ_LITERAL_STRING => {
382                let sval = unsafe { self.string_vec() };
383                let sval = String::from_utf8_lossy(sval);
384                dbg.field(
385                    "type",
386                    if self.obj_type() == DDWAF_OBJ_STRING {
387                        &stringify!(DDWAF_OBJ_STRING)
388                    } else {
389                        &stringify!(DDWAF_OBJ_LITERAL_STRING)
390                    },
391                )
392                .field("string", &sval)
393            }
394            DDWAF_OBJ_SMALL_STRING => {
395                let sval = unsafe { self.string_vec() };
396                let sval = String::from_utf8_lossy(sval);
397                dbg.field("type", &stringify!(DDWAF_OBJ_SMALL_STRING))
398                    .field("string", &sval)
399            }
400            DDWAF_OBJ_ARRAY | DDWAF_OBJ_LARGE_ARRAY => {
401                let array: &[ddwaf_object] = if self.array_len() == 0 {
402                    &[]
403                } else {
404                    unsafe { slice::from_raw_parts(self.array_ptr(), self.array_len()) }
405                };
406                let object_type = if self.obj_type() == DDWAF_OBJ_ARRAY {
407                    stringify!(DDWAF_OBJ_ARRAY)
408                } else {
409                    stringify!(DDWAF_OBJ_LARGE_ARRAY)
410                };
411                dbg.field("type", &object_type).field("array", &array)
412            }
413            DDWAF_OBJ_MAP | DDWAF_OBJ_LARGE_MAP => {
414                let map: &[_ddwaf_object_kv] = if self.map_len() == 0 {
415                    &[]
416                } else {
417                    unsafe { slice::from_raw_parts(self.map_ptr(), self.map_len()) }
418                };
419                let object_type = if self.obj_type() == DDWAF_OBJ_MAP {
420                    stringify!(DDWAF_OBJ_MAP)
421                } else {
422                    stringify!(DDWAF_OBJ_LARGE_MAP)
423                };
424                dbg.field("type", &object_type).field("map", &map)
425            }
426            DDWAF_OBJ_NULL => dbg.field("type", &stringify!(DDWAF_OBJ_NULL)),
427            DDWAF_OBJ_INVALID => dbg.field("type", &stringify!(DDWAF_OBJ_INVALID)),
428            unknown => dbg.field("type", &unknown),
429        };
430
431        dbg.finish_non_exhaustive()
432    }
433}
434
435impl std::fmt::Debug for _ddwaf_object_kv {
436    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
437        let mut dbg = f.debug_struct("ddwaf_object_kv");
438        dbg.field("key", &self.key)
439            .field("val", &self.val)
440            .finish_non_exhaustive()
441    }
442}