falco_event/types/string/
cstr_array.rs

1use crate::fields::{FromBytes, FromBytesError, ToBytes};
2use crate::types::format::CStrFormatter;
3use std::ffi::CStr;
4use std::fmt::{Debug, Formatter, Write as _};
5use std::io::Write;
6
7/// A serialized representation of a C-style string array
8///
9/// This type represents an array of C-style strings, where each string is null-terminated.
10/// To get an iterator over the strings, use the `iter` method.
11#[derive(Copy, Clone)]
12pub struct CStrArray<'a>(&'a [u8]);
13
14/// This is an iterator for CStrArray that allows iterating over the contained C-style strings.
15pub struct CStrArrayIter<'a>(&'a [u8]);
16
17impl<'a> Iterator for CStrArrayIter<'a> {
18    type Item = &'a CStr;
19
20    #[inline]
21    fn next(&mut self) -> Option<Self::Item> {
22        if self.0.is_empty() {
23            None
24        } else {
25            <&'a CStr>::from_bytes(&mut self.0).ok()
26        }
27    }
28}
29
30impl<'a> CStrArray<'a> {
31    /// Return an iterator over the C-style strings in this array
32    #[inline]
33    pub fn iter(&self) -> CStrArrayIter<'a> {
34        CStrArrayIter(self.0)
35    }
36}
37
38impl ToBytes for CStrArray<'_> {
39    #[inline]
40    fn binary_size(&self) -> usize {
41        self.0.binary_size()
42    }
43
44    #[inline]
45    fn write<W: Write>(&self, writer: W) -> std::io::Result<()> {
46        self.0.write(writer)
47    }
48
49    #[inline]
50    fn default_repr() -> impl ToBytes {
51        &[] as &[u8]
52    }
53}
54
55impl<'a> FromBytes<'a> for CStrArray<'a> {
56    #[inline]
57    fn from_bytes(buf: &mut &'a [u8]) -> Result<Self, FromBytesError> {
58        match buf.last() {
59            Some(&0) | None => Ok(CStrArray(std::mem::take(buf))),
60            _ => Err(FromBytesError::MissingNul),
61        }
62    }
63}
64
65impl Debug for CStrArray<'_> {
66    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
67        let mut is_first = true;
68        for s in self.iter() {
69            if is_first {
70                is_first = false;
71            } else {
72                f.write_char(';')?;
73            }
74            Debug::fmt(&CStrFormatter(s), f)?;
75        }
76
77        Ok(())
78    }
79}
80
81#[cfg(test)]
82mod tests {
83    use crate::fields::{FromBytes, ToBytes};
84    use crate::types::string::cstr_array::CStrArray;
85
86    #[test]
87    fn test_str_array() {
88        let binary = b"foo\0bar\0".as_slice();
89        let array = CStrArray::from_bytes(&mut &*binary).unwrap();
90
91        let mut iter = array.iter();
92        assert_eq!(iter.next().unwrap(), c"foo");
93        assert_eq!(iter.next().unwrap(), c"bar");
94        assert_eq!(iter.next(), None);
95
96        let mut serialized = Vec::new();
97        array.write(&mut serialized).unwrap();
98        assert_eq!(serialized.as_slice(), binary);
99    }
100
101    #[test]
102    fn test_str_empty_array() {
103        let binary = b"".as_slice();
104        let array = CStrArray::from_bytes(&mut &*binary).unwrap();
105
106        let mut iter = array.iter();
107        assert_eq!(iter.next(), None);
108
109        let mut serialized = Vec::new();
110        array.write(&mut serialized).unwrap();
111        assert_eq!(serialized.as_slice(), binary);
112    }
113
114    #[test]
115    fn test_str_array_with_empty_strings() {
116        let binary = b"\0\0\0".as_slice();
117        let array = CStrArray::from_bytes(&mut &*binary).unwrap();
118
119        let mut iter = array.iter();
120        assert_eq!(iter.next().unwrap(), c"");
121        assert_eq!(iter.next().unwrap(), c"");
122        assert_eq!(iter.next().unwrap(), c"");
123        assert_eq!(iter.next(), None);
124
125        let mut serialized = Vec::new();
126        array.write(&mut serialized).unwrap();
127        assert_eq!(serialized.as_slice(), binary);
128    }
129}