WIP: Actual send and receive data #5

Draft
luca.fulchir wants to merge 8 commits from transport into main
4 changed files with 273 additions and 156 deletions
Showing only changes of commit 9ec52a0151 - Show all commits

18
flake.lock generated
View File

@ -20,11 +20,11 @@
}, },
"nixpkgs": { "nixpkgs": {
"locked": { "locked": {
"lastModified": 1741862977, "lastModified": 1742751704,
"narHash": "sha256-prZ0M8vE/ghRGGZcflvxCu40ObKaB+ikn74/xQoNrGQ=", "narHash": "sha256-rBfc+H1dDBUQ2mgVITMGBPI1PGuCznf9rcWX/XIULyE=",
"owner": "nixos", "owner": "nixos",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "cdd2ef009676ac92b715ff26630164bb88fec4e0", "rev": "f0946fa5f1fb876a9dc2e1850d9d3a4e3f914092",
"type": "github" "type": "github"
}, },
"original": { "original": {
@ -36,11 +36,11 @@
}, },
"nixpkgs-unstable": { "nixpkgs-unstable": {
"locked": { "locked": {
"lastModified": 1741851582, "lastModified": 1742889210,
"narHash": "sha256-cPfs8qMccim2RBgtKGF+x9IBCduRvd/N5F4nYpU0TVE=", "narHash": "sha256-hw63HnwnqU3ZQfsMclLhMvOezpM7RSB0dMAtD5/sOiw=",
"owner": "nixos", "owner": "nixos",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "6607cf789e541e7873d40d3a8f7815ea92204f32", "rev": "698214a32beb4f4c8e3942372c694f40848b360d",
"type": "github" "type": "github"
}, },
"original": { "original": {
@ -65,11 +65,11 @@
] ]
}, },
"locked": { "locked": {
"lastModified": 1742005800, "lastModified": 1742956365,
"narHash": "sha256-6wuOGWkyW6R4A6Th9NMi6WK2jjddvZt7V2+rLPk6L3o=", "narHash": "sha256-Slrqmt6kJ/M7Z/ce4ebQWsz2aeEodrX56CsupOEPoz0=",
"owner": "oxalica", "owner": "oxalica",
"repo": "rust-overlay", "repo": "rust-overlay",
"rev": "028cd247a6375f83b94adc33d83676480fc9c294", "rev": "a0e3395c63cdbc9c1ec17915f8328c077c79c4a1",
"type": "github" "type": "github"
}, },
"original": { "original": {

View File

@ -29,6 +29,38 @@ pub enum Kind {
UUDL, UUDL,
} }
/// Tracking for a contiguous set of data
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
pub enum Fragment {
/// Beginning, no end
Start((SequenceStart, SequenceEnd)),
/// Neither beginning nor end
Middle((SequenceStart, SequenceEnd)),
/// No beginning, but with end
End((SequenceStart, SequenceEnd)),
/// both beginning and end, waiting to be delivered to the user
Ready((SequenceStart, SequenceEnd)),
/// both beginning and end, already delivered to the user
Delivered((SequenceStart, SequenceEnd)),
/// both beginning and end, data might not be available anymore
Deallocated((SequenceStart, SequenceEnd)),
}
impl Fragment {
// FIXME: sequence start/end?
/// extract the sequences from the fragment
pub fn get_seqs(&self) -> (SequenceStart, SequenceEnd) {
match self {
Fragment::Start((f, t))
| Fragment::Middle((f, t))
| Fragment::End((f, t))
| Fragment::Ready((f, t))
| Fragment::Delivered((f, t))
| Fragment::Deallocated((f, t)) => (*f, *t),
}
}
}
/// Id of the stream /// Id of the stream
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)] #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub struct ID(pub u16); pub struct ID(pub u16);
@ -60,7 +92,7 @@ impl ChunkLen {
} }
//TODO: make pub? //TODO: make pub?
#[derive(Debug, Copy, Clone)] #[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub(crate) struct SequenceStart(pub(crate) Sequence); pub(crate) struct SequenceStart(pub(crate) Sequence);
impl SequenceStart { impl SequenceStart {
pub(crate) fn offset(&self, seq: Sequence) -> usize { pub(crate) fn offset(&self, seq: Sequence) -> usize {
@ -86,7 +118,7 @@ impl ::core::ops::AddAssign<u32> for SequenceStart {
} }
// SequenceEnd is INCLUSIVE // SequenceEnd is INCLUSIVE
#[derive(Debug, Copy, Clone)] #[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub(crate) struct SequenceEnd(pub(crate) Sequence); pub(crate) struct SequenceEnd(pub(crate) Sequence);
impl ::core::ops::Add<u32> for SequenceEnd { impl ::core::ops::Add<u32> for SequenceEnd {

View File

@ -67,10 +67,8 @@ impl ReliableOrderedBytestream {
let mut ret = Vec::with_capacity(self.data.len()); let mut ret = Vec::with_capacity(self.data.len());
ret.extend_from_slice(first); ret.extend_from_slice(first);
ret.extend_from_slice(second); ret.extend_from_slice(second);
self.window_start = self.window_start = self.window_start + (ret.len() as u32);
self.window_start + (ret.len() as u32); self.window_end = self.window_end + (ret.len() as u32);
self.window_end =
self.window_end + (ret.len() as u32);
self.data.clear(); self.data.clear();
return ret; return ret;
} }
@ -78,10 +76,8 @@ impl ReliableOrderedBytestream {
let last_missing_idx = self.missing.len() - 1; let last_missing_idx = self.missing.len() - 1;
let mut last_missing = &mut self.missing[last_missing_idx]; let mut last_missing = &mut self.missing[last_missing_idx];
last_missing.1 = last_missing.1 + (data_len as u32); last_missing.1 = last_missing.1 + (data_len as u32);
self.window_start = self.window_start = self.window_start + (data_len as u32);
self.window_start + (data_len as u32); self.window_end = self.window_end + (data_len as u32);
self.window_end =
self.window_end + (data_len as u32);
let mut ret = Vec::with_capacity(data_len); let mut ret = Vec::with_capacity(data_len);
let (first, second) = self.data[..].split_at(self.pivot as usize); let (first, second) = self.data[..].split_at(self.pivot as usize);
@ -157,8 +153,7 @@ impl ReliableOrderedBytestream {
// [....chunk....] // [....chunk....]
// [...missing...] // [...missing...]
copy_ranges.push((offset, (missing_to - 0))); copy_ranges.push((offset, (missing_to - 0)));
el.1 = el.1 = el.0 + (((offset_end - missing_from) - 1) as u32);
el.0 + (((offset_end - missing_from) - 1) as u32);
} }
} }
} }

