segmentsink datastructure for hptp-recv
This commit is contained in:
parent
3820b28964
commit
f48846f325
|
@ -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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue