use std::collections::VecDeque;
use std::ops::Range;
use crate::constants;
use crate::error::WriteError;
#[derive(Debug, Clone, PartialEq, Eq)]
pub(crate) struct Chunk {
pub(crate) offset: u64,
pub(crate) data: Vec<u8>,
pub(crate) fin: bool,
pub(crate) fresh: bool,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
struct ResetState {
error_code: u64,
final_size: u64,
pending: bool,
acked: bool,
}
pub(crate) struct SendHalf {
buf: VecDeque<u8>,
base: u64,
write_offset: u64,
fresh: RangeSet,
retransmit: RangeSet,
unacked: RangeSet,
acked: RangeSet,
max_data: u64,
fin: bool,
fin_sent: bool,
fin_acked: bool,
reset: Option<ResetState>,
peer_reset: Option<u64>,
blocked: bool,
queued: bool,
}
impl SendHalf {
pub(crate) fn new() -> Self {
Self {
buf: VecDeque::new(),
base: 0,
write_offset: 0,
fresh: RangeSet::default(),
retransmit: RangeSet::default(),
unacked: RangeSet::default(),
acked: RangeSet::default(),
max_data: constants::INITIAL_MAX_STREAM_DATA,
fin: false,
fin_sent: false,
fin_acked: false,
reset: None,
peer_reset: None,
blocked: false,
queued: false,
}
}
pub(crate) fn write_offset(&self) -> u64 {
self.write_offset
}
pub(crate) fn final_size(&self) -> Option<u64> {
if let Some(reset) = self.reset {
return Some(reset.final_size);
}
self.fin.then_some(self.write_offset)
}
pub(crate) fn is_blocked(&self) -> bool {
self.blocked
}
pub(crate) fn is_queued(&self) -> bool {
self.queued
}
pub(crate) fn set_queued(&mut self, queued: bool) {
self.queued = queued;
}
pub(crate) fn has_pending(&self) -> bool {
if self.peer_reset.is_some() {
return false;
}
self.has_data_pending() || (self.reset.is_none() && self.fin && !self.fin_sent)
}
pub(crate) fn has_data_pending(&self) -> bool {
if self.reset.is_some() {
return false;
}
!self.fresh.is_empty() || !self.retransmit.is_empty()
}
pub(crate) fn next_offset(&self) -> Option<u64> {
if self.reset.is_some() {
return None;
}
if let Some(r) = self.retransmit.first() {
return Some(r.start);
}
if let Some(r) = self.fresh.first() {
return Some(r.start);
}
(self.fin && !self.fin_sent).then_some(self.write_offset)
}
pub(crate) fn reset_pending(&self) -> bool {
self.reset.is_some_and(|r| r.pending)
}
pub(crate) fn is_terminal(&self) -> bool {
if self.peer_reset.is_some() {
return true;
}
if let Some(reset) = self.reset {
return reset.acked;
}
let Some(final_size) = self.final_size() else {
return false;
};
self.fin_acked && self.acked.covers(0..final_size)
}
pub(crate) fn write(&mut self, data: &[u8], conn_room: u64) -> Result<usize, WriteError> {
if let Some(code) = self.peer_reset {
return Err(WriteError::Reset(code));
}
if self.fin || self.reset.is_some() {
return Err(WriteError::Finished);
}
if data.is_empty() {
return Ok(0);
}
let stream_room = self.max_data.saturating_sub(self.write_offset);
let room = stream_room.min(conn_room).min(data.len() as u64) as usize;
if room == 0 {
self.blocked = true;
return Ok(0);
}
self.blocked = false;
let start = self.write_offset;
self.buf.extend(&data[..room]);
self.write_offset += room as u64;
self.fresh.insert(start..self.write_offset);
Ok(room)
}
pub(crate) fn finish(&mut self) -> Result<(), WriteError> {
if let Some(code) = self.peer_reset {
return Err(WriteError::Reset(code));
}
if self.reset.is_some() {
return Err(WriteError::Finished);
}
self.fin = true;
Ok(())
}
pub(crate) fn reset(&mut self, error_code: u64) {
if self.reset.is_some() {
return;
}
let final_size = self.sent_high();
self.reset = Some(ResetState {
error_code,
final_size,
pending: true,
acked: false,
});
self.fresh.clear();
self.retransmit.clear();
self.unacked.clear();
self.buf = VecDeque::new();
self.blocked = false;
}
pub(crate) fn stopped_by_peer(&mut self, error_code: u64) -> bool {
if self.reset.is_some() || self.peer_reset.is_some() {
return false;
}
self.peer_reset = Some(error_code);
self.fresh.clear();
self.retransmit.clear();
self.unacked.clear();
self.buf = VecDeque::new();
self.blocked = false;
true
}
pub(crate) fn next_chunk(&mut self, max_len: usize) -> Option<Chunk> {
if self.reset.is_some() || self.peer_reset.is_some() {
return None;
}
let (range, fresh) = match self.retransmit.first() {
Some(r) => (r, false),
None => match self.fresh.first() {
Some(r) => (r, true),
None => {
if self.fin && !self.fin_sent {
self.fin_sent = true;
return Some(Chunk {
offset: self.write_offset,
data: Vec::new(),
fin: true,
fresh: true,
});
}
return None;
}
},
};
let take = (range.end - range.start).min(max_len as u64) as usize;
if take == 0 {
return None;
}
let start = range.start;
let end = start + take as u64;
let data = self.copy_range(start, end);
if fresh {
self.fresh.remove(start..end);
} else {
self.retransmit.remove(start..end);
}
self.unacked.insert(start..end);
let carries_fin = self.fin
&& !self.fin_sent
&& Some(end) == self.final_size()
&& self.fresh.is_empty()
&& self.retransmit.is_empty();
if carries_fin {
self.fin_sent = true;
}
Some(Chunk {
offset: start,
data,
fin: carries_fin,
fresh,
})
}
pub(crate) fn return_chunk(&mut self, range: Range<u64>, fin: bool, fresh: bool) {
self.unacked.remove(range.clone());
if fresh {
self.fresh.insert(range);
} else {
self.retransmit.insert(range);
}
if fin {
self.fin_sent = false;
}
}
pub(crate) fn take_reset(&mut self) -> Option<(u64, u64)> {
let reset = self.reset.as_mut()?;
if !reset.pending {
return None;
}
reset.pending = false;
Some((reset.error_code, reset.final_size))
}
pub(crate) fn on_max_stream_data(&mut self, max: u64) -> bool {
if max <= self.max_data {
return false;
}
self.max_data = max;
let unblocked = self.blocked;
if unblocked {
self.blocked = false;
}
unblocked
}
pub(crate) fn unblock(&mut self) -> bool {
let was = self.blocked;
self.blocked = false;
was
}
pub(crate) fn max_data(&self) -> u64 {
self.max_data
}
pub(crate) fn on_ack_range(&mut self, range: Range<u64>, fin: bool) {
self.unacked.remove(range.clone());
self.retransmit.remove(range.clone());
self.acked.insert(range);
if fin {
self.fin_acked = true;
}
self.release();
}
pub(crate) fn on_lost_range(&mut self, range: Range<u64>, fin: bool) {
if self.reset.is_some() {
return;
}
self.unacked.remove(range.clone());
let mut returning = RangeSet::default();
returning.insert(range);
for acked in self.acked.iter() {
returning.remove(acked.clone());
}
for r in returning.iter() {
self.retransmit.insert(r.clone());
}
if fin {
self.fin_sent = false;
}
}
pub(crate) fn settled_to(&self, offset: u64) -> bool {
self.reset.is_some()
|| self.peer_reset.is_some()
|| offset == 0
|| self.acked.covers(0..offset)
}
pub(crate) fn on_reset_acked(&mut self) {
if let Some(reset) = self.reset.as_mut() {
reset.acked = true;
reset.pending = false;
}
}
pub(crate) fn on_reset_lost(&mut self) {
if let Some(reset) = self.reset.as_mut().filter(|r| !r.acked) {
reset.pending = true;
}
}
fn sent_high(&self) -> u64 {
self.unacked
.last_end()
.max(self.acked.last_end())
.unwrap_or(0)
}
fn copy_range(&self, start: u64, end: u64) -> Vec<u8> {
let lo = (start - self.base) as usize;
let hi = (end - self.base) as usize;
let (front, back) = self.buf.as_slices();
let mut out = Vec::with_capacity(hi - lo);
if lo < front.len() {
out.extend_from_slice(&front[lo..hi.min(front.len())]);
}
if hi > front.len() {
out.extend_from_slice(&back[lo.saturating_sub(front.len())..hi - front.len()]);
}
out
}
fn release(&mut self) {
let Some(first) = self.acked.first() else {
return;
};
if first.start != 0 {
return;
}
let up_to = first.end.max(self.base);
if up_to <= self.base {
return;
}
let drop = (up_to - self.base) as usize;
let drop = drop.min(self.buf.len());
self.buf.drain(..drop);
self.base += drop as u64;
debug_assert!(
self.reset.is_some()
|| self.peer_reset.is_some()
|| self.buf.len() as u64 == self.write_offset - self.base,
"the ring holds exactly the live bytes"
);
}
}
#[derive(Debug, Default, Clone, PartialEq, Eq)]
pub(crate) struct RangeSet {
ranges: Vec<Range<u64>>,
}
impl RangeSet {
pub(crate) fn insert(&mut self, r: Range<u64>) {
if r.start >= r.end {
return;
}
let mut i = 0;
while i < self.ranges.len() && self.ranges[i].end < r.start {
i += 1;
}
let mut start = r.start;
let mut end = r.end;
let mut j = i;
while j < self.ranges.len() && self.ranges[j].start <= end {
start = start.min(self.ranges[j].start);
end = end.max(self.ranges[j].end);
j += 1;
}
self.ranges.splice(i..j, std::iter::once(start..end));
}
pub(crate) fn remove(&mut self, r: Range<u64>) {
if r.start >= r.end {
return;
}
let mut out = Vec::with_capacity(self.ranges.len() + 1);
for existing in std::mem::take(&mut self.ranges) {
if existing.end <= r.start || existing.start >= r.end {
out.push(existing);
continue;
}
if existing.start < r.start {
out.push(existing.start..r.start);
}
if existing.end > r.end {
out.push(r.end..existing.end);
}
}
self.ranges = out;
}
pub(crate) fn first(&self) -> Option<Range<u64>> {
self.ranges.first().cloned()
}
pub(crate) fn covers(&self, r: Range<u64>) -> bool {
if r.start >= r.end {
return true;
}
self.ranges
.iter()
.any(|e| e.start <= r.start && e.end >= r.end)
}
pub(crate) fn last_end(&self) -> Option<u64> {
self.ranges.last().map(|r| r.end)
}
pub(crate) fn is_empty(&self) -> bool {
self.ranges.is_empty()
}
pub(crate) fn len_bytes(&self) -> u64 {
self.ranges.iter().map(|r| r.end - r.start).sum()
}
pub(crate) fn clear(&mut self) {
self.ranges = Vec::new();
}
pub(crate) fn iter(&self) -> impl Iterator<Item = &Range<u64>> {
self.ranges.iter()
}
}
#[cfg(test)]
mod tests {
use super::*;
const UNLIMITED: u64 = u64::MAX;
#[test]
fn range_set_coalesces_on_insert_and_splits_on_remove() {
let mut set = RangeSet::default();
set.insert(0..10);
set.insert(20..30);
set.insert(10..20);
assert_eq!(set.first(), Some(0..30));
assert!(set.covers(5..25));
set.remove(12..15);
assert_eq!(set.first(), Some(0..12));
assert_eq!(set.len_bytes(), 27);
assert!(!set.covers(5..25));
assert!(set.covers(15..30));
}
#[test]
fn a_write_is_bounded_by_whichever_level_is_tighter() {
let mut half = SendHalf::new();
assert_eq!(half.write(&[0u8; 100], 40), Ok(40));
assert_eq!(half.write_offset(), 40);
let mut half = SendHalf::new();
half.on_max_stream_data(0); assert_eq!(half.max_data(), constants::INITIAL_MAX_STREAM_DATA);
let window = constants::INITIAL_MAX_STREAM_DATA as usize;
assert_eq!(half.write(&vec![0u8; window + 10], UNLIMITED), Ok(window));
assert_eq!(half.write(b"more", UNLIMITED), Ok(0));
assert!(half.is_blocked());
}
#[test]
fn credit_unblocks_a_parked_writer() {
let mut half = SendHalf::new();
assert_eq!(half.write(&[0u8; 8], 0), Ok(0));
assert!(half.is_blocked());
assert!(half.on_max_stream_data(constants::INITIAL_MAX_STREAM_DATA + 1));
assert!(!half.is_blocked());
let mut half = SendHalf::new();
assert_eq!(half.write(&[0u8; 8], 0), Ok(0));
assert!(!half.on_max_stream_data(constants::INITIAL_MAX_STREAM_DATA));
assert!(half.is_blocked());
}
#[test]
fn writing_after_finish_or_reset_is_finished() {
let mut half = SendHalf::new();
half.finish().unwrap();
assert_eq!(half.write(b"x", UNLIMITED), Err(WriteError::Finished));
assert_eq!(half.finish(), Ok(()));
let mut half = SendHalf::new();
half.reset(7);
assert_eq!(half.write(b"x", UNLIMITED), Err(WriteError::Finished));
assert_eq!(half.finish(), Err(WriteError::Finished));
}
#[test]
fn a_chunk_is_bounded_by_the_quantum_and_the_rest_stays_pending() {
let mut half = SendHalf::new();
half.write(&[1u8; 100], UNLIMITED).unwrap();
let chunk = half.next_chunk(30).expect("data is pending");
assert_eq!(chunk.offset, 0);
assert_eq!(chunk.data.len(), 30);
assert!(chunk.fresh);
assert!(!chunk.fin);
assert!(half.has_pending());
}
#[test]
fn a_retransmitted_chunk_is_not_a_first_transmission() {
let mut half = SendHalf::new();
half.write(&[1u8; 10], UNLIMITED).unwrap();
let first = half.next_chunk(10).unwrap();
assert!(first.fresh);
assert_eq!(half.next_chunk(10), None);
half.on_lost_range(0..10, false);
let again = half.next_chunk(10).unwrap();
assert!(!again.fresh, "a retransmission is in §7.4's quiet set");
assert_eq!(again.offset, 0);
assert_eq!(again.data.len(), 10);
}
#[test]
fn retransmissions_are_served_first() {
let mut half = SendHalf::new();
half.write(&[1u8; 20], UNLIMITED).unwrap();
half.next_chunk(10).unwrap();
half.on_lost_range(0..10, false);
let next = half.next_chunk(100).unwrap();
assert_eq!(next.offset, 0);
assert!(!next.fresh);
}
#[test]
fn an_acked_sub_range_does_not_return_on_loss() {
let mut half = SendHalf::new();
half.write(&[1u8; 20], UNLIMITED).unwrap();
half.next_chunk(20).unwrap();
half.on_ack_range(0..10, false);
half.on_lost_range(0..20, false);
let back = half.next_chunk(100).unwrap();
assert_eq!(back.offset, 10);
assert_eq!(back.data.len(), 10);
}
#[test]
fn an_empty_finish_produces_an_empty_fin_frame() {
let mut half = SendHalf::new();
half.finish().unwrap();
let chunk = half.next_chunk(1_000).expect("the FIN is owed");
assert_eq!(chunk.offset, 0);
assert!(chunk.data.is_empty());
assert!(chunk.fin);
assert_eq!(half.next_chunk(1_000), None);
}
#[test]
fn the_fin_rides_the_frame_that_ends_the_stream() {
let mut half = SendHalf::new();
half.write(&[1u8; 20], UNLIMITED).unwrap();
half.finish().unwrap();
let first = half.next_chunk(10).unwrap();
assert!(!first.fin, "there are still bytes to come");
let last = half.next_chunk(10).unwrap();
assert!(last.fin);
assert_eq!(half.next_chunk(10), None);
}
#[test]
fn data_recvd_needs_every_byte_and_the_fin_acknowledged() {
let mut half = SendHalf::new();
half.write(&[1u8; 20], UNLIMITED).unwrap();
half.finish().unwrap();
let chunk = half.next_chunk(100).unwrap();
assert!(chunk.fin);
assert!(!half.is_terminal());
half.on_ack_range(0..19, false);
assert!(!half.is_terminal(), "one byte short");
half.on_ack_range(19..20, false);
assert!(!half.is_terminal(), "the FIN is not acknowledged");
half.on_ack_range(20..20, true);
assert!(half.is_terminal());
}
#[test]
fn a_reset_stops_the_data_and_regenerates_until_acked() {
let mut half = SendHalf::new();
half.write(&[1u8; 40], UNLIMITED).unwrap();
half.next_chunk(10).unwrap();
half.reset(9);
assert_eq!(half.next_chunk(100), None, "pending data stops");
assert_eq!(half.final_size(), Some(10));
assert_eq!(half.take_reset(), Some((9, 10)));
assert_eq!(half.take_reset(), None, "emitted once until it is lost");
half.on_reset_lost();
assert_eq!(half.take_reset(), Some((9, 10)));
assert!(!half.is_terminal());
half.on_reset_acked();
assert!(half.is_terminal());
assert_eq!(half.take_reset(), None);
}
#[test]
fn a_reset_with_nothing_written_has_final_size_zero() {
let mut half = SendHalf::new();
half.reset(3);
assert_eq!(half.take_reset(), Some((3, 0)));
let mut half = SendHalf::new();
half.write(&[1u8; 100], UNLIMITED).unwrap();
half.reset(3);
assert_eq!(half.take_reset(), Some((3, 0)));
}
#[test]
fn an_empty_write_is_a_no_op_and_not_a_block() {
let mut half = SendHalf::new();
assert_eq!(half.write(b"", UNLIMITED), Ok(0));
assert!(!half.is_blocked());
assert!(!half.has_pending());
}
#[test]
fn sending_does_not_free_the_retention_set() {
let mut half = SendHalf::new();
half.write(&[1u8; 50], UNLIMITED).unwrap();
half.next_chunk(50).unwrap();
assert_eq!(half.unacked.len_bytes(), 50);
half.on_ack_range(0..50, false);
assert_eq!(half.unacked.len_bytes(), 0);
}
}