Skip to main content

libddwaf/object/
mod.rs

1#![doc = "Data model for exchanging data with the in-app WAF."]
2
3use std::alloc::Layout;
4use std::mem::ManuallyDrop;
5use std::ops::{Deref, DerefMut, Index, IndexMut};
6use std::ptr::null_mut;
7use std::sync::OnceLock;
8use std::{cmp, fmt};
9
10mod iter;
11#[doc(inline)]
12pub use iter::*;
13
14/// Identifies the type of the value stored in a [`WafObject`].
15#[non_exhaustive]
16#[derive(Copy, Clone, Debug, PartialEq, Eq)]
17pub enum WafObjectType {
18    /// An invalid value. This can be used as a placeholder to retain the key
19    /// associated with an object that was only partially encoded.
20    Invalid,
21    /// A signed integer with 64-bit precision.
22    Signed,
23    /// An unsigned integer with 64-bit precision.
24    Unsigned,
25    /// A string value.
26    String,
27    /// An array of [`WafObject`]s.
28    Array,
29    /// An array of [`Keyed<WafObject>`]s.
30    Map,
31    /// A boolean value.
32    Bool,
33    /// A floating point value (64-bit precision).
34    Float,
35    /// The null value.
36    Null,
37}
38impl WafObjectType {
39    /// Returns the raw [`libddwaf_sys::DDWAF_OBJ_TYPE`] value corresponding to this [`WafObjectType`].
40    const fn as_raw(self) -> libddwaf_sys::DDWAF_OBJ_TYPE {
41        match self {
42            WafObjectType::Invalid => libddwaf_sys::DDWAF_OBJ_INVALID,
43            WafObjectType::Signed => libddwaf_sys::DDWAF_OBJ_SIGNED,
44            WafObjectType::Unsigned => libddwaf_sys::DDWAF_OBJ_UNSIGNED,
45            WafObjectType::String => libddwaf_sys::DDWAF_OBJ_STRING,
46            WafObjectType::Array => libddwaf_sys::DDWAF_OBJ_ARRAY,
47            WafObjectType::Map => libddwaf_sys::DDWAF_OBJ_MAP,
48            WafObjectType::Bool => libddwaf_sys::DDWAF_OBJ_BOOL,
49            WafObjectType::Float => libddwaf_sys::DDWAF_OBJ_FLOAT,
50            WafObjectType::Null => libddwaf_sys::DDWAF_OBJ_NULL,
51        }
52    }
53}
54impl TryFrom<libddwaf_sys::DDWAF_OBJ_TYPE> for WafObjectType {
55    type Error = UnknownObjectTypeError;
56    fn try_from(value: libddwaf_sys::DDWAF_OBJ_TYPE) -> Result<Self, UnknownObjectTypeError> {
57        match value {
58            libddwaf_sys::DDWAF_OBJ_INVALID => Ok(WafObjectType::Invalid),
59            libddwaf_sys::DDWAF_OBJ_SIGNED => Ok(WafObjectType::Signed),
60            libddwaf_sys::DDWAF_OBJ_UNSIGNED => Ok(WafObjectType::Unsigned),
61            libddwaf_sys::DDWAF_OBJ_STRING
62            | libddwaf_sys::DDWAF_OBJ_LITERAL_STRING
63            | libddwaf_sys::DDWAF_OBJ_SMALL_STRING => Ok(WafObjectType::String),
64            libddwaf_sys::DDWAF_OBJ_ARRAY => Ok(WafObjectType::Array),
65            libddwaf_sys::DDWAF_OBJ_MAP => Ok(WafObjectType::Map),
66            libddwaf_sys::DDWAF_OBJ_BOOL => Ok(WafObjectType::Bool),
67            libddwaf_sys::DDWAF_OBJ_FLOAT => Ok(WafObjectType::Float),
68            libddwaf_sys::DDWAF_OBJ_NULL => Ok(WafObjectType::Null),
69            unknown => Err(UnknownObjectTypeError(unknown)),
70        }
71    }
72}
73
74/// The error that is returned when a [`WafObject`] does not have a known, valid [`WafObjectType`].
75#[derive(Copy, Clone, Debug)]
76pub struct UnknownObjectTypeError(libddwaf_sys::DDWAF_OBJ_TYPE);
77impl std::error::Error for UnknownObjectTypeError {}
78impl std::fmt::Display for UnknownObjectTypeError {
79    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
80        write!(f, "Unknown object type: {:?}", self.0)
81    }
82}
83
84/// The error that is returned when a [`WafObject`] does not have the expected [`WafObjectType`].
85#[derive(Copy, Clone, Debug)]
86pub struct ObjectTypeError {
87    pub expected: WafObjectType,
88    pub actual: WafObjectType,
89}
90impl std::error::Error for ObjectTypeError {}
91impl std::fmt::Display for ObjectTypeError {
92    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
93        write!(
94            f,
95            "Invalid object type (expected {:?}, got {:?})",
96            self.expected, self.actual
97        )
98    }
99}
100
101/// The error that is returned when a value's length exceeds the maximum allowed.
102///
103/// This applies to strings (max [`u32::MAX`]) and arrays/maps (max [`u16::MAX`]).
104#[derive(Copy, Clone, Debug)]
105pub struct LengthTooLargeError {
106    /// The length that was too large.
107    pub length: usize,
108    /// The maximum allowed length.
109    pub max_length: usize,
110}
111impl std::error::Error for LengthTooLargeError {}
112impl std::fmt::Display for LengthTooLargeError {
113    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
114        write!(
115            f,
116            "Length {} exceeds maximum allowed {}",
117            self.length, self.max_length
118        )
119    }
120}
121
122/// This trait allow obtaining direct mutable access to the underlying memory
123/// backing a [`WafObject`] or [`TypedWafObject`] value.
124#[doc(hidden)]
125pub trait AsRawMutObject: crate::private::Sealed + AsRef<libddwaf_sys::ddwaf_object> {
126    /// Obtains a mutable reference to the underlying raw [`libddwaf_sys::ddwaf_object`].
127    ///
128    /// # Safety
129    /// The caller must ensure that:
130    /// - it does not change the [`libddwaf_sys::ddwaf_object::type_`] field,
131    /// - it does not change the pointers to values that don't outlive the [`libddwaf_sys::ddwaf_object`]
132    ///   itself, or whose memory cannot be recclaimed byt the destructor in the same way as the
133    ///   current value,
134    /// - it does not change the lengths in such a way that the object is no longer valid.
135    ///
136    /// Additionally, the caller would incur a memory leak if it dropped the value through the
137    /// returned reference (e.g, by calling [`std::mem::replace`]), since [`libddwaf_sys::ddwaf_object`] is
138    /// not [`Drop`] (see swapped destructors in
139    /// [this playground](https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=aeea4aba8f960bf0c63f6185f016a94d).
140    #[doc(hidden)]
141    unsafe fn as_raw_mut(&mut self) -> &mut libddwaf_sys::ddwaf_object;
142}
143
144/// This trait is implemented by type-safe interfaces to the [`WafObject`], with
145/// one implementation for each [`WafObjectType`].
146pub trait TypedWafObject: AsRawMutObject {
147    /// The associated [`WafObjectType`] constant corresponding to the typed
148    /// object's type discriminator.
149    const TYPE: WafObjectType;
150}
151
152/// The low-level representation of an arbitrary WAF object.
153///
154/// It is usually converted to a [`TypedWafObject`] by calling [`WafObject::as_type`].
155#[derive(Default)]
156#[repr(transparent)]
157pub struct WafObject {
158    raw: libddwaf_sys::ddwaf_object,
159}
160impl WafObject {
161    /// Creates a new [`WafObject`] from a JSON string.
162    ///
163    /// This function is not intended to be used with un-trusted/adversarial
164    /// input. The typical use-case is to facilitate parsing rulesets for use
165    /// with [`crate::builder::Builder::add_or_update_config`].
166    ///
167    /// # Returns
168    /// Returns [`None`] if parsing the JSON string into a [`WafObject`] was not
169    /// possible, or if the input JSON string is larger than [`u32::MAX`] bytes.
170    pub fn from_json(json: impl AsRef<[u8]>) -> Option<WafOwnedOutputAllocator<Self>> {
171        let mut output = WafOwnedOutputAllocator::<Self>::default();
172        let data = json.as_ref();
173        let Ok(len) = u32::try_from(data.len()) else {
174            return None;
175        };
176        if !unsafe {
177            let alloc = WafOwnedOutputAllocator::<Self>::allocator();
178            libddwaf_sys::ddwaf_object_from_json(
179                output.as_raw_mut(),
180                data.as_ptr().cast(),
181                len,
182                alloc,
183            )
184        } {
185            return None;
186        }
187        Some(output)
188    }
189
190    /// Returns the [`WafObjectType`] of the underlying value.
191    ///
192    /// Returns [`WafObjectType::Invalid`] if the underlying value's type is not set to a
193    /// known, valid [`WafObjectType`] value.
194    #[must_use]
195    pub fn object_type(&self) -> WafObjectType {
196        self.as_ref()
197            .obj_type()
198            .try_into()
199            .unwrap_or(WafObjectType::Invalid)
200    }
201
202    /// Returns a reference to this value as a `T` if its type corresponds.
203    #[must_use]
204    pub fn as_type<T: TypedWafObject>(&self) -> Option<&T> {
205        if self.object_type() == T::TYPE {
206            Some(unsafe { self.as_type_unchecked::<T>() })
207        } else {
208            None
209        }
210    }
211
212    /// Returns a reference to this value as a `T`.
213    ///
214    /// # Safety
215    /// The caller must ensure that the [`WafObject`] can be accurately represented by `T`.
216    pub(crate) unsafe fn as_type_unchecked<T: TypedWafObject>(&self) -> &T {
217        unsafe { self.as_ref().unchecked_as_ref::<T>() }
218    }
219
220    /// Returns a mutable reference to this value as a `T` if its type corresponds.
221    pub fn as_type_mut<T: TypedWafObject>(&mut self) -> Option<&mut T> {
222        if self.object_type() == T::TYPE {
223            Some(unsafe { self.as_raw_mut().unchecked_as_ref_mut::<T>() })
224        } else {
225            None
226        }
227    }
228
229    /// Returns true if this [`WafObject`] is not [`WafObjectType::Invalid`], meaning it can be
230    /// converted to one of the [`TypedWafObject`] implementations.
231    #[must_use]
232    pub fn is_valid(&self) -> bool {
233        self.object_type() != WafObjectType::Invalid
234    }
235
236    /// Returns the value of this [`WafObject`] as a [`u64`] if its type is [`WafObjectType::Unsigned`].
237    #[must_use]
238    pub fn to_u64(&self) -> Option<u64> {
239        self.as_type::<WafUnsigned>().map(WafUnsigned::value)
240    }
241
242    /// Returns the value of this [`WafObject`] as a [`i64`] if its type is [`WafObjectType::Signed`] (or
243    /// [`WafObjectType::Unsigned`] with a value that can be represented as an [`i64`]).
244    #[must_use]
245    pub fn to_i64(&self) -> Option<i64> {
246        match self.object_type() {
247            WafObjectType::Unsigned => {
248                let obj: &WafUnsigned = unsafe { self.as_type_unchecked() };
249                obj.value().try_into().ok()
250            }
251            WafObjectType::Signed => {
252                let obj: &WafSigned = unsafe { self.as_type_unchecked() };
253                Some(obj.value())
254            }
255            _ => None,
256        }
257    }
258
259    /// Returns the value of this [`WafObject`] as a [`f64`] if its type is [`WafObjectType::Float`].
260    #[must_use]
261    pub fn to_f64(&self) -> Option<f64> {
262        self.as_type::<WafFloat>().map(WafFloat::value)
263    }
264
265    /// Returns the value of this [`WafObject`] as a [`bool`] if its type is [`WafObjectType::Bool`].
266    #[must_use]
267    pub fn to_bool(&self) -> Option<bool> {
268        self.as_type::<WafBool>().map(WafBool::value)
269    }
270
271    /// Returns the value of this [`WafObject`] as a [`&str`] if its type is [`WafObjectType::String`],
272    /// and the value is valid UTF-8.
273    #[must_use]
274    pub fn to_str(&self) -> Option<&str> {
275        self.as_type::<WafString>().and_then(|x| x.as_str().ok())
276    }
277}
278impl AsRef<libddwaf_sys::ddwaf_object> for WafObject {
279    fn as_ref(&self) -> &libddwaf_sys::ddwaf_object {
280        &self.raw
281    }
282}
283impl AsRawMutObject for WafObject {
284    unsafe fn as_raw_mut(&mut self) -> &mut libddwaf_sys::ddwaf_object {
285        &mut self.raw
286    }
287}
288impl fmt::Debug for WafObject {
289    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
290        match self.object_type() {
291            WafObjectType::Invalid => write!(f, "WafInvalid"),
292            WafObjectType::Unsigned => {
293                let obj: &WafUnsigned = self.as_type().unwrap();
294                obj.fmt(f)
295            }
296            WafObjectType::Signed => {
297                let obj: &WafSigned = self.as_type().unwrap();
298                obj.fmt(f)
299            }
300            WafObjectType::Float => {
301                let obj: &WafFloat = self.as_type().unwrap();
302                obj.fmt(f)
303            }
304            WafObjectType::Bool => {
305                let obj: &WafBool = self.as_type().unwrap();
306                obj.fmt(f)
307            }
308            WafObjectType::Null => {
309                let obj: &WafNull = self.as_type().unwrap();
310                obj.fmt(f)
311            }
312            WafObjectType::String => {
313                let obj: &WafString = self.as_type().unwrap();
314                obj.fmt(f)
315            }
316            WafObjectType::Array => {
317                let obj: &WafArray = self.as_type().unwrap();
318                obj.fmt(f)
319            }
320            WafObjectType::Map => {
321                let obj: &WafMap = self.as_type().unwrap();
322                obj.fmt(f)
323            }
324        }
325    }
326}
327impl Drop for WafObject {
328    fn drop(&mut self) {
329        unsafe { self.raw.drop_object() }
330    }
331}
332impl Clone for WafObject {
333    fn clone(&self) -> Self {
334        match self.object_type() {
335            WafObjectType::Invalid => {
336                let obj: &WafInvalid = unsafe { self.as_type_unchecked() };
337                (*obj).into()
338            }
339            WafObjectType::Signed => {
340                let obj: &WafSigned = unsafe { self.as_type_unchecked() };
341                (*obj).into()
342            }
343            WafObjectType::Unsigned => {
344                let obj: &WafUnsigned = unsafe { self.as_type_unchecked() };
345                (*obj).into()
346            }
347            WafObjectType::Bool => {
348                let obj: &WafBool = unsafe { self.as_type_unchecked() };
349                (*obj).into()
350            }
351            WafObjectType::Float => {
352                let obj: &WafFloat = unsafe { self.as_type_unchecked() };
353                (*obj).into()
354            }
355            WafObjectType::Null => {
356                let obj: &WafNull = unsafe { self.as_type_unchecked() };
357                (*obj).into()
358            }
359            WafObjectType::String => {
360                let obj: &WafString = unsafe { self.as_type_unchecked() };
361                obj.clone().into()
362            }
363            WafObjectType::Array => {
364                let obj: &WafArray = unsafe { self.as_type_unchecked() };
365                obj.clone().into()
366            }
367            WafObjectType::Map => {
368                let obj: &WafMap = unsafe { self.as_type_unchecked() };
369                obj.clone().into()
370            }
371        }
372    }
373}
374impl From<u64> for WafObject {
375    fn from(value: u64) -> Self {
376        WafUnsigned::new(value).into()
377    }
378}
379impl From<u32> for WafObject {
380    fn from(value: u32) -> Self {
381        WafUnsigned::new(value.into()).into()
382    }
383}
384impl From<i64> for WafObject {
385    fn from(value: i64) -> Self {
386        WafSigned::new(value).into()
387    }
388}
389impl From<i32> for WafObject {
390    fn from(value: i32) -> Self {
391        WafSigned::new(value.into()).into()
392    }
393}
394impl From<f64> for WafObject {
395    fn from(value: f64) -> Self {
396        WafFloat::new(value).into()
397    }
398}
399impl From<bool> for WafObject {
400    fn from(value: bool) -> Self {
401        WafBool::new(value).into()
402    }
403}
404impl From<&str> for WafObject {
405    fn from(value: &str) -> Self {
406        value.as_bytes().into()
407    }
408}
409impl From<&[u8]> for WafObject {
410    fn from(value: &[u8]) -> Self {
411        WafString::from(value).into()
412    }
413}
414impl From<()> for WafObject {
415    fn from((): ()) -> Self {
416        WafNull::new().into()
417    }
418}
419impl<T: TypedWafObject> From<T> for WafObject {
420    fn from(value: T) -> Self {
421        let res = Self {
422            raw: *value.as_ref(),
423        };
424        std::mem::forget(value);
425        res
426    }
427}
428impl<T: AsRef<libddwaf_sys::ddwaf_object>> cmp::PartialEq<T> for WafObject {
429    fn eq(&self, other: &T) -> bool {
430        self.raw == *other.as_ref()
431    }
432}
433impl crate::private::Sealed for WafObject {}
434
435/// Trait to encode which allocator should be used for deallocation in the type system.
436pub trait AllocatorType: 'static {
437    /// Get the allocator to use for deallocation.
438    fn allocator() -> libddwaf_sys::ddwaf_allocator;
439}
440
441/// Allocator type that uses libddwaf's default.
442pub struct LibddwafDefaultAllocator;
443impl AllocatorType for LibddwafDefaultAllocator {
444    fn allocator() -> libddwaf_sys::ddwaf_allocator {
445        unsafe { libddwaf_sys::ddwaf_get_default_allocator() }
446    }
447}
448
449/// Allocator type that uses the Rust-registered allocator.
450pub struct RustAllocator;
451impl AllocatorType for RustAllocator {
452    fn allocator() -> libddwaf_sys::ddwaf_allocator {
453        get_default_allocator().into()
454    }
455}
456
457/// A WAF-owned [`WafObject`] or [`TypedWafObject`] value.
458///
459/// This has different [`Drop`] behavior than a rust-owned [`WafObject`] value.
460/// The allocator used for deallocation is encoded in the type parameter `A`.
461#[repr(transparent)]
462pub struct WafOwned<T: AsRawMutObject, A: AllocatorType = RustAllocator> {
463    inner: std::mem::ManuallyDrop<T>,
464    _phantom: std::marker::PhantomData<A>,
465}
466impl<T: AsRawMutObject, A: AllocatorType> WafOwned<T, A> {
467    pub(crate) fn allocator() -> libddwaf_sys::ddwaf_allocator {
468        A::allocator()
469    }
470}
471
472impl<T: AsRawMutObject + fmt::Debug, A: AllocatorType> fmt::Debug for WafOwned<T, A> {
473    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
474        self.inner.deref().fmt(f)
475    }
476}
477impl<T: AsRawMutObject + Default, A: AllocatorType> Default for WafOwned<T, A> {
478    fn default() -> Self {
479        Self {
480            inner: std::mem::ManuallyDrop::new(Default::default()),
481            _phantom: std::marker::PhantomData,
482        }
483    }
484}
485impl<T: AsRawMutObject, A: AllocatorType> Deref for WafOwned<T, A> {
486    type Target = T;
487    fn deref(&self) -> &Self::Target {
488        &self.inner
489    }
490}
491impl<T: AsRawMutObject, A: AllocatorType> DerefMut for WafOwned<T, A> {
492    fn deref_mut(&mut self) -> &mut Self::Target {
493        &mut self.inner
494    }
495}
496impl<T: AsRawMutObject, A: AllocatorType> Drop for WafOwned<T, A> {
497    fn drop(&mut self) {
498        unsafe {
499            libddwaf_sys::ddwaf_object_destroy(self.inner.as_raw_mut(), A::allocator());
500        }
501    }
502}
503impl<T: AsRawMutObject, A: AllocatorType> PartialEq<T> for WafOwned<T, A>
504where
505    T: PartialEq<T>,
506{
507    fn eq(&self, other: &T) -> bool {
508        *self.inner == *other
509    }
510}
511
512/// Type alias for WAF-owned objects using the system default allocator.
513pub type WafOwnedDefaultAllocator<T> = WafOwned<T, LibddwafDefaultAllocator>;
514
515/// Type alias for WAF-owned objects using the Rust-registered allocator (for outputs).
516pub type WafOwnedOutputAllocator<T> = WafOwned<T, RustAllocator>;
517
518/// Allocates memory for the given [`Layout`], calling [`std::alloc::handle_alloc_error`] if the
519/// allocation failed.
520///
521/// # Safety
522/// The requirements as for [`std::alloc::alloc`] apply.
523unsafe fn no_fail_alloc(layout: Layout) -> *mut u8 {
524    if layout.size() == 0 {
525        return null_mut();
526    }
527    let ptr = unsafe { std::alloc::alloc(layout) };
528    if ptr.is_null() {
529        std::alloc::handle_alloc_error(layout);
530    }
531    ptr
532}
533
534macro_rules! typed_object {
535    (@defaults $type:expr, $name:ident) => {
536        #[doc = concat!("Returns true if this [", stringify!($name), "] is indeed [", stringify!($type), "].")]
537        #[must_use]
538        pub fn is_valid(&self) -> bool {
539            self.raw.obj_type() == $type.as_raw()
540        }
541    };
542    (@defaults $type:expr, $name:ident, $($is_valid:tt)*) => {
543        #[doc = concat!("Returns true if this [", stringify!($name), "] is indeed [", stringify!($type), "].")]
544        #[must_use]
545        $($is_valid)*
546    };
547    ($type:expr => $name:ident $(derive($($derives:ident),* $(,)?))? $(is_valid { $($is_valid:tt)* })? $({ $($impl:tt)* })?) => {
548        #[doc = concat!("The WAF object representation of a value of type [", stringify!($type), "]")]
549        #[repr(transparent)]
550        $(#[derive($($derives),*)] )?
551        pub struct $name {
552            raw: libddwaf_sys::ddwaf_object,
553        }
554        impl $name {
555            typed_object!(@defaults $type, $name $(, $($is_valid)*)?);
556
557            /// Returns a reference to this value as a [`WafObject`].
558            #[must_use]
559            pub fn as_object(&self) -> &WafObject{
560                let obj: &libddwaf_sys::ddwaf_object = self.as_ref();
561                obj.as_object_ref()
562            }
563            $(
564            $($impl)*)?
565        }
566        impl AsRef<libddwaf_sys::ddwaf_object> for $name {
567            fn as_ref(&self) -> &libddwaf_sys::ddwaf_object {
568                &self.raw
569            }
570        }
571        impl AsRawMutObject for $name {
572            unsafe fn as_raw_mut(&mut self) -> &mut libddwaf_sys::ddwaf_object {
573                &mut self.raw
574            }
575        }
576        impl Default for $name {
577            #[allow(clippy::cast_possible_truncation)]
578            fn default() -> Self {
579                // All the types admit this representation
580                let mut raw: libddwaf_sys::ddwaf_object = unsafe { std::mem::zeroed() };
581                raw.type_ = $type.as_raw() as u8;
582                Self { raw }
583            }
584        }
585        impl TryFrom<WafObject> for $name {
586            type Error = ObjectTypeError;
587            fn try_from(obj: WafObject) -> Result<Self, Self::Error> {
588                if obj.object_type() != Self::TYPE {
589                    return Err(ObjectTypeError {
590                        expected: $type,
591                        actual: obj.object_type(),
592                    });
593                }
594                let res = Self { raw: obj.raw };
595                std::mem::forget(obj);
596                Ok(res)
597            }
598        }
599        impl<T: AsRef<libddwaf_sys::ddwaf_object>> cmp::PartialEq<T> for $name {
600            fn eq(&self, other: &T) -> bool {
601                self.raw == *other.as_ref()
602            }
603        }
604        impl crate::private::Sealed for $name {}
605        impl TypedWafObject for $name {
606            const TYPE: WafObjectType = $type;
607        }
608    };
609}
610
611typed_object!(WafObjectType::Invalid => WafInvalid derive(Copy, Clone));
612
613typed_object!(WafObjectType::Signed => WafSigned derive(Copy, Clone) {
614    /// Creates a new [`WafSigned`] with the provided value.
615    #[must_use]
616    #[allow(clippy::cast_possible_truncation)]
617    pub const fn new(val: i64) -> Self {
618        Self {
619            raw: libddwaf_sys::ddwaf_object {
620                via: libddwaf_sys::_ddwaf_object__bindgen_ty_1 {
621                    i64_: libddwaf_sys::_ddwaf_object_signed {
622                        type_: libddwaf_sys::DDWAF_OBJ_SIGNED as u8,
623                        val,
624                    },
625                },
626            }
627        }
628    }
629
630    /// Returns the value of this [`WafSigned`].
631    #[must_use]
632    pub const fn value(&self) -> i64 {
633        unsafe { self.raw.via.i64_.val }
634    }
635});
636
637typed_object!(WafObjectType::Unsigned => WafUnsigned derive(Copy, Clone) {
638    /// Creates a new [`WafUnsigned`] with the provided value.
639    #[must_use]
640    #[allow(clippy::cast_possible_truncation)]
641    pub const fn new(val: u64) -> Self {
642        Self {
643            raw: libddwaf_sys::ddwaf_object {
644                via: libddwaf_sys::_ddwaf_object__bindgen_ty_1 {
645                    u64_: libddwaf_sys::_ddwaf_object_unsigned {
646                        type_: libddwaf_sys::DDWAF_OBJ_UNSIGNED as u8,
647                        val,
648                    },
649                },
650            }
651        }
652    }
653
654    /// Returns the value of this [`WafUnsigned`].
655    #[must_use]
656    pub const fn value(&self) -> u64 {
657        unsafe { self.raw.via.u64_.val }
658    }
659});
660
661typed_object!(WafObjectType::String => WafString
662    is_valid {
663        pub fn is_valid(&self) -> bool {
664            self.raw.obj_type() & libddwaf_sys::DDWAF_OBJ_STRING != 0
665        }
666    }
667    {
668    /// Creates a new [`WafString`] with the provided value.
669    /// Only returns none if the string is larger than [`u32::MAX`] bytes.
670    ///
671    /// # Panics
672    /// Panics if memory allocation fails (out of memory).
673    #[allow(clippy::cast_possible_truncation, clippy::items_after_statements)]
674    pub fn new(val: impl AsRef<[u8]>) -> Option<Self> {
675        let val = val.as_ref();
676        if val.len() > (u32::MAX as usize) {
677            return None;
678        }
679
680        const SMALL_STRING_SIZE: usize = 14;
681
682        if val.len() <= SMALL_STRING_SIZE {
683            let mut ss = libddwaf_sys::_ddwaf_object_small_string {
684                type_: libddwaf_sys::DDWAF_OBJ_SMALL_STRING as u8,
685                size: val.len() as u8,
686                data: [0; 14],
687            };
688            let valcast = unsafe {
689                std::slice::from_raw_parts(val.as_ptr().cast(), val.len())
690            };
691            ss.data[..valcast.len()].copy_from_slice(valcast);
692
693            return Some(Self {
694                raw: libddwaf_sys::ddwaf_object {
695                    via: libddwaf_sys::_ddwaf_object__bindgen_ty_1 {
696                        sstr: ss,
697                    },
698                },
699            })
700        }
701
702        let ptr: *mut ::std::os::raw::c_char = if val.is_empty() {
703            null_mut()
704        } else {
705            unsafe { no_fail_alloc(Layout::array::<::std::os::raw::c_char>(val.len()).unwrap()).cast() }
706        };
707        unsafe {
708            std::ptr::copy_nonoverlapping(val.as_ptr(), ptr.cast(), val.len());
709        }
710        Some(Self {
711            raw: libddwaf_sys::ddwaf_object {
712                via: libddwaf_sys::_ddwaf_object__bindgen_ty_1 {
713                    str_: libddwaf_sys::_ddwaf_object_string {
714                        type_: libddwaf_sys::DDWAF_OBJ_STRING as u8,
715                        size: val.len() as u32,
716                        ptr,
717                    },
718                },
719            },
720        })
721    }
722
723    /// Creates a new [`WafString`] with the provided static value.
724    ///
725    /// # Panics
726    /// Panics if the string is larger than [`u32::MAX`] bytes.
727    #[allow(clippy::cast_possible_truncation)]
728    pub fn new_literal(val: impl Into<&'static [u8]>) -> Self {
729        let val = val.into();
730        let len = u32::try_from(val.len()).expect("string is too large for this platform");
731
732        Self {
733            raw: libddwaf_sys::ddwaf_object {
734                via: libddwaf_sys::_ddwaf_object__bindgen_ty_1 {
735                    str_: libddwaf_sys::_ddwaf_object_string {
736                        type_: libddwaf_sys::DDWAF_OBJ_LITERAL_STRING as u8,
737                        size: len,
738                        ptr: val.as_ptr() as *mut _,
739                    },
740                },
741            },
742        }
743
744    }
745
746    /// Returns the length of this [`WafString`], in bytes.
747    #[must_use]
748    pub fn len(&self) -> u32 {
749        if self.raw.obj_type() == libddwaf_sys::DDWAF_OBJ_SMALL_STRING {
750            u32::from(unsafe { self.raw.via.sstr.size })
751        } else {
752            unsafe { self.raw.via.str_.size }
753        }
754    }
755
756    /// Returns true if this [`WafString`] is empty.
757    #[must_use]
758    pub fn is_empty(&self) -> bool {
759        self.len() == 0u32
760    }
761
762    /// Returns a slice of the bytes from this [`WafString`].
763    #[must_use]
764    #[allow(clippy::cast_possible_truncation)]
765    pub fn as_bytes(&self) -> &[u8] {
766        debug_assert!(self.is_valid());
767        let len = self.len();
768        if len == 0 {
769            return &[];
770        }
771
772        if self.raw.obj_type() == libddwaf_sys::DDWAF_OBJ_SMALL_STRING {
773            unsafe {
774                std::slice::from_raw_parts(
775                    self.raw.via.sstr.data.as_ptr().cast(),
776                    len as usize,
777                )
778            }
779        } else {
780            debug_assert!(!unsafe{ self.raw.via.str_.ptr }.is_null());
781            unsafe {
782                std::slice::from_raw_parts(
783                    self.raw.via.str_.ptr.cast(),
784                    len as usize,
785                )
786            }
787        }
788    }
789
790    /// Returns a string slice from this [`WafString`].
791    ///
792    /// # Errors
793    /// Returns an error if the underlying data is not a valid UTF-8 string, under the same conditions as
794    /// [`std::str::from_utf8`].
795    pub fn as_str(&self) -> Result<&str, std::str::Utf8Error> {
796        std::str::from_utf8(self.as_bytes())
797    }
798});
799typed_object!(WafObjectType::Array => WafArray {
800    /// Creates a new [`WafArray`] with the provided size. All values in the array are initialized
801    /// to an invalid [`WafObject`] instance.
802    ///
803    /// # Panics
804    /// Panics if memory allocation fails (out of memory).
805    #[must_use]
806    pub fn new(nb_entries: u16) -> Self {
807        let size = usize::from(nb_entries);
808        let layout = Layout::array::<libddwaf_sys::ddwaf_object>(size).unwrap();
809        let ptr = unsafe { no_fail_alloc(layout).cast() };
810        unsafe { std::ptr::write_bytes(ptr, 0, size)};
811        Self {
812            raw: libddwaf_sys::ddwaf_object {
813                via: libddwaf_sys::_ddwaf_object__bindgen_ty_1 {
814                    array: libddwaf_sys::_ddwaf_object_array {
815                        #[allow(clippy::cast_possible_truncation)]
816                        type_: libddwaf_sys::DDWAF_OBJ_ARRAY as u8,
817                        size: nb_entries,
818                        capacity: nb_entries,
819                        ptr,
820                    },
821                },
822            }
823        }
824    }
825
826    /// Returns the length of this [`WafArray`].
827    #[must_use]
828    pub const fn len(&self) -> u16 {
829        unsafe { self.raw.via.array.size }
830    }
831
832    /// Returns true if this [`WafArray`] is empty.
833    #[must_use]
834    pub const fn is_empty(&self) -> bool {
835        self.len() == 0
836    }
837
838    /// Returns the capacity of this [`WafArray`].
839    ///
840    /// The capacity is an implementation detail and is only used to for properly
841    /// deallocating the memory when the array is dropped.
842    #[must_use]
843    pub const fn capacity(&self) -> u16 {
844        unsafe { self.raw.via.array.capacity }
845    }
846
847    /// Truncates this [`WafArray`] to the provided size.
848    ///
849    /// Has no effect is the current length is not greater than the new size.
850    ///
851    /// It does not free the extra memory, except insofar as it drops the extra elements.
852    /// Useful when you pessimistically allocate a larger array, but later discover that you don't need all the capacity.
853    pub fn truncate(&mut self, new_size: u16) {
854        if new_size > self.len() {
855            return;
856        }
857        let arr: *mut WafObject = unsafe { self.raw.via.array.ptr.cast() };
858        for i in new_size..self.len() {
859            unsafe {
860                std::ptr::drop_in_place(arr.add(i as usize));
861            }
862        }
863        self.raw.via.array.size = new_size;
864    }
865
866    /// Returns an iterator over the [`Keyed<WafObject>`]s in this [`WafMap`].
867    pub fn iter(&self) -> impl Iterator<Item = &WafObject> {
868        let slice : &[WafObject] = self.as_ref();
869        slice.iter()
870    }
871
872    /// Returns a mutable iterator over the [`Keyed<WafObject>`]s in this [`WafMap`].
873    pub fn iter_mut(&mut self) -> impl Iterator<Item = &mut WafObject> {
874        let slice : &mut [WafObject] = AsMut::as_mut(self);
875        slice.iter_mut()
876    }
877});
878typed_object!(WafObjectType::Map => WafMap {
879    /// Creates a new [`WafMap`] with the provided size. All values in the map are initialized
880    /// to an invalid [`WafObject`] instance with a blank key.
881    ///
882    /// # Panics
883    /// Panics if memory allocation fails (out of memory).
884    #[must_use]
885    pub fn new(nb_entries: u16) -> Self {
886        let size = usize::from(nb_entries);
887        let layout = Layout::array::<libddwaf_sys::_ddwaf_object_kv>(size).unwrap();
888        let ptr = unsafe { no_fail_alloc(layout).cast() };
889        unsafe { std::ptr::write_bytes(ptr, 0, size)};
890        Self {
891            raw: libddwaf_sys::ddwaf_object {
892                via: libddwaf_sys::_ddwaf_object__bindgen_ty_1 {
893                    map: libddwaf_sys::_ddwaf_object_map {
894                        #[allow(clippy::cast_possible_truncation)]
895                        type_: libddwaf_sys::DDWAF_OBJ_MAP as u8,
896                        size: nb_entries,
897                        capacity: nb_entries,
898                        ptr,
899                    },
900                },
901            }
902        }
903    }
904
905    /// Returns the length of this [`WafMap`].
906    #[must_use]
907    pub const fn len(&self) -> u16 {
908        unsafe { self.raw.via.map.size }
909    }
910
911    /// Returns true if this [`WafMap`] is empty.
912    #[must_use]
913    pub const fn is_empty(&self) -> bool {
914        self.len() == 0
915    }
916
917    /// Returns the capacity of this [`WafMap`].
918    ///
919    /// The capacity is an implementation detail and is only used to for properly
920    /// deallocating the memory when the map is dropped.
921    #[must_use]
922    pub const fn capacity(&self) -> u16 {
923        unsafe { self.raw.via.map.capacity }
924    }
925
926    /// Truncates this [`WafMap`] to the provided size.
927    ///
928    /// Has no effect is the current length is not greater than the new size.
929    ///
930    /// It does not free the extra memory, except insofar as it drops the extra elements.
931    /// Useful when you pessimistically allocate a larger map, but later discover that you don't need all the capacity.
932    pub fn truncate(&mut self, new_size: u16) {
933        if new_size > self.len() {
934            return;
935        }
936        let entries: *mut Keyed<WafObject> = unsafe { self.raw.via.map.ptr.cast() };
937        for i in new_size..self.len() {
938            unsafe {
939                std::ptr::drop_in_place(entries.add(i as usize));
940            }
941        }
942        self.raw.via.map.size = new_size;
943    }
944
945    /// Returns an iterator over the [`Keyed<WafObject>`]s in this [`WafMap`].
946    pub fn iter(&self) -> impl Iterator<Item = &Keyed<WafObject>> {
947        let slice : &[Keyed<WafObject>] = self.as_ref();
948        slice.iter()
949    }
950
951    /// Returns a mutable iterator over the [`Keyed<WafObject>`]s in this [`WafMap`].
952    pub fn iter_mut(&mut self) -> impl Iterator<Item = &mut Keyed<WafObject>> {
953        let slice : &mut [Keyed<WafObject>] = AsMut::as_mut(self);
954        slice.iter_mut()
955    }
956
957    /// Returns a reference to the [`Keyed<WafObject>`] with the provided key, if one exists.
958    ///
959    /// If multiple such objects exist in the receiver, the first match is returned.
960    #[must_use]
961    pub fn get(&self, key: impl AsRef<libddwaf_sys::ddwaf_object>) -> Option<&Keyed<WafObject>> {
962        let key = key.as_ref();
963        self.iter().find(|o| o.key().raw.eq(key))
964    }
965
966    /// Returns a reference to the [`Keyed<WafObject>`] with the provided key, if one exists.
967    ///
968    /// If multiple such objects exist in the receiver, the first match is returned.
969    #[must_use]
970    pub fn get_bstr(&self, key: &'_ [u8]) -> Option<&Keyed<WafObject>> {
971        self.iter().find(|o| {
972            match o.key().as_type::<WafString>() {
973                Some(s) => s.as_bytes() == key,
974                None => false,
975            }
976        })
977    }
978
979    /// Returns a mutable reference to the [`Keyed<WafObject>`] with the provided key, if one exists.
980    ///
981    /// If multiple such objects exist in the receiver, the first match is returned.
982    pub fn get_mut(&mut self, key: &'_ [u8]) -> Option<&mut Keyed<WafObject>> {
983        self.iter_mut().find(|o| {
984            match o.key().as_type::<WafString>() {
985                Some(s) => s.as_bytes() == key,
986                None => false
987            }
988        })
989    }
990
991    /// Returns a reference to the [`Keyed<WafObject>`] with the provided key, if one exists.
992    #[must_use]
993    pub fn get_str(&self, key: &'_ str) -> Option<&Keyed<WafObject>> {
994        self.get_bstr(key.as_bytes())
995    }
996
997    /// Returns a mutable reference to the [`Keyed<WafObject>`] with the provided key, if one exists.
998    pub fn get_str_mut(&mut self, key: &'_ str) -> Option<&mut Keyed<WafObject>> {
999        self.get_mut(key.as_bytes())
1000    }
1001});
1002typed_object!(WafObjectType::Bool => WafBool derive(Copy, Clone) {
1003    /// Creates a new [`WafBool`] with the provided value.
1004    #[must_use]
1005    pub const fn new(val: bool) -> Self {
1006        Self {
1007            raw: libddwaf_sys::ddwaf_object {
1008                via: libddwaf_sys::_ddwaf_object__bindgen_ty_1 {
1009                    b8: libddwaf_sys::_ddwaf_object_bool {
1010                        #[allow(clippy::cast_possible_truncation)]
1011                        type_: libddwaf_sys::DDWAF_OBJ_BOOL as u8,
1012                        val,
1013                    },
1014                },
1015            }
1016        }
1017    }
1018
1019    /// Returns the value of this [`WafBool`].
1020    #[must_use]
1021    pub const fn value(&self) -> bool {
1022        unsafe { self.raw.via.b8.val }
1023    }
1024});
1025
1026typed_object!(WafObjectType::Float => WafFloat derive(Copy, Clone) {
1027    /// Creates a new [`WafFloat`] with the provided value.
1028    #[must_use]
1029    pub const fn new(val: f64) -> Self {
1030        Self {
1031            raw: libddwaf_sys::ddwaf_object {
1032                via: libddwaf_sys::_ddwaf_object__bindgen_ty_1 {
1033                    f64_: libddwaf_sys::_ddwaf_object_float {
1034                        #[allow(clippy::cast_possible_truncation)]
1035                        type_: libddwaf_sys::DDWAF_OBJ_FLOAT as u8,
1036                        val,
1037                    },
1038                },
1039            }
1040        }
1041    }
1042
1043    /// Returns the value of this [`WafFloat`].
1044    #[must_use]
1045    pub const fn value(&self) -> f64 {
1046        unsafe { self.raw.via.f64_.val }
1047    }
1048});
1049
1050typed_object!(WafObjectType::Null => WafNull derive(Copy, Clone) {
1051    /// Creates a new [`WafNull`].
1052    #[must_use]
1053    pub const fn new() -> Self {
1054        Self {
1055            raw: libddwaf_sys::ddwaf_object {
1056                via: libddwaf_sys::_ddwaf_object__bindgen_ty_1 {
1057                    u64_: libddwaf_sys::_ddwaf_object_unsigned {
1058                        #[allow(clippy::cast_possible_truncation)]
1059                        type_: libddwaf_sys::DDWAF_OBJ_NULL as u8,
1060                        val: 0,
1061                    },
1062                },
1063            }
1064        }
1065    }
1066}
1067);
1068
1069impl fmt::Debug for WafSigned {
1070    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1071        write!(f, "{}({})", stringify!(WafSigned), self.value())
1072    }
1073}
1074impl From<i64> for WafSigned {
1075    fn from(value: i64) -> Self {
1076        Self::new(value)
1077    }
1078}
1079impl From<i32> for WafSigned {
1080    fn from(value: i32) -> Self {
1081        Self::new(value.into())
1082    }
1083}
1084
1085impl fmt::Debug for WafUnsigned {
1086    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1087        write!(f, "{}({})", stringify!(WafUnsigned), self.value())
1088    }
1089}
1090impl From<u64> for WafUnsigned {
1091    fn from(value: u64) -> Self {
1092        Self::new(value)
1093    }
1094}
1095impl From<u32> for WafUnsigned {
1096    fn from(value: u32) -> Self {
1097        Self::new(value.into())
1098    }
1099}
1100
1101impl<T: AsRef<[u8]>> From<T> for WafString {
1102    fn from(val: T) -> Self {
1103        let slice = val.as_ref();
1104        let slice = &slice[..slice.len().min(u32::MAX as usize)];
1105        Self::new(slice).unwrap()
1106    }
1107}
1108impl fmt::Debug for WafString {
1109    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1110        write!(
1111            f,
1112            "{}(\"{:?}\")",
1113            stringify!(WafString),
1114            fmt_bin_str(self.as_bytes())
1115        )
1116    }
1117}
1118impl Drop for WafString {
1119    fn drop(&mut self) {
1120        // Only call drop_string for heap-allocated strings (DDWAF_OBJ_STRING)
1121        // LITERAL_STRING (static) and SMALL_STRING (inline) don't need deallocation
1122        if self.raw.obj_type() == libddwaf_sys::DDWAF_OBJ_STRING {
1123            unsafe { self.raw.drop_string() }
1124        }
1125    }
1126}
1127impl Clone for WafString {
1128    fn clone(&self) -> Self {
1129        if self.raw.obj_type() == libddwaf_sys::DDWAF_OBJ_STRING {
1130            let len = self.len();
1131            let layout = Layout::array::<std::os::raw::c_char>(len as usize).unwrap();
1132            let copied = unsafe { no_fail_alloc(layout).cast::<std::os::raw::c_char>() };
1133            unsafe {
1134                std::ptr::copy_nonoverlapping(
1135                    self.as_bytes().as_ptr().cast(),
1136                    copied,
1137                    len as usize,
1138                );
1139            }
1140            return Self {
1141                raw: libddwaf_sys::ddwaf_object {
1142                    via: libddwaf_sys::_ddwaf_object__bindgen_ty_1 {
1143                        str_: libddwaf_sys::_ddwaf_object_string {
1144                            #[allow(clippy::cast_possible_truncation)]
1145                            type_: libddwaf_sys::DDWAF_OBJ_STRING as u8,
1146                            size: len,
1147                            ptr: copied,
1148                        },
1149                    },
1150                },
1151            };
1152        }
1153
1154        // other string types, just a plain copy
1155        Self { raw: self.raw }
1156    }
1157}
1158
1159impl AsRef<[WafObject]> for WafArray {
1160    fn as_ref(&self) -> &[WafObject] {
1161        if self.is_empty() {
1162            return &[];
1163        }
1164        let array = unsafe { self.raw.via.array.ptr.cast() };
1165        unsafe { std::slice::from_raw_parts(array, self.len() as usize) }
1166    }
1167}
1168impl AsMut<[WafObject]> for WafArray {
1169    fn as_mut(&mut self) -> &mut [WafObject] {
1170        if self.is_empty() {
1171            return &mut [];
1172        }
1173        let array = unsafe { self.raw.via.array.ptr.cast() };
1174        unsafe { std::slice::from_raw_parts_mut(array, self.len() as usize) }
1175    }
1176}
1177impl fmt::Debug for WafArray {
1178    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1179        write!(f, "{}[", stringify!(WafArray))?;
1180        let mut first = true;
1181        for obj in self.iter() {
1182            if first {
1183                first = false;
1184            } else {
1185                write!(f, ", ")?;
1186            }
1187            write!(f, "{obj:?}")?;
1188        }
1189        write!(f, "]")
1190    }
1191}
1192impl Drop for WafArray {
1193    fn drop(&mut self) {
1194        unsafe { self.raw.drop_array() }
1195    }
1196}
1197impl Clone for WafArray {
1198    fn clone(&self) -> Self {
1199        let size = self.len();
1200
1201        if size == 0 {
1202            return Self::new(0);
1203        }
1204
1205        let layout = Layout::array::<libddwaf_sys::ddwaf_object>(size as usize).unwrap();
1206        let new_arr: *mut libddwaf_sys::ddwaf_object = unsafe { no_fail_alloc(layout).cast() };
1207
1208        // Clone each element
1209        for i in 0..size {
1210            let src_elem: &WafObject = &self[i as usize];
1211            let cloned_elem = ManuallyDrop::new(src_elem.clone());
1212            unsafe { new_arr.add(i as usize).write(cloned_elem.raw) };
1213        }
1214
1215        Self {
1216            raw: libddwaf_sys::ddwaf_object {
1217                via: libddwaf_sys::_ddwaf_object__bindgen_ty_1 {
1218                    array: libddwaf_sys::_ddwaf_object_array {
1219                        #[allow(clippy::cast_possible_truncation)]
1220                        type_: libddwaf_sys::DDWAF_OBJ_ARRAY as u8,
1221                        size,
1222                        capacity: size,
1223                        ptr: new_arr,
1224                    },
1225                },
1226            },
1227        }
1228    }
1229}
1230impl<T: Into<WafObject>, const N: usize> From<[T; N]> for WafArray {
1231    fn from(value: [T; N]) -> Self {
1232        let effective_length = N.min(u16::MAX as usize);
1233        #[allow(clippy::cast_possible_truncation)]
1234        let mut array = Self::new(effective_length as u16);
1235        for (i, obj) in value.into_iter().enumerate() {
1236            if i >= effective_length {
1237                break;
1238            }
1239            array[i] = obj.into();
1240        }
1241        array
1242    }
1243}
1244impl<T> From<&mut [T]> for WafArray
1245where
1246    T: Into<WafObject> + Default,
1247{
1248    fn from(value: &mut [T]) -> Self {
1249        let effective_length = value.len().min(u16::MAX as usize);
1250        #[allow(clippy::cast_possible_truncation)]
1251        let mut array = Self::new(effective_length as u16);
1252        for (i, obj) in value.iter_mut().enumerate() {
1253            if i >= effective_length {
1254                break;
1255            }
1256            let obj = std::mem::take(obj);
1257            array[i] = obj.into();
1258        }
1259        array
1260    }
1261}
1262impl Index<usize> for WafArray {
1263    type Output = WafObject;
1264    fn index(&self, index: usize) -> &Self::Output {
1265        let len = self.len() as usize;
1266        assert!(index < len, "index out of bounds ({index} >= {len})");
1267        let array = unsafe { self.raw.via.array.ptr };
1268        unsafe { &*(array.add(index) as *const _) }
1269    }
1270}
1271impl IndexMut<usize> for WafArray {
1272    fn index_mut(&mut self, index: usize) -> &mut Self::Output {
1273        let len = self.len() as usize;
1274        assert!(index < len, "index out of bounds ({index} >= {len})");
1275        let array = unsafe { self.raw.via.array.ptr };
1276        unsafe { &mut *(array.add(index).cast()) }
1277    }
1278}
1279
1280impl AsRef<[Keyed<WafObject>]> for WafMap {
1281    fn as_ref(&self) -> &[Keyed<WafObject>] {
1282        if self.is_empty() {
1283            return &[];
1284        }
1285        let ptr = unsafe { self.raw.via.map.ptr as *const _ };
1286        unsafe { std::slice::from_raw_parts(ptr, self.len() as usize) }
1287    }
1288}
1289impl AsMut<[Keyed<WafObject>]> for WafMap {
1290    fn as_mut(&mut self) -> &mut [Keyed<WafObject>] {
1291        if self.is_empty() {
1292            return &mut [];
1293        }
1294        let ptr = unsafe { self.raw.via.map.ptr.cast() };
1295        unsafe { std::slice::from_raw_parts_mut(ptr, self.len() as usize) }
1296    }
1297}
1298impl fmt::Debug for WafMap {
1299    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1300        write!(f, "{}{{", stringify!(WafMap))?;
1301        let mut first = true;
1302        for keyed_obj in self.iter() {
1303            if first {
1304                first = false;
1305            } else {
1306                write!(f, ", ")?;
1307            }
1308            write!(f, "{keyed_obj:?}")?;
1309        }
1310        write!(f, "}}")
1311    }
1312}
1313impl Drop for WafMap {
1314    fn drop(&mut self) {
1315        unsafe { self.raw.drop_map() }
1316    }
1317}
1318impl Clone for WafMap {
1319    fn clone(&self) -> Self {
1320        let size = self.len();
1321
1322        if size == 0 {
1323            return Self::new(0);
1324        }
1325
1326        let layout = Layout::array::<libddwaf_sys::_ddwaf_object_kv>(size as usize).unwrap();
1327        let new_ptr: *mut libddwaf_sys::_ddwaf_object_kv = unsafe { no_fail_alloc(layout).cast() };
1328        unsafe { std::ptr::write_bytes(new_ptr, 0, size as usize) };
1329
1330        // Clone each key-value pair
1331        for i in 0..size {
1332            let src_entry: &Keyed<WafObject> = &self[i as usize];
1333            let cloned_entry = ManuallyDrop::new(src_entry.clone());
1334            unsafe { new_ptr.add(i as usize).write(cloned_entry.raw) };
1335        }
1336
1337        Self {
1338            raw: libddwaf_sys::ddwaf_object {
1339                via: libddwaf_sys::_ddwaf_object__bindgen_ty_1 {
1340                    map: libddwaf_sys::_ddwaf_object_map {
1341                        #[allow(clippy::cast_possible_truncation)]
1342                        type_: libddwaf_sys::DDWAF_OBJ_MAP as u8,
1343                        size,
1344                        capacity: size,
1345                        ptr: new_ptr,
1346                    },
1347                },
1348            },
1349        }
1350    }
1351}
1352impl Index<usize> for WafMap {
1353    type Output = Keyed<WafObject>;
1354    fn index(&self, index: usize) -> &Self::Output {
1355        let len = self.len() as usize;
1356        assert!(index < len, "index out of bounds ({index} >= {len})");
1357        let ptr = unsafe { self.raw.via.map.ptr };
1358        unsafe { &*ptr.add(index).cast() }
1359    }
1360}
1361impl IndexMut<usize> for WafMap {
1362    fn index_mut(&mut self, index: usize) -> &mut Self::Output {
1363        let len = self.len() as usize;
1364        assert!(index < len, "index out of bounds ({index} >= {len})");
1365        let ptr = unsafe { self.raw.via.map.ptr };
1366        unsafe { &mut *ptr.add(index).cast() }
1367    }
1368}
1369impl<K: AsRef<[u8]>, V: Into<WafObject>, const N: usize> From<[(K, V); N]> for WafMap {
1370    fn from(vals: [(K, V); N]) -> Self {
1371        let effective_length = N.min(u16::MAX as usize);
1372        #[allow(clippy::cast_possible_truncation)]
1373        let mut map = WafMap::new(effective_length as u16);
1374        for (i, (k, v)) in vals.into_iter().enumerate() {
1375            if i >= effective_length {
1376                break;
1377            }
1378            map[i] = Keyed::from((k.as_ref(), v.into()));
1379        }
1380        map
1381    }
1382}
1383impl<V: Into<WafObject>, const N: usize> From<[(WafObject, V); N]> for WafMap {
1384    fn from(vals: [(WafObject, V); N]) -> Self {
1385        let effective_length = N.min(u16::MAX as usize);
1386        #[allow(clippy::cast_possible_truncation)]
1387        let mut map = WafMap::new(effective_length as u16);
1388        for (i, (k, v)) in vals.into_iter().enumerate() {
1389            if i >= effective_length {
1390                break;
1391            }
1392            map[i] = (k, v.into()).into();
1393        }
1394        map
1395    }
1396}
1397impl<K, V> From<&mut [(K, V)]> for WafMap
1398where
1399    K: Into<WafObject> + Default,
1400    V: Into<WafObject> + Default,
1401{
1402    fn from(value: &mut [(K, V)]) -> Self {
1403        let effective_length = value.len().min(u16::MAX as usize);
1404        #[allow(clippy::cast_possible_truncation)]
1405        let mut map = Self::new(effective_length as u16);
1406        for (i, (k, v)) in value.iter_mut().enumerate() {
1407            if i >= effective_length {
1408                break;
1409            }
1410            let k = std::mem::take(k);
1411            let v = std::mem::take(v);
1412            map[i] = (k.into(), v.into()).into();
1413        }
1414        map
1415    }
1416}
1417
1418impl fmt::Debug for WafBool {
1419    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1420        write!(f, "{}({})", stringify!(WafBool), self.value())
1421    }
1422}
1423impl From<bool> for WafBool {
1424    fn from(value: bool) -> Self {
1425        Self::new(value)
1426    }
1427}
1428
1429impl fmt::Debug for WafFloat {
1430    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1431        write!(f, "{}({})", stringify!(WafFloat), self.value())
1432    }
1433}
1434impl From<f64> for WafFloat {
1435    fn from(value: f64) -> Self {
1436        Self::new(value)
1437    }
1438}
1439
1440impl fmt::Debug for WafNull {
1441    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1442        write!(f, "{}", stringify!(WafNull))
1443    }
1444}
1445impl From<()> for WafNull {
1446    fn from((): ()) -> Self {
1447        Self::new()
1448    }
1449}
1450
1451/// An [`WafObject`] or [`TypedWafObject`] associated with a key.
1452#[repr(transparent)]
1453pub struct Keyed<T: AsRawMutObject> {
1454    raw: libddwaf_sys::_ddwaf_object_kv,
1455    _marker: std::marker::PhantomData<T>,
1456}
1457impl<T: AsRawMutObject> Keyed<T> {
1458    /// Creates a new [`Keyed<WafObject>`] with the provided key and value.
1459    pub fn new(key: impl Into<WafObject>, value: T) -> Self {
1460        let key = key.into();
1461        let val = *value.as_ref();
1462        let ret = Self {
1463            raw: libddwaf_sys::_ddwaf_object_kv { key: key.raw, val },
1464            _marker: std::marker::PhantomData,
1465        };
1466        std::mem::forget(key);
1467        std::mem::forget(value);
1468        ret
1469    }
1470
1471    // Obtains a reference to the map entry key.
1472    #[must_use]
1473    pub fn key(&self) -> &WafObject {
1474        unsafe { self.raw.key.unchecked_as_ref() }
1475    }
1476
1477    /// Obtains a mutable reference to the map entry key.
1478    #[must_use]
1479    pub fn key_mut(&mut self) -> &mut WafObject {
1480        unsafe { self.raw.key.unchecked_as_ref_mut() }
1481    }
1482
1483    /// Obtains a reference to the map entry value.
1484    #[must_use]
1485    pub fn value(&self) -> &T {
1486        unsafe { self.raw.val.unchecked_as_ref() }
1487    }
1488
1489    /// Obtains a mutable reference to the map entry value.
1490    #[must_use]
1491    pub fn value_mut(&mut self) -> &mut T {
1492        unsafe { self.raw.val.unchecked_as_ref_mut() }
1493    }
1494
1495    /// Obtains the key associated with this [`Keyed<WafObject>`] as a string.
1496    ///
1497    /// # Errors
1498    /// Returns an error if the underlying key data is not a valid UTF-8 string, under the same conditions as
1499    /// [`std::str::from_utf8`] or if the key is not a [`WafString`].
1500    #[allow(invalid_from_utf8)]
1501    pub fn key_str(&self) -> Result<&str, Box<dyn std::error::Error>> {
1502        std::str::from_utf8(self.key_bytes()?).map_err(std::convert::Into::into)
1503    }
1504
1505    /// Obtains the key associated with this [`Keyed<WafObject>`] as a byte slice.
1506    ///
1507    /// # Errors
1508    /// Returns an error if the underlying key data is not a [`WafString`].
1509    pub fn key_bytes(&self) -> Result<&[u8], ObjectTypeError> {
1510        let key = self.key();
1511        match key.as_type::<WafString>() {
1512            Some(s) => Ok(s.as_bytes()),
1513            None => Err(ObjectTypeError {
1514                expected: WafObjectType::String,
1515                actual: key.object_type(),
1516            }),
1517        }
1518    }
1519}
1520impl Keyed<WafObject> {
1521    #[must_use]
1522    pub fn as_type<T: TypedWafObject>(&self) -> Option<&Keyed<T>> {
1523        if self.value().object_type() == T::TYPE {
1524            Some(unsafe { &*(std::ptr::from_ref(self).cast()) })
1525        } else {
1526            None
1527        }
1528    }
1529
1530    pub fn as_type_mut<T: TypedWafObject>(&mut self) -> Option<&mut Keyed<T>> {
1531        if self.value().object_type() == T::TYPE {
1532            Some(unsafe { &mut *(std::ptr::from_mut(self).cast()) })
1533        } else {
1534            None
1535        }
1536    }
1537}
1538// Note - We are not implementing DerefMut for Keyed as it'd allow leaking the key if it is used
1539// through [std::mem::take] or [std::mem::replace].
1540impl Keyed<WafArray> {
1541    pub fn iter(&self) -> impl Iterator<Item = &WafObject> {
1542        self.value().iter()
1543    }
1544
1545    pub fn iter_mut(&mut self) -> impl Iterator<Item = &mut WafObject> {
1546        self.value_mut().iter_mut()
1547    }
1548}
1549// Note - We are not implementing DerefMut for Keyed as it'd allow leaking the key if it is used
1550// through [std::mem::take] or [std::mem::replace].
1551impl Keyed<WafMap> {
1552    pub fn iter(&self) -> impl Iterator<Item = &Keyed<WafObject>> {
1553        self.value().iter()
1554    }
1555
1556    pub fn iter_mut(&mut self) -> impl Iterator<Item = &mut Keyed<WafObject>> {
1557        self.value_mut().iter_mut()
1558    }
1559}
1560// impl<T: AsRawMutObject> AsRawMutObject for Keyed<T> {
1561//     unsafe fn as_raw_mut(&mut self) -> &mut libddwaf_sys::ddwaf_object {
1562//         unsafe { self.value_mut().as_raw_mut() }
1563//     }
1564// }
1565impl<T: AsRawMutObject> crate::private::Sealed for Keyed<T> {}
1566impl<T: AsRawMutObject> AsRef<libddwaf_sys::_ddwaf_object_kv> for Keyed<T> {
1567    fn as_ref(&self) -> &libddwaf_sys::_ddwaf_object_kv {
1568        &self.raw
1569    }
1570}
1571impl<T: Default + AsRawMutObject> std::default::Default for Keyed<T> {
1572    fn default() -> Self {
1573        let key = WafObject::default();
1574        let mut value = T::default();
1575        let ret = Self {
1576            raw: libddwaf_sys::_ddwaf_object_kv {
1577                key: key.raw,
1578                val: *unsafe { value.as_raw_mut() },
1579            },
1580            _marker: std::marker::PhantomData,
1581        };
1582        std::mem::forget(key);
1583        std::mem::forget(value);
1584        ret
1585    }
1586}
1587impl<T: AsRawMutObject> Deref for Keyed<T> {
1588    type Target = T;
1589    fn deref(&self) -> &Self::Target {
1590        self.value()
1591    }
1592}
1593impl<T: AsRawMutObject> std::ops::Drop for Keyed<T> {
1594    fn drop(&mut self) {
1595        unsafe { self.raw.key.drop_object() };
1596        unsafe { self.raw.val.drop_object() };
1597    }
1598}
1599impl<T: AsRawMutObject + fmt::Debug> fmt::Debug for Keyed<T> {
1600    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1601        let k = self.key();
1602        if k.object_type() == WafString::TYPE {
1603            write!(
1604                f,
1605                "\"{:?}\"={:?}",
1606                fmt_bin_str(unsafe { self.key().as_type_unchecked::<WafString>() }.as_bytes()),
1607                self.value()
1608            )
1609        } else {
1610            write!(f, "{:?}={:?}", k, self.value())
1611        }
1612    }
1613}
1614impl<T, U: AsRawMutObject> From<(&str, T)> for Keyed<U>
1615where
1616    T: Into<U>,
1617{
1618    fn from(value: (&str, T)) -> Self {
1619        (value.0.as_bytes(), value.1).into()
1620    }
1621}
1622impl<T, U: AsRawMutObject> From<(&[u8], T)> for Keyed<U>
1623where
1624    T: Into<U>,
1625{
1626    fn from(value: (&[u8], T)) -> Self {
1627        let key: WafObject = value.0.into();
1628        let value: U = value.1.into();
1629        Keyed::new(key, value)
1630    }
1631}
1632impl<T: TypedWafObject> From<Keyed<T>> for Keyed<WafObject> {
1633    fn from(value: Keyed<T>) -> Self {
1634        let res = Self {
1635            raw: value.raw,
1636            _marker: std::marker::PhantomData,
1637        };
1638        std::mem::forget(value);
1639        res
1640    }
1641}
1642impl From<(WafObject, WafObject)> for Keyed<WafObject> {
1643    fn from(value: (WafObject, WafObject)) -> Self {
1644        Keyed::new(value.0, value.1)
1645    }
1646}
1647impl<T: TypedWafObject> From<(WafObject, T)> for Keyed<T> {
1648    fn from(value: (WafObject, T)) -> Self {
1649        Keyed::new(value.0, value.1)
1650    }
1651}
1652impl<T: AsRawMutObject + Clone> Clone for Keyed<T> {
1653    fn clone(&self) -> Self {
1654        let cloned_key = self.key().clone();
1655        let cloned_value = self.value().clone();
1656
1657        let ret = Self {
1658            raw: libddwaf_sys::_ddwaf_object_kv {
1659                key: cloned_key.raw,
1660                val: *cloned_value.as_ref(),
1661            },
1662            _marker: std::marker::PhantomData,
1663        };
1664
1665        std::mem::forget(cloned_key);
1666        std::mem::forget(cloned_value);
1667        ret
1668    }
1669}
1670trait UncheckedAsRef: crate::private::Sealed {
1671    /// Converts a naked reference to a [`libddwaf_sys::ddwaf_object`] into a reference to one of the
1672    /// user-friendlier types.
1673    ///
1674    /// # Safety
1675    /// The type `T` must be able to represent this [`libddwaf_sys::ddwaf_object`]'s type (per its
1676    /// associated [`libddwaf_sys::DDWAF_OBJ_TYPE`] value).
1677    unsafe fn unchecked_as_ref<T: AsRef<libddwaf_sys::ddwaf_object> + crate::private::Sealed>(
1678        &self,
1679    ) -> &T;
1680
1681    /// Converts a naked mutable reference to a `ddwaf_object` into a mutable reference to one of the
1682    ///
1683    /// # Safety
1684    /// - The type `T` must be able to represent this [`libddwaf_sys::ddwaf_object`]'s type (per its
1685    ///   associated [`libddwaf_sys::DDWAF_OBJ_TYPE`] value).
1686    /// - The destructor of `T` must be compatible with the value of self.
1687    unsafe fn unchecked_as_ref_mut<T: AsRef<libddwaf_sys::ddwaf_object> + crate::private::Sealed>(
1688        &mut self,
1689    ) -> &mut T;
1690}
1691impl crate::private::Sealed for libddwaf_sys::ddwaf_object {}
1692impl UncheckedAsRef for libddwaf_sys::ddwaf_object {
1693    unsafe fn unchecked_as_ref<T: AsRef<libddwaf_sys::ddwaf_object> + crate::private::Sealed>(
1694        &self,
1695    ) -> &T {
1696        unsafe { &*(std::ptr::from_ref(self).cast()) }
1697    }
1698
1699    unsafe fn unchecked_as_ref_mut<
1700        T: AsRef<libddwaf_sys::ddwaf_object> + crate::private::Sealed,
1701    >(
1702        &mut self,
1703    ) -> &mut T {
1704        unsafe { &mut *(std::ptr::from_mut(self).cast()) }
1705    }
1706}
1707trait UncheckedAsWafObject: crate::private::Sealed {
1708    /// Converts a naked reference to a [`libddwaf_sys::ddwaf_object`] into a reference to an [`WafObject`].
1709    fn as_object_ref(&self) -> &WafObject;
1710}
1711impl<T: UncheckedAsRef> UncheckedAsWafObject for T {
1712    /// Converts a naked reference to a [`libddwaf_sys::ddwaf_object`] into a reference to an [`WafObject`].
1713    fn as_object_ref(&self) -> &WafObject {
1714        unsafe { self.unchecked_as_ref::<WafObject>() }
1715    }
1716}
1717
1718/// Formats a byte slice as an ASCII string, hex-escaping any non-printable characters.
1719fn fmt_bin_str(bytes: &[u8]) -> impl fmt::Debug + '_ {
1720    struct BinFormatter<'a>(&'a [u8]);
1721    impl fmt::Debug for BinFormatter<'_> {
1722        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1723            for &c in self.0 {
1724                if c == b'"' || c == b'\\' {
1725                    write!(f, "\\{}", c as char)?;
1726                } else if c.is_ascii_graphic() || c == b' ' {
1727                    write!(f, "{}", c as char)?;
1728                } else {
1729                    write!(f, "\\x{c:02X}")?;
1730                }
1731            }
1732            Ok(())
1733        }
1734    }
1735    BinFormatter(bytes)
1736}
1737
1738pub(crate) struct RustDdwafAllocator {
1739    raw: libddwaf_sys::ddwaf_allocator,
1740}
1741impl RustDdwafAllocator {
1742    fn new() -> Option<Self> {
1743        let allocator = unsafe {
1744            libddwaf_sys::ddwaf_user_allocator_init(
1745                Some(Self::alloc_fn),
1746                Some(Self::free_fn),
1747                std::ptr::null_mut(),
1748                Option::None,
1749            )
1750        };
1751        if allocator.is_null() {
1752            None
1753        } else {
1754            Some(Self { raw: allocator })
1755        }
1756    }
1757    extern "C" fn alloc_fn(
1758        _udata: *mut ::std::os::raw::c_void,
1759        size: usize,
1760        alignment: usize,
1761    ) -> *mut ::std::os::raw::c_void {
1762        let layout = Layout::from_size_align(size, alignment);
1763        if let Ok(layout) = layout {
1764            unsafe { std::alloc::alloc(layout).cast() }
1765        } else {
1766            debug_assert!(false, "Invalid layout");
1767            std::ptr::null_mut()
1768        }
1769    }
1770
1771    extern "C" fn free_fn(
1772        _udata: *mut ::std::os::raw::c_void,
1773        ptr: *mut ::std::os::raw::c_void,
1774        size: usize,
1775        alignment: usize,
1776    ) {
1777        let layout = Layout::from_size_align(size, alignment);
1778        match layout {
1779            Ok(layout) => unsafe { std::alloc::dealloc(ptr.cast(), layout) },
1780            Err(_) => {
1781                debug_assert!(false, "Invalid layout");
1782            }
1783        }
1784    }
1785}
1786
1787impl Drop for RustDdwafAllocator {
1788    fn drop(&mut self) {
1789        unsafe { libddwaf_sys::ddwaf_allocator_destroy(self.raw) };
1790    }
1791}
1792
1793impl From<&RustDdwafAllocator> for libddwaf_sys::ddwaf_allocator {
1794    fn from(allocator: &RustDdwafAllocator) -> Self {
1795        allocator.raw
1796    }
1797}
1798
1799// RustDdwafAllocator is immutable
1800unsafe impl Sync for RustDdwafAllocator {}
1801unsafe impl Send for RustDdwafAllocator {}
1802
1803static DEFAULT_ALLOCATOR: OnceLock<RustDdwafAllocator> = OnceLock::new();
1804
1805pub(crate) fn get_default_allocator() -> &'static RustDdwafAllocator {
1806    DEFAULT_ALLOCATOR.get_or_init(|| RustDdwafAllocator::new().unwrap())
1807}
1808
1809/// Helper macro to create [`WafObject`]s.
1810#[macro_export]
1811macro_rules! waf_object {
1812    (null) => {
1813        $crate::object::WafObject::from(())
1814    };
1815    ($l:expr) => {
1816        $crate::object::WafObject::from($l)
1817    };
1818}
1819
1820/// Helper macro to create [`WafArray`]s.
1821#[macro_export]
1822macro_rules! waf_array {
1823    () => { $crate::object::WafArray::new(0) };
1824    ($($e:expr),* $(,)?) => {
1825        {
1826            let size = [$($crate::__repl_expr_with_unit!($e)),*].len();
1827            let mut res = $crate::object::WafArray::new(size as u16);
1828            let mut i = usize::MAX;
1829            $(
1830                i = i.wrapping_add(1);
1831                res[i] = $crate::waf_object!($e);
1832            )*
1833            res
1834        }
1835    };
1836}
1837
1838/// Helper macro to create [`WafMap`]s.
1839#[macro_export]
1840macro_rules! waf_map {
1841    () => { $crate::object::WafMap::new(0) };
1842    ($(($k:literal, $v:expr)),* $(,)?) => {
1843        {
1844            let size = [$($crate::__repl_expr_with_unit!($v)),*].len();
1845            let mut res = $crate::object::WafMap::new(u16::try_from(size).unwrap());
1846            let mut i = usize::MAX;
1847            $(
1848                i = i.wrapping_add(1);
1849                let k = $crate::object::WafString::new_literal($k.as_bytes());
1850                let val: $crate::object::WafObject = $v.into();
1851                res[i] = $crate::object::Keyed::new(k, val);
1852            )*
1853            res
1854        }
1855    };
1856}
1857
1858/// Helper macro to facilitate counting token trees within other macros.
1859///
1860/// Not intended for use outside of this crate, but must be exported as it is used by macros in this crate.
1861#[doc(hidden)]
1862#[macro_export]
1863macro_rules! __repl_expr_with_unit {
1864    ($e:expr) => {
1865        ()
1866    };
1867}
1868
1869#[cfg(test)]
1870#[cfg_attr(coverage_nightly, coverage(off))]
1871mod tests {
1872    use std::str::FromStr;
1873
1874    use super::*;
1875
1876    #[test]
1877    #[allow(clippy::float_cmp)] // No operations are done on the values, they should be the same.
1878    #[allow(clippy::cast_possible_truncation)]
1879    fn unsafe_changes_to_default_objects() {
1880        unsafe {
1881            let mut unsigned = WafUnsigned::default();
1882            unsigned.as_raw_mut().via.u64_.val += 1;
1883            assert_eq!(unsigned.value(), 1);
1884
1885            let mut signed = WafSigned::default();
1886            signed.as_raw_mut().via.i64_.val -= 1;
1887            assert_eq!(signed.value(), -1);
1888
1889            let mut float = WafFloat::default();
1890            float.as_raw_mut().via.f64_.val += 1.0;
1891            assert_eq!(float.value(), 1.0);
1892
1893            let mut boolean = WafBool::default();
1894            boolean.as_raw_mut().via.b8.val = true;
1895            assert!(boolean.value());
1896
1897            let null = WafNull::default();
1898            // nothing interesting to do for null; let's try manually setting
1899            // the parameter name
1900            let s = String::from_str("foobar").unwrap();
1901            let keyed_null = Keyed::new(WafString::from(s.as_str()), null);
1902            std::mem::drop(keyed_null);
1903
1904            let mut string = WafString::default();
1905            let str_mut = string.as_raw_mut();
1906            let p: *mut u8 =
1907                no_fail_alloc(Layout::array::<::std::os::raw::c_char>(s.len()).unwrap()).cast();
1908            std::ptr::copy_nonoverlapping(s.as_ptr(), p.cast(), s.len());
1909            str_mut.drop_string();
1910            str_mut.via.str_.ptr = p.cast();
1911            str_mut.via.str_.size = s.len() as u32;
1912            assert_eq!(string.as_str().unwrap(), "foobar");
1913            assert_eq!(string.len(), s.len() as u32);
1914            assert!(!string.is_empty());
1915        }
1916    }
1917
1918    #[test]
1919    #[allow(clippy::cast_possible_truncation)]
1920    fn string_representations_are_equivalent() {
1921        const HELLO: &[u8] = b"hello";
1922
1923        let ss = WafString::new("hello").unwrap();
1924        assert_eq!(ss.raw.obj_type(), libddwaf_sys::DDWAF_OBJ_SMALL_STRING);
1925        assert!(ss.is_valid());
1926        assert!(ss.raw.is_string());
1927
1928        let ls = WafString::new_literal(HELLO);
1929        assert_eq!(ls.raw.obj_type(), libddwaf_sys::DDWAF_OBJ_LITERAL_STRING);
1930        assert!(ls.is_valid());
1931        assert!(ls.raw.is_string());
1932        assert_eq!(ss, ls);
1933
1934        let ns = libddwaf_sys::ddwaf_object {
1935            via: libddwaf_sys::_ddwaf_object__bindgen_ty_1 {
1936                str_: libddwaf_sys::_ddwaf_object_string {
1937                    type_: libddwaf_sys::DDWAF_OBJ_STRING as u8,
1938                    size: HELLO.len() as u32,
1939                    ptr: HELLO.as_ptr() as *mut _,
1940                },
1941            },
1942        };
1943        let ns = unsafe { ns.unchecked_as_ref::<WafString>() };
1944        assert_eq!(ns.raw.obj_type(), libddwaf_sys::DDWAF_OBJ_STRING);
1945        assert!(ns.is_valid());
1946        assert!(ns.raw.is_string());
1947        assert_eq!(ns.as_bytes(), HELLO);
1948        assert_eq!(*ns, ss);
1949        assert_eq!(*ns, ls);
1950    }
1951}