Skip to main content

rs_matter/im/
events.rs

1/*
2 *
3 *    Copyright (c) 2026 Project CHIP Authors
4 *
5 *    Licensed under the Apache License, Version 2.0 (the "License");
6 *    you may not use this file except in compliance with the License.
7 *    You may obtain a copy of the License at
8 *
9 *        http://www.apache.org/licenses/LICENSE-2.0
10 *
11 *    Unless required by applicable law or agreed to in writing, software
12 *    distributed under the License is distributed on an "AS IS" BASIS,
13 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 *    See the License for the specific language governing permissions and
15 *    limitations under the License.
16 */
17
18use core::fmt::Debug;
19
20use embassy_time::Instant;
21
22use crate::acl::Accessor;
23use crate::dm::{ClusterId, EndptId, EventId, Node};
24use crate::error::{Error, ErrorCode};
25use crate::im::{
26    EventData, EventDataTag, EventDataTimestamp, EventFilter, EventNumber, EventPath,
27    EventPriority, EventResp, EventRespTag,
28};
29use crate::persist::{KvBlobStore, KvBlobStoreAccess, Persist, EVENT_EPOCH_KEY};
30use crate::tlv::{
31    FromTLV, TLVArray, TLVBuilderParent, TLVElement, TLVSequence, TLVSequenceIter, TLVTag,
32    TLVWrite, TagType, ToTLV,
33};
34use crate::utils::cell::RefCell;
35use crate::utils::init::{init, Init};
36use crate::utils::storage::WriteBuf;
37use crate::utils::sync::blocking::Mutex;
38
39/// The default size of each event buffer in bytes.
40/// This is a tradeoff between memory use and the risk of evicting events before subscribers have had a chance to read them.
41pub const DEFAULT_MAX_EVENTS_BUF_SIZE: usize = 256;
42
43/// The size when events won't be used
44pub const NO_EVENTS_BUF_SIZE: usize = 0;
45
46/// A type alias for `Events` with zero capacity, for when events need to be disabled.
47pub type NoEvents = Events<NO_EVENTS_BUF_SIZE>;
48
49/// Only persist every `EVENT_NUMBER_EPOCH_SIZE` event numbers to avoid flash wear.
50const EVENT_NUMBER_EPOCH_SIZE: EventNumber = 10000;
51
52/// Events queue.
53///
54/// It lets one publish Matter Events into a priority queue,
55/// and allows subscribers and remote clients to read the published events.
56///
57/// The queue is implemented as three equally sized ring buffers, the size of the buffers is set by N.
58/// Hence the memory use of the buffers will be 3 * N.
59/// If a very small N is picked, then clients that poll may miss events as they fall out of the queue;
60/// but a large N of course uses more memory.
61///
62/// If the app emits no events, this subsystem can be disabled by using the `NoEvents` type alias.
63pub struct Events<const N: usize = DEFAULT_MAX_EVENTS_BUF_SIZE> {
64    inner: Mutex<RefCell<EventsInner<N>>>,
65}
66
67impl<const N: usize> Events<N> {
68    #[inline(always)]
69    pub const fn new() -> Self {
70        Self {
71            inner: Mutex::new(RefCell::new(EventsInner::new())),
72        }
73    }
74
75    pub fn init() -> impl Init<Self> {
76        init!(Self {
77            inner <- Mutex::init(RefCell::init(EventsInner::init())),
78        })
79    }
80
81    pub fn reset(&mut self) {
82        self.inner.get_mut().borrow_mut().reset();
83    }
84
85    /// Borrow the inner events state mutably.
86    ///
87    /// Available only with exclusive (`&mut`) access - used by
88    /// [`InteractionModelState`](crate::im::InteractionModelState) at startup to
89    /// drive the (sync) persistence helpers without locking.
90    pub(crate) fn inner_mut(&mut self) -> &mut EventsInner<N> {
91        self.inner.get_mut().get_mut()
92    }
93
94    pub(crate) fn fetch<F, R>(&self, f: F) -> R
95    where
96        F: FnOnce(EventsIter<'_, N>) -> R,
97    {
98        self.inner.lock(|state| {
99            let state = state.borrow();
100
101            f(state.iter())
102        })
103    }
104
105    pub(crate) fn watermark(&self) -> EventNumber {
106        self.inner
107            .lock(|state| state.borrow().next_event_number.wrapping_sub(1))
108    }
109
110    /// Push a new event into the event queue.
111    ///
112    /// # Arguments
113    /// - `endpoint_id`: The endpoint ID of the event source.
114    /// - `cluster_id`: The cluster ID of the event source.
115    /// - `event_id`: The event ID of the event source.
116    /// - `priority`: The priority of the event.
117    /// - `kv`: A key-value store access object for persisting event state as needed.
118    /// - `f`: A closure that takes an `EventTLVWrite` and writes
119    ///   the event data into it using TLV encoding. The closure should return an error if writing the event data fails for any reason, in which case the event will not be pushed into the queue.
120    ///
121    /// # Returns
122    /// - `Ok(EventNumber)`: The sequence number of the emitted event, if the event was successfully emitted.
123    /// - `Err(Error)`: An error if the event could not be emitted.
124    pub fn push<S, F>(
125        &self,
126        endpoint_id: EndptId,
127        cluster_id: ClusterId,
128        event_id: EventId,
129        priority: EventPriority,
130        kv: S,
131        f: F,
132    ) -> Result<EventNumber, Error>
133    where
134        S: KvBlobStoreAccess,
135        F: FnOnce(EventTLVWrite<'_>) -> Result<(), Error>,
136    {
137        let mut persist = Persist::new(kv);
138
139        let event_number = self.inner.lock(|state| {
140            let mut state = state.borrow_mut();
141
142            let event_number = state.next_event_number(&mut persist)?;
143
144            // TODO: Support `EpochTimestamp` / `PosixTimestamp` variants too
145            // for devices that have a real-time clock available, gated on a
146            // future wall-clock hook. `SystemTimestamp` (ms since boot) is
147            // spec-compliant per Matter Core and always available
148            // via the monotonic clock.
149            let timestamp = EventDataTimestamp::SystemTimestamp(Instant::now().as_millis());
150
151            state.push(
152                endpoint_id,
153                cluster_id,
154                event_id,
155                event_number,
156                priority,
157                timestamp,
158                f,
159            )?;
160
161            Ok::<_, Error>(event_number)
162        })?;
163
164        persist.run()?;
165
166        Ok(event_number)
167    }
168}
169
170impl<const N: usize> Default for Events<N> {
171    fn default() -> Self {
172        Self::new()
173    }
174}
175
176/// The inner state of the events queue, protected by a mutex in the outer Events struct. This is where all the actual logic lives.
177///
178/// It's modeled after the tiered ring buffer design used in the C++ matter SDK:
179/// - *Every* new event is written to the next slot in the DEBUG buffer.
180/// - If there is no space for the new event, events are FIFO evicted from the first buffer.
181/// - If the evicted event has a priority as high as or higher than the next buffer in the chain,
182///   then the evicted event is promoted to there, surviving to see another day.
183/// - Promotion may in turn require more eviction in the next buffer, and so on up the chain.
184/// - The end result is that critical events get to live in any of the buffers, making their way through
185///   all three until they finally age out. Info lives in one of the first two, and debug only in the first.
186///
187/// N.B: the discussion in PR 361 that introduced this:
188/// We were not able to determine a way to implement a priority queue that both met the specs requirements
189/// (low-prio events must not cause eviction of high-prio events) while also allowing debug and info-prio events
190/// to be emitted at all.
191/// Instead we opted to replicate the approach used in the C++ impl, which we believe is reasonable but also seemingly in violation of the spec.
192#[derive(Debug)]
193#[cfg_attr(feature = "defmt", derive(defmt::Format))]
194pub(crate) struct EventsInner<const N: usize> {
195    // TODO(events): Allow per-ring const generics, so the rings can be sized independently
196    buf_debug: EventsBuf<N>,
197    buf_info: EventsBuf<N>,
198    buf_critical: EventsBuf<N>,
199    /// The first assigned event number is 1; `0` is reserved as the "no events seen yet"
200    /// sentinel used by fresh subscriptions.
201    next_event_number: EventNumber,
202}
203
204impl<const N: usize> EventsInner<N> {
205    const fn new() -> Self {
206        Self {
207            buf_debug: EventsBuf::new(),
208            buf_info: EventsBuf::new(),
209            buf_critical: EventsBuf::new(),
210            next_event_number: 1,
211        }
212    }
213
214    fn init() -> impl Init<Self> {
215        init!(Self {
216            buf_debug <- EventsBuf::init(),
217            buf_info <- EventsBuf::init(),
218            buf_critical <- EventsBuf::init(),
219            next_event_number: 1,
220        })
221    }
222
223    fn reset(&mut self) {
224        self.buf_debug.reset();
225        self.buf_info.reset();
226        self.buf_critical.reset();
227        self.next_event_number = 1;
228    }
229
230    /// Remove persisted state from the given key-value store.
231    pub(crate) fn reset_persist(
232        &mut self,
233        kv: &mut dyn KvBlobStore,
234        buf: &mut [u8],
235    ) -> Result<(), Error> {
236        self.reset();
237
238        kv.remove(EVENT_EPOCH_KEY, buf)?;
239
240        info!("Removed events counter from storage");
241
242        Ok(())
243    }
244
245    /// Load persisted state from the given key-value store, so that we can continue emitting events without reusing event numbers.
246    pub(crate) fn load_persist(
247        &mut self,
248        kv: &mut dyn KvBlobStore,
249        buf: &mut [u8],
250    ) -> Result<(), Error> {
251        self.reset();
252
253        if let Some(data) = kv.load(EVENT_EPOCH_KEY, buf)? {
254            self.load(data)?;
255
256            info!("Loaded events counter from storage");
257        }
258
259        Ok(())
260    }
261
262    /// Restore events from previously persisted state.
263    fn load(&mut self, data: &[u8]) -> Result<(), Error> {
264        self.next_event_number = TLVElement::new(data).u64()?;
265
266        Ok(())
267    }
268
269    #[allow(clippy::too_many_arguments)]
270    fn push<F>(
271        &mut self,
272        endpoint_id: EndptId,
273        cluster_id: ClusterId,
274        event_id: EventId,
275        event_number: EventNumber,
276        priority: EventPriority,
277        timestamp: EventDataTimestamp,
278        f: F,
279    ) -> Result<(), Error>
280    where
281        F: FnOnce(EventTLVWrite<'_>) -> Result<(), Error>,
282    {
283        let mut event_writer = EventWriter::new(self);
284
285        let pos = event_writer.get_tail();
286
287        let result = (|| {
288            EventData {
289                path: EventPath {
290                    endpoint: Some(endpoint_id),
291                    cluster: Some(cluster_id),
292                    event: Some(event_id),
293                    ..Default::default()
294                },
295                event_number,
296                priority,
297                timestamp,
298                data: TLVElement::new(&[]),
299            }
300            .write_preamble(&EVENT_TAG, event_writer.tw())?;
301
302            f(event_writer.tw())?;
303
304            event_writer.tw().end_container()
305        })();
306
307        if result.is_err() {
308            event_writer.rewind_to(pos);
309        }
310
311        result
312    }
313
314    fn next_event_number<S>(&mut self, persist: &mut Persist<S>) -> Result<EventNumber, Error>
315    where
316        S: KvBlobStoreAccess,
317    {
318        let event_number = self.next_event_number;
319
320        if event_number == 1 || event_number.is_multiple_of(EVENT_NUMBER_EPOCH_SIZE) {
321            // We're at an epoch start boundary. Therefore, we need to persist the new epoch to storage
322            // so we don't lose it on reboot and end up reusing event numbers.
323            persist.store_tlv(
324                EVENT_EPOCH_KEY,
325                if event_number == 1 {
326                    EVENT_NUMBER_EPOCH_SIZE
327                } else {
328                    event_number.wrapping_add(EVENT_NUMBER_EPOCH_SIZE).max(1)
329                },
330            )?;
331        }
332
333        self.next_event_number = event_number.wrapping_add(1).max(1);
334
335        Ok(event_number)
336    }
337
338    fn iter(&self) -> EventsIter<'_, N> {
339        EventsIter {
340            events: self,
341            buf_ref: EventPriority::Critical,
342            buf_iter: self.buf_critical.iter(),
343        }
344    }
345
346    /// Return a reference to the buffer corresponding to the provided priority level
347    fn buf(&self, priority: EventPriority) -> &EventsBuf<N> {
348        match priority {
349            EventPriority::Debug => &self.buf_debug,
350            EventPriority::Info => &self.buf_info,
351            EventPriority::Critical => &self.buf_critical,
352        }
353    }
354
355    /// Return a mutable reference to the buffer corresponding to the provided priority level
356    fn buf_mut(&mut self, priority: EventPriority) -> &mut EventsBuf<N> {
357        match priority {
358            EventPriority::Debug => &mut self.buf_debug,
359            EventPriority::Info => &mut self.buf_info,
360            EventPriority::Critical => &mut self.buf_critical,
361        }
362    }
363
364    /// Return a reference to the buffer corresponding to the provided priority level, and a mutable reference to the next buffer in the chain if it exists
365    fn buf_and_next_mut(
366        &mut self,
367        priority: EventPriority,
368    ) -> (&EventsBuf<N>, Option<&mut EventsBuf<N>>) {
369        match priority {
370            EventPriority::Debug => (&self.buf_debug, Some(&mut self.buf_info)),
371            EventPriority::Info => (&self.buf_info, Some(&mut self.buf_critical)),
372            EventPriority::Critical => (&self.buf_critical, None),
373        }
374    }
375}
376
377/// An iterator over the events in the queue, starting from the highest priority and oldest event.
378pub struct EventsIter<'a, const N: usize> {
379    events: &'a EventsInner<N>,
380    buf_ref: EventPriority,
381    buf_iter: TLVSequenceIter<'a>,
382}
383
384impl<'a, const N: usize> Iterator for EventsIter<'a, N> {
385    type Item = EventData<'a>;
386
387    fn next(&mut self) -> Option<Self::Item> {
388        if let Some(res) = self.buf_iter.next() {
389            let event = unwrap!(
390                res,
391                "Should not have iter errors as we only put well-formed TLVs in the buffer"
392            );
393            let event = unwrap!(
394                EventData::from_tlv(&event),
395                "Should not have parsing errors as we only put well-formed TLVs in the buffer"
396            );
397
398            return Some(event);
399        }
400
401        if let Some(next_buf_ref) = self.buf_ref.prev() {
402            self.buf_iter = self.events.buf(next_buf_ref).iter();
403            self.buf_ref = next_buf_ref;
404
405            self.next()
406        } else {
407            None
408        }
409    }
410}
411
412/// A helper struct for writing event data into the buffers, handling eviction and promotion as needed to make space for the new event.
413struct EventWriter<'a, const N: usize> {
414    events: &'a mut EventsInner<N>,
415    bytes_written: usize,
416}
417
418impl<'a, const N: usize> EventWriter<'a, N> {
419    // We always write at the end of the debug buffer
420    // Events are flowing to higher-prio buffers by eviction
421    const OPER_BUF: EventPriority = EventPriority::Debug;
422
423    /// Create a new EventWriter for the given EventsInner, starting with zero bytes written.
424    #[inline(always)]
425    const fn new(events: &'a mut EventsInner<N>) -> Self {
426        Self {
427            events,
428            bytes_written: 0,
429        }
430    }
431
432    /// Get a TLVWrite decorator for this EventWriter,
433    /// which handles writing TLV data and rolling back on errors by rewinding the write head to the position before the write started.
434    #[inline(always)]
435    fn tw(&mut self) -> EventTLVWrite<'_> {
436        EventTLVWrite(self)
437    }
438
439    /// Write a byte to the current buffer, evicting and promoting events as needed to make space for the new byte.
440    fn write(&mut self, byte: u8) -> Result<(), Error> {
441        if N == 0 {
442            // Events are disabled, we should never write anything to the buffer and should always succeed.
443            return Ok(());
444        }
445
446        if self.bytes_written == N {
447            // This event is larger than the buffer, the client needs to change the buffer size for this to work
448            return Err(Error::new(ErrorCode::ResourceExhausted));
449        }
450
451        while self.events.buf_mut(Self::OPER_BUF).append(byte).is_err() {
452            // Overflow, need to evict an event to make space.
453            // This may cascade and cause evictions in the higher priority buffers, but that's fine,
454            // as we just want to make space for this new event and the priority guarantees are maintained by the eviction logic.
455            self.evict(Self::OPER_BUF);
456        }
457
458        self.bytes_written += 1;
459
460        Ok(())
461    }
462
463    /// Rewind the write head to the position before the current event started being written, effectively discarding any bytes written for the current event so far. This is used to roll back writes when an error occurs during event writing.
464    fn rewind_to(&mut self, bytes_written: usize) {
465        assert!(self.bytes_written >= bytes_written);
466
467        self.events
468            .buf_mut(Self::OPER_BUF)
469            .rewind_by(self.bytes_written - bytes_written);
470        self.bytes_written = bytes_written;
471    }
472
473    /// Evict the first event from the buffer corresponding to the provided priority level,
474    /// promoting it to the next buffer if its priority meets the threshold, and cascading evictions/promotions as needed.
475    fn evict(&mut self, buf_ref: EventPriority) {
476        let event_len = self.events.buf(buf_ref).first_event_len();
477
478        if let Some(next_buf_ref) = buf_ref.next() {
479            let event_prio = self.events.buf(buf_ref).first_event_prio();
480
481            if next_buf_ref as u8 <= event_prio {
482                // There is another level and our event meets the priority threshold, so we should promote it
483                self.promote(buf_ref, next_buf_ref, event_len);
484            }
485        }
486
487        // Evict the event from the current buffer, whether we promoted it or not
488        self.events.buf_mut(buf_ref).evict_first_event();
489    }
490
491    // Promote the first event from the source buffer to the destination buffer,
492    // evicting events from the destination buffer as needed to make space and potentially
493    // cascading promotion to higher buffers if evicted events meet the priority threshold
494    fn promote(&mut self, src_buf: EventPriority, dst_buf: EventPriority, event_len: usize) {
495        // Make space (n.b. this assumes the next buffer is always at least as large as the current buffer, which is currently always true)
496        while self.events.buf(dst_buf).capacity() < event_len {
497            self.evict(dst_buf);
498        }
499
500        let (src, dst) = self.events.buf_and_next_mut(src_buf);
501
502        let dst = unwrap!(
503            dst,
504            "Dst buffer should always exist as this is checked in evict()"
505        );
506
507        unwrap!(
508            dst.append_slice(src.slice(event_len)),
509            "Should not overflow as eviction should have cleared space"
510        );
511    }
512}
513
514/// A dyn-compatible writer for event data
515/// Necessary so that we can implement `EventTLVWrite`, which is a `TLVWrite` with an erased `const N: usize` generic
516trait DynEventWriter {
517    fn write(&mut self, byte: u8) -> Result<(), Error>;
518
519    fn get_tail(&self) -> usize;
520
521    fn rewind_to(&mut self, pos: usize);
522}
523
524impl<'a, const N: usize> DynEventWriter for EventWriter<'a, N> {
525    fn write(&mut self, byte: u8) -> Result<(), Error> {
526        EventWriter::write(self, byte)
527    }
528
529    fn get_tail(&self) -> usize {
530        self.bytes_written
531    }
532
533    fn rewind_to(&mut self, pos: usize) {
534        EventWriter::rewind_to(self, pos)
535    }
536}
537
538/// A `TLVWrite` wrapper around EventWriter that erases the const generic,
539/// allowing it to be used in the closure passed to `Events::push()` and the various `emit_event` handler context methods.
540pub struct EventTLVWrite<'a>(&'a mut dyn DynEventWriter);
541
542impl core::fmt::Debug for EventTLVWrite<'_> {
543    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
544        f.debug_tuple("EventTLVWrite").finish()
545    }
546}
547
548#[cfg(feature = "defmt")]
549impl defmt::Format for EventTLVWrite<'_> {
550    fn format(&self, fmt: defmt::Formatter) {
551        defmt::write!(fmt, "EventTLVWrite")
552    }
553}
554
555impl TLVWrite for EventTLVWrite<'_> {
556    type Position = usize;
557
558    fn write(&mut self, byte: u8) -> Result<(), Error> {
559        self.0.write(byte)
560    }
561
562    fn get_tail(&self) -> Self::Position {
563        self.0.get_tail()
564    }
565
566    fn rewind_to(&mut self, pos: Self::Position) {
567        self.0.rewind_to(pos)
568    }
569}
570
571impl TLVBuilderParent for EventTLVWrite<'_> {
572    type Write = Self;
573
574    fn writer(&mut self) -> &mut Self::Write {
575        self
576    }
577}
578
579const EVENT_TAG: TLVTag = TLVTag::Context(EventRespTag::Data as _);
580
581/// The context tag corresponding to the event data field in the Event Response TLV structure.
582pub const EVENT_DATA_TAG: TLVTag = TLVTag::Context(EventDataTag::Data as _);
583
584/// Stores one "priority level" of events, see the doc string on EventsInner for more info on that.
585///
586/// This behaves very similar to a ring buffer - you can append data at the write head, and eventually
587/// the head will catch up and "eat" the tail, implementing a sort of sliding window of visible data.
588///
589/// This is a much less efficient variant though - it left-shifts the entire buffer to "evict" old events,
590/// rather than just track head/tail pointers with wrap-around.
591///
592/// The thing we gain from the less efficient implementation is that events are never "split up", they are
593/// always complete TLVs in contiguous memory, which allows to use TLVElement to read/iterate over the events.
594///
595/// If you feel enthusiastic, it might give some performance gains to replace this with a real ring buffer.
596#[derive(Debug, Clone)]
597#[cfg_attr(feature = "defmt", derive(defmt::Format))]
598struct EventsBuf<const N: usize> {
599    data: [u8; N],
600    head: usize,
601}
602
603impl<const N: usize> EventsBuf<N> {
604    const fn new() -> Self {
605        Self {
606            data: [0; N],
607            head: 0,
608        }
609    }
610
611    fn init() -> impl Init<Self> {
612        init!(Self {
613            data <- crate::utils::init::zeroed(),
614            head: 0,
615        })
616    }
617
618    fn reset(&mut self) {
619        self.head = 0;
620    }
621
622    fn rewind_by(&mut self, bytes_written: usize) {
623        assert!(self.head >= bytes_written);
624
625        self.head -= bytes_written;
626    }
627
628    fn slice(&self, len: usize) -> &[u8] {
629        assert!(self.head >= len);
630        &self.data[..len]
631    }
632
633    fn append(&mut self, byte: u8) -> Result<(), OverflowError> {
634        if self.capacity() == 0 {
635            return Err(OverflowError);
636        }
637
638        self.data[self.head] = byte;
639        self.head += 1;
640        Ok(())
641    }
642
643    fn append_slice(&mut self, data: &[u8]) -> Result<(), OverflowError> {
644        if self.capacity() < data.len() {
645            return Err(OverflowError);
646        }
647
648        self.data[self.head..self.head + data.len()].copy_from_slice(data);
649        self.head += data.len();
650
651        Ok(())
652    }
653
654    fn capacity(&self) -> usize {
655        self.data.len() - self.head
656    }
657
658    /// Get the TLV length of the event in the buffer
659    ///
660    /// The method will panic if the buffer is empty or if the buffer contains invalid data
661    fn first_event_len(&self) -> usize {
662        assert!(self.head > 0);
663        unwrap!(TLVSequence(&self.data[..self.head]).container_len())
664    }
665
666    /// Get the priority of the event at the start of the buffer
667    ///
668    /// The method will panic if the buffer is empty or if the buffer contains invalid data
669    fn first_event_prio(&self) -> u8 {
670        unwrap!(unwrap!(
671            unwrap!(TLVElement::new(&self.data[..self.head]).structure())
672                .find_ctx(EventDataTag::Priority as _)
673        )
674        .u8())
675    }
676
677    fn evict_first_event(&mut self) {
678        let tlv_len = self.first_event_len();
679
680        self.data.copy_within(tlv_len..self.head, 0);
681        self.head -= tlv_len;
682    }
683
684    fn iter(&self) -> TLVSequenceIter<'_> {
685        TLVSequence(&self.data[0..self.head]).iter()
686    }
687}
688
689#[derive(Debug)]
690#[cfg_attr(feature = "defmt", derive(defmt::Format))]
691struct OverflowError;
692
693pub struct EventReader {
694    max_seen_event_number: u64,
695    next_max_seen_event_number: u64,
696    /// Whether the originating Read/Subscribe request had `fabricFiltered=true`.
697    /// When set, fabric-sensitive events (those whose payload carries a
698    /// `FabricIndex` context-tag 254) are dropped if their fabric index does
699    /// not match the accessor's. See Matter Core spec.
700    fabric_filtered: bool,
701}
702
703impl EventReader {
704    pub const fn new(
705        max_seen_event_number: u64,
706        next_max_seen_event_number: u64,
707        fabric_filtered: bool,
708    ) -> Self {
709        Self {
710            max_seen_event_number,
711            next_max_seen_event_number,
712            fabric_filtered,
713        }
714    }
715
716    pub fn process_read(
717        &mut self,
718        event: EventData<'_>,
719        paths: &TLVArray<'_, EventPath>,
720        event_filters: &Option<TLVArray<'_, EventFilter>>,
721        node: &Node<'_>,
722        accessor: &Accessor<'_>,
723        tw: &mut WriteBuf<'_>,
724    ) -> Result<bool, Error> {
725        let event_number = event.event_number;
726        if !(event_number > self.max_seen_event_number
727            && event_number <= self.next_max_seen_event_number)
728        {
729            // This event is outside the range of interest, skip
730            return Ok(false);
731        }
732
733        let tail = tw.get_tail();
734
735        let result = self.do_process_read(event, paths, event_filters, node, accessor, &mut *tw);
736
737        if result.is_err() {
738            // If there was an error, rewind to the tail so we don't write any data
739            // and leave `max_seen_event_number` untouched so this event will be
740            // retried on the next chunk.
741            tw.rewind_to(tail);
742        } else {
743            // The event was considered (whether or not it actually matched the
744            // path/filter/access checks). Advance the local watermark so that
745            // chunked reads do not re-consider the same event again on
746            // continuation, and so that the iteration converges.
747            self.max_seen_event_number = event_number;
748        }
749
750        result
751    }
752
753    fn do_process_read(
754        &mut self,
755        event: EventData<'_>,
756        paths: &TLVArray<'_, EventPath>,
757        event_filters: &Option<TLVArray<'_, EventFilter>>,
758        node: &Node<'_>,
759        accessor: &Accessor<'_>,
760        tw: &mut WriteBuf<'_>,
761    ) -> Result<bool, Error> {
762        if self.fabric_filtered && !Self::matches_fabric(&event, accessor) {
763            return Ok(false);
764        }
765
766        if Self::matches_paths(&event, paths, node, accessor)?
767            && Self::matches_filters(&event, event_filters)?
768            && Self::matches_access(&event, node, accessor)?
769        {
770            EventResp::Data(event).to_tlv(&TagType::Anonymous, &mut *tw)?;
771
772            Ok(true)
773        } else {
774            Ok(false)
775        }
776    }
777
778    /// Per Matter Core spec (Fabric-Sensitive Reporting):
779    /// When `fabricFiltered=true`, fabric-sensitive events (those whose payload
780    /// carries a `FabricIndex` field at context tag 254) SHALL only be reported
781    /// to the requesting fabric.
782    ///
783    /// Events without a `FabricIndex` field are not fabric-sensitive and pass
784    /// through unfiltered. Events with a `FabricIndex` field that matches the
785    /// accessor's fabric are reported as well.
786    fn matches_fabric(event: &EventData<'_>, accessor: &Accessor<'_>) -> bool {
787        // Inspect the event payload struct for context tag 254 (FabricIndex).
788        let Ok(payload) = event.data.structure() else {
789            // Not a struct payload — treat as non-fabric-sensitive.
790            return true;
791        };
792
793        let Ok(elem) = payload.find_ctx(crate::im::encoding::FABRIC_INDEX_TAG) else {
794            // No `FabricIndex` field — non-fabric-sensitive, allow.
795            return true;
796        };
797
798        match elem.non_empty().and_then(|e| e.u8().ok()) {
799            Some(fab_idx) => fab_idx == accessor.fab_idx,
800            // Field present but unreadable / null — be conservative and allow.
801            None => true,
802        }
803    }
804
805    fn matches_paths(
806        event: &EventData<'_>,
807        paths: &TLVArray<'_, EventPath>,
808        node: &Node<'_>,
809        accessor: &Accessor<'_>,
810    ) -> Result<bool, Error> {
811        for path in paths {
812            let path = path?;
813
814            if Self::matches_path(event, path, node, accessor) {
815                return Ok(true);
816            }
817        }
818
819        Ok(false)
820    }
821
822    fn matches_path(
823        event: &EventData<'_>,
824        path: EventPath,
825        node: &Node<'_>,
826        accessor: &Accessor<'_>,
827    ) -> bool {
828        if node.validate_event_path(&path, accessor).is_err() {
829            return false;
830        }
831
832        let epath = &event.path;
833
834        epath
835            .node
836            .is_none_or(|node| path.node.is_none_or(|expected_node| expected_node == node))
837            && epath.endpoint.is_none_or(|endpoint| {
838                path.endpoint
839                    .is_none_or(|expected_endpoint| expected_endpoint == endpoint)
840            })
841            && epath.cluster.is_none_or(|cluster| {
842                path.cluster
843                    .is_none_or(|expected_cluster| expected_cluster == cluster)
844            })
845            && epath.event.is_none_or(|event| {
846                path.event
847                    .is_none_or(|expected_event| expected_event == event)
848            })
849    }
850
851    fn matches_filters(
852        event: &EventData<'_>,
853        event_filters: &Option<TLVArray<'_, EventFilter>>,
854    ) -> Result<bool, Error> {
855        if let Some(filters) = &event_filters {
856            // Check if the event passes the filters. If it doesn't pass any of them, skip it.
857            // We assume the 99% case is that there is a single filter, on event-no, so just brute force filtering
858            for filter in filters {
859                if let Some(event_min) = filter?.event_min {
860                    if event.event_number < event_min {
861                        return Ok(false);
862                    }
863                }
864            }
865        }
866
867        Ok(true)
868    }
869
870    fn matches_access(
871        event: &EventData<'_>,
872        node: &Node<'_>,
873        accessor: &Accessor<'_>,
874    ) -> Result<bool, Error> {
875        Ok(node.validate_event_path(&event.path, accessor).is_ok())
876    }
877}
878
879#[cfg(test)]
880mod tests {
881    use crate::tlv::ToTLV;
882
883    use super::*;
884
885    #[test]
886    fn one_entry() {
887        let crit1 = TestEvent::new(1, EventPriority::Critical);
888        let mut q: EventsInner<32> = EventsInner::new();
889
890        crit1.push_into(&mut q).unwrap();
891
892        assert_eq!(TestEvent::vec_from(&q.buf_debug).unwrap(), &[crit1]);
893    }
894
895    #[test]
896    fn critical_is_promoted() {
897        let crit1 = TestEvent::new(1, EventPriority::Critical);
898        let crit2 = TestEvent::new(2, EventPriority::Info);
899        let crit3 = TestEvent::new(3, EventPriority::Info);
900        let mut q: EventsInner<32> = EventsInner::new();
901
902        crit1.push_into(&mut q).unwrap();
903        crit2.push_into(&mut q).unwrap();
904        crit3.push_into(&mut q).unwrap();
905
906        assert_eq!(TestEvent::vec_from(&q.buf_debug).unwrap(), &[crit3]);
907        assert_eq!(TestEvent::vec_from(&q.buf_info).unwrap(), &[crit2]);
908        assert_eq!(TestEvent::vec_from(&q.buf_critical).unwrap(), &[crit1]);
909    }
910
911    #[test]
912    fn debug_is_dropped() {
913        let crit1 = TestEvent::new(1, EventPriority::Critical);
914        let dbg2 = TestEvent::new(2, EventPriority::Debug);
915        let crit3: TestEvent = TestEvent::new(3, EventPriority::Critical);
916        let mut q: EventsInner<32> = EventsInner::new();
917
918        crit1.push_into(&mut q).unwrap();
919        dbg2.push_into(&mut q).unwrap();
920        crit3.push_into(&mut q).unwrap();
921
922        // Then the dbg level has the last event - crit3
923        assert_eq!(TestEvent::vec_from(&q.buf_debug).unwrap(), &[crit3]);
924        // The info level has the first critical event, the dbg event evicted it to there
925        // but when crit3 was pushed the debug event didn't get promoted, so crit1 stays put
926        assert_eq!(TestEvent::vec_from(&q.buf_info).unwrap(), &[crit1]);
927        // And finally there's then nothing here
928        assert_eq!(TestEvent::vec_from(&q.buf_critical).unwrap(), &[]);
929    }
930
931    #[test]
932    fn event_larger_than_buffer() {
933        let crit1 = TestEvent::new(1, EventPriority::Critical);
934        let mut q: EventsInner<8> = EventsInner::new();
935
936        assert_eq!(
937            crit1.push_into(&mut q).expect_err("").code(),
938            ErrorCode::ResourceExhausted
939        );
940    }
941
942    // Test utilities for this suite
943
944    #[derive(PartialEq, Clone, Debug)]
945    struct TestEvent {
946        endpoint: EndptId,
947        cluster: ClusterId,
948        event: EventId,
949        event_number: EventNumber,
950        priority: EventPriority,
951        timestamp: EventDataTimestamp,
952        data: u64,
953    }
954
955    impl TestEvent {
956        const fn new(event_number: EventNumber, priority: EventPriority) -> Self {
957            Self {
958                endpoint: 42,
959                cluster: 1,
960                event: 0xB33F,
961                event_number,
962                priority,
963                timestamp: EventDataTimestamp::EpochTimestamp(10_000 + event_number),
964                data: 1337,
965            }
966        }
967
968        fn vec_from<const N: usize>(
969            buf: &EventsBuf<N>,
970        ) -> Result<heapless::Vec<TestEvent, N>, Error> {
971            let mut out = heapless::Vec::new();
972            for tr in buf.iter() {
973                let e = EventData::from_tlv(&tr?)?;
974                out.push(TestEvent {
975                    endpoint: e.path.endpoint.unwrap(),
976                    cluster: e.path.cluster.unwrap(),
977                    event: e.path.event.unwrap(),
978                    event_number: e.event_number,
979                    priority: e.priority,
980                    timestamp: e.timestamp,
981                    data: e.data.u64()?,
982                })
983                .unwrap();
984            }
985
986            Ok(out)
987        }
988
989        fn push_into<const N: usize>(&self, q: &mut EventsInner<N>) -> Result<(), Error> {
990            q.push(
991                self.endpoint,
992                self.cluster,
993                self.event,
994                self.event_number,
995                self.priority,
996                self.timestamp.clone(),
997                |tw| self.data.to_tlv(&EVENT_DATA_TAG, tw),
998            )
999        }
1000    }
1001}