Skip to main content

datum_net/
stream_ref.rs

1//! Remote StreamRefs carriers.
2//!
3//! `datum-core` owns the protobuf protocol and state machine. This module is
4//! the carrier layer: it length-prefixes protobuf frames and pumps them over
5//! reliable, ordered bidirectional byte streams such as QUIC or plaintext TCP.
6
7use std::{
8    collections::VecDeque,
9    future::Future,
10    net::SocketAddr,
11    sync::{Arc, Mutex, OnceLock, mpsc},
12    time::Duration,
13};
14
15use bytes::{Buf, BytesMut};
16use datum::{
17    NotUsed, Sink, Source, SourceRef, StreamCompletion, StreamError, StreamRefFrame, StreamRefId,
18    StreamRefMessage, StreamRefOutbound, StreamRefPayload, StreamRefPayloadBatch,
19    StreamRefProtoConsumer, StreamRefProtoEndpoint, StreamRefProtoProducer, StreamRefSettings,
20    StreamResult,
21    actor::stream_ref_proto::{StreamRefOutboundPoll, StreamRefProtoEndpointWake},
22};
23use tokio::{
24    io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt},
25    net::{TcpListener, TcpStream, ToSocketAddrs},
26    runtime::{Handle, Runtime},
27    sync::mpsc as tokio_mpsc,
28    task::JoinHandle,
29};
30
31use crate::QuicBidirectionalStream;
32
33const FRAME_LEN_BYTES: usize = 4;
34const MAX_STREAM_REF_FRAME_BYTES: usize = 16 * 1024 * 1024;
35const STREAM_REF_TCP_CHUNK_SIZE: usize = 8192;
36const STREAM_REF_QUIC_READ_BUFFER_BYTES: usize = 2048;
37const STREAM_REF_OUTBOUND_BATCH_FRAMES: usize = 64;
38const STREAM_REF_OUTBOUND_RECHECK_INTERVAL: Duration = Duration::from_millis(5);
39
40// Carrier wire v1 keeps protobuf for control frames. A high bit in the
41// length-prefix marks compact SequencedOnNext batches: version, kind,
42// stream_ref_id, first seqNr, count, then length-prefixed payloads.
43const COMPACT_FRAME_FLAG: u32 = 0x8000_0000;
44const COMPACT_FRAME_LEN_MASK: u32 = 0x7fff_ffff;
45const COMPACT_FRAME_VERSION: u8 = 1;
46const COMPACT_SEQUENCED_ON_NEXT_BATCH: u8 = 1;
47const COMPACT_BATCH_HEADER_BYTES: usize = 1 + 1 + 16 + 8 + 2;
48const COMPACT_BATCH_ELEMENT_LEN_BYTES: usize = 4;
49
50#[derive(Clone, Copy)]
51struct CarrierReadMode {
52    chunk_size: usize,
53    emit_available: bool,
54    fail_on_eof: bool,
55}
56
57impl CarrierReadMode {
58    fn new(chunk_size: usize, emit_available: bool, fail_on_eof: bool) -> Self {
59        assert!(chunk_size > 0, "chunk size must be greater than zero");
60        Self {
61            chunk_size,
62            emit_available,
63            fail_on_eof,
64        }
65    }
66}
67
68/// Counts selected StreamRefs protocol messages successfully written by a
69/// carrier endpoint.
70#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
71pub struct StreamRefProtocolMessageCounts {
72    pub cumulative_demand: u64,
73    pub sequenced_on_next: u64,
74    pub ack: u64,
75}
76
77/// Shared collector for StreamRefs protocol message-count diagnostics.
78///
79/// The carrier records counts only after the encoded frame has been written
80/// successfully. This is intentionally transport-level instrumentation; it does
81/// not change the protobuf protocol or wire format.
82#[derive(Clone, Default)]
83pub struct StreamRefProtocolDiagnostics {
84    counts: Arc<Mutex<StreamRefProtocolMessageCounts>>,
85}
86
87impl StreamRefProtocolDiagnostics {
88    #[must_use]
89    pub fn new() -> Self {
90        Self::default()
91    }
92
93    #[must_use]
94    pub fn snapshot(&self) -> StreamRefProtocolMessageCounts {
95        *self
96            .counts
97            .lock()
98            .unwrap_or_else(|poison| poison.into_inner())
99    }
100
101    fn record_counts(&self, delta: StreamRefProtocolMessageCounts) {
102        if delta == StreamRefProtocolMessageCounts::default() {
103            return;
104        }
105        let mut counts = self
106            .counts
107            .lock()
108            .unwrap_or_else(|poison| poison.into_inner());
109        counts.cumulative_demand = counts
110            .cumulative_demand
111            .saturating_add(delta.cumulative_demand);
112        counts.sequenced_on_next = counts
113            .sequenced_on_next
114            .saturating_add(delta.sequenced_on_next);
115        counts.ack = counts.ack.saturating_add(delta.ack);
116    }
117}
118
119fn outbound_counts(outbound: &StreamRefOutbound) -> StreamRefProtocolMessageCounts {
120    let mut counts = StreamRefProtocolMessageCounts::default();
121    match outbound {
122        StreamRefOutbound::Frame(frame) => match &frame.message {
123            StreamRefMessage::CumulativeDemand { .. } => {
124                counts.cumulative_demand = 1;
125            }
126            StreamRefMessage::SequencedOnNext { .. } => {
127                counts.sequenced_on_next = 1;
128            }
129            StreamRefMessage::Ack => {
130                counts.ack = 1;
131            }
132            StreamRefMessage::OnSubscribeHandshake
133            | StreamRefMessage::RemoteStreamCompleted { .. }
134            | StreamRefMessage::RemoteStreamFailure { .. } => {}
135        },
136        StreamRefOutbound::SequencedBatch(batch) => {
137            counts.sequenced_on_next = batch.count() as u64;
138        }
139    }
140    counts
141}
142
143#[derive(Clone, Copy)]
144struct PendingDiagnostic {
145    remaining: usize,
146    counts: StreamRefProtocolMessageCounts,
147}
148
149/// Completion handle for a StreamRefs-over-QUIC carrier.
150#[must_use = "wait for the QUIC StreamRefs carrier to observe completion or failure"]
151pub struct StreamRefQuicHandle {
152    completion: EndpointTaskCompletion,
153}
154
155impl StreamRefQuicHandle {
156    pub fn wait(self) -> StreamResult<NotUsed> {
157        self.completion.wait()
158    }
159
160    #[must_use]
161    pub fn try_wait(&self) -> Option<StreamResult<NotUsed>> {
162        self.completion.try_wait()
163    }
164
165    #[must_use]
166    pub fn is_finished(&self) -> bool {
167        self.completion.is_finished()
168    }
169}
170
171/// Local TCP listener binding used by StreamRefs-over-TCP producer endpoints.
172#[derive(Debug, Clone, Copy, PartialEq, Eq)]
173pub struct StreamRefTcpBinding {
174    local_addr: SocketAddr,
175}
176
177impl StreamRefTcpBinding {
178    #[must_use]
179    pub fn local_addr(&self) -> SocketAddr {
180        self.local_addr
181    }
182}
183
184/// Completion handle for a StreamRefs-over-TCP carrier.
185#[must_use = "wait for the TCP StreamRefs carrier to observe completion or failure"]
186pub struct StreamRefTcpHandle {
187    completion: EndpointTaskCompletion,
188}
189
190impl StreamRefTcpHandle {
191    pub fn wait(self) -> StreamResult<NotUsed> {
192        self.completion.wait()
193    }
194
195    #[must_use]
196    pub fn try_wait(&self) -> Option<StreamResult<NotUsed>> {
197        self.completion.try_wait()
198    }
199
200    #[must_use]
201    pub fn is_finished(&self) -> bool {
202        self.completion.is_finished()
203    }
204}
205
206struct EndpointTaskCompletion {
207    receiver: mpsc::Receiver<StreamResult<NotUsed>>,
208    task: Option<JoinHandle<()>>,
209}
210
211impl EndpointTaskCompletion {
212    fn wait(mut self) -> StreamResult<NotUsed> {
213        let result = self
214            .receiver
215            .recv()
216            .unwrap_or(Err(StreamError::AbruptTermination));
217        self.task.take();
218        result
219    }
220
221    fn try_wait(&self) -> Option<StreamResult<NotUsed>> {
222        self.receiver.try_recv().ok()
223    }
224
225    fn is_finished(&self) -> bool {
226        self.task.as_ref().is_some_and(JoinHandle::is_finished)
227    }
228}
229
230impl Drop for EndpointTaskCompletion {
231    fn drop(&mut self) {
232        if let Some(task) = self.task.take() {
233            task.abort();
234        }
235    }
236}
237
238/// Serves a local `SourceRef` over an accepted or opened QUIC bidi stream.
239pub fn serve_source_ref_over_quic<T>(
240    stream: QuicBidirectionalStream,
241    source_ref: SourceRef<T>,
242    stream_ref_id: StreamRefId,
243    settings: StreamRefSettings,
244) -> StreamResult<StreamRefQuicHandle>
245where
246    T: StreamRefPayload,
247{
248    let producer = StreamRefProtoProducer::from_source_ref(source_ref, stream_ref_id, settings)?;
249    Ok(drive_stream_ref_endpoint_over_quic(stream, producer, None))
250}
251
252/// Serves a local `Source` over an accepted or opened QUIC bidi stream.
253pub fn serve_source_over_quic<T, Mat>(
254    stream: QuicBidirectionalStream,
255    source: Source<T, Mat>,
256    stream_ref_id: StreamRefId,
257    settings: StreamRefSettings,
258) -> StreamResult<StreamRefQuicHandle>
259where
260    T: StreamRefPayload,
261    Mat: Send + 'static,
262{
263    let producer = StreamRefProtoProducer::from_source(source, stream_ref_id, settings)?;
264    Ok(drive_stream_ref_endpoint_over_quic(stream, producer, None))
265}
266
267/// Creates a local source fed by a remote QUIC StreamRef producer.
268pub fn source_ref_over_quic<T>(
269    stream: QuicBidirectionalStream,
270    stream_ref_id: StreamRefId,
271    settings: StreamRefSettings,
272) -> (Source<T, NotUsed>, StreamRefQuicHandle)
273where
274    T: StreamRefPayload,
275{
276    let consumer = StreamRefProtoConsumer::new(stream_ref_id, settings);
277    let source = consumer.source();
278    let handle = drive_stream_ref_endpoint_over_quic(stream, consumer, None);
279    (source, handle)
280}
281
282/// Serves a local `SinkRef` receiver over an accepted or opened QUIC bidi
283/// stream, returning a [`Source`] of inbound elements.
284///
285/// This is the local/receiver side of the SinkRef-over-QUIC pair: the remote
286/// sender pushes elements into a [`sink_ref_over_quic`](fn.sink_ref_over_quic)
287/// `Sink`, and this side surfaces them as a `Source`. The caller runs the
288/// returned source into a local `Sink` (for example `Sink::collect` or a fold).
289pub fn serve_sink_ref_over_quic<T>(
290    stream: QuicBidirectionalStream,
291    stream_ref_id: StreamRefId,
292    settings: StreamRefSettings,
293) -> (Source<T, NotUsed>, StreamRefQuicHandle)
294where
295    T: StreamRefPayload,
296{
297    let consumer = StreamRefProtoConsumer::new(stream_ref_id, settings);
298    let source = consumer.source();
299    let handle = drive_stream_ref_endpoint_over_quic(stream, consumer, None);
300    (source, handle)
301}
302
303/// Creates a local `Sink` that sends its incoming elements over QUIC to a
304/// remote `SinkRef` receiver.
305pub fn sink_ref_over_quic<T>(
306    stream: QuicBidirectionalStream,
307    stream_ref_id: StreamRefId,
308    settings: StreamRefSettings,
309) -> (Sink<T, StreamCompletion<NotUsed>>, StreamRefQuicHandle)
310where
311    T: StreamRefPayload,
312{
313    let producer = StreamRefProtoProducer::new_lazy(stream_ref_id, settings);
314    let sink = producer.sink();
315    let handle = drive_stream_ref_endpoint_over_quic(stream, producer, None);
316    (sink, handle)
317}
318
319/// Serves a local `SourceRef` over a one-shot plaintext TCP listener.
320///
321/// The listener binds immediately, accepts one connection, then runs the same
322/// StreamRefs protocol used by the QUIC carrier. The remote receiver should use
323/// [`source_ref_over_tcp`] to open the TCP connection and send the initial
324/// subscribe+demand frames.
325pub fn serve_source_ref_over_tcp<T, A>(
326    addr: A,
327    source_ref: SourceRef<T>,
328    stream_ref_id: StreamRefId,
329    settings: StreamRefSettings,
330) -> StreamResult<(StreamRefTcpBinding, StreamRefTcpHandle)>
331where
332    T: StreamRefPayload,
333    A: ToSocketAddrs + Send + 'static,
334{
335    serve_source_ref_over_tcp_with_diagnostics(addr, source_ref, stream_ref_id, settings, None)
336}
337
338pub fn serve_source_ref_over_tcp_with_diagnostics<T, A>(
339    addr: A,
340    source_ref: SourceRef<T>,
341    stream_ref_id: StreamRefId,
342    settings: StreamRefSettings,
343    diagnostics: Option<StreamRefProtocolDiagnostics>,
344) -> StreamResult<(StreamRefTcpBinding, StreamRefTcpHandle)>
345where
346    T: StreamRefPayload,
347    A: ToSocketAddrs + Send + 'static,
348{
349    let producer = StreamRefProtoProducer::from_source_ref(source_ref, stream_ref_id, settings)?;
350    let (listener, binding, handle) = bind_tcp_listener(addr)?;
351    Ok((
352        binding,
353        drive_stream_ref_endpoint_over_tcp_listener(listener, handle, producer, diagnostics),
354    ))
355}
356
357/// Serves a local `SourceRef` over an already-connected Tokio TCP stream.
358///
359/// This is the stream-shaped counterpart to [`serve_source_ref_over_tcp`],
360/// intended for callers that own listener lifecycle separately, such as a
361/// benchmark server that reuses one bound listener across operations.
362pub fn serve_source_ref_over_tcp_stream<T>(
363    stream: TcpStream,
364    source_ref: SourceRef<T>,
365    stream_ref_id: StreamRefId,
366    settings: StreamRefSettings,
367) -> StreamResult<StreamRefTcpHandle>
368where
369    T: StreamRefPayload,
370{
371    serve_source_ref_over_tcp_stream_with_diagnostics(
372        stream,
373        source_ref,
374        stream_ref_id,
375        settings,
376        None,
377    )
378}
379
380pub fn serve_source_ref_over_tcp_stream_with_diagnostics<T>(
381    stream: TcpStream,
382    source_ref: SourceRef<T>,
383    stream_ref_id: StreamRefId,
384    settings: StreamRefSettings,
385    diagnostics: Option<StreamRefProtocolDiagnostics>,
386) -> StreamResult<StreamRefTcpHandle>
387where
388    T: StreamRefPayload,
389{
390    let producer = StreamRefProtoProducer::from_source_ref(source_ref, stream_ref_id, settings)?;
391    let handle = current_tokio_handle()?;
392    Ok(drive_stream_ref_endpoint_over_tcp_stream(
393        stream,
394        handle,
395        producer,
396        diagnostics,
397    ))
398}
399
400/// Creates a local source fed by a remote plaintext TCP StreamRef producer.
401///
402/// This receiver side opens the TCP connection and, once the returned source is
403/// materialized, sends the initial handshake and cumulative demand.
404pub fn source_ref_over_tcp<T, A>(
405    addr: A,
406    stream_ref_id: StreamRefId,
407    settings: StreamRefSettings,
408) -> StreamResult<(Source<T, NotUsed>, StreamRefTcpHandle)>
409where
410    T: StreamRefPayload,
411    A: ToSocketAddrs + Send + 'static,
412{
413    source_ref_over_tcp_with_diagnostics(addr, stream_ref_id, settings, None)
414}
415
416pub fn source_ref_over_tcp_with_diagnostics<T, A>(
417    addr: A,
418    stream_ref_id: StreamRefId,
419    settings: StreamRefSettings,
420    diagnostics: Option<StreamRefProtocolDiagnostics>,
421) -> StreamResult<(Source<T, NotUsed>, StreamRefTcpHandle)>
422where
423    T: StreamRefPayload,
424    A: ToSocketAddrs + Send + 'static,
425{
426    let consumer = StreamRefProtoConsumer::new(stream_ref_id, settings);
427    let source = consumer.source();
428    let (stream, handle) = connect_tcp_stream(addr)?;
429    let handle = drive_stream_ref_endpoint_over_tcp_stream(stream, handle, consumer, diagnostics);
430    Ok((source, handle))
431}
432
433/// Creates a local source fed by a remote producer over an already-connected
434/// Tokio TCP stream.
435pub fn source_ref_over_tcp_stream<T>(
436    stream: TcpStream,
437    stream_ref_id: StreamRefId,
438    settings: StreamRefSettings,
439) -> StreamResult<(Source<T, NotUsed>, StreamRefTcpHandle)>
440where
441    T: StreamRefPayload,
442{
443    source_ref_over_tcp_stream_with_diagnostics(stream, stream_ref_id, settings, None)
444}
445
446pub fn source_ref_over_tcp_stream_with_diagnostics<T>(
447    stream: TcpStream,
448    stream_ref_id: StreamRefId,
449    settings: StreamRefSettings,
450    diagnostics: Option<StreamRefProtocolDiagnostics>,
451) -> StreamResult<(Source<T, NotUsed>, StreamRefTcpHandle)>
452where
453    T: StreamRefPayload,
454{
455    let consumer = StreamRefProtoConsumer::new(stream_ref_id, settings);
456    let source = consumer.source();
457    let handle = current_tokio_handle()?;
458    let handle = drive_stream_ref_endpoint_over_tcp_stream(stream, handle, consumer, diagnostics);
459    Ok((source, handle))
460}
461
462/// Serves a remote `SinkRef` receiver over plaintext TCP.
463///
464/// This receiver side opens the TCP connection to a sender created with
465/// [`sink_ref_over_tcp`] and, once the returned source is materialized, sends
466/// the initial handshake and cumulative demand.
467pub fn serve_sink_ref_over_tcp<T, A>(
468    addr: A,
469    stream_ref_id: StreamRefId,
470    settings: StreamRefSettings,
471) -> StreamResult<(Source<T, NotUsed>, StreamRefTcpHandle)>
472where
473    T: StreamRefPayload,
474    A: ToSocketAddrs + Send + 'static,
475{
476    serve_sink_ref_over_tcp_with_diagnostics(addr, stream_ref_id, settings, None)
477}
478
479pub fn serve_sink_ref_over_tcp_with_diagnostics<T, A>(
480    addr: A,
481    stream_ref_id: StreamRefId,
482    settings: StreamRefSettings,
483    diagnostics: Option<StreamRefProtocolDiagnostics>,
484) -> StreamResult<(Source<T, NotUsed>, StreamRefTcpHandle)>
485where
486    T: StreamRefPayload,
487    A: ToSocketAddrs + Send + 'static,
488{
489    let consumer = StreamRefProtoConsumer::new(stream_ref_id, settings);
490    let source = consumer.source();
491    let (stream, handle) = connect_tcp_stream(addr)?;
492    let handle = drive_stream_ref_endpoint_over_tcp_stream(stream, handle, consumer, diagnostics);
493    Ok((source, handle))
494}
495
496/// Serves a remote `SinkRef` receiver over an already-connected Tokio TCP
497/// stream.
498pub fn serve_sink_ref_over_tcp_stream<T>(
499    stream: TcpStream,
500    stream_ref_id: StreamRefId,
501    settings: StreamRefSettings,
502) -> StreamResult<(Source<T, NotUsed>, StreamRefTcpHandle)>
503where
504    T: StreamRefPayload,
505{
506    serve_sink_ref_over_tcp_stream_with_diagnostics(stream, stream_ref_id, settings, None)
507}
508
509pub fn serve_sink_ref_over_tcp_stream_with_diagnostics<T>(
510    stream: TcpStream,
511    stream_ref_id: StreamRefId,
512    settings: StreamRefSettings,
513    diagnostics: Option<StreamRefProtocolDiagnostics>,
514) -> StreamResult<(Source<T, NotUsed>, StreamRefTcpHandle)>
515where
516    T: StreamRefPayload,
517{
518    let consumer = StreamRefProtoConsumer::new(stream_ref_id, settings);
519    let source = consumer.source();
520    let handle = current_tokio_handle()?;
521    let handle = drive_stream_ref_endpoint_over_tcp_stream(stream, handle, consumer, diagnostics);
522    Ok((source, handle))
523}
524
525/// Creates a local `Sink` whose incoming elements are sent over a one-shot
526/// plaintext TCP listener to a remote `SinkRef` receiver.
527///
528/// The producer/listener side waits for the receiver to open the TCP
529/// connection. This mirrors the QUIC SinkRef direction where the receiver
530/// opens the bidi stream so its handshake+demand establish the stream before
531/// the producer has elements to send.
532pub fn sink_ref_over_tcp<T, A>(
533    addr: A,
534    stream_ref_id: StreamRefId,
535    settings: StreamRefSettings,
536) -> StreamResult<(
537    Sink<T, StreamCompletion<NotUsed>>,
538    StreamRefTcpBinding,
539    StreamRefTcpHandle,
540)>
541where
542    T: StreamRefPayload,
543    A: ToSocketAddrs + Send + 'static,
544{
545    sink_ref_over_tcp_with_diagnostics(addr, stream_ref_id, settings, None)
546}
547
548pub fn sink_ref_over_tcp_with_diagnostics<T, A>(
549    addr: A,
550    stream_ref_id: StreamRefId,
551    settings: StreamRefSettings,
552    diagnostics: Option<StreamRefProtocolDiagnostics>,
553) -> StreamResult<(
554    Sink<T, StreamCompletion<NotUsed>>,
555    StreamRefTcpBinding,
556    StreamRefTcpHandle,
557)>
558where
559    T: StreamRefPayload,
560    A: ToSocketAddrs + Send + 'static,
561{
562    let producer = StreamRefProtoProducer::new_lazy(stream_ref_id, settings);
563    let sink = producer.sink();
564    let (listener, binding, handle) = bind_tcp_listener(addr)?;
565    let handle =
566        drive_stream_ref_endpoint_over_tcp_listener(listener, handle, producer, diagnostics);
567    Ok((sink, binding, handle))
568}
569
570/// Creates a local `Sink` whose incoming elements are sent over an
571/// already-connected Tokio TCP stream to a remote `SinkRef` receiver.
572pub fn sink_ref_over_tcp_stream<T>(
573    stream: TcpStream,
574    stream_ref_id: StreamRefId,
575    settings: StreamRefSettings,
576) -> StreamResult<(Sink<T, StreamCompletion<NotUsed>>, StreamRefTcpHandle)>
577where
578    T: StreamRefPayload,
579{
580    sink_ref_over_tcp_stream_with_diagnostics(stream, stream_ref_id, settings, None)
581}
582
583pub fn sink_ref_over_tcp_stream_with_diagnostics<T>(
584    stream: TcpStream,
585    stream_ref_id: StreamRefId,
586    settings: StreamRefSettings,
587    diagnostics: Option<StreamRefProtocolDiagnostics>,
588) -> StreamResult<(Sink<T, StreamCompletion<NotUsed>>, StreamRefTcpHandle)>
589where
590    T: StreamRefPayload,
591{
592    let producer = StreamRefProtoProducer::new_lazy(stream_ref_id, settings);
593    let sink = producer.sink();
594    let handle = current_tokio_handle()?;
595    let handle = drive_stream_ref_endpoint_over_tcp_stream(stream, handle, producer, diagnostics);
596    Ok((sink, handle))
597}
598
599fn drive_stream_ref_endpoint_over_quic<E>(
600    stream: QuicBidirectionalStream,
601    endpoint: E,
602    diagnostics: Option<StreamRefProtocolDiagnostics>,
603) -> StreamRefQuicHandle
604where
605    E: StreamRefProtoEndpointWake,
606{
607    let (reader, writer, handle, chunk_size, emit_available) = stream.into_stream_ref_parts();
608    let read_mode = CarrierReadMode::new(chunk_size, emit_available, false);
609    StreamRefQuicHandle {
610        completion: spawn_endpoint_task(&handle, async move {
611            run_stream_ref_endpoint_quic_task(reader, writer, endpoint, read_mode, diagnostics)
612                .await
613        }),
614    }
615}
616
617fn drive_stream_ref_endpoint_over_tcp_listener<E>(
618    listener: TcpListener,
619    handle: Handle,
620    endpoint: E,
621    diagnostics: Option<StreamRefProtocolDiagnostics>,
622) -> StreamRefTcpHandle
623where
624    E: StreamRefProtoEndpointWake,
625{
626    StreamRefTcpHandle {
627        completion: spawn_endpoint_task(&handle, async move {
628            let (stream, _) = listener.accept().await.map_err(io_error)?;
629            run_stream_ref_endpoint_tcp_task(stream, endpoint, diagnostics).await
630        }),
631    }
632}
633
634fn drive_stream_ref_endpoint_over_tcp_stream<E>(
635    stream: TcpStream,
636    handle: Handle,
637    endpoint: E,
638    diagnostics: Option<StreamRefProtocolDiagnostics>,
639) -> StreamRefTcpHandle
640where
641    E: StreamRefProtoEndpointWake,
642{
643    StreamRefTcpHandle {
644        completion: spawn_endpoint_task(&handle, async move {
645            run_stream_ref_endpoint_tcp_task(stream, endpoint, diagnostics).await
646        }),
647    }
648}
649
650async fn run_stream_ref_endpoint_quic_task<R, W, E>(
651    reader: R,
652    writer: W,
653    endpoint: E,
654    read_mode: CarrierReadMode,
655    diagnostics: Option<StreamRefProtocolDiagnostics>,
656) -> StreamResult<NotUsed>
657where
658    R: AsyncRead + Unpin + Send + 'static,
659    W: AsyncWrite + Unpin + Send + 'static,
660    E: StreamRefProtoEndpointWake,
661{
662    let (wake_sender, wake_receiver) = tokio_mpsc::channel(1);
663    endpoint.install_outbound_wake(wake_sender.clone());
664    let _ = wake_sender.try_send(());
665
666    let result = QuicEndpointTask {
667        reader,
668        writer,
669        endpoint: endpoint.clone(),
670        diagnostics,
671        read_mode,
672        decoder: FrameDecoder::default(),
673        read_buffer: vec![
674            0_u8;
675            read_mode
676                .chunk_size
677                .clamp(1, STREAM_REF_QUIC_READ_BUFFER_BYTES)
678        ],
679        pending_tail: Vec::new(),
680        write_buffer: BytesMut::new(),
681        encode_buffer: Vec::new(),
682        pending_diagnostics: VecDeque::new(),
683        read_closed: false,
684        inbound_seen: false,
685        outbound_written: false,
686        recheck_outbound: false,
687        outbound_closed: false,
688        write_shutdown: false,
689        wake_receiver,
690    }
691    .run()
692    .await;
693
694    endpoint.clear_outbound_wake();
695    if let Err(error) = &result {
696        endpoint.fail_connection(error.clone());
697    }
698    result
699}
700
701struct QuicEndpointTask<R, W, E>
702where
703    E: StreamRefProtoEndpointWake,
704{
705    reader: R,
706    writer: W,
707    endpoint: E,
708    diagnostics: Option<StreamRefProtocolDiagnostics>,
709    read_mode: CarrierReadMode,
710    decoder: FrameDecoder,
711    read_buffer: Vec<u8>,
712    pending_tail: Vec<u8>,
713    write_buffer: BytesMut,
714    encode_buffer: Vec<u8>,
715    pending_diagnostics: VecDeque<PendingDiagnostic>,
716    read_closed: bool,
717    inbound_seen: bool,
718    outbound_written: bool,
719    recheck_outbound: bool,
720    outbound_closed: bool,
721    write_shutdown: bool,
722    wake_receiver: tokio_mpsc::Receiver<()>,
723}
724
725impl<R, W, E> QuicEndpointTask<R, W, E>
726where
727    R: AsyncRead + Unpin,
728    W: AsyncWrite + Unpin,
729    E: StreamRefProtoEndpointWake,
730{
731    async fn run(mut self) -> StreamResult<NotUsed> {
732        loop {
733            if self.read_closed && self.write_shutdown {
734                return Ok(NotUsed);
735            }
736
737            self.drain_outbound()?;
738            if self.read_closed
739                && self.outbound_written
740                && !self.outbound_closed
741                && self.write_buffer.is_empty()
742                && self.recheck_outbound
743            {
744                return Err(StreamError::Failed(
745                    "StreamRefs QUIC peer closed before remote terminal".to_owned(),
746                ));
747            }
748            if self.outbound_closed && self.write_buffer.is_empty() && !self.write_shutdown {
749                self.writer.shutdown().await.map_err(io_error)?;
750                self.write_shutdown = true;
751                continue;
752            }
753            tokio::select! {
754                biased;
755                wake = self.wake_receiver.recv(), if !self.outbound_closed => {
756                    if wake.is_none() {
757                        self.drain_outbound()?;
758                    }
759                }
760                _ = tokio::time::sleep(STREAM_REF_OUTBOUND_RECHECK_INTERVAL), if self.recheck_outbound && !self.outbound_closed && self.write_buffer.is_empty() => {
761                    self.drain_outbound()?;
762                }
763                written = self.writer.write(&self.write_buffer), if !self.write_buffer.is_empty() => {
764                    self.handle_written(written)?;
765                }
766                read = self.reader.read(&mut self.read_buffer), if !self.read_closed => {
767                    match read {
768                        Ok(0) => self.handle_eof()?,
769                        Ok(read) => {
770                            self.feed_read_buffer(read)?;
771                            self.drain_outbound()?;
772                        }
773                        Err(error) => {
774                            let error = io_error(error);
775                            if self.write_shutdown && is_quic_teardown_loss(&error) {
776                                return Ok(NotUsed);
777                            }
778                            return Err(error);
779                        }
780                    }
781                }
782            }
783        }
784    }
785
786    fn drain_outbound(&mut self) -> StreamResult<()> {
787        self.recheck_outbound = false;
788        while !self.outbound_closed && self.write_buffer.len() < MAX_STREAM_REF_FRAME_BYTES {
789            match self
790                .endpoint
791                .try_next_outbound(STREAM_REF_OUTBOUND_BATCH_FRAMES, MAX_STREAM_REF_FRAME_BYTES)
792            {
793                StreamRefOutboundPoll::Ready(Ok(outbound)) => {
794                    encode_carrier_outbound_into(&outbound, &mut self.encode_buffer)?;
795                    let encoded_len = self.encode_buffer.len();
796                    if encoded_len == 0 {
797                        continue;
798                    }
799                    if self.diagnostics.is_some() {
800                        self.pending_diagnostics.push_back(PendingDiagnostic {
801                            remaining: encoded_len,
802                            counts: outbound_counts(&outbound),
803                        });
804                    }
805                    self.outbound_written = true;
806                    self.write_buffer.extend_from_slice(&self.encode_buffer);
807                }
808                StreamRefOutboundPoll::Ready(Err(error)) => return Err(error),
809                StreamRefOutboundPoll::Pending => {
810                    self.recheck_outbound =
811                        !self.outbound_written || self.inbound_seen || self.read_closed;
812                    break;
813                }
814                StreamRefOutboundPoll::Closed => {
815                    self.outbound_closed = true;
816                    break;
817                }
818            }
819        }
820        Ok(())
821    }
822
823    fn handle_written(&mut self, written: Result<usize, std::io::Error>) -> StreamResult<()> {
824        match written {
825            Ok(0) => Err(StreamError::Failed(
826                "StreamRefs QUIC stream accepted zero write bytes".to_owned(),
827            )),
828            Ok(written) => {
829                self.write_buffer.advance(written);
830                self.record_written_bytes(written);
831                Ok(())
832            }
833            Err(error) => Err(io_error(error)),
834        }
835    }
836
837    fn record_written_bytes(&mut self, mut written: usize) {
838        let Some(diagnostics) = &self.diagnostics else {
839            return;
840        };
841        while written > 0 {
842            let Some(front) = self.pending_diagnostics.front_mut() else {
843                return;
844            };
845            if written < front.remaining {
846                front.remaining -= written;
847                return;
848            }
849            written -= front.remaining;
850            let counts = front.counts;
851            self.pending_diagnostics.pop_front();
852            diagnostics.record_counts(counts);
853        }
854    }
855
856    fn feed_read_buffer(&mut self, read: usize) -> StreamResult<()> {
857        feed_read_bytes(
858            &mut self.decoder,
859            &self.endpoint,
860            self.read_mode,
861            &mut self.pending_tail,
862            &self.read_buffer[..read],
863        )?;
864        self.inbound_seen = true;
865        Ok(())
866    }
867
868    fn handle_eof(&mut self) -> StreamResult<()> {
869        if !self.pending_tail.is_empty() {
870            feed_inbound_chunk(&mut self.decoder, &self.endpoint, &self.pending_tail)?;
871            self.pending_tail.clear();
872        }
873        if self.read_mode.fail_on_eof {
874            self.endpoint
875                .fail_connection(StreamError::AbruptTermination);
876        }
877        self.read_closed = true;
878        self.recheck_outbound = true;
879        Ok(())
880    }
881}
882
883async fn run_stream_ref_endpoint_tcp_task<E>(
884    stream: TcpStream,
885    endpoint: E,
886    diagnostics: Option<StreamRefProtocolDiagnostics>,
887) -> StreamResult<NotUsed>
888where
889    E: StreamRefProtoEndpointWake,
890{
891    let (wake_sender, wake_receiver) = tokio_mpsc::channel(1);
892    endpoint.install_outbound_wake(wake_sender.clone());
893    let _ = wake_sender.try_send(());
894
895    let result = TcpEndpointTask {
896        stream,
897        endpoint: endpoint.clone(),
898        diagnostics,
899        read_mode: CarrierReadMode::new(STREAM_REF_TCP_CHUNK_SIZE, true, true),
900        decoder: FrameDecoder::default(),
901        read_buffer: BytesMut::with_capacity(STREAM_REF_TCP_CHUNK_SIZE),
902        pending_tail: Vec::with_capacity(STREAM_REF_TCP_CHUNK_SIZE),
903        write_buffer: BytesMut::with_capacity(STREAM_REF_TCP_CHUNK_SIZE),
904        encode_buffer: Vec::with_capacity(STREAM_REF_TCP_CHUNK_SIZE),
905        pending_diagnostics: VecDeque::new(),
906        outbound_closed: false,
907        write_shutdown: false,
908        wake_receiver,
909    }
910    .run()
911    .await;
912
913    endpoint.clear_outbound_wake();
914    if let Err(error) = &result {
915        endpoint.fail_connection(error.clone());
916    }
917    result
918}
919
920struct TcpEndpointTask<E>
921where
922    E: StreamRefProtoEndpointWake,
923{
924    stream: TcpStream,
925    endpoint: E,
926    diagnostics: Option<StreamRefProtocolDiagnostics>,
927    read_mode: CarrierReadMode,
928    decoder: FrameDecoder,
929    read_buffer: BytesMut,
930    pending_tail: Vec<u8>,
931    write_buffer: BytesMut,
932    encode_buffer: Vec<u8>,
933    pending_diagnostics: VecDeque<PendingDiagnostic>,
934    outbound_closed: bool,
935    write_shutdown: bool,
936    wake_receiver: tokio_mpsc::Receiver<()>,
937}
938
939impl<E> TcpEndpointTask<E>
940where
941    E: StreamRefProtoEndpointWake,
942{
943    async fn run(mut self) -> StreamResult<NotUsed> {
944        self.stream.set_nodelay(true).map_err(io_error)?;
945        loop {
946            self.drain_outbound()?;
947            if !self.write_buffer.is_empty() || (self.outbound_closed && !self.write_shutdown) {
948                self.flush_write_buffer().await?;
949            }
950
951            tokio::select! {
952                biased;
953                wake = self.wake_receiver.recv() => {
954                    if wake.is_none() && !self.outbound_closed {
955                        self.drain_outbound()?;
956                    }
957                }
958                ready = self.stream.readable() => {
959                    ready.map_err(io_error)?;
960                    if self.read_available()? {
961                        return Ok(NotUsed);
962                    }
963                }
964                ready = self.stream.writable(), if !self.write_buffer.is_empty() || (self.outbound_closed && !self.write_shutdown) => {
965                    ready.map_err(io_error)?;
966                    self.flush_ready_write_buffer()?;
967                }
968            }
969        }
970    }
971
972    fn drain_outbound(&mut self) -> StreamResult<()> {
973        while !self.outbound_closed && self.write_buffer.len() < MAX_STREAM_REF_FRAME_BYTES {
974            match self
975                .endpoint
976                .try_next_outbound(STREAM_REF_OUTBOUND_BATCH_FRAMES, MAX_STREAM_REF_FRAME_BYTES)
977            {
978                StreamRefOutboundPoll::Ready(Ok(outbound)) => {
979                    encode_carrier_outbound_into(&outbound, &mut self.encode_buffer)?;
980                    let encoded_len = self.encode_buffer.len();
981                    if encoded_len == 0 {
982                        continue;
983                    }
984                    if self.diagnostics.is_some() {
985                        self.pending_diagnostics.push_back(PendingDiagnostic {
986                            remaining: encoded_len,
987                            counts: outbound_counts(&outbound),
988                        });
989                    }
990                    self.write_buffer.extend_from_slice(&self.encode_buffer);
991                }
992                StreamRefOutboundPoll::Ready(Err(error)) => return Err(error),
993                StreamRefOutboundPoll::Pending => break,
994                StreamRefOutboundPoll::Closed => {
995                    self.outbound_closed = true;
996                    break;
997                }
998            }
999        }
1000        Ok(())
1001    }
1002
1003    async fn flush_write_buffer(&mut self) -> StreamResult<()> {
1004        if !self.write_buffer.is_empty() {
1005            self.stream.writable().await.map_err(io_error)?;
1006            self.flush_ready_write_buffer()?;
1007        }
1008        if self.outbound_closed && self.write_buffer.is_empty() && !self.write_shutdown {
1009            self.stream.shutdown().await.map_err(io_error)?;
1010            self.write_shutdown = true;
1011        }
1012        Ok(())
1013    }
1014
1015    fn flush_ready_write_buffer(&mut self) -> StreamResult<()> {
1016        while !self.write_buffer.is_empty() {
1017            match self.stream.try_write(&self.write_buffer) {
1018                Ok(0) => {
1019                    return Err(StreamError::Failed(
1020                        "StreamRefs TCP socket accepted zero write bytes".to_owned(),
1021                    ));
1022                }
1023                Ok(written) => {
1024                    self.write_buffer.advance(written);
1025                    self.record_written_bytes(written);
1026                }
1027                Err(error) if error.kind() == std::io::ErrorKind::WouldBlock => return Ok(()),
1028                Err(error) => return Err(io_error(error)),
1029            }
1030        }
1031
1032        Ok(())
1033    }
1034
1035    fn record_written_bytes(&mut self, mut written: usize) {
1036        let Some(diagnostics) = &self.diagnostics else {
1037            return;
1038        };
1039        while written > 0 {
1040            let Some(front) = self.pending_diagnostics.front_mut() else {
1041                return;
1042            };
1043            if written < front.remaining {
1044                front.remaining -= written;
1045                return;
1046            }
1047            written -= front.remaining;
1048            let counts = front.counts;
1049            self.pending_diagnostics.pop_front();
1050            diagnostics.record_counts(counts);
1051        }
1052    }
1053
1054    fn read_available(&mut self) -> StreamResult<bool> {
1055        loop {
1056            self.read_buffer.reserve(self.read_mode.chunk_size);
1057            match self.stream.try_read_buf(&mut self.read_buffer) {
1058                Ok(0) => return self.handle_eof(),
1059                Ok(_) => {
1060                    feed_read_bytes(
1061                        &mut self.decoder,
1062                        &self.endpoint,
1063                        self.read_mode,
1064                        &mut self.pending_tail,
1065                        &self.read_buffer,
1066                    )?;
1067                    self.read_buffer.clear();
1068                    self.drain_outbound()?;
1069                }
1070                Err(error) if error.kind() == std::io::ErrorKind::WouldBlock => return Ok(false),
1071                Err(error) => return Err(io_error(error)),
1072            }
1073        }
1074    }
1075
1076    fn handle_eof(&mut self) -> StreamResult<bool> {
1077        if !self.pending_tail.is_empty() {
1078            feed_inbound_chunk(&mut self.decoder, &self.endpoint, &self.pending_tail)?;
1079            self.pending_tail.clear();
1080        }
1081        if self.read_mode.fail_on_eof {
1082            self.endpoint
1083                .fail_connection(StreamError::AbruptTermination);
1084        }
1085        Ok(true)
1086    }
1087}
1088
1089fn feed_read_bytes<E>(
1090    decoder: &mut FrameDecoder,
1091    endpoint: &E,
1092    read_mode: CarrierReadMode,
1093    pending_tail: &mut Vec<u8>,
1094    read_buffer: &[u8],
1095) -> StreamResult<()>
1096where
1097    E: StreamRefProtoEndpoint,
1098{
1099    if read_mode.emit_available {
1100        if !pending_tail.is_empty() {
1101            pending_tail.extend_from_slice(read_buffer);
1102            feed_inbound_chunk(decoder, endpoint, pending_tail)?;
1103            pending_tail.clear();
1104            return Ok(());
1105        }
1106        return feed_inbound_chunk(decoder, endpoint, read_buffer);
1107    }
1108
1109    let mut offset = 0;
1110    if !pending_tail.is_empty() {
1111        let needed = read_mode.chunk_size - pending_tail.len();
1112        let take = needed.min(read_buffer.len());
1113        pending_tail.extend_from_slice(&read_buffer[..take]);
1114        offset += take;
1115        if pending_tail.len() == read_mode.chunk_size {
1116            feed_inbound_chunk(decoder, endpoint, pending_tail)?;
1117            pending_tail.clear();
1118        }
1119    }
1120
1121    while offset + read_mode.chunk_size <= read_buffer.len() {
1122        let next = offset + read_mode.chunk_size;
1123        feed_inbound_chunk(decoder, endpoint, &read_buffer[offset..next])?;
1124        offset = next;
1125    }
1126
1127    if offset < read_buffer.len() {
1128        pending_tail.extend_from_slice(&read_buffer[offset..]);
1129    }
1130    Ok(())
1131}
1132
1133fn feed_inbound_chunk<E>(decoder: &mut FrameDecoder, endpoint: &E, chunk: &[u8]) -> StreamResult<()>
1134where
1135    E: StreamRefProtoEndpoint,
1136{
1137    decoder.push_chunk(chunk, endpoint)
1138}
1139
1140fn bind_tcp_listener<A>(addr: A) -> StreamResult<(TcpListener, StreamRefTcpBinding, Handle)>
1141where
1142    A: ToSocketAddrs + Send + 'static,
1143{
1144    let runtime = stream_ref_tcp_runtime()?;
1145    let listener = runtime
1146        .block_on(async { TcpListener::bind(addr).await })
1147        .map_err(io_error)?;
1148    let local_addr = listener.local_addr().map_err(io_error)?;
1149    Ok((
1150        listener,
1151        StreamRefTcpBinding { local_addr },
1152        runtime.handle().clone(),
1153    ))
1154}
1155
1156fn connect_tcp_stream<A>(addr: A) -> StreamResult<(TcpStream, Handle)>
1157where
1158    A: ToSocketAddrs + Send + 'static,
1159{
1160    let runtime = stream_ref_tcp_runtime()?;
1161    let stream = runtime
1162        .block_on(async { TcpStream::connect(addr).await })
1163        .map_err(io_error)?;
1164    stream.set_nodelay(true).map_err(io_error)?;
1165    Ok((stream, runtime.handle().clone()))
1166}
1167
1168fn stream_ref_tcp_runtime() -> StreamResult<&'static Runtime> {
1169    static RUNTIME: OnceLock<Result<Runtime, String>> = OnceLock::new();
1170    match RUNTIME.get_or_init(|| {
1171        tokio::runtime::Builder::new_multi_thread()
1172            .thread_name("datum-streamref-tcp")
1173            .enable_all()
1174            .build()
1175            .map_err(|error| error.to_string())
1176    }) {
1177        Ok(runtime) => Ok(runtime),
1178        Err(error) => Err(StreamError::Failed(format!(
1179            "failed to start StreamRefs TCP runtime: {error}"
1180        ))),
1181    }
1182}
1183
1184fn current_tokio_handle() -> StreamResult<Handle> {
1185    Handle::try_current().map_err(|error| {
1186        StreamError::Failed(format!(
1187            "StreamRefs TCP stream helper requires a current Tokio runtime: {error}"
1188        ))
1189    })
1190}
1191
1192fn io_error(error: std::io::Error) -> StreamError {
1193    StreamError::Failed(error.to_string())
1194}
1195
1196fn is_quic_teardown_loss(error: &StreamError) -> bool {
1197    matches!(error, StreamError::Failed(message) if message == "connection lost")
1198}
1199
1200fn spawn_endpoint_task<F>(handle: &Handle, run: F) -> EndpointTaskCompletion
1201where
1202    F: Future<Output = StreamResult<NotUsed>> + Send + 'static,
1203{
1204    let (sender, receiver) = mpsc::channel();
1205    let task = handle.spawn(async move {
1206        let result = run.await;
1207        let _ = sender.send(result);
1208    });
1209    EndpointTaskCompletion {
1210        receiver,
1211        task: Some(task),
1212    }
1213}
1214
1215fn encode_carrier_outbound_into(
1216    outbound: &StreamRefOutbound,
1217    bytes: &mut Vec<u8>,
1218) -> StreamResult<()> {
1219    bytes.clear();
1220    match outbound {
1221        StreamRefOutbound::Frame(frame) => append_protobuf_carrier_frame(frame, bytes)?,
1222        StreamRefOutbound::SequencedBatch(batch) => {
1223            append_compact_payload_batch(batch, bytes)?;
1224        }
1225    }
1226    Ok(())
1227}
1228
1229#[cfg(test)]
1230fn encode_carrier_frames(frames: &[StreamRefFrame]) -> StreamResult<Vec<u8>> {
1231    let mut bytes = Vec::new();
1232    let mut index = 0;
1233    while index < frames.len() {
1234        if sequenced_on_next(&frames[index]).is_some() {
1235            let end = sequenced_run_end(frames, index);
1236            append_compact_sequenced_batches(&frames[index..end], &mut bytes)?;
1237            index = end;
1238        } else {
1239            append_protobuf_carrier_frame(&frames[index], &mut bytes)?;
1240            index += 1;
1241        }
1242    }
1243    Ok(bytes)
1244}
1245
1246fn append_compact_payload_batch(
1247    batch: &StreamRefPayloadBatch,
1248    bytes: &mut Vec<u8>,
1249) -> StreamResult<()> {
1250    let mut start = 0;
1251    while start < batch.count() {
1252        let mut end = start;
1253        let mut payload_len = COMPACT_BATCH_HEADER_BYTES;
1254        while end < batch.count() {
1255            let element_len = COMPACT_BATCH_ELEMENT_LEN_BYTES
1256                .checked_add(batch.payload_len(end))
1257                .ok_or(StreamError::LimitExceeded {
1258                    max: MAX_STREAM_REF_FRAME_BYTES as u64,
1259                })?;
1260            let next_payload_len =
1261                payload_len
1262                    .checked_add(element_len)
1263                    .ok_or(StreamError::LimitExceeded {
1264                        max: MAX_STREAM_REF_FRAME_BYTES as u64,
1265                    })?;
1266            if end > start
1267                && (next_payload_len > MAX_STREAM_REF_FRAME_BYTES
1268                    || end - start >= u16::MAX as usize)
1269            {
1270                break;
1271            }
1272            if next_payload_len > MAX_STREAM_REF_FRAME_BYTES {
1273                return Err(StreamError::LimitExceeded {
1274                    max: MAX_STREAM_REF_FRAME_BYTES as u64,
1275                });
1276            }
1277            payload_len = next_payload_len;
1278            end += 1;
1279        }
1280        append_compact_payload_batch_slice(batch, start, end, payload_len, bytes)?;
1281        start = end;
1282    }
1283    Ok(())
1284}
1285
1286fn append_compact_payload_batch_slice(
1287    batch: &StreamRefPayloadBatch,
1288    start: usize,
1289    end: usize,
1290    payload_len: usize,
1291    bytes: &mut Vec<u8>,
1292) -> StreamResult<()> {
1293    let payload_len = u32::try_from(payload_len).map_err(|_| StreamError::LimitExceeded {
1294        max: MAX_STREAM_REF_FRAME_BYTES as u64,
1295    })?;
1296    let count = u16::try_from(end - start).map_err(|_| StreamError::LimitExceeded {
1297        max: u16::MAX as u64,
1298    })?;
1299    let first_seq = batch
1300        .first_seq_nr()
1301        .checked_add(start as u64)
1302        .ok_or_else(|| StreamError::Failed("compact StreamRefs seq_nr overflow".to_owned()))?;
1303    bytes.extend((COMPACT_FRAME_FLAG | payload_len).to_be_bytes());
1304    bytes.push(COMPACT_FRAME_VERSION);
1305    bytes.push(COMPACT_SEQUENCED_ON_NEXT_BATCH);
1306    bytes.extend(batch.stream_ref_id().to_bytes());
1307    bytes.extend(first_seq.to_be_bytes());
1308    bytes.extend(count.to_be_bytes());
1309    for index in start..end {
1310        let payload = batch.payload(index);
1311        let payload_len = u32::try_from(payload.len()).map_err(|_| StreamError::LimitExceeded {
1312            max: u32::MAX as u64,
1313        })?;
1314        bytes.extend(payload_len.to_be_bytes());
1315        bytes.extend(payload);
1316    }
1317    Ok(())
1318}
1319
1320fn append_protobuf_carrier_frame(frame: &StreamRefFrame, bytes: &mut Vec<u8>) -> StreamResult<()> {
1321    let payload = frame.encode_to_vec();
1322    let len = u32::try_from(payload.len()).map_err(|_| StreamError::LimitExceeded {
1323        max: COMPACT_FRAME_LEN_MASK as u64,
1324    })?;
1325    if payload.len() > MAX_STREAM_REF_FRAME_BYTES || len > COMPACT_FRAME_LEN_MASK {
1326        return Err(StreamError::LimitExceeded {
1327            max: MAX_STREAM_REF_FRAME_BYTES as u64,
1328        });
1329    }
1330    bytes.extend(len.to_be_bytes());
1331    bytes.extend(payload);
1332    Ok(())
1333}
1334
1335#[cfg(test)]
1336fn append_compact_sequenced_batches(
1337    frames: &[StreamRefFrame],
1338    bytes: &mut Vec<u8>,
1339) -> StreamResult<()> {
1340    let mut start = 0;
1341    while start < frames.len() {
1342        let mut end = start;
1343        let mut payload_len = COMPACT_BATCH_HEADER_BYTES;
1344        while end < frames.len() {
1345            let (_, payload) = sequenced_on_next(&frames[end]).expect("sequenced frame");
1346            let element_len = COMPACT_BATCH_ELEMENT_LEN_BYTES
1347                .checked_add(payload.len())
1348                .ok_or(StreamError::LimitExceeded {
1349                    max: MAX_STREAM_REF_FRAME_BYTES as u64,
1350                })?;
1351            let next_payload_len =
1352                payload_len
1353                    .checked_add(element_len)
1354                    .ok_or(StreamError::LimitExceeded {
1355                        max: MAX_STREAM_REF_FRAME_BYTES as u64,
1356                    })?;
1357            if end > start
1358                && (next_payload_len > MAX_STREAM_REF_FRAME_BYTES
1359                    || end - start >= u16::MAX as usize)
1360            {
1361                break;
1362            }
1363            if next_payload_len > MAX_STREAM_REF_FRAME_BYTES {
1364                return Err(StreamError::LimitExceeded {
1365                    max: MAX_STREAM_REF_FRAME_BYTES as u64,
1366                });
1367            }
1368            payload_len = next_payload_len;
1369            end += 1;
1370        }
1371        append_compact_sequenced_batch(&frames[start..end], payload_len, bytes)?;
1372        start = end;
1373    }
1374    Ok(())
1375}
1376
1377#[cfg(test)]
1378fn append_compact_sequenced_batch(
1379    frames: &[StreamRefFrame],
1380    payload_len: usize,
1381    bytes: &mut Vec<u8>,
1382) -> StreamResult<()> {
1383    let (first_seq, _) = sequenced_on_next(&frames[0]).expect("sequenced frame");
1384    let payload_len = u32::try_from(payload_len).map_err(|_| StreamError::LimitExceeded {
1385        max: MAX_STREAM_REF_FRAME_BYTES as u64,
1386    })?;
1387    let count = u16::try_from(frames.len()).map_err(|_| StreamError::LimitExceeded {
1388        max: u16::MAX as u64,
1389    })?;
1390    bytes.extend((COMPACT_FRAME_FLAG | payload_len).to_be_bytes());
1391    bytes.push(COMPACT_FRAME_VERSION);
1392    bytes.push(COMPACT_SEQUENCED_ON_NEXT_BATCH);
1393    bytes.extend(frames[0].stream_ref_id.to_bytes());
1394    bytes.extend(first_seq.to_be_bytes());
1395    bytes.extend(count.to_be_bytes());
1396    for frame in frames {
1397        let (_, payload) = sequenced_on_next(frame).expect("sequenced frame");
1398        let payload_len = u32::try_from(payload.len()).map_err(|_| StreamError::LimitExceeded {
1399            max: u32::MAX as u64,
1400        })?;
1401        bytes.extend(payload_len.to_be_bytes());
1402        bytes.extend(payload);
1403    }
1404    Ok(())
1405}
1406
1407#[cfg(test)]
1408fn sequenced_run_end(frames: &[StreamRefFrame], start: usize) -> usize {
1409    let mut end = start + 1;
1410    while end < frames.len() {
1411        let Some((previous_seq, _)) = sequenced_on_next(&frames[end - 1]) else {
1412            break;
1413        };
1414        let Some((next_seq, _)) = sequenced_on_next(&frames[end]) else {
1415            break;
1416        };
1417        if frames[end].stream_ref_id != frames[start].stream_ref_id
1418            || next_seq != previous_seq.saturating_add(1)
1419        {
1420            break;
1421        }
1422        end += 1;
1423    }
1424    end
1425}
1426
1427#[cfg(test)]
1428fn sequenced_on_next(frame: &StreamRefFrame) -> Option<(u64, &[u8])> {
1429    match &frame.message {
1430        StreamRefMessage::SequencedOnNext { seq_nr, payload } => {
1431            Some((*seq_nr, payload.bytes.as_slice()))
1432        }
1433        _ => None,
1434    }
1435}
1436
1437#[derive(Default)]
1438struct FrameDecoder {
1439    buffer: BytesMut,
1440    offset: usize,
1441}
1442
1443impl FrameDecoder {
1444    fn push_chunk<E>(&mut self, chunk: &[u8], endpoint: &E) -> StreamResult<()>
1445    where
1446        E: StreamRefProtoEndpoint,
1447    {
1448        self.buffer.extend_from_slice(chunk);
1449        while let Some(header) = self.peek_header()? {
1450            if self.buffer.len().saturating_sub(self.offset) < FRAME_LEN_BYTES + header.len {
1451                break;
1452            }
1453            let payload_start = self.offset + FRAME_LEN_BYTES;
1454            let payload_end = payload_start + header.len;
1455            let payload = &self.buffer[payload_start..payload_end];
1456            match header.kind {
1457                CarrierFrameKind::Protobuf => {
1458                    endpoint.handle_frame(StreamRefFrame::decode(payload)?)?;
1459                }
1460                CarrierFrameKind::Compact => {
1461                    decode_compact_carrier_frame(payload, endpoint)?;
1462                }
1463            }
1464            self.offset = payload_end;
1465        }
1466        if self.offset > 0 && (self.offset == self.buffer.len() || self.offset >= 64 * 1024) {
1467            self.buffer.advance(self.offset);
1468            self.offset = 0;
1469        }
1470        Ok(())
1471    }
1472
1473    fn peek_header(&self) -> StreamResult<Option<CarrierFrameHeader>> {
1474        if self.buffer.len().saturating_sub(self.offset) < FRAME_LEN_BYTES {
1475            return Ok(None);
1476        }
1477        let len = self.buffer[self.offset..self.offset + FRAME_LEN_BYTES]
1478            .try_into()
1479            .expect("frame header length");
1480        let raw_len = u32::from_be_bytes(len);
1481        let kind = if raw_len & COMPACT_FRAME_FLAG == 0 {
1482            CarrierFrameKind::Protobuf
1483        } else {
1484            CarrierFrameKind::Compact
1485        };
1486        let len = (raw_len & COMPACT_FRAME_LEN_MASK) as usize;
1487        if len > MAX_STREAM_REF_FRAME_BYTES {
1488            return Err(StreamError::LimitExceeded {
1489                max: MAX_STREAM_REF_FRAME_BYTES as u64,
1490            });
1491        }
1492        Ok(Some(CarrierFrameHeader { kind, len }))
1493    }
1494}
1495
1496#[derive(Clone, Copy)]
1497struct CarrierFrameHeader {
1498    kind: CarrierFrameKind,
1499    len: usize,
1500}
1501
1502#[derive(Clone, Copy)]
1503enum CarrierFrameKind {
1504    Protobuf,
1505    Compact,
1506}
1507
1508fn decode_compact_carrier_frame<E>(payload: &[u8], endpoint: &E) -> StreamResult<()>
1509where
1510    E: StreamRefProtoEndpoint,
1511{
1512    if payload.len() < COMPACT_BATCH_HEADER_BYTES {
1513        return Err(StreamError::Failed(
1514            "compact StreamRefs carrier frame too short".to_owned(),
1515        ));
1516    }
1517    let version = payload[0];
1518    if version != COMPACT_FRAME_VERSION {
1519        return Err(StreamError::Failed(format!(
1520            "unsupported compact StreamRefs carrier frame version: {version}"
1521        )));
1522    }
1523    let kind = payload[1];
1524    if kind != COMPACT_SEQUENCED_ON_NEXT_BATCH {
1525        return Err(StreamError::Failed(format!(
1526            "unsupported compact StreamRefs carrier frame kind: {kind}"
1527        )));
1528    }
1529    let stream_ref_id = StreamRefId::from_bytes(&payload[2..18])?;
1530    let first_seq = u64::from_be_bytes(payload[18..26].try_into().expect("seq len"));
1531    let count = u16::from_be_bytes(payload[26..28].try_into().expect("count len")) as usize;
1532    if count == 0 {
1533        return Err(StreamError::Failed(
1534            "compact StreamRefs carrier batch is empty".to_owned(),
1535        ));
1536    }
1537
1538    let mut offset = COMPACT_BATCH_HEADER_BYTES;
1539    let mut payloads = Vec::with_capacity(count);
1540    for index in 0..count {
1541        if payload.len().saturating_sub(offset) < COMPACT_BATCH_ELEMENT_LEN_BYTES {
1542            return Err(StreamError::Failed(
1543                "compact StreamRefs carrier batch has truncated payload length".to_owned(),
1544            ));
1545        }
1546        let payload_len = u32::from_be_bytes(
1547            payload[offset..offset + COMPACT_BATCH_ELEMENT_LEN_BYTES]
1548                .try_into()
1549                .expect("payload len"),
1550        ) as usize;
1551        offset += COMPACT_BATCH_ELEMENT_LEN_BYTES;
1552        if payload.len().saturating_sub(offset) < payload_len {
1553            return Err(StreamError::Failed(
1554                "compact StreamRefs carrier batch has truncated payload".to_owned(),
1555            ));
1556        }
1557        first_seq
1558            .checked_add(index as u64)
1559            .ok_or_else(|| StreamError::Failed("compact StreamRefs seq_nr overflow".to_owned()))?;
1560        payloads.push(&payload[offset..offset + payload_len]);
1561        offset += payload_len;
1562    }
1563    if offset != payload.len() {
1564        return Err(StreamError::Failed(
1565            "compact StreamRefs carrier batch has trailing bytes".to_owned(),
1566        ));
1567    }
1568    endpoint.handle_sequenced_on_next_batch(stream_ref_id, first_seq, &payloads)
1569}
1570
1571#[cfg(test)]
1572mod tests {
1573    use super::*;
1574    use std::sync::{Arc, Mutex};
1575
1576    #[derive(Clone)]
1577    struct RecordingEndpoint {
1578        stream_ref_id: StreamRefId,
1579        frames: Arc<Mutex<Vec<StreamRefFrame>>>,
1580    }
1581
1582    impl RecordingEndpoint {
1583        fn new(stream_ref_id: StreamRefId) -> Self {
1584            Self {
1585                stream_ref_id,
1586                frames: Arc::new(Mutex::new(Vec::new())),
1587            }
1588        }
1589
1590        fn frames(&self) -> Vec<StreamRefFrame> {
1591            self.frames.lock().expect("recording endpoint").clone()
1592        }
1593    }
1594
1595    impl StreamRefProtoEndpoint for RecordingEndpoint {
1596        fn stream_ref_id(&self) -> StreamRefId {
1597            self.stream_ref_id
1598        }
1599
1600        fn next_frame(&self) -> Option<StreamResult<StreamRefFrame>> {
1601            None
1602        }
1603
1604        fn handle_frame(&self, frame: StreamRefFrame) -> StreamResult<()> {
1605            self.frames.lock().expect("recording endpoint").push(frame);
1606            Ok(())
1607        }
1608
1609        fn fail_connection(&self, _error: StreamError) {}
1610    }
1611
1612    #[test]
1613    fn carrier_frame_decoder_reassembles_split_frames() {
1614        let frame = StreamRefFrame::new(
1615            StreamRefId::from_u128(1),
1616            datum::StreamRefMessage::CumulativeDemand { seq_nr: 32 },
1617        );
1618        let bytes = encode_carrier_frames(std::slice::from_ref(&frame)).unwrap();
1619        let split = bytes.len() / 2;
1620        let mut decoder = FrameDecoder::default();
1621        let endpoint = RecordingEndpoint::new(StreamRefId::from_u128(1));
1622
1623        decoder.push_chunk(&bytes[..split], &endpoint).unwrap();
1624        assert!(endpoint.frames().is_empty());
1625        decoder.push_chunk(&bytes[split..], &endpoint).unwrap();
1626        assert_eq!(endpoint.frames(), vec![frame]);
1627    }
1628
1629    #[test]
1630    fn compact_carrier_batch_round_trips_sequenced_frames() {
1631        let frames = (0_u64..3)
1632            .map(|seq_nr| {
1633                StreamRefFrame::new(
1634                    StreamRefId::from_u128(7),
1635                    datum::StreamRefMessage::SequencedOnNext {
1636                        seq_nr,
1637                        payload: datum::StreamRefPayloadBytes {
1638                            bytes: seq_nr.to_be_bytes().to_vec(),
1639                        },
1640                    },
1641                )
1642            })
1643            .collect::<Vec<_>>();
1644        let bytes = encode_carrier_frames(&frames).unwrap();
1645        let header = u32::from_be_bytes(bytes[..FRAME_LEN_BYTES].try_into().unwrap());
1646        assert_ne!(header & COMPACT_FRAME_FLAG, 0);
1647
1648        let mut decoder = FrameDecoder::default();
1649        let endpoint = RecordingEndpoint::new(StreamRefId::from_u128(7));
1650        decoder.push_chunk(&bytes, &endpoint).unwrap();
1651        assert_eq!(endpoint.frames(), frames);
1652    }
1653
1654    #[test]
1655    fn compact_carrier_batch_reassembles_split_frames() {
1656        let frames = (4_u64..8)
1657            .map(|seq_nr| {
1658                StreamRefFrame::new(
1659                    StreamRefId::from_u128(8),
1660                    datum::StreamRefMessage::SequencedOnNext {
1661                        seq_nr,
1662                        payload: datum::StreamRefPayloadBytes {
1663                            bytes: vec![seq_nr as u8],
1664                        },
1665                    },
1666                )
1667            })
1668            .collect::<Vec<_>>();
1669        let bytes = encode_carrier_frames(&frames).unwrap();
1670        let split = FRAME_LEN_BYTES + 5;
1671        let mut decoder = FrameDecoder::default();
1672        let endpoint = RecordingEndpoint::new(StreamRefId::from_u128(8));
1673
1674        decoder.push_chunk(&bytes[..split], &endpoint).unwrap();
1675        assert!(endpoint.frames().is_empty());
1676        decoder.push_chunk(&bytes[split..], &endpoint).unwrap();
1677        assert_eq!(endpoint.frames(), frames);
1678    }
1679}