View File

@ -7,80 +7,70 @@
use crate::{ use crate::{
connection::stream::{ connection::stream::{
Chunk, Error, Sequence, SequenceEnd, SequenceStart, StreamData, Chunk, Error, Fragment, Sequence, SequenceEnd, SequenceStart,
StreamData,
}, },
enc::Random, enc::Random,
}; };
use ::core::{
cmp::{self, Ordering},
marker::PhantomData,
num::Wrapping,
ops,
};
use ::std::collections::{BTreeMap, VecDeque}; use ::std::collections::{BTreeMap, VecDeque};
#[cfg(test)] #[cfg(test)]
mod tests; mod tests;
#[derive(Debug, PartialEq, Eq)]
enum Fragment {
Start((Sequence, Sequence)),
Middle((Sequence, Sequence)),
End((Sequence, Sequence)),
Full((Sequence, Sequence)),
Delivered((Sequence, Sequence)),
}
impl Fragment {
// FIXME: sequence start/end?
fn get_seqs(&self) -> (Sequence, Sequence) {
match self {
Fragment::Start((f, t))
| Fragment::Middle((f, t))
| Fragment::End((f, t))
| Fragment::Full((f, t))
| Fragment::Delivered((f, t)) => (*f, *t),
}
}
fn is_start(&self) -> bool {
match self {
Fragment::Start(_) | Fragment::Full(_) | Fragment::Delivered(_) => {
true
}
Fragment::End(_) | Fragment::Middle(_) => false,
}
}
fn is_end(&self) -> bool {
match self {
Fragment::End(_) | Fragment::Full(_) | Fragment::Delivered(_) => {
true
}
Fragment::Start(_) | Fragment::Middle(_) => false,
}
}
}
/*
impl ::std::cmp::PartialEq<Sequence> for Fragment {
fn eq(&self, other: &Sequence) -> bool {
self.get_seq() == *other
}
}
impl ::std::cmp::PartialOrd<Sequence> for Fragment {
fn partial_cmp(&self, other: &Sequence) -> Option<::std::cmp::Ordering> {
Some(self.get_seq().cmp(other))
}
}
impl ::std::cmp::PartialOrd<Fragment> for Fragment {
fn partial_cmp(&self, other: &Fragment) -> Option<::std::cmp::Ordering> {
Some(self.get_seq().cmp(&other.get_seq()))
}
}
impl Ord for Fragment {
fn cmp(&self, other: &Fragment) -> ::std::cmp::Ordering {
self.get_seq().cmp(&other.get_seq())
}
}
*/
type Timer = u64; type Timer = u64;
pub struct Data<'a> {
data_first: &'a mut [u8],
data_second: &'a mut [u8],
pub from: SequenceStart,
//pub(crate) stream: &'a Uud,
pub(crate) stream: ::std::ptr::NonNull<Uud>,
_not_send_sync: PhantomData<*const ()>,
}
impl<'a> Drop for Data<'a> {
fn drop(&mut self) {
// safe because we are !Send
#[allow(unsafe_code)]
unsafe {
let uud = self.stream.as_mut();
uud.free(
self.from,
(self.data_first.len() + self.data_second.len()) as u32,
);
}
}
}
impl<'a> ops::Index<usize> for Data<'a> {
type Output = u8;
fn index(&self, index: usize) -> &Self::Output {
let first_len = self.data_first.len();
if index < first_len {
return &self.data_first[index];
}
return &self.data_second[index - first_len];
}
}
impl<'a> ops::IndexMut<usize> for Data<'a> {
fn index_mut(&mut self, index: usize) -> &mut Self::Output {
let first_len = self.data_first.len();
if index < first_len {
return &mut self.data_first[index];
}
return &mut self.data_second[index - first_len];
}
}
pub(crate) struct Uud { pub(crate) struct Uud {
pub(crate) window_start: SequenceStart, pub(crate) window_start: SequenceStart,
window_end: SequenceEnd, window_end: SequenceEnd,
@ -128,6 +118,78 @@ impl Uud {
pub(crate) fn window_size(&self) -> u32 { pub(crate) fn window_size(&self) -> u32 {
self.data.len() as u32 self.data.len() as u32
} }
pub(crate) fn get(&mut self) -> Option<Data> {
let self_ptr = ::std::ptr::NonNull::new(self).unwrap();
for track in self.track.iter_mut() {
if let Fragment::Ready((start, end)) = track.0 {
let data_from = (self.window_start.offset(start.0)
+ self.pivot as usize)
% self.data.len();
let data_to = (self.window_start.offset(end.0)
+ self.pivot as usize)
% self.data.len();
track.0 = Fragment::Delivered((start, end));
let first: &mut [u8];
let second: &mut [u8];
if data_from < data_to {
let (tmp_first, tmp_second) =
self.data.split_at_mut(data_to);
first = &mut tmp_first[data_from..];
second = &mut tmp_second[0..0];
} else {
let (tmp_second, tmp_first) =
self.data.split_at_mut(self.pivot as usize);
first = &mut tmp_first[(data_from - self.pivot as usize)..];
second = &mut tmp_second[..data_to];
}
return Some(Data {
from: start,
data_first: first,
data_second: second,
stream: self_ptr,
_not_send_sync: PhantomData::default(),
});
}
}
None
}
pub(crate) fn free(&mut self, from: SequenceStart, len: u32) {
if !from.0.is_between(self.window_start, self.window_end) {
return;
}
let mut first_keep = 0;
let mut last_sequence = self.window_start.0;
let mut deallocated = false;
for (idx, track) in self.track.iter_mut().enumerate() {
if let Fragment::Delivered((start, to)) = track.0 {
if start == from && to.0 == from.0 + len {
track.0 = Fragment::Deallocated((start, to));
deallocated = true;
if idx == first_keep {
first_keep = idx + 1;
last_sequence = to.0;
continue;
}
}
}
if idx == first_keep {
if let Fragment::Deallocated((_, to)) = track.0 {
first_keep = idx + 1;
last_sequence = to.0;
continue;
}
}
if deallocated {
break;
}
}
self.track.drain(..first_keep);
self.pivot = ((self.pivot as usize
+ self.window_start.offset(last_sequence))
% self.data.len()) as u32;
}
pub(crate) fn recv(&mut self, chunk: Chunk) -> Result<StreamData, Error> { pub(crate) fn recv(&mut self, chunk: Chunk) -> Result<StreamData, Error> {
let chunk_to = chunk.sequence + chunk.data.len() as u32; let chunk_to = chunk.sequence + chunk.data.len() as u32;
if !chunk if !chunk
@ -164,16 +226,14 @@ impl Uud {
let mut last_usable = self.window_end.0; let mut last_usable = self.window_end.0;
let mut ret = StreamData::NotReady; let mut ret = StreamData::NotReady;
let mut copy_data_idx_from = 0; let mut copy_data_idx_from = 0;
// FIXME: and on receiving first fragment after the second, or empty
// track?
for (idx, (fragment, _)) in self.track.iter_mut().enumerate().rev() { for (idx, (fragment, _)) in self.track.iter_mut().enumerate().rev() {
let (from, to) = fragment.get_seqs(); let (from, to) = fragment.get_seqs();
let to_next = to + 1; let to_next = to + 1;
match to_next.cmp_in_window(self.window_start, chunk.sequence) { match to_next.0.cmp_in_window(self.window_start, chunk.sequence) {
::core::cmp::Ordering::Equal => { Ordering::Equal => {
// `chunk` is immediately after `fragment` // `chunk` is immediately after `fragment`
if !chunk_to.is_between( if !chunk_to.is_between(
SequenceStart(to_next), SequenceStart(to_next.0),
SequenceEnd(last_usable), SequenceEnd(last_usable),
) { ) {
return Err(Error::Reconstructing); return Err(Error::Reconstructing);
@ -186,7 +246,7 @@ impl Uud {
return Err(Error::WrongFlags); return Err(Error::WrongFlags);
} }
if chunk_flag_end { if chunk_flag_end {
*fragment = Fragment::Full(( *fragment = Fragment::Ready((
from, from,
to + (data.len() as u32), to + (data.len() as u32),
)); ));
@ -217,16 +277,23 @@ impl Uud {
as usize; as usize;
} }
Fragment::End(_) Fragment::End(_)
| Fragment::Full(_) | Fragment::Ready(_)
| Fragment::Delivered(_) => { | Fragment::Delivered(_)
| Fragment::Deallocated(_) => {
if !chunk.flag_start { if !chunk.flag_start {
return Err(Error::WrongFlags); return Err(Error::WrongFlags);
} }
let toinsert = if chunk_flag_end { let toinsert = if chunk_flag_end {
ret = StreamData::Ready; ret = StreamData::Ready;
Fragment::Full((chunk.sequence, chunk_to)) Fragment::Ready((
SequenceStart(chunk.sequence),
SequenceEnd(chunk_to),
))
} else { } else {
Fragment::Start((chunk.sequence, chunk_to)) Fragment::Start((
SequenceStart(chunk.sequence),
SequenceEnd(chunk_to),
))
}; };
self.track.insert(idx + 1, (toinsert, 0)); self.track.insert(idx + 1, (toinsert, 0));
copy_data_idx_from = copy_data_idx_from =
@ -236,11 +303,11 @@ impl Uud {
} }
break; break;
} }
::core::cmp::Ordering::Less => { Ordering::Less => {
// there is a data hole between `chunk` and `fragment` // there is a data hole between `chunk` and `fragment`
if !chunk_to.is_between( if !chunk_to.is_between(
SequenceStart(to_next), SequenceStart(to_next.0),
SequenceEnd(last_usable), SequenceEnd(last_usable),
) { ) {
return Err(Error::Reconstructing); return Err(Error::Reconstructing);
@ -248,15 +315,27 @@ impl Uud {
let toinsert = if chunk.flag_start { let toinsert = if chunk.flag_start {
if chunk_flag_end { if chunk_flag_end {
ret = StreamData::Ready; ret = StreamData::Ready;
Fragment::Full((chunk.sequence, chunk_to)) Fragment::Ready((
SequenceStart(chunk.sequence),
SequenceEnd(chunk_to),
))
} else { } else {
Fragment::Start((chunk.sequence, chunk_to)) Fragment::Start((
SequenceStart(chunk.sequence),
SequenceEnd(chunk_to),
))
} }
} else { } else {
if chunk_flag_end { if chunk_flag_end {
Fragment::End((chunk.sequence, chunk_to)) Fragment::End((
SequenceStart(chunk.sequence),
SequenceEnd(chunk_to),
))
} else { } else {
Fragment::Middle((chunk.sequence, chunk_to)) Fragment::Middle((
SequenceStart(chunk.sequence),
SequenceEnd(chunk_to),
))
} }
}; };
self.track.insert(idx + 1, (toinsert, 0)); self.track.insert(idx + 1, (toinsert, 0));
@ -264,12 +343,12 @@ impl Uud {
chunk.sequence.diff_from(self.window_start.0) as usize; chunk.sequence.diff_from(self.window_start.0) as usize;
break; break;
} }
::core::cmp::Ordering::Greater => { Ordering::Greater => {
// to_next > chunk.sequence // to_next > chunk.sequence
// `fragment` is too new, need to look at older ones // `fragment` is too new, need to look at older ones
if from.cmp_in_window(self.window_start, chunk.sequence) if from.0.cmp_in_window(self.window_start, chunk.sequence)
!= ::core::cmp::Ordering::Greater != Ordering::Greater
{ {
// to_next > chunk.sequence >= from // to_next > chunk.sequence >= from
// overlapping not yet allowed // overlapping not yet allowed
@ -277,45 +356,45 @@ impl Uud {
} }
if idx == 0 { if idx == 0 {
// check if we can add before everything // check if we can add before everything
if chunk_to == from { if chunk_to == from.0 {
if fragment.is_start() { match fragment {
Fragment::Middle(_) => {
if chunk.flag_start {
*fragment = Fragment::Start((
SequenceStart(chunk.sequence),
to,
));
} else {
*fragment = Fragment::Middle((
SequenceStart(chunk.sequence),
to,
));
}
}
Fragment::End(_) => {
if chunk.flag_start {
*fragment = Fragment::Ready((
SequenceStart(chunk.sequence),
to,
));
ret = StreamData::Ready;
} else {
*fragment = Fragment::End((
SequenceStart(chunk.sequence),
to,
));
}
}
Fragment::Start(_)
| Fragment::Ready(_)
| Fragment::Delivered(_)
| Fragment::Deallocated(_) => {
if chunk_flag_end { if chunk_flag_end {
// add, don't merge // add, don't merge
} else { } else {
// fragment.start, but !chunk.end // fragment.start, but !chunk.end
return Err(Error::WrongFlags); return Err(Error::WrongFlags);
} }
} else {
if chunk_flag_end {
//chunk.end but !fragment.start
return Err(Error::WrongFlags);
} else {
if chunk.flag_start {
if fragment.is_end() {
*fragment = Fragment::Full((
chunk.sequence,
to,
));
ret = StreamData::Ready;
} else {
*fragment = Fragment::Start((
chunk.sequence,
to,
));
}
} else {
if fragment.is_end() {
*fragment = Fragment::End((
chunk.sequence,
to,
));
} else {
*fragment = Fragment::Middle((
chunk.sequence,
to,
));
}
}
} }
} }
copy_data_idx_from = copy_data_idx_from =
@ -327,15 +406,27 @@ impl Uud {
let toinsert = if chunk.flag_start { let toinsert = if chunk.flag_start {
if chunk_flag_end { if chunk_flag_end {
ret = StreamData::Ready; ret = StreamData::Ready;
Fragment::Full((chunk.sequence, chunk_to)) Fragment::Ready((
SequenceStart(chunk.sequence),
SequenceEnd(chunk_to),
))
} else { } else {
Fragment::Start((chunk.sequence, chunk_to)) Fragment::Start((
SequenceStart(chunk.sequence),
SequenceEnd(chunk_to),
))
} }
} else { } else {
if chunk_flag_end { if chunk_flag_end {
Fragment::End((chunk.sequence, chunk_to)) Fragment::End((
SequenceStart(chunk.sequence),
SequenceEnd(chunk_to),
))
} else { } else {
Fragment::Middle((chunk.sequence, chunk_to)) Fragment::Middle((
SequenceStart(chunk.sequence),
SequenceEnd(chunk_to),
))
} }
}; };
self.track.insert(0, (toinsert, 0)); self.track.insert(0, (toinsert, 0));
@ -344,7 +435,7 @@ impl Uud {
as usize; as usize;
break; break;
} }
last_usable = from - 1; last_usable = from.0 - 1;
} }
} }
} }
@ -357,7 +448,7 @@ impl Uud {
let data_pivot = self.data.len() - data_idx_from; let data_pivot = self.data.len() - data_idx_from;
let (first, second) = data.split_at(data_pivot); let (first, second) = data.split_at(data_pivot);
self.data[data_idx_from..].copy_from_slice(&first); self.data[data_idx_from..].copy_from_slice(&first);
self.data[..data_idx_to].copy_from_slice(&data); self.data[..data_idx_to].copy_from_slice(&second);
} }
Ok(ret) Ok(ret)
} }
@ -419,9 +510,8 @@ impl UnreliableUnorderedDatagram {
ret.extend_from_slice(first); ret.extend_from_slice(first);
ret.extend_from_slice(second); ret.extend_from_slice(second);
self.window_start += ret.len() as u32; self.window_start += ret.len() as u32;
self.window_end = SequenceEnd(Sequence( self.window_end =
::core::num::Wrapping::<u32>(ret.len() as u32), SequenceEnd(Sequence(Wrapping::<u32>(ret.len() as u32)));
));
self.data.clear(); self.data.clear();
return ret; return ret;
} }
@ -434,7 +524,7 @@ impl UnreliableUnorderedDatagram {
let mut ret = Vec::with_capacity(data_len); let mut ret = Vec::with_capacity(data_len);
let (first, second) = self.data[..].split_at(self.pivot as usize); let (first, second) = self.data[..].split_at(self.pivot as usize);
let first_len = ::core::cmp::min(data_len, first.len()); let first_len = cmp::min(data_len, first.len());
let second_len = data_len - first_len; let second_len = data_len - first_len;
ret.extend_from_slice(&first[..first_len]); ret.extend_from_slice(&first[..first_len]);
@ -527,7 +617,7 @@ impl UnreliableUnorderedDatagram {
let to = to + 1; let to = to + 1;
if from <= first.len() { if from <= first.len() {
let first_from = from; let first_from = from;
let first_to = ::core::cmp::min(first.len(), to); let first_to = cmp::min(first.len(), to);
let data_first_from = from - offset; let data_first_from = from - offset;
let data_first_to = first_to - offset; let data_first_to = first_to - offset;
first[first_from..first_to] first[first_from..first_to]