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