Add comments
This commit is contained in:
parent
59f6aa1eed
commit
d921fcf556
|
@ -23,6 +23,7 @@ enum Error {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// starts start in a new tokio runtime
|
||||||
fn entry() -> Result<(), Error> {
|
fn entry() -> Result<(), Error> {
|
||||||
let mut rt = tokio::runtime::Runtime::new().unwrap();
|
let mut rt = tokio::runtime::Runtime::new().unwrap();
|
||||||
let mut log = Logger::new();
|
let mut log = Logger::new();
|
||||||
|
@ -39,6 +40,7 @@ fn main() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// async entry point
|
||||||
async fn start(log: &mut Logger) -> Result<(), Error> {
|
async fn start(log: &mut Logger) -> Result<(), Error> {
|
||||||
let sock = tokio::net::UdpSocket::bind((std::net::Ipv4Addr::LOCALHOST, 0)).await?;
|
let sock = tokio::net::UdpSocket::bind((std::net::Ipv4Addr::LOCALHOST, 0)).await?;
|
||||||
log.bound(sock.local_addr()?.port()).await;
|
log.bound(sock.local_addr()?.port()).await;
|
||||||
|
@ -47,17 +49,26 @@ async fn start(log: &mut Logger) -> Result<(), Error> {
|
||||||
download(log, &mut peer, &mut out).await
|
download(log, &mut peer, &mut out).await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// keeps track of data we have received
|
||||||
struct SegmentSink<'o, OUT> {
|
struct SegmentSink<'o, OUT> {
|
||||||
|
// where to write out the data
|
||||||
out: &'o mut OUT,
|
out: &'o mut OUT,
|
||||||
|
// a MeowCoder instance for decoding
|
||||||
meow_coder: MeowCoder,
|
meow_coder: MeowCoder,
|
||||||
|
// a map of currently received segments
|
||||||
segs: HashMap<SegIdx, Option<SegData>>,
|
segs: HashMap<SegIdx, Option<SegData>>,
|
||||||
|
// which segments we have flushed to out
|
||||||
n_flushed: u32,
|
n_flushed: u32,
|
||||||
|
// whether we are complete
|
||||||
complete: bool,
|
complete: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// what happened when you put a new segment into a sink
|
||||||
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
|
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
|
||||||
enum Put {
|
enum Put {
|
||||||
|
// we already had this segment
|
||||||
Duplicate,
|
Duplicate,
|
||||||
|
// this is a new segment
|
||||||
Fresh,
|
Fresh,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -75,6 +86,7 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Flushes segments that we have all in order so far to out
|
||||||
async fn flush(&mut self) {
|
async fn flush(&mut self) {
|
||||||
use tokio::io::AsyncWriteExt;
|
use tokio::io::AsyncWriteExt;
|
||||||
while let Some(cache) = self.segs.get_mut(&self.n_flushed) {
|
while let Some(cache) = self.segs.get_mut(&self.n_flushed) {
|
||||||
|
@ -96,6 +108,7 @@ where
|
||||||
self.out.flush().await.expect("god help us");
|
self.out.flush().await.expect("god help us");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Processes a new received segment
|
||||||
async fn put(&mut self, seg_idx: SegIdx, data: SegData) -> Put {
|
async fn put(&mut self, seg_idx: SegIdx, data: SegData) -> Put {
|
||||||
if seg_idx < self.n_flushed || self.segs.contains_key(&seg_idx) {
|
if seg_idx < self.n_flushed || self.segs.contains_key(&seg_idx) {
|
||||||
Put::Duplicate
|
Put::Duplicate
|
||||||
|
@ -106,23 +119,30 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Generates the acks to send back
|
||||||
fn ack_idxs(&self) -> Vec<SegIdx> {
|
fn ack_idxs(&self) -> Vec<SegIdx> {
|
||||||
let mut idxs: Vec<_> = self.segs.keys().cloned().collect();
|
let mut idxs: Vec<_> = self.segs.keys().cloned().collect();
|
||||||
idxs.sort();
|
idxs.sort();
|
||||||
idxs
|
idxs
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Whether the receiving is complete yet
|
||||||
fn is_file_complete(&self) -> bool {
|
fn is_file_complete(&self) -> bool {
|
||||||
self.complete
|
self.complete
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Downloads a file from a sender
|
||||||
async fn download<OUT>(log: &mut Logger, peer: &mut DownPeer, out: &mut OUT) -> Result<(), Error>
|
async fn download<OUT>(log: &mut Logger, peer: &mut DownPeer, out: &mut OUT) -> Result<(), Error>
|
||||||
where
|
where
|
||||||
OUT: AsyncWrite + Unpin,
|
OUT: AsyncWrite + Unpin,
|
||||||
{
|
{
|
||||||
use tokio::time::{Duration, Instant};
|
use tokio::time::{Duration, Instant};
|
||||||
|
|
||||||
|
// General strategy: Listen for segments coming in, and ack once we have seen some number of
|
||||||
|
// segments or some time has elapsed. Once we see the eof (or detect that we must have passed
|
||||||
|
// eof and started retransmitting earlier packets) we ack slightly more often.
|
||||||
|
|
||||||
enum Evt {
|
enum Evt {
|
||||||
Recv(UpMsg),
|
Recv(UpMsg),
|
||||||
Timer,
|
Timer,
|
||||||
|
@ -153,12 +173,14 @@ where
|
||||||
|
|
||||||
match evt {
|
match evt {
|
||||||
Evt::Timer => {
|
Evt::Timer => {
|
||||||
|
// timeout, make sure to send an ack now
|
||||||
deadline += Duration::from_millis(1000);
|
deadline += Duration::from_millis(1000);
|
||||||
log.debug_msg("timeout").await;
|
log.debug_msg("timeout").await;
|
||||||
ack_buildup += 1;
|
ack_buildup += 10;
|
||||||
}
|
}
|
||||||
|
|
||||||
Evt::Recv(UpMsg::Data { payload, seg_idx }) => {
|
Evt::Recv(UpMsg::Data { payload, seg_idx }) => {
|
||||||
|
// got some data, update our data set
|
||||||
let len = payload.len();
|
let len = payload.len();
|
||||||
let eof = payload.is_last_segment || seg_idx < max_seen_idx;
|
let eof = payload.is_last_segment || seg_idx < max_seen_idx;
|
||||||
max_seen_idx = cmp::max(seg_idx + 1, max_seen_idx);
|
max_seen_idx = cmp::max(seg_idx + 1, max_seen_idx);
|
||||||
|
@ -174,6 +196,7 @@ where
|
||||||
}
|
}
|
||||||
|
|
||||||
if eof && !seen_eof {
|
if eof && !seen_eof {
|
||||||
|
// eof seen, probably ack now
|
||||||
seen_eof = true;
|
seen_eof = true;
|
||||||
ack_buildup += 999;
|
ack_buildup += 999;
|
||||||
}
|
}
|
||||||
|
@ -181,11 +204,16 @@ where
|
||||||
};
|
};
|
||||||
|
|
||||||
let num_acks = if sink.is_file_complete() {
|
let num_acks = if sink.is_file_complete() {
|
||||||
|
// spam out 5 acks on shutdown just to be sure the sender gets at least 1
|
||||||
|
// instead of taking extra time with a closing handshake we make the assumption
|
||||||
|
// that at least one of these will go through
|
||||||
5
|
5
|
||||||
} else if ack_buildup > 10 {
|
} else if ack_buildup > 10 {
|
||||||
|
// spam 2 acks at a time to hopefully avoid drops
|
||||||
ack_buildup = 0;
|
ack_buildup = 0;
|
||||||
2
|
2
|
||||||
} else {
|
} else {
|
||||||
|
// send no acks right now
|
||||||
0
|
0
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -201,6 +229,7 @@ where
|
||||||
}
|
}
|
||||||
|
|
||||||
if sink.is_file_complete() {
|
if sink.is_file_complete() {
|
||||||
|
// we're done
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,6 +29,7 @@ enum Error {
|
||||||
InvalidArgs,
|
InvalidArgs,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Main function
|
||||||
fn main() {
|
fn main() {
|
||||||
match entry() {
|
match entry() {
|
||||||
Err(Error::InvalidArgs) => print_usage(),
|
Err(Error::InvalidArgs) => print_usage(),
|
||||||
|
@ -47,12 +48,14 @@ fn print_usage() {
|
||||||
print!("Usage:\n./3700send <host-ip>:<host-port>\n")
|
print!("Usage:\n./3700send <host-ip>:<host-port>\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Starts async code in a new tokio runtime
|
||||||
fn entry() -> Result<(), Error> {
|
fn entry() -> Result<(), Error> {
|
||||||
let mut rt = tokio::runtime::Runtime::new().unwrap();
|
let mut rt = tokio::runtime::Runtime::new().unwrap();
|
||||||
let mut log = Logger::new();
|
let mut log = Logger::new();
|
||||||
rt.block_on(start(&mut log))
|
rt.block_on(start(&mut log))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The entry point for the async code
|
||||||
async fn start(log: &mut Logger) -> Result<(), Error> {
|
async fn start(log: &mut Logger) -> Result<(), Error> {
|
||||||
let targ_addr = parse_args(std::env::args())?;
|
let targ_addr = parse_args(std::env::args())?;
|
||||||
let sock = tokio::net::UdpSocket::bind((std::net::Ipv4Addr::LOCALHOST, 0)).await?;
|
let sock = tokio::net::UdpSocket::bind((std::net::Ipv4Addr::LOCALHOST, 0)).await?;
|
||||||
|
@ -64,6 +67,7 @@ async fn start(log: &mut Logger) -> Result<(), Error> {
|
||||||
upload(log, &mut peer, &mut out).await
|
upload(log, &mut peer, &mut out).await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Parses command line arguments
|
||||||
fn parse_args(mut args: impl Iterator<Item = String>) -> Result<SocketAddr, Error> {
|
fn parse_args(mut args: impl Iterator<Item = String>) -> Result<SocketAddr, Error> {
|
||||||
args.nth(1)
|
args.nth(1)
|
||||||
.ok_or(Error::InvalidArgs)?
|
.ok_or(Error::InvalidArgs)?
|
||||||
|
@ -71,19 +75,30 @@ fn parse_args(mut args: impl Iterator<Item = String>) -> Result<SocketAddr, Erro
|
||||||
.map_err(|_| Error::InvalidArgs)
|
.map_err(|_| Error::InvalidArgs)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Tracks information about the ongoing data upload
|
||||||
struct SegmentSource<'i, IN> {
|
struct SegmentSource<'i, IN> {
|
||||||
|
// The source of data to upload
|
||||||
inp: &'i mut IN,
|
inp: &'i mut IN,
|
||||||
|
// A buffer holding data we read from inp that hasn't been sent yet
|
||||||
inp_buffer: Vec<u8>,
|
inp_buffer: Vec<u8>,
|
||||||
|
// Whether we read all data from inp yet
|
||||||
read_input: bool,
|
read_input: bool,
|
||||||
|
// Whether to use MeowCoder or not
|
||||||
is_meow: bool,
|
is_meow: bool,
|
||||||
|
// Whether MeowCoder::encode reported cut off data
|
||||||
is_cut: bool,
|
is_cut: bool,
|
||||||
// ordered by seg idx
|
// Currently unacknowledged segments ordered by seg idx
|
||||||
unacked_segs: Vec<(SegIdx, UnAcked)>,
|
unacked_segs: Vec<(SegIdx, UnAcked)>,
|
||||||
|
// The next segment to send during the initial pass over the data
|
||||||
next_seg_idx: u32,
|
next_seg_idx: u32,
|
||||||
|
// The next segment to retransmit
|
||||||
next_retrans_idx: usize, // NOTE: this is an index into *unacked_segs* array
|
next_retrans_idx: usize, // NOTE: this is an index into *unacked_segs* array
|
||||||
|
// Whether we have reached the end of data to transmit (note: check unacked_segs.is_empty() as
|
||||||
|
// well)
|
||||||
eof: bool,
|
eof: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// A currently unacknowledged segment with a counter for how many times it's been nacked
|
||||||
struct UnAcked {
|
struct UnAcked {
|
||||||
payload: SegData,
|
payload: SegData,
|
||||||
nacks: usize,
|
nacks: usize,
|
||||||
|
@ -116,6 +131,7 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Reads the next SegData to transmit from inp
|
||||||
async fn read_next(&mut self) -> SegData {
|
async fn read_next(&mut self) -> SegData {
|
||||||
use tokio::io::AsyncReadExt;
|
use tokio::io::AsyncReadExt;
|
||||||
if !self.read_input {
|
if !self.read_input {
|
||||||
|
@ -147,6 +163,7 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Gets the next segment index to retransmit, if any
|
||||||
fn retrans_seg_idx(&mut self) -> Option<SegIdx> {
|
fn retrans_seg_idx(&mut self) -> Option<SegIdx> {
|
||||||
if self.unacked_segs.is_empty() {
|
if self.unacked_segs.is_empty() {
|
||||||
return None
|
return None
|
||||||
|
@ -156,6 +173,7 @@ where
|
||||||
Some(seg_idx)
|
Some(seg_idx)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Gets the next new segment to transmit, if any
|
||||||
async fn fresh_seg_idx(&mut self) -> Option<SegIdx> {
|
async fn fresh_seg_idx(&mut self) -> Option<SegIdx> {
|
||||||
if self.eof {
|
if self.eof {
|
||||||
None
|
None
|
||||||
|
@ -172,6 +190,7 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Gets a segment to transmit next (either a new segment or a retransmit segment)
|
||||||
async fn get_segment(&mut self) -> Option<(SegIdx, &SegData)> {
|
async fn get_segment(&mut self) -> Option<(SegIdx, &SegData)> {
|
||||||
let seg_idx = {
|
let seg_idx = {
|
||||||
if let Some(si) = self.fresh_seg_idx().await {
|
if let Some(si) = self.fresh_seg_idx().await {
|
||||||
|
@ -190,7 +209,8 @@ where
|
||||||
Some((seg_idx, &self.unacked_segs[i].1.payload))
|
Some((seg_idx, &self.unacked_segs[i].1.payload))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// `seg_idxs` should be distinct and in increasing order.
|
// Processes received acks
|
||||||
|
// `seg_idxs` should be distinct and in increasing order.
|
||||||
fn ack(&mut self, ack_seg_idxs: &[SegIdx]) {
|
fn ack(&mut self, ack_seg_idxs: &[SegIdx]) {
|
||||||
let mut new_unacked_segs = Vec::new();
|
let mut new_unacked_segs = Vec::new();
|
||||||
for (seg_idx, unack) in self.unacked_segs.drain(..) {
|
for (seg_idx, unack) in self.unacked_segs.drain(..) {
|
||||||
|
@ -202,20 +222,20 @@ where
|
||||||
self.next_retrans_idx = 0;
|
self.next_retrans_idx = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Checks if we are completely done with the upload
|
||||||
fn is_file_completed(&self) -> bool {
|
fn is_file_completed(&self) -> bool {
|
||||||
self.eof && self.unacked_segs.is_empty()
|
self.eof && self.unacked_segs.is_empty()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Performs upload
|
||||||
async fn upload<IN>(log: &mut Logger, peer: &mut UpPeer, inp: &mut IN) -> Result<(), Error>
|
async fn upload<IN>(log: &mut Logger, peer: &mut UpPeer, inp: &mut IN) -> Result<(), Error>
|
||||||
where
|
where
|
||||||
IN: AsyncRead + Unpin,
|
IN: AsyncRead + Unpin,
|
||||||
{
|
{
|
||||||
// TODO:
|
// The general strategy is to send all the segments in order, then go back and retransmit
|
||||||
// 1. prioritize segments to send
|
// the ones that weren't acked in order until all segments are acked.
|
||||||
// 2. set a timer for sending the next segment
|
// We adjust the send rate according to estimations of drop rate based on estimations of RTT
|
||||||
// 3. recieve ack's in the meantime
|
|
||||||
// 4. adjust timer delay based on ack information
|
|
||||||
|
|
||||||
enum Evt {
|
enum Evt {
|
||||||
Recv(DownMsg),
|
Recv(DownMsg),
|
||||||
|
@ -225,23 +245,29 @@ where
|
||||||
let mut src = SegmentSource::new(inp);
|
let mut src = SegmentSource::new(inp);
|
||||||
|
|
||||||
const DEFAULT_IN_FLIGHT: i32 = 66;
|
const DEFAULT_IN_FLIGHT: i32 = 66;
|
||||||
|
// the number of packets per second to limit to
|
||||||
let mut in_flight = DEFAULT_IN_FLIGHT;
|
let mut in_flight = DEFAULT_IN_FLIGHT;
|
||||||
let mut deadline = Instant::now();
|
let mut deadline = Instant::now();
|
||||||
let mut to_send = vec![];
|
let mut to_send = vec![];
|
||||||
|
|
||||||
|
// this keeps track of stuff that has happened since the last time we adjusted in_flight
|
||||||
|
// once we have enough data, we make an estimation of rtt and use this to estimate drop rate
|
||||||
let mut send_history: Vec<(SegIdx, Instant)> = vec![];
|
let mut send_history: Vec<(SegIdx, Instant)> = vec![];
|
||||||
let mut ack_history: Vec<(SegIdx, Instant)> = vec![];
|
let mut ack_history: Vec<(SegIdx, Instant)> = vec![];
|
||||||
let mut acked: HashMap<SegIdx, Instant> = HashMap::new();
|
let mut acked: HashMap<SegIdx, Instant> = HashMap::new();
|
||||||
let mut sent: HashMap<SegIdx, Instant> = HashMap::new();
|
let mut sent: HashMap<SegIdx, Instant> = HashMap::new();
|
||||||
let mut rtt_est: Duration = Duration::from_millis(1050);
|
let mut rtt_est: Duration = Duration::from_millis(1050);
|
||||||
|
// fixed point representing droprate*100 as usize
|
||||||
let mut drop_est: usize = 0;
|
let mut drop_est: usize = 0;
|
||||||
let mut drop_est_made: bool = false;
|
let mut drop_est_made: bool = false;
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
let now = Instant::now();
|
let now = Instant::now();
|
||||||
log.debug_msg(format!("rtt_est is {}, drop_est is {}", rtt_est.as_millis(), drop_est)).await;
|
// log.debug_msg(format!("rtt_est is {}, drop_est is {}",
|
||||||
|
// rtt_est.as_millis(), drop_est)).await;
|
||||||
|
|
||||||
if drop_est_made {
|
if drop_est_made {
|
||||||
|
// we made an estimation, change parameters
|
||||||
if drop_est > 50 {
|
if drop_est > 50 {
|
||||||
in_flight = DEFAULT_IN_FLIGHT;
|
in_flight = DEFAULT_IN_FLIGHT;
|
||||||
} else if drop_est > 35 {
|
} else if drop_est > 35 {
|
||||||
|
@ -296,6 +322,7 @@ where
|
||||||
log.debug_msg(format!("got {} acks", idxs.len())).await;
|
log.debug_msg(format!("got {} acks", idxs.len())).await;
|
||||||
src.ack(&idxs);
|
src.ack(&idxs);
|
||||||
|
|
||||||
|
// update the histories of what happened and try to make estimations
|
||||||
let mut rtts: Vec<Duration> = vec![];
|
let mut rtts: Vec<Duration> = vec![];
|
||||||
|
|
||||||
for idx in idxs.iter().cloned() {
|
for idx in idxs.iter().cloned() {
|
||||||
|
@ -311,13 +338,18 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// estimate rtt
|
||||||
if rtts.len() > 10 {
|
if rtts.len() > 10 {
|
||||||
rtts.sort();
|
rtts.sort();
|
||||||
|
// the lower part of the rtts is going to be closer to the real rtt
|
||||||
|
// since the receiver batching and drops etc may impact the higher
|
||||||
|
// sections of the rtt array
|
||||||
let slice = &rtts[rtts.len()/5 - 1..=rtts.len()/5 + 1];
|
let slice = &rtts[rtts.len()/5 - 1..=rtts.len()/5 + 1];
|
||||||
let mean = (slice[0] + slice[1] + slice[2]) / 3;
|
let mean = (slice[0] + slice[1] + slice[2]) / 3;
|
||||||
rtt_est = mean;
|
rtt_est = mean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// estimate drops
|
||||||
let mut total_pkts = 0;
|
let mut total_pkts = 0;
|
||||||
let mut acked_pkts = 0;
|
let mut acked_pkts = 0;
|
||||||
for (sent_idx, sent_time) in send_history.iter().rev().cloned() {
|
for (sent_idx, sent_time) in send_history.iter().rev().cloned() {
|
||||||
|
|
|
@ -40,10 +40,12 @@ impl MeowCoder {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Creates a new MeowCoder
|
||||||
pub fn new() -> MeowCoder {
|
pub fn new() -> MeowCoder {
|
||||||
MeowCoder { line_index: 0 }
|
MeowCoder { line_index: 0 }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Converts a single hex character to the 4 bits of data it represents
|
||||||
pub fn hex_to_nibble(chr: u8) -> u8 {
|
pub fn hex_to_nibble(chr: u8) -> u8 {
|
||||||
const _AL: u8 = 'a' as u8;
|
const _AL: u8 = 'a' as u8;
|
||||||
const _FL: u8 = 'f' as u8;
|
const _FL: u8 = 'f' as u8;
|
||||||
|
@ -59,6 +61,7 @@ impl MeowCoder {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Converts any u8 into 2 chars of corresponding hex
|
||||||
pub fn u8_to_hex(val: u8) -> (u8, u8) {
|
pub fn u8_to_hex(val: u8) -> (u8, u8) {
|
||||||
let first = val >> 4;
|
let first = val >> 4;
|
||||||
let second = val & 0xF;
|
let second = val & 0xF;
|
||||||
|
@ -66,6 +69,9 @@ impl MeowCoder {
|
||||||
(LOOKUP[first as usize], LOOKUP[second as usize])
|
(LOOKUP[first as usize], LOOKUP[second as usize])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Encodes a buffer using Meow coding
|
||||||
|
// Returns a coded buffer and a boolean indicating whether the input was cut off in the middle
|
||||||
|
// of a hex sequence
|
||||||
pub fn encode(input: &Vec<u8>) -> (Vec<u8>, bool) {
|
pub fn encode(input: &Vec<u8>) -> (Vec<u8>, bool) {
|
||||||
let mut out: Vec<u8> = Vec::new();
|
let mut out: Vec<u8> = Vec::new();
|
||||||
let mut prev_char: u8 = 0;
|
let mut prev_char: u8 = 0;
|
||||||
|
@ -93,6 +99,8 @@ impl MeowCoder {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Decodes a buffer using Meow coding
|
||||||
|
// Pass in the input buffer and was_cut from encode()
|
||||||
pub fn decode(&mut self, input: &Vec<u8>, was_cut: bool) -> Vec<u8> {
|
pub fn decode(&mut self, input: &Vec<u8>, was_cut: bool) -> Vec<u8> {
|
||||||
let mut out: Vec<u8> = Vec::new();
|
let mut out: Vec<u8> = Vec::new();
|
||||||
for (pos, byte) in input.iter().enumerate() {
|
for (pos, byte) in input.iter().enumerate() {
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
// HPTP core functionality
|
||||||
|
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate thiserror;
|
extern crate thiserror;
|
||||||
extern crate byteorder;
|
extern crate byteorder;
|
||||||
|
|
|
@ -3,17 +3,20 @@ extern crate tokio;
|
||||||
use std::fmt::Display;
|
use std::fmt::Display;
|
||||||
use tokio::io::{AsyncWriteExt, Stderr};
|
use tokio::io::{AsyncWriteExt, Stderr};
|
||||||
|
|
||||||
|
// Handles logging according to the assignment specs
|
||||||
pub struct Logger {
|
pub struct Logger {
|
||||||
out: Stderr,
|
out: Stderr,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Logger {
|
impl Logger {
|
||||||
|
// creates a new Logger sending to stderr
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Logger {
|
Logger {
|
||||||
out: tokio::io::stderr(),
|
out: tokio::io::stderr(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Helper function to log a structured message to out
|
||||||
async fn log_message(&mut self, msg: LogMessage<'_>) {
|
async fn log_message(&mut self, msg: LogMessage<'_>) {
|
||||||
// These two lines should never fail and we realistically can't do anything about
|
// These two lines should never fail and we realistically can't do anything about
|
||||||
// it if they do.
|
// it if they do.
|
||||||
|
@ -24,54 +27,65 @@ impl Logger {
|
||||||
self.out.flush().await.expect("failed to flush stderr");
|
self.out.flush().await.expect("failed to flush stderr");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Timestamps the given payload and logs it with log_message
|
||||||
async fn log_payload(&mut self, payload: LogPayload<'_>) {
|
async fn log_payload(&mut self, payload: LogPayload<'_>) {
|
||||||
self.log_message(LogMessage::now(payload)).await
|
self.log_message(LogMessage::now(payload)).await
|
||||||
}
|
}
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
// Logs a debug message
|
||||||
pub async fn debug_msg<S: AsRef<str>>(&mut self, what: S) {
|
pub async fn debug_msg<S: AsRef<str>>(&mut self, what: S) {
|
||||||
let what = what.as_ref();
|
let what = what.as_ref();
|
||||||
self.log_payload(LogPayload::Debug { what }).await
|
self.log_payload(LogPayload::Debug { what }).await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Logs a message indicating which port we bound
|
||||||
pub async fn bound(&mut self, port: u16) {
|
pub async fn bound(&mut self, port: u16) {
|
||||||
self.log_payload(LogPayload::Bound { port }).await
|
self.log_payload(LogPayload::Bound { port }).await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Logs a message indicating we accepted some data
|
||||||
pub async fn recv_data_accepted(&mut self, start: usize, len: usize, order: AcceptedOrder) {
|
pub async fn recv_data_accepted(&mut self, start: usize, len: usize, order: AcceptedOrder) {
|
||||||
self.log_payload(LogPayload::RecvDataAccepted { start, len, order })
|
self.log_payload(LogPayload::RecvDataAccepted { start, len, order })
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Logs a message indicating we ignored some received data
|
||||||
pub async fn recv_data_ignored(&mut self, start: usize, len: usize) {
|
pub async fn recv_data_ignored(&mut self, start: usize, len: usize) {
|
||||||
self.log_payload(LogPayload::RecvDataIgnored { start, len })
|
self.log_payload(LogPayload::RecvDataIgnored { start, len })
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Logs a message indicating we got an ack
|
||||||
pub async fn recv_ack(&mut self, last_offset: usize) {
|
pub async fn recv_ack(&mut self, last_offset: usize) {
|
||||||
self.log_payload(LogPayload::RecvAck { last_offset }).await
|
self.log_payload(LogPayload::RecvAck { last_offset }).await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Logs a message indicating we got corrupted data
|
||||||
pub async fn recv_corrupt(&mut self) {
|
pub async fn recv_corrupt(&mut self) {
|
||||||
self.log_payload(LogPayload::RecvCorrupt).await
|
self.log_payload(LogPayload::RecvCorrupt).await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Logs a message indicating we sent data
|
||||||
pub async fn send_data(&mut self, start: usize, len: usize) {
|
pub async fn send_data(&mut self, start: usize, len: usize) {
|
||||||
self.log_payload(LogPayload::SendData { start, len }).await
|
self.log_payload(LogPayload::SendData { start, len }).await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Logs a message indicating we completed transmission of data
|
||||||
pub async fn completed(&mut self) {
|
pub async fn completed(&mut self) {
|
||||||
self.log_payload(LogPayload::Completed).await
|
self.log_payload(LogPayload::Completed).await
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// A single timestamped message
|
||||||
struct LogMessage<'a> {
|
struct LogMessage<'a> {
|
||||||
pub timestamp: chrono::DateTime<chrono::Local>,
|
pub timestamp: chrono::DateTime<chrono::Local>,
|
||||||
pub payload: LogPayload<'a>,
|
pub payload: LogPayload<'a>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> LogMessage<'a> {
|
impl<'a> LogMessage<'a> {
|
||||||
|
// Timestamps a given payload
|
||||||
fn now(payload: LogPayload<'a>) -> Self {
|
fn now(payload: LogPayload<'a>) -> Self {
|
||||||
LogMessage {
|
LogMessage {
|
||||||
timestamp: chrono::Local::now(),
|
timestamp: chrono::Local::now(),
|
||||||
|
@ -80,6 +94,7 @@ impl<'a> LogMessage<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Represents different kinds of messages we can log in the assignment-specified format
|
||||||
enum LogPayload<'a> {
|
enum LogPayload<'a> {
|
||||||
Debug {
|
Debug {
|
||||||
what: &'a str,
|
what: &'a str,
|
||||||
|
@ -107,6 +122,7 @@ enum LogPayload<'a> {
|
||||||
Completed,
|
Completed,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Whether packets were received in order or not
|
||||||
#[derive(Copy, Clone, Eq, PartialEq)]
|
#[derive(Copy, Clone, Eq, PartialEq)]
|
||||||
pub enum AcceptedOrder {
|
pub enum AcceptedOrder {
|
||||||
InOrder,
|
InOrder,
|
||||||
|
@ -115,6 +131,7 @@ pub enum AcceptedOrder {
|
||||||
|
|
||||||
pub use AcceptedOrder::*;
|
pub use AcceptedOrder::*;
|
||||||
|
|
||||||
|
// Serializes a LogMessage to string
|
||||||
impl Display for LogMessage<'_> {
|
impl Display for LogMessage<'_> {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||||
use chrono::Timelike;
|
use chrono::Timelike;
|
||||||
|
@ -130,6 +147,7 @@ impl Display for LogMessage<'_> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Serializes a LogPayload to string
|
||||||
impl Display for LogPayload<'_> {
|
impl Display for LogPayload<'_> {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
|
@ -149,6 +167,7 @@ impl Display for LogPayload<'_> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Serializes AcceptedOrder to string
|
||||||
impl Display for AcceptedOrder {
|
impl Display for AcceptedOrder {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||||
f.write_str(match self {
|
f.write_str(match self {
|
||||||
|
|
|
@ -2,11 +2,13 @@ use super::seg::{SegData, SegIdx};
|
||||||
pub use super::seg::{DOWN_HEADER_SIZE, MAX_TOTAL_PACKET_SIZE, UP_HEADER_SIZE, MAX_SEG_SIZE};
|
pub use super::seg::{DOWN_HEADER_SIZE, MAX_TOTAL_PACKET_SIZE, UP_HEADER_SIZE, MAX_SEG_SIZE};
|
||||||
use byteorder::ByteOrder;
|
use byteorder::ByteOrder;
|
||||||
|
|
||||||
|
// A message containing data for the receiver
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub enum UpMsg {
|
pub enum UpMsg {
|
||||||
Data { payload: SegData, seg_idx: SegIdx },
|
Data { payload: SegData, seg_idx: SegIdx },
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// An ack message containing info about which segments were received
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub enum DownMsg {
|
pub enum DownMsg {
|
||||||
/// `idxs` must be distinct and in increasing order.
|
/// `idxs` must be distinct and in increasing order.
|
||||||
|
@ -17,6 +19,7 @@ pub enum DownMsg {
|
||||||
#[error("deserialization failed; malformed packet")]
|
#[error("deserialization failed; malformed packet")]
|
||||||
pub struct DesError;
|
pub struct DesError;
|
||||||
|
|
||||||
|
// Something that we can serialize and deserialize with u8 slices
|
||||||
pub trait SerDes: Sized {
|
pub trait SerDes: Sized {
|
||||||
// `buf.len()` must be <= `MAX_TOTAL_PACKET_SIZE`
|
// `buf.len()` must be <= `MAX_TOTAL_PACKET_SIZE`
|
||||||
fn des(buf: &[u8]) -> Result<Self, DesError>;
|
fn des(buf: &[u8]) -> Result<Self, DesError>;
|
||||||
|
@ -27,6 +30,7 @@ pub trait SerDes: Sized {
|
||||||
|
|
||||||
type BO = byteorder::LE;
|
type BO = byteorder::LE;
|
||||||
|
|
||||||
|
// Masks for additional booleans encoded as bits in the segment index field
|
||||||
const LAST_SEG_MASK: u32 = 1 << 31;
|
const LAST_SEG_MASK: u32 = 1 << 31;
|
||||||
const MEOW_CODED_MASK: u32 = 1 << 30;
|
const MEOW_CODED_MASK: u32 = 1 << 30;
|
||||||
const CUT_MASK: u32 = 1 << 29;
|
const CUT_MASK: u32 = 1 << 29;
|
||||||
|
|
|
@ -4,6 +4,7 @@ use tokio::net::UdpSocket;
|
||||||
|
|
||||||
use super::msg::{self, SerDes};
|
use super::msg::{self, SerDes};
|
||||||
|
|
||||||
|
// Represents an endpoint we are getting packets from
|
||||||
pub struct Peer<S, R> {
|
pub struct Peer<S, R> {
|
||||||
sock: UdpSocket,
|
sock: UdpSocket,
|
||||||
targ: Option<SocketAddr>,
|
targ: Option<SocketAddr>,
|
||||||
|
@ -39,6 +40,7 @@ pub enum SendError {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<S, R> Peer<S, R> {
|
impl<S, R> Peer<S, R> {
|
||||||
|
// Wraps a local UDP socket in a peer struct
|
||||||
pub fn new(sock: UdpSocket) -> Self {
|
pub fn new(sock: UdpSocket) -> Self {
|
||||||
Peer {
|
Peer {
|
||||||
sock,
|
sock,
|
||||||
|
@ -47,10 +49,12 @@ impl<S, R> Peer<S, R> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Sets the actual address of the peer, once known
|
||||||
pub fn set_known_target(&mut self, addr: SocketAddr) {
|
pub fn set_known_target(&mut self, addr: SocketAddr) {
|
||||||
self.targ = Some(addr);
|
self.targ = Some(addr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Sends as message to the peer
|
||||||
pub async fn send(&mut self, msg: S) -> Result<(), SendError>
|
pub async fn send(&mut self, msg: S) -> Result<(), SendError>
|
||||||
where
|
where
|
||||||
S: SerDes,
|
S: SerDes,
|
||||||
|
@ -62,6 +66,7 @@ impl<S, R> Peer<S, R> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Receives a message from the peer
|
||||||
pub async fn recv(&mut self) -> Result<R, RecvError>
|
pub async fn recv(&mut self) -> Result<R, RecvError>
|
||||||
where
|
where
|
||||||
R: SerDes,
|
R: SerDes,
|
||||||
|
|
|
@ -45,46 +45,22 @@ pub struct SegmentSet {
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
// Represents one segment of data to transfer
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct SegData {
|
pub struct SegData {
|
||||||
|
// the data
|
||||||
pub bytes: Vec<u8>,
|
pub bytes: Vec<u8>,
|
||||||
|
// whether this is the last segment in the set
|
||||||
pub is_last_segment: bool,
|
pub is_last_segment: bool,
|
||||||
|
// was this encoded with MeowCoder
|
||||||
pub is_meow_encoded: bool,
|
pub is_meow_encoded: bool,
|
||||||
|
// was this cut off according to MeowCoder::encode
|
||||||
pub is_cut: bool,
|
pub is_cut: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SegData {
|
impl SegData {
|
||||||
|
// gets the length of the segment data
|
||||||
pub fn len(&self) -> usize {
|
pub fn len(&self) -> usize {
|
||||||
self.bytes.len()
|
self.bytes.len()
|
||||||
}
|
}
|
||||||
|
|
||||||
// pub async fn read<IN>(inp: &mut IN) -> Result<SegData, tokio::io::Error>
|
|
||||||
// where
|
|
||||||
// IN: AsyncRead + Unpin,
|
|
||||||
// {
|
|
||||||
// use tokio::io::AsyncReadExt;
|
|
||||||
// let mut buf = [0u8; MAX_SEG_SIZE];
|
|
||||||
// let len = inp.read(&mut buf).await.unwrap_or(0);
|
|
||||||
// if len > 0 {
|
|
||||||
// Ok(SegData {
|
|
||||||
// bytes: Vec::from(&buf[..len]),
|
|
||||||
// is_last_segment: false,
|
|
||||||
// is_meow_encoded: false,
|
|
||||||
// })
|
|
||||||
// } else {
|
|
||||||
// Ok(SegData {
|
|
||||||
// bytes: vec![],
|
|
||||||
// is_last_segment: true,
|
|
||||||
// is_meow_encoded: false,
|
|
||||||
// })
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// pub async fn write<OUT>(&self, out: &mut OUT) -> Result<(), tokio::io::Error>
|
|
||||||
// where
|
|
||||||
// OUT: AsyncWrite + Unpin,
|
|
||||||
// {
|
|
||||||
// use tokio::io::AsyncWriteExt;
|
|
||||||
// out.write_all(&self.bytes).await
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue