use {
core::{ num::NonZeroU16, ops::Range },
crate::types::*,
};
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Span {
pub start: usize,
pub length: NonZeroU16,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Frame {
pub channel: ChannelId,
pub sequence: SequenceId,
pub payload: Payload,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Payload {
Check {
ids: Span,
},
Checked {
lost: Option<Span>,
},
InitHandshake {
desirable_window: NonZeroU16
},
EndHandshake {
window: NonZeroU16
},
Packet {
data: Span,
},
Disconnect,
KeepAlive,
}
impl Span {
/// Create Span object with zero start and specified length.
///
/// # Safety
///
/// Same safety as `Self::new_unchecked`
pub const unsafe fn only_length_unchecked(length: u16) -> Self {
Self::new_unchecked(0, length)
}
/// Try create Span object with zero start and specified length.
///
/// Returns `None` if `length == 0`.
pub const fn try_only_length(length: u16) -> Option<Self> {
Self::try_new(0, length)
}
/// Create Span object with zero start and speicfied length.
///
/// Panics when length == 0.
pub const fn only_length(length: u16) -> Self {
Self::new(0, length)
}
/// Convert Span into exclusive range.
///
/// ```rust
/// use medusa_proto::frame::Span;
///
/// let array = [0_u8, 1, 2, 3, 4];
/// let span = Span::new(1, 2);
/// let slice = &array[span.into_range()];
///
/// assert!(slice == &[1, 2]);
/// ```
pub const fn into_range(self) -> Range<usize> {
self.start..(self.start + self.length.get() as usize)
}
/// Convert Span into tuple containing start and length.
///
/// ```rust
/// use medusa_proto::frame::Span;
///
/// let span = Span::new(0, 2);
/// let (start, length) = span.into_tuple();
///
/// assert_eq!(start, 0);
/// assert_eq!(length, 2.try_into().unwrap());
/// ```
pub const fn into_tuple(self) -> (usize, NonZeroU16) {
(self.start, self.length)
}
/// Try create Span object. Returns None if `length == 0`.
///
/// ```rust
/// use medusa_proto::frame::Span;
///
/// assert!(Span::try_new(0, 0).is_none());
/// assert!(Span::try_new(0, 1).is_some());
/// ```
#[allow(clippy::question_mark)]
pub const fn try_new(start: usize, length: u16) -> Option<Span> {
Some(Self {
start,
// const try currently is not stable
length: if let Some(value) = NonZeroU16::new(length) {
value
} else {
return None
}
})
}
/// Create Span object. The main difference from `Span::new` or `Span::try_new` is that
/// length type is NonZeroU16 instead of u16. This eliminates zero check for already non zero number.
///
/// ```rust
/// use medusa_proto::frame::Span;
///
/// let span = Span::new_nonzero(0, 10.try_into().unwrap());
/// assert_eq!(span.start, 0);
/// assert_eq!(span.length, 10.try_into().unwrap());
/// ```
pub const fn new_nonzero(start: usize, length: NonZeroU16) -> Self {
Self { start, length }
}
/// Create Span object without any zero checks for `length`.
///
/// # Safety
///
/// This will result in undefined behavior of `length == 0`.
///
/// # Examples
///
/// This is ok.
///
/// ```
/// use medusa_proto::frame::Span;
///
/// let span = unsafe { Span::new_unchecked(0, 10) };
/// assert_eq!(span.start, 0);
/// assert_eq!(span.length, 10.try_into().unwrap());
/// ```
///
/// But this is undefined behavior
///
/// ```no_run
/// use medusa_proto::frame::Span;
/// use core::num::NonZeroU16;
///
/// let span = unsafe { Span::new_unchecked(0, 0) };
/// assert_eq!(span.start, 0);
/// assert_eq!(span.length, unsafe { NonZeroU16::new_unchecked(0) }); // passed! (Probably, behavior is undefined after all)
/// ```
pub const unsafe fn new_unchecked(start: usize, length: u16) -> Self {
Self { start, length: NonZeroU16::new_unchecked(length) }
}
/// Create Span object.
/// Panics if `length == 0`.
///
/// This is ok.
/// ```rust
/// use medusa_proto::frame::Span;
///
/// let span = Span::new(0, 1);
/// assert_eq!(span.start, 0);
/// assert_eq!(span.length, 1.try_into().unwrap());
/// ```
///
/// But this will panic
///
/// ```should_panic
/// use medusa_proto::frame::Span;
/// use std::panic::catch_unwind;
///
/// let result = Span::new(0, 0); // panicked
/// ```
pub const fn new(start: usize, length: u16) -> Self {
if let Some(instance) = Self::try_new(start, length) {
instance
} else {
panic!("`length` should not be zero")
}
}
}
impl Payload {
pub const DEFAULT_WINDOW: NonZeroU16 = unsafe { NonZeroU16::new_unchecked(4096) };
pub const INIT_HANDSHAKE: u8 = 0;
pub const END_HANDSHAKE: u8 = 1;
pub const PACKET: u8 = 2;
pub const DISCONNECT: u8 = 3;
pub const CHECK: u8 = 4;
pub const CHECKED: u8 = 5;
pub const KEEP_ALIVE: u8 = 6;
}