net-mesh 0.34.0

High-performance, schema-agnostic, backend-agnostic event bus
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
//! Stream window subprotocol — receiver → sender credit grants.
//!
//! Ships over `SUBPROTOCOL_STREAM_WINDOW` on existing encrypted
//! sessions. Each grant carries the receiver's **absolute**
//! cumulative bytes-consumed count on the named stream; the sender
//! reconciles its credit state from that authoritative value.
//!
//! ## Why absolute, not additive
//!
//! An additive grant (`credit_bytes` added to the sender's
//! remaining credit) permanently strands credit when either a data
//! packet OR a grant is dropped on the wire. An absolute
//! `total_consumed` grant is self-healing: every arriving grant
//! carries the receiver's full accounting, so any single lost
//! grant is reconciled by the next one. Lost data packets leave
//! `tx_bytes_sent - total_consumed` elevated until recovery
//! (retransmit for `Reliable`, stream reset for `FireAndForget`),
//! but the sender's credit view converges exactly when the
//! receiver's view does.
//!
//! Wire layout: 16 bytes per message (`u64 stream_id LE` +
//! `u64 total_consumed LE`).

use bytes::{Buf, BufMut};

/// Subprotocol ID for stream-window credit grants.
pub const SUBPROTOCOL_STREAM_WINDOW: u16 = 0x0B00;

/// Subprotocol ID for receiver → sender retransmit NACKs
/// ([`StreamNack`]). Sibling of the window grant: both are small
/// receiver-driven control messages about a stream's progress.
pub const SUBPROTOCOL_STREAM_NACK: u16 = 0x0B01;

/// Subprotocol ID for a sender → receiver stream reset ([`StreamReset`]):
/// the sender's reliable layer gave up retransmitting a gap, so the
/// receiver should fail any pending read on this stream now rather than
/// stall to a timeout (H-3).
pub const SUBPROTOCOL_STREAM_RESET: u16 = 0x0B02;

/// Subprotocol ID for receiver → sender positive SACK-range ACKs
/// ([`StreamAckRanges`]) — STREAM_ACK_BATCHING_AND_RANGES R-1.
/// Emission is capability-gated (`net.reliable.stream_ack_ranges@1`);
/// receivers accept unconditionally.
pub const SUBPROTOCOL_STREAM_ACK: u16 = 0x0B03;

/// Fixed wire size of a [`StreamReset`] in bytes.
pub const STREAM_RESET_SIZE: usize = 8;

/// Fixed wire size in bytes (`stream_id` + `total_consumed` + `ack_seq`).
pub const STREAM_WINDOW_SIZE: usize = 24;

/// Fixed wire size of a [`StreamNack`] in bytes.
pub const STREAM_NACK_SIZE: usize = 24;

/// Fixed header size of a [`StreamAckRanges`] (`stream_id` + `ack_seq`);
/// each range adds [`STREAM_ACK_RANGE_SIZE`] bytes.
pub const STREAM_ACK_HEADER_SIZE: usize = 16;

/// Wire size of one half-open `[start, end)` range in a
/// [`StreamAckRanges`].
pub const STREAM_ACK_RANGE_SIZE: usize = 16;

/// Max ranges carried per [`StreamAckRanges`] message. 16 ranges is
/// 272 wire bytes — comfortably one event inside a batched control
/// frame. The receiver's range index truncates newest-first to this
/// cap; the dropped oldest ranges are exactly the ones the next
/// cumulative-ack advance covers first.
pub const MAX_ACK_RANGES: usize = 16;

/// Receiver → sender credit grant. Authoritative: `total_consumed`
/// is the receiver's cumulative bytes-consumed count on the named
/// stream since it was opened. The sender uses this to recompute
/// `tx_credit_remaining = tx_window - (tx_bytes_sent - total_consumed)`,
/// making the mechanism self-healing against lost grants.
///
/// # Consumer-side validation
///
/// The codec accepts any `total_consumed: u64`. Pre-fix the doc-
/// comment's "self-healing" framing implied no further validation
/// was needed, but the formula
/// `tx_credit_remaining = tx_window - (tx_bytes_sent - total_consumed)`
/// underflows if a malformed or hostile peer sends
/// `total_consumed > tx_bytes_sent`. **The consumer MUST clamp
/// `total_consumed` to its local `tx_bytes_sent` watermark before
/// applying.** `StreamState::apply_authoritative_grant`
/// (`adapter/net/session.rs:1153-1154`) does this today; any
/// future consumer of this codec must do the same. The codec
/// layer cannot do the clamp itself because it doesn't know the
/// sender's local state.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct StreamWindow {
    /// Stream the grant applies to.
    pub stream_id: u64,
    /// Receiver's cumulative consumed-byte count on this stream.
    ///
    /// Consumers MUST clamp this to the local `tx_bytes_sent`
    /// watermark before deriving credit.
    pub total_consumed: u64,
    /// Receiver's cumulative reliable ack — the lowest sequence not yet
    /// contiguously received (`next_expected`). The sender prunes its
    /// retransmit window of everything below this (H-9). 0 for
    /// non-reliable receive streams (nothing to prune).
    pub ack_seq: u64,
}

