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