use crate::core::{ChannelId, Error, ErrorKind, MessageId, PacketHeader, Result};
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct MessageKey {
pub channel_id: ChannelId,
pub message_id: MessageId,
}
impl MessageKey {
#[must_use]
pub const fn new(channel_id: ChannelId, message_id: MessageId) -> Self {
Self {
channel_id,
message_id,
}
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct FragmentRange {
pub offset: u32,
pub len: u32,
}
impl FragmentRange {
#[must_use]
pub const fn new(offset: u32, len: u32) -> Self {
Self { offset, len }
}
#[must_use]
pub const fn end(self) -> u32 {
self.offset.saturating_add(self.len)
}
#[must_use]
pub const fn is_empty(self) -> bool {
self.len == 0
}
#[must_use]
pub const fn fits_in(self, message_len: u32) -> bool {
self.offset <= message_len && self.end() <= message_len
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct MessageFragment {
pub key: MessageKey,
pub message_len: u32,
pub range: FragmentRange,
}
impl MessageFragment {
#[must_use]
pub const fn new(key: MessageKey, message_len: u32, range: FragmentRange) -> Self {
Self {
key,
message_len,
range,
}
}
pub fn try_from_packet_header(header: PacketHeader, payload_len: usize) -> Result<Self> {
let len = u32::try_from(payload_len).map_err(|_| Error::new(ErrorKind::Reliability))?;
let message_len =
u32::try_from(header.message_len).map_err(|_| Error::new(ErrorKind::Reliability))?;
let fragment_offset = u32::try_from(header.fragment_offset)
.map_err(|_| Error::new(ErrorKind::Reliability))?;
let range = FragmentRange::new(fragment_offset, len);
if !range.fits_in(message_len) {
return Err(Error::new(ErrorKind::Reliability));
}
Ok(Self::new(
MessageKey::new(header.channel_id, header.message_id),
message_len,
range,
))
}
}
#[cfg(test)]
mod tests {
use crate::core::{ChannelId, Flags, MessageFlags, MessageId, PacketHeader, PacketNumber};
use super::{FragmentRange, MessageFragment, MessageKey};
#[test]
fn fragment_range_must_fit_message_len() {
assert!(FragmentRange::new(2, 3).fits_in(5));
assert!(!FragmentRange::new(3, 3).fits_in(5));
}
#[test]
fn packet_header_maps_to_message_fragment() {
let header = PacketHeader::data(
PacketNumber::new(3),
Flags::ACK_ELICITING,
ChannelId::new(7),
MessageId::new(9),
8,
2,
MessageFlags::EMPTY,
);
let fragment = MessageFragment::try_from_packet_header(header, 3).unwrap();
assert_eq!(
fragment.key,
MessageKey::new(ChannelId::new(7), MessageId::new(9))
);
assert_eq!(fragment.range, FragmentRange::new(2, 3));
}
}