/// Errors produced by the codec. Shared by all three fixed-size
/// messages in this module, so the expected size is carried per
/// error rather than hardcoded in the message.
#[derive(Debug, thiserror::Error)]
pub enum StreamWindowCodecError {
    /// Buffer shorter than the fixed wire size.
    #[error("truncated stream subprotocol message: {got} bytes (need {need})")]
    Truncated {
        /// Bytes received.
        got: usize,
        /// The message's fixed wire size.
        need: usize,
    },
    /// Buffer longer than the fixed wire size. Rejects garbage
    /// trailers rather than silently ignoring them.
    #[error("oversize stream subprotocol message: {got} bytes (need {need})")]
    Oversize {
        /// Bytes received.
        got: usize,
        /// The message's fixed wire size.
        need: usize,
    },
    /// Structurally invalid [`StreamAckRanges`] payload — bad length
    /// shape, range count, range bounds, ordering, or overlap.
    #[error("invalid stream-ack ranges: {reason}")]
    InvalidRanges {
        /// Which validation failed.
        reason: &'static str,
    },
}

/// Length gate shared by the three fixed-size codecs (`StreamWindow`,
/// `StreamNack`, `StreamReset`): require exactly `need` bytes, else
/// the matching `Truncated` / `Oversize` error carrying the REAL
/// expected size. Centralizing this is what stops a per-decoder size
/// literal from drifting out of sync with the wire size (the pre-fix
/// "need 16" message that outlived the 24-byte grant growth).
#[inline]
fn require_exact_len(data: &[u8], need: usize) -> Result<(), StreamWindowCodecError> {
    match data.len() {
        n if n < need => Err(StreamWindowCodecError::Truncated { got: n, need }),
        n if n > need => Err(StreamWindowCodecError::Oversize { got: n, need }),
        _ => Ok(()),
    }
}

impl StreamWindow {
    /// Encode to a fixed 16-byte buffer.
    #[inline]
    pub fn encode(&self) -> [u8; STREAM_WINDOW_SIZE] {
        let mut buf = [0u8; STREAM_WINDOW_SIZE];
        (&mut buf[..8]).put_u64_le(self.stream_id);
        (&mut buf[8..16]).put_u64_le(self.total_consumed);
        (&mut buf[16..]).put_u64_le(self.ack_seq);
        buf
    }

    /// Decode a fixed-size message. Returns an error on truncated or
    /// oversize input.
    pub fn decode(data: &[u8]) -> Result<Self, StreamWindowCodecError> {
        require_exact_len(data, STREAM_WINDOW_SIZE)?;
        let mut cur = std::io::Cursor::new(data);
        let stream_id = cur.get_u64_le();
        let total_consumed = cur.get_u64_le();
        let ack_seq = cur.get_u64_le();
        Ok(Self {
            stream_id,
            total_consumed,
            ack_seq,
        })
    }
}

/// Receiver → sender retransmit request. Names a stream and the gaps
/// the receiver is missing: `next_expected` is the lowest sequence not
/// yet received contiguously, and `missing_bitmap` bit `i` is set iff
/// `next_expected + 1 + i` is also still missing. The sender feeds this
/// to `ReliableStream::on_nack` to pull the matching retransmit
/// descriptors. Carries `stream_id` itself (rides `CONTROL_STREAM_ID`
/// like the window grant), so the sender doesn't depend on the packet
/// header's stream field.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct StreamNack {
    /// Stream the NACK applies to.
    pub stream_id: u64,
    /// Lowest sequence the receiver has not received contiguously.
    pub next_expected: u64,
    /// Bitmap of further missing sequences after `next_expected`.
    pub missing_bitmap: u64,
}

impl StreamNack {
    /// Encode to a fixed 24-byte buffer.
    #[inline]
    pub fn encode(&self) -> [u8; STREAM_NACK_SIZE] {
        let mut buf = [0u8; STREAM_NACK_SIZE];
        (&mut buf[..8]).put_u64_le(self.stream_id);
        (&mut buf[8..16]).put_u64_le(self.next_expected);
        (&mut buf[16..]).put_u64_le(self.missing_bitmap);
        buf
    }

