falco_event/events/
raw_event.rs

1use crate::events::payload::PayloadFromBytesError;
2use crate::events::{AnyEventPayload, Event, EventMetadata, EventToBytes};
3use crate::fields::{FromBytes, FromBytesError};
4use std::io::Write;
5use std::marker::PhantomData;
6use std::num::TryFromIntError;
7
8/// A trait for types that can be converted from a raw event
9pub trait FromRawEvent<'a>: Sized {
10    /// Parse a raw event into the type implementing this trait
11    fn parse(raw_event: &RawEvent<'a>) -> Result<Self, PayloadFromBytesError>;
12}
13
14pub trait LengthField: TryFrom<usize, Error = TryFromIntError> {
15    fn read(buf: &mut &[u8]) -> Option<usize>;
16
17    fn to_usize(&self) -> usize;
18}
19
20impl LengthField for u16 {
21    #[inline]
22    fn read(buf: &mut &[u8]) -> Option<usize> {
23        let len = buf.split_off(..size_of::<u16>())?;
24        Some(u16::from_ne_bytes(len.try_into().unwrap()) as usize)
25    }
26
27    #[inline]
28    fn to_usize(&self) -> usize {
29        *self as usize
30    }
31}
32
33impl LengthField for u32 {
34    #[inline]
35    fn read(buf: &mut &[u8]) -> Option<usize> {
36        let len = buf.split_off(..size_of::<u32>())?;
37        Some(u32::from_ne_bytes(len.try_into().unwrap()) as usize)
38    }
39
40    #[inline]
41    fn to_usize(&self) -> usize {
42        *self as usize
43    }
44}
45
46/// A struct to represent an iterator over event parameters
47///
48/// It supports iteration over raw parameter payloads (via the [`Iterator`] trait),
49/// as well as typed access to each field, using the [`ParamIter::next_field`] method.
50///
51/// It's obtained from [`RawEvent::params`].
52pub struct ParamIter<'a, T: LengthField> {
53    lengths: &'a [u8],
54    params: &'a [u8],
55    length_type: PhantomData<T>,
56}
57
58impl<'a, T: LengthField> Iterator for ParamIter<'a, T> {
59    type Item = Result<&'a [u8], FromBytesError>;
60
61    #[inline]
62    fn next(&mut self) -> Option<Self::Item> {
63        let len = T::read(&mut self.lengths)?;
64        match self.params.split_off(..len) {
65            Some(param) => Some(Ok(param)),
66            None => Some(Err(FromBytesError::TruncatedField {
67                wanted: len,
68                got: self.params.len(),
69            })),
70        }
71    }
72}
73
74impl<'a, T: LengthField> ParamIter<'a, T> {
75    /// Return the next field, decoded into type `U`.
76    #[inline]
77    pub fn next_field<U>(&mut self) -> Result<U, FromBytesError>
78    where
79        U: FromBytes<'a>,
80    {
81        let mut maybe_next_field = self.next().transpose()?;
82        let val = FromBytes::from_maybe_bytes(maybe_next_field.as_mut())?;
83        if let Some(buf) = maybe_next_field {
84            if !buf.is_empty() {
85                return Err(FromBytesError::LeftoverData);
86            }
87        }
88
89        Ok(val)
90    }
91}
92
93/// A raw event, containing the metadata and payload
94///
95/// This struct is used to represent an event as it is read from a raw byte stream, with
96/// minimal parsing of the header. The payload is left as a raw byte buffer, which can be
97/// parsed later using the `FromRawEvent` trait.
98#[derive(Debug)]
99pub struct RawEvent<'a> {
100    /// Metadata for the event, including timestamp and thread ID
101    pub metadata: EventMetadata,
102
103    /// Length of the event in bytes, including the header
104    pub len: u32,
105
106    /// Type of the event, represented as a 16-bit unsigned integer
107    pub event_type: u16,
108
109    /// Number of parameters in the event, represented as a 32-bit unsigned integer
110    pub nparams: u32,
111
112    /// The payload of the event, containing the raw bytes after the header
113    ///
114    /// The payload contains `nparams` lengths of either `u16` or `u32` (depending on the event type)
115    /// and the actual parameter values. The length of the payload is `len - 26` bytes.
116    pub payload: &'a [u8],
117}
118
119impl<'e> RawEvent<'e> {
120    #[inline]
121    fn from_impl(mut buf: &[u8]) -> Option<RawEvent<'_>> {
122        let ts_buf = buf.split_off(..8)?;
123        let ts = u64::from_ne_bytes(ts_buf.try_into().unwrap());
124
125        let tid_buf = buf.split_off(..8)?;
126        let tid = i64::from_ne_bytes(tid_buf.try_into().unwrap());
127
128        let len_buf = buf.split_off(..4)?;
129        let len = u32::from_ne_bytes(len_buf.try_into().unwrap());
130
131        let event_type_buf = buf.split_off(..2)?;
132        let event_type = u16::from_ne_bytes(event_type_buf.try_into().unwrap());
133
134        let nparams_buf = buf.split_off(..4)?;
135        let nparams = u32::from_ne_bytes(nparams_buf.try_into().unwrap());
136
137        Some(RawEvent {
138            metadata: EventMetadata { ts, tid },
139            len,
140            event_type,
141            nparams,
142            payload: buf,
143        })
144    }
145
146    /// Parse a byte slice into a RawEvent
147    ///
148    /// This decodes the header while leaving the payload as a raw byte buffer.
149    #[inline]
150    pub fn from(buf: &[u8]) -> std::io::Result<RawEvent<'_>> {
151        Self::from_impl(buf).ok_or(std::io::ErrorKind::InvalidData.into())
152    }
153
154    /// Trim event payload
155    ///
156    /// This limits the payload to the length actually indicated in the `len` field
157    /// and returns the excess data. Useful when reading a raw event stream without
158    /// any external structure
159    ///
160    /// Example
161    /// ```
162    /// use falco_event::events::{PayloadFromBytesError, RawEvent};
163    /// # fn main() -> anyhow::Result<()> {
164    /// let mut events: &[u8] = &[ /* raw event bytes */ ];
165    ///
166    /// while !events.is_empty() {
167    ///     let mut event = RawEvent::from(events)?;
168    ///     match event.trim() {
169    ///         Some(tail) => events = tail,
170    ///         None => return Err(PayloadFromBytesError::TruncatedEvent {
171    ///             wanted: event.len as usize,
172    ///             got: events.len(),
173    ///         })?
174    ///     }
175    /// }
176    ///
177    /// # Ok(())
178    /// # }
179    /// ```
180    #[inline]
181    pub fn trim(&mut self) -> Option<&'e [u8]> {
182        let payload_len = self.len as usize - 26;
183        self.payload.split_off(payload_len..)
184    }
185
186    /// Iterate over a buffer with multiple raw events
187    ///
188    /// This function takes a byte slice and returns an iterator that yields `RawEvent` instances
189    /// until the whole buffer is consumed.
190    #[inline]
191    pub fn scan(mut buf: &'e [u8]) -> impl Iterator<Item = Result<RawEvent<'e>, std::io::Error>> {
192        std::iter::from_fn(move || {
193            if buf.is_empty() {
194                return None;
195            }
196            match Self::from(buf) {
197                Ok(mut raw_event) => {
198                    if let Some(tail) = raw_event.trim() {
199                        buf = tail;
200                    }
201                    Some(Ok(raw_event))
202                }
203
204                Err(err) => Some(Err(err)),
205            }
206        })
207    }
208
209    /// Parse a byte buffer (from a raw pointer) into a RawEvent
210    ///
211    /// # Safety
212    ///
213    /// `buf` must point to a complete event, i.e.
214    ///  - include the length field
215    ///  - include `nparams` lengths
216    ///  - have enough data bytes for all the fields (sum of lengths)
217    #[inline]
218    pub unsafe fn from_ptr<'a>(buf: *const u8) -> std::io::Result<RawEvent<'a>> {
219        let len_buf = unsafe { std::slice::from_raw_parts(buf.offset(16), 4) };
220        let len = u32::from_ne_bytes(len_buf.try_into().unwrap());
221
222        let buf: &'a [u8] = unsafe { std::slice::from_raw_parts(buf, len as usize) };
223        Self::from(buf)
224    }
225
226    /// Load the event parameters into a strongly typed `Event<T>`
227    ///
228    /// This method uses the `FromRawEvent` trait to parse the raw event payload into a specific type `T`.
229    /// The returned `Event<T>` contains the metadata (copied from the raw event) and the parsed parameters.
230    #[inline]
231    pub fn load<'a, T: FromRawEvent<'e>>(&'a self) -> Result<Event<T>, PayloadFromBytesError> {
232        #[allow(clippy::question_mark)]
233        let params = match T::parse(self) {
234            Ok(p) => p,
235            Err(e) => return Err(e),
236        };
237        Ok(Event {
238            metadata: self.metadata.clone(),
239            params,
240        })
241    }
242
243    /// Get an iterator over the event parameters
244    ///
245    /// `T` must correspond to the type of the length field (u16 or u32, depending on the event type)
246    #[inline]
247    pub fn params<T: LengthField>(&self) -> Result<ParamIter<'e, T>, PayloadFromBytesError> {
248        let length_size = size_of::<T>();
249        let ll = self.nparams as usize * length_size;
250
251        if self.payload.len() < ll {
252            return Err(PayloadFromBytesError::TruncatedEvent {
253                wanted: ll,
254                got: self.payload.len(),
255            });
256        }
257
258        let (lengths, params) = self.payload.split_at(ll);
259
260        Ok(ParamIter {
261            lengths,
262            params,
263            length_type: PhantomData,
264        })
265    }
266}
267
268impl<'a, 'b> From<&'a RawEvent<'b>> for RawEvent<'b> {
269    #[inline]
270    fn from(event: &'a RawEvent<'b>) -> Self {
271        Self {
272            metadata: event.metadata.clone(),
273            len: event.len,
274            event_type: event.event_type,
275            nparams: event.nparams,
276            payload: event.payload,
277        }
278    }
279}
280
281impl EventToBytes for RawEvent<'_> {
282    #[inline]
283    fn binary_size(&self) -> usize {
284        self.len as usize
285    }
286
287    #[inline]
288    fn write<W: Write>(&self, mut writer: W) -> std::io::Result<()> {
289        self.metadata
290            .write_header(self.len, self.event_type, self.nparams, &mut writer)?;
291        writer.write_all(self.payload)
292    }
293}
294
295impl AnyEventPayload for RawEvent<'_> {
296    const SOURCES: &'static [Option<&'static str>] = &[];
297    const EVENT_TYPES: &'static [u16] = &[];
298}