zenoh_protocol/network/timestamp_stack.rs
1//
2// Copyright (c) 2026 ZettaScale Technology
3//
4// This program and the accompanying materials are made available under the
5// terms of the Eclipse Public License 2.0 which is available at
6// http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
7// which is available at https://www.apache.org/licenses/LICENSE-2.0.
8//
9// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
10//
11// Contributors:
12// ZettaScale Zenoh Team, <zenoh@zettascale.tech>
13//
14use alloc::vec::Vec;
15
16pub const MAX_STACK_SIZE: usize = 255;
17
18/// Zenoh extension wrapper for the timestamp stack.
19///
20/// The `const ID: u8` parameter encodes the extension's wire ID, ensuring
21/// type-safety across different message contexts.
22#[derive(Debug, Clone, PartialEq, Eq)]
23pub struct TsStackType<const ID: u8> {
24 pub ts_stack: TimestampStack,
25}
26
27impl<const ID: u8> TsStackType<{ ID }> {
28 #[cfg(feature = "test")]
29 #[doc(hidden)]
30 pub fn rand() -> Self {
31 use rand::Rng;
32 let mut rng = rand::thread_rng();
33
34 let conf_flags: u8 = rng.gen_range(1..=7);
35 let n: usize = rng.gen_range(0..=3);
36 let mut stack = Vec::with_capacity(n);
37 let points = [
38 interception_point::SEND,
39 interception_point::ROUTE,
40 interception_point::RECEIVE,
41 ];
42 for _ in 0..n {
43 let point = points[rng.gen_range(0..points.len())];
44 let is_custom = rng.gen_bool(0.5);
45 let flags = point
46 | if is_custom {
47 interception_point::IS_CUSTOM_TS
48 } else {
49 0
50 };
51 let ts_len: usize = rng.gen_range(0..=16);
52 let timestamp: Vec<u8> = (0..ts_len).map(|_| rng.gen()).collect();
53 stack.push(Interception { flags, timestamp });
54 }
55
56 Self {
57 ts_stack: TimestampStack { conf_flags, stack },
58 }
59 }
60}
61
62/// Bitmask flags indicating which interception points are activated in a timestamp stack.
63pub mod interception_point {
64 pub const SEND: u8 = 0b0000_0001;
65 pub const ROUTE: u8 = 0b0000_0010;
66 pub const RECEIVE: u8 = 0b0000_0100;
67
68 /// Bit 7 of the `Interception.flags` field: set when the timestamp was produced by a
69 /// user-defined callback (custom format), cleared when it is a standard UHLC timestamp.
70 pub const IS_CUSTOM_TS: u8 = 0b1000_0000;
71}
72
73/// A single interception record containing the interception point identifier,
74/// timestamp format flag, and raw timestamp bytes.
75///
76/// ```text
77/// Flags:
78/// - U: User-defined If U==1 then the timestamp format is user-defined;
79/// otherwise it is a standard UHLC timestamp.
80/// - X: Reserved Must be 0.
81/// - point: Interception point 0b001=SEND, 0b010=ROUTE, 0b100=RECEIVE.
82///
83/// 7 6 5 4 3 2 1 0
84/// +-+-+-+-+-+-+-+-+
85/// |U|X|X|X|X|point|
86/// +---------------+
87/// ~ timestamp ~
88/// +---------------+
89/// ```
90#[derive(Debug, Clone, PartialEq, Eq)]
91pub struct Interception {
92 /// Bitfield: interception point id + timestamp format.
93 pub flags: u8,
94 /// Raw timestamp bytes (format defined by `flags`).
95 pub timestamp: Vec<u8>,
96}
97
98/// A stack of interception timestamps carried as a message extension.
99///
100/// ```text
101/// Flags:
102/// - S: Send If S==1 then the send interception point is activated.
103/// - R: Route If R==1 then the route interception point is activated.
104/// - E: Receive If E==1 then the receive interception point is activated.
105/// - X: Reserved
106/// 7 6 5 4 3 2 1 0
107/// +-+-+-+-+-+-+-+-+
108/// |X|X|X|X|X|E|R|S|
109/// +---------------+
110/// ~ count: zint ~
111/// +---------------+
112/// ~ [Interception] ~
113/// +---------------+
114/// ```
115#[derive(Debug, Clone, PartialEq, Eq)]
116pub struct TimestampStack {
117 /// Bitmask of which interception points are activated.
118 pub conf_flags: u8,
119 /// Ordered list of interceptions collected along the message path.
120 pub stack: Vec<Interception>,
121}