    /// Decode a 24-byte message. Errors on truncated / oversize input.
    pub fn decode(data: &[u8]) -> Result<Self, StreamWindowCodecError> {
        require_exact_len(data, STREAM_NACK_SIZE)?;
        let mut cur = std::io::Cursor::new(data);
        let stream_id = cur.get_u64_le();
        let next_expected = cur.get_u64_le();
        let missing_bitmap = cur.get_u64_le();
        Ok(Self {
            stream_id,
            next_expected,
            missing_bitmap,
        })
    }
}

/// Receiver → sender positive SACK-range ACK (STREAM_ACK_BATCHING R-1).
///
/// Semantics, precisely: `ack_seq` cumulatively acknowledges every
/// sequence `< ack_seq` (identical to `StreamWindow::ack_seq` — the
/// receiver's `next_expected`). `ranges` selectively acknowledge
/// received runs **strictly above** `ack_seq` as half-open
/// `[start, end)` intervals. `ack_seq` itself is by definition the
/// missing head — it is never inside a range, and the cumulative run
/// is never duplicated as a range. Example: `ack_seq = 101`,
/// `ranges = [(102, 10001)]` ⇒ everything below 101 received, 101
/// missing, 102..=10000 received.
///
/// Wire order is DESCENDING by `end` (newest first): when the
/// receiver truncates to [`MAX_ACK_RANGES`] it drops the *oldest*
/// ranges — the ones the next cumulative advance covers first.
/// Ranges are fully merged: non-overlapping and non-adjacent.
///
/// The sender feeds this to `ReliableStream::on_ack_ranges`, which
/// removes SACKed packets from the retransmit window so one lost
/// head packet no longer RTO-floods everything behind it.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct StreamAckRanges {
    /// Stream the ack applies to.
    pub stream_id: u64,
    /// Cumulative ack — every sequence `< ack_seq` is received.
    pub ack_seq: u64,
    /// Received runs strictly above `ack_seq`: half-open
    /// `[start, end)`, descending by `end`, merged,
    /// `1..=MAX_ACK_RANGES` entries.
    pub ranges: Vec<(u64, u64)>,
}

impl StreamAckRanges {
    /// Minimum wire size: header + one range. A rangeless message is
    /// meaningless (the grant's `ack_seq` already covers the
    /// contiguous case) and is rejected by [`Self::decode`].
    pub const MIN_SIZE: usize = STREAM_ACK_HEADER_SIZE + STREAM_ACK_RANGE_SIZE;

    /// Maximum wire size (header + [`MAX_ACK_RANGES`] ranges).
    pub const MAX_SIZE: usize = STREAM_ACK_HEADER_SIZE + MAX_ACK_RANGES * STREAM_ACK_RANGE_SIZE;

    /// Encode to `16 + 16·n` bytes.
    pub fn encode(&self) -> Vec<u8> {
        let mut buf =
            Vec::with_capacity(STREAM_ACK_HEADER_SIZE + self.ranges.len() * STREAM_ACK_RANGE_SIZE);
        buf.put_u64_le(self.stream_id);
        buf.put_u64_le(self.ack_seq);
        for &(start, end) in &self.ranges {
            buf.put_u64_le(start);
            buf.put_u64_le(end);
        }
        buf
    }

