Skip to main content

rtc_sctp/queue/
reassembly_queue.rs

1use crate::StreamId;
2use crate::chunk::chunk_payload_data::{ChunkPayloadData, PayloadProtocolIdentifier};
3use crate::util::*;
4use shared::error::{Error, Result};
5
6use bytes::{Bytes, BytesMut};
7use std::collections::VecDeque;
8use std::time::Instant;
9
10/// A chunk of data from the stream
11#[derive(Debug, PartialEq)]
12pub struct Chunk {
13    /// The contents of the chunk
14    pub bytes: Bytes,
15}
16
17/// Chunks is a set of chunks that share the same SSN
18#[derive(Debug, Clone)]
19pub struct Chunks {
20    /// used only with the ordered chunks
21    pub ssn: u16,
22    /// The payload protocol identifier shared by every fragment of this message.
23    pub ppi: PayloadProtocolIdentifier,
24    /// The fragments, in order, that make up one complete message.
25    pub chunks: Vec<ChunkPayloadData>,
26    offset: usize,
27    index: usize,
28    timestamp: Instant,
29}
30
31impl Chunks {
32    /// Whether the reassembled message carries no bytes.
33    pub fn is_empty(&self) -> bool {
34        self.len() == 0
35    }
36
37    /// The total length in bytes of the reassembled message.
38    pub fn len(&self) -> usize {
39        let mut l = 0;
40        for c in &self.chunks {
41            l += c.user_data.len();
42        }
43        l
44    }
45
46    /// Reassemble all fragments into a single freshly-allocated, exactly-sized
47    /// buffer with one copy.
48    ///
49    /// Unlike [`read`](Self::read), this does not round-trip through a
50    /// caller-provided scratch buffer, eliminating one full-payload copy on the
51    /// receive path (the reassembled message would otherwise be copied into the
52    /// scratch buffer and then again into the delivered `BytesMut`). Returns
53    /// [`Error::ErrShortBuffer`] when the message exceeds `max_len`, mirroring
54    /// `read`'s bound so oversized inbound messages are still rejected.
55    pub fn to_payload(&self, max_len: usize) -> Result<BytesMut> {
56        let total = self.len();
57        if total > max_len {
58            return Err(Error::ErrShortBuffer);
59        }
60        let mut buf = BytesMut::with_capacity(total);
61        for c in &self.chunks {
62            buf.extend_from_slice(&c.user_data);
63        }
64        Ok(buf)
65    }
66
67    /// Concatenates every fragment into `buf`, returning the number of bytes written.
68    ///
69    /// # Errors
70    ///
71    /// Returns [`Error::ErrShortBuffer`](shared::error::Error::ErrShortBuffer) if `buf` cannot
72    /// hold the whole message; the partial copy is left in place.
73    pub fn read(&self, buf: &mut [u8]) -> Result<usize> {
74        let mut n_written = 0;
75        for c in &self.chunks {
76            let to_copy = c.user_data.len();
77            let n = std::cmp::min(to_copy, buf.len() - n_written);
78            buf[n_written..n_written + n].copy_from_slice(&c.user_data[..n]);
79            n_written += n;
80            if n < to_copy {
81                return Err(Error::ErrShortBuffer);
82            }
83        }
84        Ok(n_written)
85    }
86
87    /// Yields the next slice of the reassembled message, up to `max_length` bytes.
88    ///
89    /// Advances an internal cursor, so repeated calls walk the message; returns `None` once it is
90    /// exhausted.
91    pub fn next(&mut self, max_length: usize) -> Option<Chunk> {
92        if self.index >= self.chunks.len() {
93            return None;
94        }
95
96        let mut buf = BytesMut::with_capacity(max_length);
97
98        let mut n_written = 0;
99        while self.index < self.chunks.len() {
100            let to_copy = self.chunks[self.index].user_data[self.offset..].len();
101            let n = std::cmp::min(to_copy, max_length - n_written);
102            buf.extend_from_slice(&self.chunks[self.index].user_data[self.offset..self.offset + n]);
103            n_written += n;
104            if n < to_copy {
105                self.offset += n;
106                return Some(Chunk {
107                    bytes: buf.freeze(),
108                });
109            }
110            self.index += 1;
111            self.offset = 0;
112        }
113
114        Some(Chunk {
115            bytes: buf.freeze(),
116        })
117    }
118
119    pub(crate) fn new(
120        ssn: u16,
121        ppi: PayloadProtocolIdentifier,
122        chunks: Vec<ChunkPayloadData>,
123    ) -> Self {
124        Chunks {
125            ssn,
126            ppi,
127            chunks,
128            offset: 0,
129            index: 0,
130            timestamp: Instant::now(),
131        }
132    }
133
134    pub(crate) fn push(&mut self, chunk: ChunkPayloadData) -> bool {
135        // Binary-search the insertion point (fragments are kept in TSN order),
136        // which also detects duplicates -- instead of an O(n) dup scan plus a
137        // full re-sort of every fragment on each push.
138        let idx = self.chunks.partition_point(|c| sna32lt(c.tsn, chunk.tsn));
139        if idx < self.chunks.len() && self.chunks[idx].tsn == chunk.tsn {
140            return false;
141        }
142        self.chunks.insert(idx, chunk);
143
144        // Check if we now have a complete set
145        self.is_complete()
146    }
147
148    pub(crate) fn is_complete(&self) -> bool {
149        // Condition for complete set
150        //   0. Has at least one chunk.
151        //   1. Begins with beginningFragment set to true
152        //   2. Ends with endingFragment set to true
153        //   3. TSN monotinically increase by 1 from beginning to end
154
155        // 0.
156        let n_chunks = self.chunks.len();
157        if n_chunks == 0 {
158            return false;
159        }
160
161        // 1.
162        if !self.chunks[0].beginning_fragment {
163            return false;
164        }
165
166        // 2.
167        if !self.chunks[n_chunks - 1].ending_fragment {
168            return false;
169        }
170
171        // 3.
172        let mut last_tsn = 0u32;
173        for (i, c) in self.chunks.iter().enumerate() {
174            if i > 0 {
175                // Fragments must have contiguous TSN
176                // From RFC 4960 Section 3.3.1:
177                //   When a user message is fragmented into multiple chunks, the TSNs are
178                //   used by the receiver to reassemble the message.  This means that the
179                //   TSNs for each fragment of a fragmented user message MUST be strictly
180                //   sequential.
181                if c.tsn != last_tsn + 1 {
182                    // mid or end fragment is missing
183                    return false;
184                }
185            }
186
187            last_tsn = c.tsn;
188        }
189
190        true
191    }
192}
193
194#[derive(Default, Debug)]
195pub(crate) struct ReassemblyQueue {
196    pub(crate) si: StreamId,
197    pub(crate) next_ssn: u16,
198    /// expected SSN for next ordered chunk
199    ///
200    /// `ordered`/`unordered` are consumed strictly from the front by `read`;
201    /// `VecDeque` makes that O(1) instead of `Vec::remove(0)`'s full shift.
202    pub(crate) ordered: VecDeque<Chunks>,
203    pub(crate) unordered: VecDeque<Chunks>,
204    pub(crate) unordered_chunks: Vec<ChunkPayloadData>,
205    pub(crate) n_bytes: usize,
206}
207
208impl ReassemblyQueue {
209    /// From RFC 4960 Sec 6.5:
210    ///   The Stream Sequence Number in all the streams MUST start from 0 when
211    ///   the association is Established.  Also, when the Stream Sequence
212    ///   Number reaches the value 65535 the next Stream Sequence Number MUST
213    ///   be set to 0.
214    pub(crate) fn new(si: StreamId) -> Self {
215        ReassemblyQueue {
216            si,
217            next_ssn: 0, // From RFC 4960 Sec 6.5:
218            ordered: VecDeque::new(),
219            unordered: VecDeque::new(),
220            unordered_chunks: vec![],
221            n_bytes: 0,
222        }
223    }
224
225    pub(crate) fn push(&mut self, chunk: ChunkPayloadData) -> bool {
226        if chunk.stream_identifier != self.si {
227            return false;
228        }
229
230        if chunk.unordered {
231            // First, insert into unordered_chunks array
232            //atomic.AddUint64(&r.n_bytes, uint64(len(chunk.userData)))
233            self.n_bytes += chunk.user_data.len();
234            let idx = self
235                .unordered_chunks
236                .partition_point(|c| sna32lt(c.tsn, chunk.tsn));
237            self.unordered_chunks.insert(idx, chunk);
238
239            // Scan unordered_chunks that are contiguous (in TSN)
240            // If found, append the complete set to the unordered array
241            if let Some(cset) = self.find_complete_unordered_chunk_set() {
242                self.unordered.push_back(cset);
243                return true;
244            }
245
246            false
247        } else {
248            // This is an ordered chunk
249            if sna16lt(chunk.stream_sequence_number, self.next_ssn) {
250                return false;
251            }
252
253            self.n_bytes += chunk.user_data.len();
254
255            // `ordered` is kept sorted by SSN, so binary-search for the chunk
256            // set instead of scanning linearly (O(N) per arriving chunk when
257            // the application drains slower than data arrives).
258            let ssn = chunk.stream_sequence_number;
259            let idx = self.ordered.partition_point(|s| sna16lt(s.ssn, ssn));
260            if let Some(s) = self.ordered.get_mut(idx)
261                && s.ssn == ssn
262            {
263                return s.push(chunk);
264            }
265
266            // If not found, create a new chunkSet and insert it in SSN order
267            // (this branch is only reached for ordered chunks).
268            let mut cset = Chunks::new(ssn, chunk.payload_type, vec![]);
269            let ok = cset.push(chunk);
270            self.ordered.insert(idx, cset);
271
272            ok
273        }
274    }
275
276    pub(crate) fn find_complete_unordered_chunk_set(&mut self) -> Option<Chunks> {
277        let mut start_idx = -1isize;
278        let mut n_chunks = 0usize;
279        let mut last_tsn = 0u32;
280        let mut found = false;
281
282        for (i, c) in self.unordered_chunks.iter().enumerate() {
283            // seek beginning
284            if c.beginning_fragment {
285                start_idx = i as isize;
286                n_chunks = 1;
287                last_tsn = c.tsn;
288
289                if c.ending_fragment {
290                    found = true;
291                    break;
292                }
293                continue;
294            }
295
296            if start_idx < 0 {
297                continue;
298            }
299
300            // Check if contiguous in TSN
301            if c.tsn != last_tsn + 1 {
302                start_idx = -1;
303                continue;
304            }
305
306            last_tsn = c.tsn;
307            n_chunks += 1;
308
309            if c.ending_fragment {
310                found = true;
311                break;
312            }
313        }
314
315        if !found {
316            return None;
317        }
318
319        // Extract the range of chunks
320        let chunks: Vec<ChunkPayloadData> = self
321            .unordered_chunks
322            .drain(start_idx as usize..(start_idx as usize) + n_chunks)
323            .collect();
324        Some(Chunks::new(0, chunks[0].payload_type, chunks))
325    }
326
327    pub(crate) fn is_readable(&self) -> bool {
328        // Check unordered first
329        if !self.unordered.is_empty() {
330            // The chunk sets in self.unordered should all be complete.
331            return true;
332        }
333
334        // Check ordered sets
335        if !self.ordered.is_empty() {
336            let cset = &self.ordered[0];
337            if cset.is_complete() && sna16lte(cset.ssn, self.next_ssn) {
338                return true;
339            }
340        }
341        false
342    }
343
344    fn readable_unordered_chunks(&self) -> Option<&Chunks> {
345        self.unordered.front()
346    }
347
348    fn readable_ordered_chunks(&self) -> Option<&Chunks> {
349        let ordered = self.ordered.front();
350        if let Some(chunks) = ordered {
351            if !chunks.is_complete() {
352                return None;
353            }
354            if sna16gt(chunks.ssn, self.next_ssn) {
355                return None;
356            }
357            Some(chunks)
358        } else {
359            None
360        }
361    }
362
363    pub(crate) fn read(&mut self) -> Option<Chunks> {
364        let chunks = if let (Some(unordered_chunks), Some(ordered_chunks)) = (
365            self.readable_unordered_chunks(),
366            self.readable_ordered_chunks(),
367        ) {
368            if unordered_chunks.timestamp < ordered_chunks.timestamp {
369                self.unordered.pop_front().unwrap()
370            } else {
371                if ordered_chunks.ssn == self.next_ssn {
372                    self.next_ssn = self.next_ssn.wrapping_add(1);
373                }
374                self.ordered.pop_front().unwrap()
375            }
376        } else {
377            // Check unordered first
378            if !self.unordered.is_empty() {
379                self.unordered.pop_front().unwrap()
380            } else if !self.ordered.is_empty() {
381                // Now, check ordered
382                let chunks = &self.ordered[0];
383                if !chunks.is_complete() {
384                    return None;
385                }
386                if sna16gt(chunks.ssn, self.next_ssn) {
387                    return None;
388                }
389                if chunks.ssn == self.next_ssn {
390                    self.next_ssn = self.next_ssn.wrapping_add(1);
391                }
392                self.ordered.pop_front().unwrap()
393            } else {
394                return None;
395            }
396        };
397
398        self.subtract_num_bytes(chunks.len());
399
400        Some(chunks)
401    }
402
403    /// Use last_ssn to locate a chunkSet then remove it if the set has
404    /// not been complete
405    pub(crate) fn forward_tsn_for_ordered(&mut self, last_ssn: u16) {
406        let num_bytes = self
407            .ordered
408            .iter()
409            .filter(|s| sna16lte(s.ssn, last_ssn) && !s.is_complete())
410            .fold(0, |n, s| {
411                n + s.chunks.iter().fold(0, |acc, c| acc + c.user_data.len())
412            });
413        self.subtract_num_bytes(num_bytes);
414
415        self.ordered
416            .retain(|s| !sna16lte(s.ssn, last_ssn) || s.is_complete());
417
418        // Finally, forward next_ssn
419        if sna16lte(self.next_ssn, last_ssn) {
420            self.next_ssn = last_ssn.wrapping_add(1);
421        }
422    }
423
424    /// Remove all fragments in the unordered sets that contains chunks
425    /// equal to or older than `new_cumulative_tsn`.
426    /// We know all sets in the r.unordered are complete ones.
427    /// Just remove chunks that are equal to or older than new_cumulative_tsn
428    /// from the unordered_chunks
429    pub(crate) fn forward_tsn_for_unordered(&mut self, new_cumulative_tsn: u32) {
430        let mut last_idx: isize = -1;
431        for (i, c) in self.unordered_chunks.iter().enumerate() {
432            if sna32gt(c.tsn, new_cumulative_tsn) {
433                break;
434            }
435            last_idx = i as isize;
436        }
437        if last_idx >= 0 {
438            for i in 0..(last_idx + 1) as usize {
439                self.subtract_num_bytes(self.unordered_chunks[i].user_data.len());
440            }
441            self.unordered_chunks.drain(..(last_idx + 1) as usize);
442        }
443    }
444
445    pub(crate) fn subtract_num_bytes(&mut self, n_bytes: usize) {
446        if self.n_bytes >= n_bytes {
447            self.n_bytes -= n_bytes;
448        } else {
449            self.n_bytes = 0;
450        }
451    }
452
453    pub(crate) fn get_num_bytes(&self) -> usize {
454        self.n_bytes
455    }
456}