Skip to main content

ipfrs_network/
stream_multiplexer.rs

1//! Stream Multiplexer — multiplexes multiple logical streams over a single connection
2//! with flow control, priority scheduling, and proper frame lifecycle management.
3//!
4//! ## Design
5//!
6//! Each logical [`StreamId`] carries its own sequence counter, send/receive windows,
7//! and priority. The shared send queue is a max-[`BinaryHeap`] keyed on
8//! `(priority_weight, Reverse(enqueue_sequence))` so that within the same priority
9//! tier frames are emitted in FIFO order.
10//!
11//! ## Frame flags (FrameFlags bit positions)
12//!
13//! | Bit  | Name | Value | Meaning                        |
14//! |------|------|-------|-------------------------------|
15//! | 0    | SYN  | 0x01  | Open (create) a new stream     |
16//! | 1    | FIN  | 0x02  | Close stream after this frame  |
17//! | 2    | RST  | 0x04  | Reset stream immediately       |
18//! | 3    | ACK  | 0x08  | Acknowledgement                |
19//! | 4    | DATA | 0x10  | Frame carries payload data     |
20//!
21//! ## Legacy flag constants (raw u8)
22//!
23//! For backwards compatibility the original raw constants are preserved:
24//! `FLAG_FIN=0x01`, `FLAG_RST=0x02`, `FLAG_SYN=0x04`.
25//!
26//! ## Flow control
27//!
28//! `send_window` tracks how many bytes may still be enqueued for sending on a
29//! given stream.  Each call to [`StreamMultiplexer::send`] checks that
30//! `data.len() <= send_window` and deducts the amount from the window.
31//! [`StreamMultiplexer::update_window`] adds credits back (simulating receipt of
32//! a window-update ACK from the remote side).
33
34use std::cmp::Reverse;
35use std::collections::{BinaryHeap, HashMap, VecDeque};
36use std::fmt;
37
38// ─────────────────────────────────────────────────────────────────────────────
39// Legacy raw flag constants (backward-compatible; raw u8)
40// ─────────────────────────────────────────────────────────────────────────────
41
42/// Bit mask for the FIN flag in [`StreamFrame::flags`] (legacy raw u8 form).
43pub const FLAG_FIN: u8 = 0b0000_0001;
44/// Bit mask for the RST flag in [`StreamFrame::flags`] (legacy raw u8 form).
45pub const FLAG_RST: u8 = 0b0000_0010;
46/// Bit mask for the SYN flag in [`StreamFrame::flags`] (legacy raw u8 form).
47pub const FLAG_SYN: u8 = 0b0000_0100;
48
49// ─────────────────────────────────────────────────────────────────────────────
50// FrameFlags — typed bitfield
51// ─────────────────────────────────────────────────────────────────────────────
52
53/// Bitfield wrapper for stream-frame control flags.
54///
55/// # Flag layout
56///
57/// | Bit | Constant              | Meaning                        |
58/// |-----|-----------------------|-------------------------------|
59/// | 0   | [`FrameFlags::SYN`]   | Open (create) a new stream     |
60/// | 1   | [`FrameFlags::FIN`]   | Close stream gracefully        |
61/// | 2   | [`FrameFlags::RST`]   | Reset stream immediately       |
62/// | 3   | [`FrameFlags::ACK`]   | Acknowledgement frame          |
63/// | 4   | [`FrameFlags::DATA`]  | Frame carries payload data     |
64#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
65pub struct FrameFlags(pub u8);
66
67impl FrameFlags {
68    /// SYN — open a new stream (bit 0).
69    pub const SYN: u8 = 0x01;
70    /// FIN — close stream gracefully (bit 1).
71    pub const FIN: u8 = 0x02;
72    /// RST — reset stream immediately (bit 2).
73    pub const RST: u8 = 0x04;
74    /// ACK — acknowledgement (bit 3).
75    pub const ACK: u8 = 0x08;
76    /// DATA — frame carries payload (bit 4).
77    pub const DATA: u8 = 0x10;
78
79    /// Create `FrameFlags` from a raw byte.
80    #[inline]
81    pub fn new(raw: u8) -> Self {
82        Self(raw)
83    }
84
85    /// Returns `true` if the SYN bit is set.
86    #[inline]
87    pub fn is_syn(self) -> bool {
88        self.0 & Self::SYN != 0
89    }
90
91    /// Returns `true` if the FIN bit is set.
92    #[inline]
93    pub fn is_fin(self) -> bool {
94        self.0 & Self::FIN != 0
95    }
96
97    /// Returns `true` if the RST bit is set.
98    #[inline]
99    pub fn is_rst(self) -> bool {
100        self.0 & Self::RST != 0
101    }
102
103    /// Returns `true` if the ACK bit is set.
104    #[inline]
105    pub fn is_ack(self) -> bool {
106        self.0 & Self::ACK != 0
107    }
108
109    /// Returns `true` if the DATA bit is set.
110    #[inline]
111    pub fn is_data(self) -> bool {
112        self.0 & Self::DATA != 0
113    }
114
115    /// Set a flag bit and return the updated value.
116    #[inline]
117    pub fn with(self, flag: u8) -> Self {
118        Self(self.0 | flag)
119    }
120}
121
122impl fmt::Display for FrameFlags {
123    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
124        let mut parts: Vec<&str> = Vec::new();
125        if self.is_syn() {
126            parts.push("SYN");
127        }
128        if self.is_fin() {
129            parts.push("FIN");
130        }
131        if self.is_rst() {
132            parts.push("RST");
133        }
134        if self.is_ack() {
135            parts.push("ACK");
136        }
137        if self.is_data() {
138            parts.push("DATA");
139        }
140        if parts.is_empty() {
141            write!(f, "NONE")
142        } else {
143            write!(f, "{}", parts.join("|"))
144        }
145    }
146}
147
148// ─────────────────────────────────────────────────────────────────────────────
149// StreamId
150// ─────────────────────────────────────────────────────────────────────────────
151
152/// Newtype identifier for a logical stream.
153#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
154pub struct StreamId(pub u32);
155
156impl fmt::Display for StreamId {
157    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
158        write!(f, "stream:{}", self.0)
159    }
160}
161
162// ─────────────────────────────────────────────────────────────────────────────
163// StreamPriority
164// ─────────────────────────────────────────────────────────────────────────────
165
166/// Priority level for a logical stream.
167///
168/// Higher priority streams are dequeued first by the multiplexer.  Within the
169/// same priority, frames are emitted in FIFO order.
170#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
171pub enum StreamPriority {
172    /// Best-effort delivery — weight 0.
173    Background = 0,
174    /// Low priority — weight 1.
175    Low = 1,
176    /// Normal priority — weight 2.
177    Normal = 2,
178    /// High priority — weight 4.
179    High = 4,
180    /// Critical (real-time) — weight 8.
181    Critical = 8,
182}
183
184impl StreamPriority {
185    /// Numeric weight used for priority scheduling.
186    ///
187    /// `Critical` → 8, `High` → 4, `Normal` → 2, `Low` → 1, `Background` → 0.
188    #[inline]
189    pub fn weight(self) -> u32 {
190        match self {
191            Self::Critical => 8,
192            Self::High => 4,
193            Self::Normal => 2,
194            Self::Low => 1,
195            Self::Background => 0,
196        }
197    }
198}
199
200/// Convert a raw priority byte (0-255) to the nearest [`StreamPriority`] tier.
201pub fn priority_from_u8(p: u8) -> StreamPriority {
202    match p {
203        0 => StreamPriority::Background,
204        1..=63 => StreamPriority::Low,
205        64..=127 => StreamPriority::Normal,
206        128..=191 => StreamPriority::High,
207        192..=255 => StreamPriority::Critical,
208    }
209}
210
211// ─────────────────────────────────────────────────────────────────────────────
212// StreamState
213// ─────────────────────────────────────────────────────────────────────────────
214
215/// Lifecycle state of a logical stream.
216#[derive(Debug, Clone, Copy, PartialEq, Eq)]
217pub enum StreamState {
218    /// SYN has been sent but not yet acknowledged; stream is being established.
219    Opening,
220    /// The stream is fully open and can send/receive data.
221    Open,
222    /// A FIN has been sent or received; the stream is draining.
223    HalfClosed,
224    /// The stream has been cleanly closed (both sides have finished).
225    Closed,
226    /// The stream was abruptly reset (RST sent or received).
227    Reset,
228}
229
230// ─────────────────────────────────────────────────────────────────────────────
231// StreamFrame
232// ─────────────────────────────────────────────────────────────────────────────
233
234/// Fixed header length in bytes for [`StreamFrame`] wire encoding.
235///
236/// Layout: `stream_id(4) + sequence_num(8) + payload_len(4) + flags(1) + timestamp(8) = 25`.
237const FRAME_HEADER_LEN: usize = 25;
238
239/// A framed unit of data for one logical stream.
240///
241/// # Wire format (little-endian)
242///
243/// ```text
244/// [stream_id: u32 LE][sequence_num: u64 LE][payload_len: u32 LE][flags: u8][timestamp: u64 LE][payload...]
245/// ```
246#[derive(Debug, Clone)]
247pub struct StreamFrame {
248    /// Which stream this frame belongs to.
249    pub stream_id: StreamId,
250    /// Monotonically increasing per-stream sequence number.
251    pub sequence: u64,
252    /// Payload bytes.
253    pub data: Vec<u8>,
254    /// Control flags: [`FLAG_FIN`], [`FLAG_RST`], [`FLAG_SYN`].
255    pub flags: u8,
256    /// Wall-clock timestamp at the time the frame was created (caller-supplied).
257    pub timestamp: u64,
258}
259
260impl StreamFrame {
261    // ── Legacy flag helpers (raw u8 constants) ─────────────────────────────
262
263    /// Returns `true` if the FIN flag is set (legacy raw-u8 form).
264    #[inline]
265    pub fn is_fin(&self) -> bool {
266        self.flags & FLAG_FIN != 0
267    }
268
269    /// Returns `true` if the RST flag is set (legacy raw-u8 form).
270    #[inline]
271    pub fn is_rst(&self) -> bool {
272        self.flags & FLAG_RST != 0
273    }
274
275    /// Returns `true` if the SYN flag is set (legacy raw-u8 form).
276    #[inline]
277    pub fn is_syn(&self) -> bool {
278        self.flags & FLAG_SYN != 0
279    }
280
281    /// Returns `true` if this is a pure control frame (no payload bytes matter).
282    #[inline]
283    pub fn is_control(&self) -> bool {
284        self.flags != 0
285    }
286
287    // ── Wire encoding / decoding ───────────────────────────────────────────
288
289    /// Encode the frame into a `Vec<u8>` using the fixed wire format.
290    ///
291    /// Wire layout (all little-endian):
292    /// `stream_id(4) + sequence_num(8) + payload_len(4) + flags(1) + timestamp(8) + payload`
293    ///
294    /// Total header: 25 bytes.
295    pub fn encode(&self) -> Vec<u8> {
296        let payload_len = self.data.len();
297        let mut buf = Vec::with_capacity(FRAME_HEADER_LEN + payload_len);
298        buf.extend_from_slice(&self.stream_id.0.to_le_bytes());
299        buf.extend_from_slice(&self.sequence.to_le_bytes());
300        buf.extend_from_slice(&(payload_len as u32).to_le_bytes());
301        buf.push(self.flags);
302        buf.extend_from_slice(&self.timestamp.to_le_bytes());
303        buf.extend_from_slice(&self.data);
304        buf
305    }
306
307    /// Decode a frame from a byte slice produced by [`StreamFrame::encode`].
308    ///
309    /// # Errors
310    ///
311    /// Returns [`MuxError::FrameTooLarge`] if `data` is shorter than the
312    /// minimum header size or shorter than the declared payload length.
313    pub fn decode(data: &[u8]) -> Result<Self, MuxError> {
314        if data.len() < FRAME_HEADER_LEN {
315            return Err(MuxError::FrameTooLarge(data.len()));
316        }
317
318        let stream_id = u32::from_le_bytes(
319            data[0..4]
320                .try_into()
321                .map_err(|_| MuxError::FrameTooLarge(data.len()))?,
322        );
323        let sequence = u64::from_le_bytes(
324            data[4..12]
325                .try_into()
326                .map_err(|_| MuxError::FrameTooLarge(data.len()))?,
327        );
328        let payload_len = u32::from_le_bytes(
329            data[12..16]
330                .try_into()
331                .map_err(|_| MuxError::FrameTooLarge(data.len()))?,
332        ) as usize;
333        let flags = data[16];
334        let timestamp = u64::from_le_bytes(
335            data[17..25]
336                .try_into()
337                .map_err(|_| MuxError::FrameTooLarge(data.len()))?,
338        );
339
340        let expected_total = FRAME_HEADER_LEN + payload_len;
341        if data.len() < expected_total {
342            return Err(MuxError::FrameTooLarge(data.len()));
343        }
344
345        let payload = data[FRAME_HEADER_LEN..FRAME_HEADER_LEN + payload_len].to_vec();
346
347        Ok(Self {
348            stream_id: StreamId(stream_id),
349            sequence,
350            data: payload,
351            flags,
352            timestamp,
353        })
354    }
355}
356
357// ─────────────────────────────────────────────────────────────────────────────
358// StreamInfo — read-only statistics snapshot for a single stream
359// ─────────────────────────────────────────────────────────────────────────────
360
361/// Read-only snapshot of per-stream statistics.
362#[derive(Debug, Clone)]
363pub struct StreamInfo {
364    /// Stream identifier.
365    pub id: StreamId,
366    /// Current lifecycle state.
367    pub state: StreamState,
368    /// Total payload bytes enqueued for sending.
369    pub bytes_sent: u64,
370    /// Total payload bytes received.
371    pub bytes_received: u64,
372    /// Total frames enqueued for sending (including control frames).
373    pub frames_sent: u64,
374    /// Total frames received.
375    pub frames_received: u64,
376    /// Timestamp (µs) when the stream was opened.
377    pub opened_at: u64,
378    /// Timestamp (µs) of the last send or receive activity.
379    pub last_activity: u64,
380    /// Raw priority byte supplied at stream creation.
381    pub priority: u8,
382}
383
384// ─────────────────────────────────────────────────────────────────────────────
385// LogicalStream — internal mutable state for a single stream
386// ─────────────────────────────────────────────────────────────────────────────
387
388/// State and bookkeeping for a single logical stream.
389#[derive(Debug, Clone)]
390pub struct LogicalStream {
391    /// Identifier of this stream.
392    pub id: StreamId,
393    /// Priority used for scheduling send frames.
394    pub priority: StreamPriority,
395    /// Current lifecycle state.
396    pub state: StreamState,
397    /// Remaining send window (bytes that may still be enqueued for sending).
398    pub send_window: u32,
399    /// Remaining receive window (bytes we are willing to accept from remote).
400    pub recv_window: u32,
401    /// Next sequence number to use when enqueuing a send frame.
402    pub send_seq: u64,
403    /// Expected sequence number for the next incoming frame.
404    pub recv_seq: u64,
405    /// Cumulative bytes enqueued for sending (payload only).
406    pub bytes_sent: u64,
407    /// Cumulative bytes received (payload only).
408    pub bytes_received: u64,
409    /// Cumulative frames enqueued for sending (including control frames).
410    pub frames_sent: u64,
411    /// Cumulative frames received.
412    pub frames_received: u64,
413    /// Timestamp (µs) when the stream was opened.
414    pub opened_at: u64,
415    /// Timestamp (µs) of the last activity (send or receive).
416    pub last_activity: u64,
417    /// Raw priority byte (0-255) supplied at stream creation.
418    pub priority_raw: u8,
419    /// Bounded receive buffer (payloads in arrival order).
420    recv_buffer: VecDeque<Vec<u8>>,
421    /// Maximum number of payloads buffered on the receive side.
422    recv_buffer_max: usize,
423}
424
425// ─────────────────────────────────────────────────────────────────────────────
426// MultiplexerConfig
427// ─────────────────────────────────────────────────────────────────────────────
428
429/// Configuration for [`StreamMultiplexer`].
430#[derive(Debug, Clone)]
431pub struct MultiplexerConfig {
432    /// Maximum number of concurrently open streams.
433    pub max_streams: usize,
434    /// Initial send/receive window size for each new stream (bytes).
435    pub default_window_size: u32,
436    /// Maximum payload bytes per frame when fragmenting.
437    pub max_frame_size: usize,
438    /// When `false`, all frames are enqueued as equal priority (FIFO).
439    pub enable_priority: bool,
440    /// Maximum number of payload entries held in the per-stream receive buffer.
441    pub recv_buffer_capacity: usize,
442    /// Maximum number of payload entries held in the per-stream send buffer
443    /// (currently informational; actual cap is `send_window`).
444    pub send_buffer_capacity: usize,
445    /// Idle timeout in microseconds.  Streams inactive longer than this are
446    /// eligible for expiry via [`StreamMultiplexer::expire_idle`].
447    pub idle_timeout_us: u64,
448}
449
450impl Default for MultiplexerConfig {
451    fn default() -> Self {
452        Self {
453            max_streams: 256,
454            default_window_size: 65_536,
455            max_frame_size: 16_384,
456            enable_priority: true,
457            recv_buffer_capacity: 256,
458            send_buffer_capacity: 256,
459            idle_timeout_us: 30_000_000, // 30 seconds
460        }
461    }
462}
463
464// ─────────────────────────────────────────────────────────────────────────────
465// MultiplexerStats — richer statistics snapshot
466// ─────────────────────────────────────────────────────────────────────────────
467
468/// Rich statistics snapshot for the multiplexer (task-spec version).
469#[derive(Debug, Clone, PartialEq, Eq)]
470pub struct MultiplexerStats {
471    /// Number of streams currently in the [`StreamState::Open`] or
472    /// [`StreamState::Opening`] state.
473    pub active_streams: usize,
474    /// Total streams ever opened (monotonically increasing).
475    pub total_streams_opened: u64,
476    /// Total frames ever dequeued and handed to the caller.
477    pub total_frames_sent: u64,
478    /// Total frames processed via [`StreamMultiplexer::receive`].
479    pub total_frames_received: u64,
480    /// Total payload bytes enqueued for sending across all streams.
481    pub total_bytes_sent: u64,
482    /// Total payload bytes received across all streams.
483    pub total_bytes_received: u64,
484    /// Frames that were dropped due to a full receive buffer.
485    pub dropped_frames: u64,
486}
487
488// ─────────────────────────────────────────────────────────────────────────────
489// PrioritizedFrame (internal heap element)
490// ─────────────────────────────────────────────────────────────────────────────
491
492/// Wrapper that gives [`StreamFrame`] a total ordering for the
493/// [`BinaryHeap`]-based priority send queue.
494///
495/// Ordering rule: higher `priority_weight` first; ties broken by lower
496/// enqueue `sequence` first (FIFO within a priority tier).
497#[derive(Debug)]
498struct PrioritizedFrame {
499    /// Effective weight of the stream's priority at enqueue time.
500    priority_weight: u32,
501    /// Global monotonic enqueue counter used for FIFO within a priority tier.
502    sequence: u64,
503    /// The actual frame payload.
504    frame: StreamFrame,
505}
506
507impl PartialEq for PrioritizedFrame {
508    fn eq(&self, other: &Self) -> bool {
509        self.priority_weight == other.priority_weight && self.sequence == other.sequence
510    }
511}
512
513impl Eq for PrioritizedFrame {}
514
515impl PartialOrd for PrioritizedFrame {
516    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
517        Some(self.cmp(other))
518    }
519}
520
521impl Ord for PrioritizedFrame {
522    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
523        // Primary: higher weight wins (max-heap).
524        self.priority_weight
525            .cmp(&other.priority_weight)
526            // Tie-break: lower enqueue sequence wins (FIFO).
527            .then_with(|| Reverse(self.sequence).cmp(&Reverse(other.sequence)))
528    }
529}
530
531// ─────────────────────────────────────────────────────────────────────────────
532// MuxEvent
533// ─────────────────────────────────────────────────────────────────────────────
534
535/// Events emitted by [`StreamMultiplexer::receive`] and other operations.
536#[derive(Debug, Clone, PartialEq, Eq)]
537pub enum MuxEvent {
538    /// A new stream was opened (SYN received from remote).
539    StreamOpened(StreamId),
540    /// A stream was gracefully closed (FIN received).
541    StreamClosed(StreamId),
542    /// A stream was forcefully reset (RST received).
543    StreamReset(StreamId),
544    /// A data frame was received on the given stream.
545    FrameReceived(StreamId),
546    /// The receive buffer for the stream is full; a frame was dropped.
547    SendBufferFull(StreamId),
548    /// An idle stream was expired by [`StreamMultiplexer::expire_idle`].
549    IdleStreamExpired(StreamId),
550}
551
552// ─────────────────────────────────────────────────────────────────────────────
553// MuxError
554// ─────────────────────────────────────────────────────────────────────────────
555
556/// Errors returned by [`StreamMultiplexer`] operations.
557#[derive(Debug, Clone, PartialEq, Eq)]
558pub enum MuxError {
559    /// All stream slots are occupied.
560    MaxStreamsReached,
561    /// No stream with the given id exists.
562    StreamNotFound(u32),
563    /// The stream exists but is not in [`StreamState::Open`].
564    StreamNotOpen(u32),
565    /// The send window would be exceeded by the requested send.
566    WindowExhausted(u32),
567    /// The incoming frame carries an out-of-order sequence number.
568    SequenceError {
569        /// The sequence number we expected.
570        expected: u64,
571        /// The sequence number carried by the frame.
572        got: u64,
573    },
574    /// The encoded frame is shorter than the minimum header length.
575    FrameTooLarge(usize),
576    /// The receive (or send) buffer for the stream is full.
577    BufferOverflow(u32),
578    /// A stream with the given id was already open when a SYN arrived.
579    StreamAlreadyOpen(u32),
580}
581
582impl fmt::Display for MuxError {
583    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
584        match self {
585            Self::MaxStreamsReached => write!(f, "maximum number of streams reached"),
586            Self::StreamNotFound(id) => write!(f, "stream {id} not found"),
587            Self::StreamNotOpen(id) => write!(f, "stream {id} is not open"),
588            Self::WindowExhausted(id) => write!(f, "send window exhausted on stream {id}"),
589            Self::SequenceError { expected, got } => {
590                write!(f, "sequence error: expected {expected}, got {got}")
591            }
592            Self::FrameTooLarge(len) => write!(f, "frame data too short to decode ({len} bytes)"),
593            Self::BufferOverflow(id) => write!(f, "buffer overflow on stream {id}"),
594            Self::StreamAlreadyOpen(id) => write!(f, "stream {id} is already open"),
595        }
596    }
597}
598
599impl std::error::Error for MuxError {}
600
601// ─────────────────────────────────────────────────────────────────────────────
602// MuxStats — original compact statistics snapshot
603// ─────────────────────────────────────────────────────────────────────────────
604
605/// Compact snapshot of [`StreamMultiplexer`] statistics (original form).
606#[derive(Debug, Clone, PartialEq, Eq)]
607pub struct MuxStats {
608    /// Total streams ever opened (including currently open ones).
609    pub total_streams: usize,
610    /// Number of streams currently in the [`StreamState::Open`] state.
611    pub open_streams: usize,
612    /// Frames currently waiting in the send queue.
613    pub queued_frames: usize,
614    /// Total frames that have been dequeued and handed to the caller.
615    pub total_frames_sent: u64,
616    /// Total frames that have been processed via [`StreamMultiplexer::receive`].
617    pub total_frames_received: u64,
618    /// Total payload bytes that have been enqueued for sending across all streams.
619    pub total_bytes_sent: u64,
620}
621
622// ─────────────────────────────────────────────────────────────────────────────
623// StreamMultiplexer
624// ─────────────────────────────────────────────────────────────────────────────
625
626/// Multiplexes multiple logical streams over a single connection with
627/// flow control and priority-based scheduling.
628///
629/// # Example
630///
631/// ```rust
632/// use ipfrs_network::stream_multiplexer::{
633///     MultiplexerConfig, StreamMultiplexer, StreamPriority,
634/// };
635///
636/// let config = MultiplexerConfig::default();
637/// let mut mux = StreamMultiplexer::new(config);
638///
639/// let sid = mux.open_stream(StreamPriority::Normal).expect("open stream");
640/// let bytes = mux.send(sid, b"hello world".to_vec(), 0).expect("send");
641/// assert_eq!(bytes, 11);
642///
643/// // Drain all enqueued frames (SYN + data)
644/// let frames = mux.dequeue_frames(10);
645/// assert_eq!(frames.len(), 2);
646/// ```
647pub struct StreamMultiplexer {
648    /// Effective configuration for this multiplexer instance.
649    pub config: MultiplexerConfig,
650    /// All known streams, keyed by their id.
651    streams: HashMap<StreamId, LogicalStream>,
652    /// Priority send queue.
653    send_queue: BinaryHeap<PrioritizedFrame>,
654    /// Monotonically increasing stream id allocator.
655    next_stream_id: u32,
656    /// Global enqueue counter used for FIFO ordering within a priority tier.
657    enqueue_seq: u64,
658    /// Total frames dequeued (sent to the caller).
659    total_frames_sent: u64,
660    /// Total frames processed via `receive`.
661    total_frames_received: u64,
662    /// Total streams ever opened.
663    total_streams_opened: u64,
664    /// Frames dropped due to a full receive buffer.
665    dropped_frames: u64,
666}
667
668impl StreamMultiplexer {
669    // ─────────────────────────────────────────────────────────────
670    // Construction
671    // ─────────────────────────────────────────────────────────────
672
673    /// Create a new multiplexer with the given configuration.
674    pub fn new(config: MultiplexerConfig) -> Self {
675        Self {
676            streams: HashMap::with_capacity(config.max_streams.min(256)),
677            send_queue: BinaryHeap::new(),
678            next_stream_id: 1,
679            enqueue_seq: 0,
680            total_frames_sent: 0,
681            total_frames_received: 0,
682            total_streams_opened: 0,
683            dropped_frames: 0,
684            config,
685        }
686    }
687
688    // ─────────────────────────────────────────────────────────────
689    // Stream lifecycle
690    // ─────────────────────────────────────────────────────────────
691
692    /// Open a new logical stream with the specified priority.
693    ///
694    /// A SYN frame (flags = [`FLAG_SYN`]) is immediately enqueued.
695    ///
696    /// # Errors
697    ///
698    /// Returns [`MuxError::MaxStreamsReached`] if [`MultiplexerConfig::max_streams`]
699    /// streams are already open.
700    pub fn open_stream(&mut self, priority: StreamPriority) -> Result<StreamId, MuxError> {
701        if self.streams.len() >= self.config.max_streams {
702            return Err(MuxError::MaxStreamsReached);
703        }
704
705        let id = StreamId(self.next_stream_id);
706        self.next_stream_id = self.next_stream_id.wrapping_add(1);
707
708        let recv_cap = self.config.recv_buffer_capacity;
709        let stream = LogicalStream {
710            id,
711            priority,
712            state: StreamState::Open,
713            send_window: self.config.default_window_size,
714            recv_window: self.config.default_window_size,
715            send_seq: 1, // 0 is consumed by the SYN frame below
716            recv_seq: 0,
717            bytes_sent: 0,
718            bytes_received: 0,
719            frames_sent: 1, // SYN frame counted here
720            frames_received: 0,
721            opened_at: 0,
722            last_activity: 0,
723            priority_raw: priority.weight() as u8,
724            recv_buffer: VecDeque::with_capacity(recv_cap.min(64)),
725            recv_buffer_max: recv_cap,
726        };
727        self.streams.insert(id, stream);
728        self.total_streams_opened = self.total_streams_opened.wrapping_add(1);
729
730        // Enqueue SYN frame with sequence 0.
731        self.enqueue_control(id, FLAG_SYN, 0, priority, 0);
732
733        Ok(id)
734    }
735
736    /// Open a new logical stream with a raw priority byte (0-255).
737    ///
738    /// The raw byte is mapped to the nearest [`StreamPriority`] tier via
739    /// [`priority_from_u8`].  The raw value is preserved in [`LogicalStream::priority_raw`].
740    ///
741    /// # Errors
742    ///
743    /// Returns [`MuxError::MaxStreamsReached`] if the maximum stream count is exceeded.
744    pub fn open_stream_with_priority(&mut self, priority_raw: u8) -> Result<StreamId, MuxError> {
745        if self.streams.len() >= self.config.max_streams {
746            return Err(MuxError::MaxStreamsReached);
747        }
748
749        let priority = priority_from_u8(priority_raw);
750        let id = StreamId(self.next_stream_id);
751        self.next_stream_id = self.next_stream_id.wrapping_add(1);
752
753        let recv_cap = self.config.recv_buffer_capacity;
754        let stream = LogicalStream {
755            id,
756            priority,
757            state: StreamState::Open,
758            send_window: self.config.default_window_size,
759            recv_window: self.config.default_window_size,
760            send_seq: 1,
761            recv_seq: 0,
762            bytes_sent: 0,
763            bytes_received: 0,
764            frames_sent: 1,
765            frames_received: 0,
766            opened_at: 0,
767            last_activity: 0,
768            priority_raw,
769            recv_buffer: VecDeque::with_capacity(recv_cap.min(64)),
770            recv_buffer_max: recv_cap,
771        };
772        self.streams.insert(id, stream);
773        self.total_streams_opened = self.total_streams_opened.wrapping_add(1);
774
775        self.enqueue_control(id, FLAG_SYN, 0, priority, 0);
776
777        Ok(id)
778    }
779
780    /// Send a FIN frame and transition the stream to [`StreamState::HalfClosed`].
781    ///
782    /// # Errors
783    ///
784    /// - [`MuxError::StreamNotFound`] if the stream does not exist.
785    /// - [`MuxError::StreamNotOpen`] if the stream is already Closed or Reset.
786    pub fn close_stream(&mut self, stream_id: StreamId) -> Result<(), MuxError> {
787        let stream = self
788            .streams
789            .get_mut(&stream_id)
790            .ok_or(MuxError::StreamNotFound(stream_id.0))?;
791
792        match stream.state {
793            StreamState::Closed | StreamState::Reset => {
794                return Err(MuxError::StreamNotOpen(stream_id.0));
795            }
796            _ => {}
797        }
798
799        let seq = stream.send_seq;
800        stream.send_seq = stream.send_seq.wrapping_add(1);
801        stream.frames_sent = stream.frames_sent.wrapping_add(1);
802        let priority = stream.priority;
803        stream.state = StreamState::HalfClosed;
804
805        self.enqueue_control(stream_id, FLAG_FIN, seq, priority, 0);
806        Ok(())
807    }
808
809    /// Send an RST frame and transition the stream to [`StreamState::Reset`].
810    ///
811    /// # Errors
812    ///
813    /// - [`MuxError::StreamNotFound`] if the stream does not exist.
814    pub fn reset_stream(&mut self, stream_id: StreamId) -> Result<(), MuxError> {
815        let stream = self
816            .streams
817            .get_mut(&stream_id)
818            .ok_or(MuxError::StreamNotFound(stream_id.0))?;
819
820        let seq = stream.send_seq;
821        stream.send_seq = stream.send_seq.wrapping_add(1);
822        stream.frames_sent = stream.frames_sent.wrapping_add(1);
823        let priority = stream.priority;
824        stream.state = StreamState::Reset;
825
826        self.enqueue_control(stream_id, FLAG_RST, seq, priority, 0);
827        Ok(())
828    }
829
830    // ─────────────────────────────────────────────────────────────
831    // Data path
832    // ─────────────────────────────────────────────────────────────
833
834    /// Fragment `data` into frames of at most [`MultiplexerConfig::max_frame_size`]
835    /// bytes and enqueue them on the send queue.
836    ///
837    /// Returns the total number of bytes enqueued.
838    ///
839    /// # Errors
840    ///
841    /// - [`MuxError::StreamNotFound`] if the stream does not exist.
842    /// - [`MuxError::StreamNotOpen`] if the stream is not [`StreamState::Open`].
843    /// - [`MuxError::WindowExhausted`] if `data.len()` exceeds the remaining send window.
844    pub fn send(
845        &mut self,
846        stream_id: StreamId,
847        data: Vec<u8>,
848        now: u64,
849    ) -> Result<usize, MuxError> {
850        // Validate before mutating.
851        {
852            let stream = self
853                .streams
854                .get(&stream_id)
855                .ok_or(MuxError::StreamNotFound(stream_id.0))?;
856
857            if stream.state != StreamState::Open {
858                return Err(MuxError::StreamNotOpen(stream_id.0));
859            }
860
861            if data.len() as u64 > stream.send_window as u64 {
862                return Err(MuxError::WindowExhausted(stream_id.0));
863            }
864        }
865
866        let total = data.len();
867
868        // Fragment and enqueue.
869        let chunk_size = self.config.max_frame_size.max(1);
870        let mut offset = 0usize;
871
872        while offset < data.len() {
873            let end = (offset + chunk_size).min(data.len());
874            let chunk = data[offset..end].to_vec();
875            let chunk_len = chunk.len();
876
877            let stream = self
878                .streams
879                .get_mut(&stream_id)
880                .ok_or(MuxError::StreamNotFound(stream_id.0))?;
881
882            let seq = stream.send_seq;
883            stream.send_seq = stream.send_seq.wrapping_add(1);
884            stream.bytes_sent = stream.bytes_sent.saturating_add(chunk_len as u64);
885            stream.frames_sent = stream.frames_sent.wrapping_add(1);
886            // Deduct from send window immediately upon enqueue.
887            stream.send_window = stream.send_window.saturating_sub(chunk_len as u32);
888            stream.last_activity = now;
889            let priority = stream.priority;
890            let weight = if self.config.enable_priority {
891                priority.weight()
892            } else {
893                0
894            };
895
896            let enqueue_seq = self.enqueue_seq;
897            self.enqueue_seq = self.enqueue_seq.wrapping_add(1);
898
899            self.send_queue.push(PrioritizedFrame {
900                priority_weight: weight,
901                sequence: enqueue_seq,
902                frame: StreamFrame {
903                    stream_id,
904                    sequence: seq,
905                    data: chunk,
906                    flags: 0,
907                    timestamp: now,
908                },
909            });
910
911            offset = end;
912        }
913
914        Ok(total)
915    }
916
917    /// Process an incoming frame from the remote peer.
918    ///
919    /// Returns a list of [`MuxEvent`]s describing what happened:
920    /// - SYN → [`MuxEvent::StreamOpened`] (opens the stream if not present)
921    /// - DATA → [`MuxEvent::FrameReceived`] (payload buffered in recv buffer)
922    /// - FIN → [`MuxEvent::StreamClosed`]
923    /// - RST → [`MuxEvent::StreamReset`]
924    /// - Recv buffer full → [`MuxEvent::SendBufferFull`] (frame dropped, counted in `dropped_frames`)
925    ///
926    /// # Errors
927    ///
928    /// - [`MuxError::StreamNotFound`] if the stream is unknown and frame is not SYN.
929    /// - [`MuxError::SequenceError`] if the frame arrives out of order.
930    pub fn receive_events(
931        &mut self,
932        frame: StreamFrame,
933        now: u64,
934    ) -> Result<Vec<MuxEvent>, MuxError> {
935        let mut events = Vec::new();
936
937        // SYN: open a new stream if it doesn't exist yet.
938        if frame.is_syn() {
939            if self.streams.contains_key(&frame.stream_id) {
940                return Err(MuxError::StreamAlreadyOpen(frame.stream_id.0));
941            }
942
943            if self.streams.len() >= self.config.max_streams {
944                return Err(MuxError::MaxStreamsReached);
945            }
946
947            let recv_cap = self.config.recv_buffer_capacity;
948            let stream = LogicalStream {
949                id: frame.stream_id,
950                priority: StreamPriority::Normal,
951                state: StreamState::Open,
952                send_window: self.config.default_window_size,
953                recv_window: self.config.default_window_size,
954                send_seq: 0,
955                recv_seq: 1, // SYN consumed sequence 0
956                bytes_sent: 0,
957                bytes_received: 0,
958                frames_sent: 0,
959                frames_received: 1,
960                opened_at: now,
961                last_activity: now,
962                priority_raw: StreamPriority::Normal.weight() as u8,
963                recv_buffer: VecDeque::with_capacity(recv_cap.min(64)),
964                recv_buffer_max: recv_cap,
965            };
966            self.streams.insert(frame.stream_id, stream);
967            self.total_streams_opened = self.total_streams_opened.wrapping_add(1);
968            self.total_frames_received = self.total_frames_received.wrapping_add(1);
969            events.push(MuxEvent::StreamOpened(frame.stream_id));
970            return Ok(events);
971        }
972
973        let stream = self
974            .streams
975            .get_mut(&frame.stream_id)
976            .ok_or(MuxError::StreamNotFound(frame.stream_id.0))?;
977
978        // RST takes immediate effect regardless of sequence.
979        if frame.is_rst() {
980            stream.state = StreamState::Reset;
981            stream.last_activity = now;
982            stream.frames_received = stream.frames_received.wrapping_add(1);
983            self.total_frames_received = self.total_frames_received.wrapping_add(1);
984            events.push(MuxEvent::StreamReset(frame.stream_id));
985            return Ok(events);
986        }
987
988        // Sequence check.
989        if frame.sequence != stream.recv_seq {
990            return Err(MuxError::SequenceError {
991                expected: stream.recv_seq,
992                got: frame.sequence,
993            });
994        }
995
996        stream.recv_seq = stream.recv_seq.wrapping_add(1);
997        let data_len = frame.data.len();
998        stream.frames_received = stream.frames_received.wrapping_add(1);
999        stream.last_activity = now;
1000        self.total_frames_received = self.total_frames_received.wrapping_add(1);
1001
1002        if frame.is_fin() {
1003            stream.bytes_received = stream.bytes_received.saturating_add(data_len as u64);
1004            stream.state = if stream.state == StreamState::HalfClosed {
1005                StreamState::Closed
1006            } else {
1007                StreamState::HalfClosed
1008            };
1009            events.push(MuxEvent::StreamClosed(frame.stream_id));
1010        } else if !frame.data.is_empty() {
1011            // DATA frame: buffer the payload.
1012            stream.bytes_received = stream.bytes_received.saturating_add(data_len as u64);
1013            if stream.recv_buffer.len() >= stream.recv_buffer_max {
1014                self.dropped_frames = self.dropped_frames.wrapping_add(1);
1015                events.push(MuxEvent::SendBufferFull(frame.stream_id));
1016            } else {
1017                stream.recv_buffer.push_back(frame.data);
1018                events.push(MuxEvent::FrameReceived(frame.stream_id));
1019            }
1020        } else {
1021            events.push(MuxEvent::FrameReceived(frame.stream_id));
1022        }
1023
1024        Ok(events)
1025    }
1026
1027    /// Process an incoming frame from the remote peer (original API).
1028    ///
1029    /// Handles RST, FIN, and data frames.  Returns the payload bytes.
1030    ///
1031    /// # Errors
1032    ///
1033    /// - [`MuxError::StreamNotFound`] if the stream is unknown.
1034    /// - [`MuxError::SequenceError`] if the frame arrives out of order.
1035    pub fn receive(&mut self, frame: StreamFrame, _now: u64) -> Result<Vec<u8>, MuxError> {
1036        let stream = self
1037            .streams
1038            .get_mut(&frame.stream_id)
1039            .ok_or(MuxError::StreamNotFound(frame.stream_id.0))?;
1040
1041        // RST takes immediate effect regardless of sequence.
1042        if frame.is_rst() {
1043            stream.state = StreamState::Reset;
1044            self.total_frames_received = self.total_frames_received.wrapping_add(1);
1045            return Ok(Vec::new());
1046        }
1047
1048        // Sequence check (SYN frames reset the counter expectation).
1049        if !frame.is_syn() && frame.sequence != stream.recv_seq {
1050            return Err(MuxError::SequenceError {
1051                expected: stream.recv_seq,
1052                got: frame.sequence,
1053            });
1054        }
1055
1056        stream.recv_seq = stream.recv_seq.wrapping_add(1);
1057        let data_len = frame.data.len();
1058        stream.bytes_received = stream.bytes_received.saturating_add(data_len as u64);
1059
1060        if frame.is_fin() {
1061            stream.state = StreamState::HalfClosed;
1062        }
1063
1064        self.total_frames_received = self.total_frames_received.wrapping_add(1);
1065        Ok(frame.data)
1066    }
1067
1068    // ─────────────────────────────────────────────────────────────
1069    // Receive buffer drain
1070    // ─────────────────────────────────────────────────────────────
1071
1072    /// Drain all buffered receive payloads for `stream_id`.
1073    ///
1074    /// Returns a `Vec` of payload byte vectors in arrival order.
1075    /// The receive buffer is emptied after this call.
1076    ///
1077    /// # Errors
1078    ///
1079    /// Returns [`MuxError::StreamNotFound`] if the stream does not exist.
1080    pub fn drain_recv_buffer(&mut self, stream_id: StreamId) -> Result<Vec<Vec<u8>>, MuxError> {
1081        let stream = self
1082            .streams
1083            .get_mut(&stream_id)
1084            .ok_or(MuxError::StreamNotFound(stream_id.0))?;
1085
1086        let payloads: Vec<Vec<u8>> = stream.recv_buffer.drain(..).collect();
1087        Ok(payloads)
1088    }
1089
1090    // ─────────────────────────────────────────────────────────────
1091    // Dequeue
1092    // ─────────────────────────────────────────────────────────────
1093
1094    /// Pop the highest-priority frame from the send queue.
1095    ///
1096    /// Updates `total_frames_sent`.  For data frames (no control flags),
1097    /// the stream's `send_window` has already been decremented in `send`;
1098    /// no further deduction is needed here.
1099    pub fn dequeue_frame(&mut self) -> Option<StreamFrame> {
1100        let pf = self.send_queue.pop()?;
1101        self.total_frames_sent = self.total_frames_sent.wrapping_add(1);
1102        Some(pf.frame)
1103    }
1104
1105    /// Pop up to `n` frames from the send queue in priority order.
1106    pub fn dequeue_frames(&mut self, n: usize) -> Vec<StreamFrame> {
1107        let mut result = Vec::with_capacity(n);
1108        for _ in 0..n {
1109            match self.dequeue_frame() {
1110                Some(f) => result.push(f),
1111                None => break,
1112            }
1113        }
1114        result
1115    }
1116
1117    /// Drain **all** pending outbound frames across all streams in priority order.
1118    ///
1119    /// Equivalent to calling `dequeue_frames` with `usize::MAX`, but more
1120    /// efficient since it drains the heap directly.
1121    pub fn drain_send_queue(&mut self) -> Vec<StreamFrame> {
1122        let mut result = Vec::with_capacity(self.send_queue.len());
1123        while let Some(pf) = self.send_queue.pop() {
1124            self.total_frames_sent = self.total_frames_sent.wrapping_add(1);
1125            result.push(pf.frame);
1126        }
1127        result
1128    }
1129
1130    // ─────────────────────────────────────────────────────────────
1131    // Idle expiry
1132    // ─────────────────────────────────────────────────────────────
1133
1134    /// Expire streams that have been idle longer than
1135    /// [`MultiplexerConfig::idle_timeout_us`].
1136    ///
1137    /// A stream is considered idle if
1138    /// `last_activity + idle_timeout_us < current_ts`.
1139    ///
1140    /// Expired streams are transitioned to [`StreamState::Closed`] and a
1141    /// [`MuxEvent::IdleStreamExpired`] event is emitted for each one.
1142    pub fn expire_idle(&mut self, current_ts: u64) -> Vec<MuxEvent> {
1143        let timeout = self.config.idle_timeout_us;
1144        let mut expired = Vec::new();
1145
1146        for (id, stream) in self.streams.iter_mut() {
1147            if matches!(
1148                stream.state,
1149                StreamState::Open | StreamState::Opening | StreamState::HalfClosed
1150            ) && stream.last_activity.saturating_add(timeout) < current_ts
1151            {
1152                stream.state = StreamState::Closed;
1153                expired.push(*id);
1154            }
1155        }
1156
1157        expired
1158            .into_iter()
1159            .map(MuxEvent::IdleStreamExpired)
1160            .collect()
1161    }
1162
1163    // ─────────────────────────────────────────────────────────────
1164    // Flow control
1165    // ─────────────────────────────────────────────────────────────
1166
1167    /// Add `increment` bytes to the send window for `stream_id`.
1168    ///
1169    /// Returns `true` if the stream exists and the window was updated,
1170    /// `false` otherwise.
1171    pub fn update_window(&mut self, stream_id: StreamId, increment: u32) -> bool {
1172        if let Some(stream) = self.streams.get_mut(&stream_id) {
1173            stream.send_window = stream.send_window.saturating_add(increment);
1174            true
1175        } else {
1176            false
1177        }
1178    }
1179
1180    // ─────────────────────────────────────────────────────────────
1181    // Introspection
1182    // ─────────────────────────────────────────────────────────────
1183
1184    /// Return a reference to the [`LogicalStream`] for the given id, or `None`.
1185    pub fn stream_state(&self, stream_id: StreamId) -> Option<&LogicalStream> {
1186        self.streams.get(&stream_id)
1187    }
1188
1189    /// Return a read-only [`StreamInfo`] snapshot for the given stream.
1190    ///
1191    /// # Errors
1192    ///
1193    /// Returns [`MuxError::StreamNotFound`] if the stream does not exist.
1194    pub fn stream_info(&self, stream_id: StreamId) -> Result<StreamInfo, MuxError> {
1195        let s = self
1196            .streams
1197            .get(&stream_id)
1198            .ok_or(MuxError::StreamNotFound(stream_id.0))?;
1199
1200        Ok(StreamInfo {
1201            id: s.id,
1202            state: s.state,
1203            bytes_sent: s.bytes_sent,
1204            bytes_received: s.bytes_received,
1205            frames_sent: s.frames_sent,
1206            frames_received: s.frames_received,
1207            opened_at: s.opened_at,
1208            last_activity: s.last_activity,
1209            priority: s.priority_raw,
1210        })
1211    }
1212
1213    /// Return the ids of all currently active (Open or Opening) streams.
1214    pub fn active_streams(&self) -> Vec<StreamId> {
1215        self.streams
1216            .values()
1217            .filter(|s| matches!(s.state, StreamState::Open | StreamState::Opening))
1218            .map(|s| s.id)
1219            .collect()
1220    }
1221
1222    /// Number of streams currently in [`StreamState::Open`].
1223    pub fn open_stream_count(&self) -> usize {
1224        self.streams
1225            .values()
1226            .filter(|s| s.state == StreamState::Open)
1227            .count()
1228    }
1229
1230    /// Return the original compact statistics snapshot ([`MuxStats`]).
1231    pub fn stats(&self) -> MuxStats {
1232        let total_bytes_sent = self
1233            .streams
1234            .values()
1235            .map(|s| s.bytes_sent)
1236            .fold(0u64, |acc, b| acc.saturating_add(b));
1237
1238        MuxStats {
1239            total_streams: self.streams.len(),
1240            open_streams: self.open_stream_count(),
1241            queued_frames: self.send_queue.len(),
1242            total_frames_sent: self.total_frames_sent,
1243            total_frames_received: self.total_frames_received,
1244            total_bytes_sent,
1245        }
1246    }
1247
1248    /// Return the richer [`MultiplexerStats`] snapshot.
1249    pub fn multiplexer_stats(&self) -> MultiplexerStats {
1250        let (total_bytes_sent, total_bytes_received) =
1251            self.streams.values().fold((0u64, 0u64), |(s, r), stream| {
1252                (
1253                    s.saturating_add(stream.bytes_sent),
1254                    r.saturating_add(stream.bytes_received),
1255                )
1256            });
1257
1258        let active = self
1259            .streams
1260            .values()
1261            .filter(|s| matches!(s.state, StreamState::Open | StreamState::Opening))
1262            .count();
1263
1264        MultiplexerStats {
1265            active_streams: active,
1266            total_streams_opened: self.total_streams_opened,
1267            total_frames_sent: self.total_frames_sent,
1268            total_frames_received: self.total_frames_received,
1269            total_bytes_sent,
1270            total_bytes_received,
1271            dropped_frames: self.dropped_frames,
1272        }
1273    }
1274
1275    // ─────────────────────────────────────────────────────────────
1276    // Private helpers
1277    // ─────────────────────────────────────────────────────────────
1278
1279    /// Enqueue a control frame (SYN / FIN / RST) without touching the send window.
1280    fn enqueue_control(
1281        &mut self,
1282        stream_id: StreamId,
1283        flags: u8,
1284        sequence: u64,
1285        priority: StreamPriority,
1286        now: u64,
1287    ) {
1288        let weight = if self.config.enable_priority {
1289            priority.weight()
1290        } else {
1291            0
1292        };
1293        let enqueue_seq = self.enqueue_seq;
1294        self.enqueue_seq = self.enqueue_seq.wrapping_add(1);
1295
1296        self.send_queue.push(PrioritizedFrame {
1297            priority_weight: weight,
1298            sequence: enqueue_seq,
1299            frame: StreamFrame {
1300                stream_id,
1301                sequence,
1302                data: Vec::new(),
1303                flags,
1304                timestamp: now,
1305            },
1306        });
1307    }
1308}
1309
1310// ─────────────────────────────────────────────────────────────────────────────
1311// PRNG helper (no external rand dependency)
1312// ─────────────────────────────────────────────────────────────────────────────
1313
1314/// Xorshift64 PRNG — seedable, dependency-free random number source for tests.
1315///
1316/// # Example
1317/// ```ignore
1318/// let mut state = 12345u64;
1319/// let r = xorshift64(&mut state);
1320/// ```
1321pub fn xorshift64(state: &mut u64) -> u64 {
1322    let mut x = *state;
1323    x ^= x << 13;
1324    x ^= x >> 7;
1325    x ^= x << 17;
1326    *state = x;
1327    x
1328}
1329
1330// ─────────────────────────────────────────────────────────────────────────────
1331// Tests (split to stream_multiplexer_tests.rs to keep this file < 2000 lines)
1332// ─────────────────────────────────────────────────────────────────────────────
1333
1334#[cfg(test)]
1335#[path = "stream_multiplexer_tests.rs"]
1336mod tests;