    /// Decode with strict validation. Rejects: truncated / non-`16·n`
    /// bodies, zero or more than [`MAX_ACK_RANGES`] ranges, empty or
    /// inverted ranges, ranges not strictly above `ack_seq`, and any
    /// ordering that is not strictly-descending / merged (overlap or
    /// adjacency means the producer failed to merge — treat as
    /// malformed rather than guessing).
    pub fn decode(data: &[u8]) -> Result<Self, StreamWindowCodecError> {
        if data.len() < Self::MIN_SIZE {
            return Err(StreamWindowCodecError::Truncated {
                got: data.len(),
                need: Self::MIN_SIZE,
            });
        }
        let body = data.len() - STREAM_ACK_HEADER_SIZE;
        if !body.is_multiple_of(STREAM_ACK_RANGE_SIZE) {
            return Err(StreamWindowCodecError::InvalidRanges {
                reason: "length is not header + 16*n",
            });
        }
        let n = body / STREAM_ACK_RANGE_SIZE;
        if n > MAX_ACK_RANGES {
            return Err(StreamWindowCodecError::InvalidRanges {
                reason: "range count exceeds MAX_ACK_RANGES",
            });
        }
        let mut cur = std::io::Cursor::new(data);
        let stream_id = cur.get_u64_le();
        let ack_seq = cur.get_u64_le();
        let mut ranges = Vec::with_capacity(n);
        // Descending, merged: each range must end strictly below the
        // previous (newer) range's start — `end == prev_start` would
        // be adjacency the producer should have merged.
        let mut prev_start: Option<u64> = None;
        for _ in 0..n {
            let start = cur.get_u64_le();
            let end = cur.get_u64_le();
            if start >= end {
                return Err(StreamWindowCodecError::InvalidRanges {
                    reason: "empty or inverted range",
                });
            }
            if start <= ack_seq {
                return Err(StreamWindowCodecError::InvalidRanges {
                    reason: "range not strictly above ack_seq",
                });
            }
            if let Some(ps) = prev_start {
                if end >= ps {
                    return Err(StreamWindowCodecError::InvalidRanges {
                        reason: "ranges not descending/merged",
                    });
                }
            }
            prev_start = Some(start);
            ranges.push((start, end));
        }
        Ok(Self {
            stream_id,
            ack_seq,
            ranges,
        })
    }
}

/// Sender → receiver stream reset (H-3). Names a stream the sender has
/// given up retransmitting; the receiver fails any pending read on it.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct StreamReset {
    /// Stream the reset applies to.
    pub stream_id: u64,
}

impl StreamReset {
    /// Encode to a fixed 8-byte buffer.
    #[inline]
    pub fn encode(&self) -> [u8; STREAM_RESET_SIZE] {
        self.stream_id.to_le_bytes()
    }

