segmentsink datastructure for hptp-recv

This commit is contained in:
Milo Turner 2020-03-10 20:50:15 -04:00
parent 3820b28964
commit f48846f325
1 changed files with 88 additions and 38 deletions

View File

@ -7,6 +7,7 @@ extern crate thiserror;
use hptp::logger::Logger; use hptp::logger::Logger;
use hptp::msg::{DownMsg, UpMsg}; use hptp::msg::{DownMsg, UpMsg};
use hptp::peer::{self, DownPeer, Peer}; use hptp::peer::{self, DownPeer, Peer};
use hptp::seg::SegIdx;
use std::collections::HashMap; use std::collections::HashMap;
use tokio::io::AsyncWrite; use tokio::io::AsyncWrite;
@ -44,11 +45,55 @@ async fn start(log: &mut Logger) -> Result<(), Error> {
download(log, &mut peer, &mut out).await download(log, &mut peer, &mut out).await
} }
async fn send(peer: &mut DownPeer, m: DownMsg) -> Result<(), Error> { struct SegmentSink<'o, OUT> {
match peer.send(m).await { out: &'o mut OUT,
Ok(()) => Ok(()), segs: HashMap<SegIdx, Option<Vec<u8>>>,
Err(peer::SendError::Io { source }) => Err(source.into()), n_flushed: u32,
Err(peer::SendError::NoTarget) => unreachable!(), }
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
enum Put {
Duplicate,
Fresh,
}
impl<'o, OUT> SegmentSink<'o, OUT>
where
OUT: AsyncWrite + Unpin,
{
fn new(out: &'o mut OUT) -> Self {
SegmentSink {
out,
segs: HashMap::new(),
n_flushed: 0,
}
}
async fn flush(&mut self) {
use tokio::io::AsyncWriteExt;
while let Some(cache) = self.segs.get_mut(&self.n_flushed) {
if let Some(data) = cache.take() {
self.out.write_all(&data).await.expect("god help us");
self.out.flush().await.expect("god help us");
}
self.n_flushed += 1;
}
}
async fn put(&mut self, seg_idx: SegIdx, data: Vec<u8>) -> Put {
if seg_idx < self.n_flushed || self.segs.contains_key(&seg_idx) {
Put::Duplicate
} else {
self.segs.insert(seg_idx, Some(data));
self.flush().await;
Put::Fresh
}
}
fn ack_msg(&self) -> DownMsg {
DownMsg::Ack {
idxs: self.segs.keys().cloned().collect(),
}
} }
} }
@ -56,45 +101,50 @@ async fn download<OUT>(log: &mut Logger, peer: &mut DownPeer, out: &mut OUT) ->
where where
OUT: AsyncWrite + Unpin, OUT: AsyncWrite + Unpin,
{ {
let mut segs = HashMap::new(); let mut sink = SegmentSink::new(out);
let mut flush_seg_idx = 0; let mut to_send = vec![];
loop { loop {
match peer.recv().await { let msg = match peer.recv().await {
Ok(UpMsg::Data { payload, seg_idx }) => { Ok(m) => m,
if segs.contains_key(&seg_idx) { Err(peer::RecvError::InvalidMessage { .. }) => {
log.recv_data_ignored(seg_idx as usize, payload.len()).await; log.recv_corrupt().await;
} else { continue;
log.recv_data_accepted( }
seg_idx as usize, Err(peer::RecvError::Io { source }) => {
payload.len(), return Err(source.into());
hptp::logger::OutOfOrder, }
) };
.await;
segs.insert(seg_idx, Some(payload)); match msg {
let ack = DownMsg::Ack { UpMsg::Data { payload, seg_idx } => {
idxs: segs.keys().cloned().collect(), let len = payload.len();
}; match sink.put(seg_idx, payload).await {
log.debug_msg(format!("sent ack: {:?}", { Put::Duplicate => {
let mut idxs = segs.keys().collect::<Vec<_>>(); log.recv_data_ignored(seg_idx as usize, len).await;
idxs.sort_unstable(); }
idxs
})) Put::Fresh => {
.await; log.recv_data_accepted(seg_idx as usize, len, hptp::logger::OutOfOrder)
send(peer, ack).await?; .await;
log.debug_msg(format!("sending acks: {:?}", {
let mut idxs = sink.segs.keys().cloned().collect::<Vec<_>>();
idxs.sort();
idxs
}))
.await;
to_send.push(sink.ack_msg());
}
} }
} }
Err(peer::RecvError::InvalidMessage { .. }) => log.recv_corrupt().await,
Err(peer::RecvError::Io { source }) => return Err(source.into()),
} }
while let Some(v) = segs.get_mut(&flush_seg_idx) { for m in to_send.drain(..) {
if let Some(payload) = v.take() { match peer.send(m).await {
use tokio::io::AsyncWriteExt; Ok(()) => (),
out.write_all(&payload).await?; Err(hptp::peer::SendError::NoTarget) => log.debug_msg("no target").await,
out.flush().await?; Err(hptp::peer::SendError::Io { source }) => return Err(source.into()),
} }
flush_seg_idx += 1;
} }
} }
} }