    /// Decode an 8-byte message. Errors on truncated / oversize input.
    pub fn decode(data: &[u8]) -> Result<Self, StreamWindowCodecError> {
        require_exact_len(data, STREAM_RESET_SIZE)?;
        let mut cur = std::io::Cursor::new(data);
        Ok(Self {
            stream_id: cur.get_u64_le(),
        })
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_round_trip() {
        let msg = StreamWindow {
            stream_id: 0xDEAD_BEEF_CAFE_F00D,
            total_consumed: 0x0102_0304_0506_0708,
            ack_seq: 0x1122_3344_5566_7788,
        };
        let bytes = msg.encode();
        assert_eq!(bytes.len(), STREAM_WINDOW_SIZE);
        let parsed = StreamWindow::decode(&bytes).unwrap();
        assert_eq!(parsed, msg);
    }

    #[test]
    fn test_decode_truncated_rejected() {
        let err = StreamWindow::decode(&[0u8; STREAM_WINDOW_SIZE - 1]).unwrap_err();
        assert!(matches!(
            err,
            StreamWindowCodecError::Truncated {
                need: STREAM_WINDOW_SIZE,
                ..
            }
        ));
        // The message must report the real expected size, not a
        // stale hardcoded one (pre-fix it said "need 16" while the
        // wire size had grown to 24 with `ack_seq`).
        assert!(err.to_string().contains("need 24"), "got: {err}");
    }

    #[test]
    fn test_decode_oversize_rejected() {
        let err = StreamWindow::decode(&[0u8; STREAM_WINDOW_SIZE + 1]).unwrap_err();
        assert!(matches!(
            err,
            StreamWindowCodecError::Oversize {
                need: STREAM_WINDOW_SIZE,
                ..
            }
        ));
    }

    #[test]
    fn test_decode_empty_rejected() {
        let err = StreamWindow::decode(&[]).unwrap_err();
        assert!(matches!(
            err,
            StreamWindowCodecError::Truncated { got: 0, .. }
        ));
    }

    #[test]
    fn test_endianness_is_little_endian() {
        // Explicit LE check — stream_id=1 must produce `01 00 ... 00`.
        let msg = StreamWindow {
            stream_id: 1,
            total_consumed: 1,
            ack_seq: 1,
        };
        let bytes = msg.encode();
        assert_eq!(bytes[0], 0x01);
        assert_eq!(bytes[1], 0x00);
        assert_eq!(bytes[8], 0x01);
        assert_eq!(bytes[9], 0x00);
        assert_eq!(bytes[16], 0x01);
        assert_eq!(bytes[17], 0x00);
    }

    #[test]
    fn stream_nack_round_trip() {
        let msg = StreamNack {
            stream_id: 0xABCD,
            next_expected: 7,
            missing_bitmap: 0b1010,
        };
        assert_eq!(StreamNack::decode(&msg.encode()).unwrap(), msg);
    }

    #[test]
    fn stream_reset_round_trip() {
        let msg = StreamReset {
            stream_id: 0x2000_0000_0000_0001,
        };
        assert_eq!(StreamReset::decode(&msg.encode()).unwrap(), msg);
    }

    // ── StreamAckRanges (R-1) ────────────────────────────────────

    fn ack(ack_seq: u64, ranges: &[(u64, u64)]) -> StreamAckRanges {
        StreamAckRanges {
            stream_id: 0xF00D,
            ack_seq,
            ranges: ranges.to_vec(),
        }
    }

    #[test]
    fn ack_ranges_round_trip_single_and_max() {
        // The plan's canonical example: <101 received, 101 missing,
        // 102..=10000 received.
        let one = ack(101, &[(102, 10_001)]);
        assert_eq!(StreamAckRanges::decode(&one.encode()).unwrap(), one);

        // MAX_ACK_RANGES descending disjoint ranges.
        let ranges: Vec<(u64, u64)> = (0..MAX_ACK_RANGES as u64)
            .map(|i| {
                let hi = 10_000 - i * 100;
                (hi - 10, hi)
            })
            .collect();
        let full = ack(5, &ranges);
        let bytes = full.encode();
        assert_eq!(bytes.len(), StreamAckRanges::MAX_SIZE);
        assert_eq!(StreamAckRanges::decode(&bytes).unwrap(), full);
    }

    #[test]
    fn ack_ranges_rejects_rangeless_and_truncated() {
        // Header only (rangeless) is below MIN_SIZE.
        let err = StreamAckRanges::decode(&[0u8; STREAM_ACK_HEADER_SIZE]).unwrap_err();
        assert!(matches!(err, StreamWindowCodecError::Truncated { .. }));
        let err = StreamAckRanges::decode(&[]).unwrap_err();
        assert!(matches!(err, StreamWindowCodecError::Truncated { .. }));
    }

    #[test]
    fn ack_ranges_rejects_non_multiple_length() {
        let bytes = ack(1, &[(2, 3)]).encode();
        let mut long = bytes.clone();
        long.push(0);
        assert!(matches!(
            StreamAckRanges::decode(&long).unwrap_err(),
            StreamWindowCodecError::InvalidRanges { .. }
        ));
    }

    #[test]
    fn ack_ranges_rejects_too_many_ranges() {
        let ranges: Vec<(u64, u64)> = (0..(MAX_ACK_RANGES as u64 + 1))
            .map(|i| {
                let hi = 100_000 - i * 10;
                (hi - 2, hi)
            })
            .collect();
        let bytes = ack(1, &ranges).encode();
        assert!(matches!(
            StreamAckRanges::decode(&bytes).unwrap_err(),
            StreamWindowCodecError::InvalidRanges {
                reason: "range count exceeds MAX_ACK_RANGES"
            }
        ));
    }

    #[test]
    fn ack_ranges_rejects_bad_range_shapes() {
        // Empty range.
        assert!(matches!(
            StreamAckRanges::decode(&ack(1, &[(5, 5)]).encode()).unwrap_err(),
            StreamWindowCodecError::InvalidRanges {
                reason: "empty or inverted range"
            }
        ));
        // Inverted range.
        assert!(StreamAckRanges::decode(&ack(1, &[(9, 5)]).encode()).is_err());
        // Range at ack_seq (must be strictly above — ack_seq is the
        // missing head by definition).
        assert!(matches!(
            StreamAckRanges::decode(&ack(10, &[(10, 12)]).encode()).unwrap_err(),
            StreamWindowCodecError::InvalidRanges {
                reason: "range not strictly above ack_seq"
            }
        ));
        // Range below ack_seq (would duplicate the cumulative run).
        assert!(StreamAckRanges::decode(&ack(10, &[(3, 6)]).encode()).is_err());
    }

    #[test]
    fn ack_ranges_rejects_unmerged_or_ascending_order() {
        // Ascending (oldest first) — wire order must be newest first.
        assert!(matches!(
            StreamAckRanges::decode(&ack(1, &[(2, 4), (6, 8)]).encode()).unwrap_err(),
            StreamWindowCodecError::InvalidRanges {
                reason: "ranges not descending/merged"
            }
        ));
        // Adjacent (end == next start) — producer failed to merge.
        assert!(StreamAckRanges::decode(&ack(1, &[(6, 8), (4, 6)]).encode()).is_err());
        // Overlapping.
        assert!(StreamAckRanges::decode(&ack(1, &[(5, 9), (3, 7)]).encode()).is_err());
    }
}