use crate::{Buf, BufError, BufMut, BufResult, Codec, Cursor};
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Options {
length: u8,
data: [u8; 40],
position: usize,
}
impl Options {
#[inline]
pub fn length(&self) -> u8 {
self.length
}
}
impl Default for Options {
fn default() -> Self {
Self {
length: 0,
data: [0u8; 40],
position: 0,
}
}
}
impl TryFrom<&[u8]> for Options {
type Error = BufError;
fn try_from(slice: &[u8]) -> Result<Self, Self::Error> {
if slice.len() > 40 {
return Err(BufError::UnexpectedValue);
}
let mut data = [0u8; 40];
data[..slice.len()].copy_from_slice(slice);
Ok(Self {
length: slice.len() as u8,
data,
position: 0,
})
}
}
impl<const N: usize> TryFrom<&[u8; N]> for Options {
type Error = BufError;
fn try_from(slice: &[u8; N]) -> Result<Self, Self::Error> {
if slice.len() > 40 {
return Err(BufError::UnexpectedValue);
}
let mut data = [0u8; 40];
data[..slice.len()].copy_from_slice(slice);
Ok(Self {
length: slice.len() as u8,
data,
position: 0,
})
}
}
impl Codec<usize> for Options {
fn encode<W: BufMut>(&self, writer: &mut Cursor<W>, _: usize) -> BufResult<()> {
writer.write_slice(&self.data[..self.length as usize])
}
fn decode<R: Buf>(reader: &mut Cursor<R>, len: usize) -> BufResult<Self> {
if len > 40 {
return Err(BufError::UnexpectedValue);
}
let length = len as u8;
let mut data = [0u8; 40];
if length > 0 {
reader.read_into(&mut data[..length as usize])?;
}
Ok(Self {
length,
data,
position: 0,
})
}
}
impl Iterator for Options {
type Item = Result<self::Option, BufError>;
fn next(&mut self) -> core::option::Option<Self::Item> {
if self.position >= self.length as usize {
return None;
}
let slice = &self.data[self.position..self.length as usize];
let mut cursor = Cursor::new(slice);
let result = self::Option::decode(&mut cursor, ());
if result.is_ok() {
self.position += cursor.position();
} else {
self.position = self.length as usize;
}
Some(result)
}
}
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum Option {
EndOfOptionList(EndOfOptionList),
NoOperation(NoOperation),
Security(Security),
LooseSourceRoute(LooseSourceRoute),
StrictSourceRoute(StrictSourceRoute),
RecordRoute(RecordRoute),
StreamId(StreamId),
InternetTimestamp(InternetTimestamp),
RouterAlert(RouterAlert),
}
impl Codec for Option {
fn encode<W: BufMut>(&self, writer: &mut Cursor<W>, _: ()) -> BufResult<()> {
match self {
Option::EndOfOptionList(opt) => opt.encode(writer, ()),
Option::NoOperation(opt) => opt.encode(writer, ()),
Option::Security(opt) => opt.encode(writer, ()),
Option::LooseSourceRoute(opt) => opt.encode(writer, ()),
Option::StrictSourceRoute(opt) => opt.encode(writer, ()),
Option::RecordRoute(opt) => opt.encode(writer, ()),
Option::StreamId(opt) => opt.encode(writer, ()),
Option::InternetTimestamp(opt) => opt.encode(writer, ()),
Option::RouterAlert(opt) => opt.encode(writer, ()),
}
}
fn decode<R: Buf>(reader: &mut Cursor<R>, _: ()) -> BufResult<Self> {
let option_type_peek = reader.peek_u8()?;
match option_type_peek {
0 => Ok(Self::EndOfOptionList(EndOfOptionList::decode(reader, ())?)),
1 => Ok(Self::NoOperation(NoOperation::decode(reader, ())?)),
7 => Ok(Self::RecordRoute(RecordRoute::decode(reader, ())?)),
68 => Ok(Self::InternetTimestamp(InternetTimestamp::decode(
reader,
(),
)?)),
130 => Ok(Self::Security(Security::decode(reader, ())?)),
131 => Ok(Self::LooseSourceRoute(LooseSourceRoute::decode(
reader,
(),
)?)),
136 => Ok(Self::StreamId(StreamId::decode(reader, ())?)),
137 => Ok(Self::StrictSourceRoute(StrictSourceRoute::decode(
reader,
(),
)?)),
148 => Ok(Self::RouterAlert(RouterAlert::decode(reader, ())?)),
_ => Err(BufError::UnexpectedValue),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[repr(u8)]
pub enum Type {
EndOfOptionList = 0,
NoOperation = 1,
RecordRoute = 7,
InternetTimestamp = 68,
Security = 130,
LooseSourceRoute = 131,
StreamId = 136,
StrictSourceRoute = 137,
RouterAlert = 148,
}
impl Codec for Type {
fn encode<W: BufMut>(&self, writer: &mut Cursor<W>, _: ()) -> BufResult<()> {
(*self as u8).encode(writer, ())
}
fn decode<R: Buf>(reader: &mut Cursor<R>, _: ()) -> BufResult<Self> {
match u8::decode(reader, ())? {
x if x == Self::EndOfOptionList as u8 => Ok(Self::EndOfOptionList),
x if x == Self::NoOperation as u8 => Ok(Self::NoOperation),
x if x == Self::RecordRoute as u8 => Ok(Self::RecordRoute),
x if x == Self::InternetTimestamp as u8 => Ok(Self::InternetTimestamp),
x if x == Self::Security as u8 => Ok(Self::Security),
x if x == Self::LooseSourceRoute as u8 => Ok(Self::LooseSourceRoute),
x if x == Self::StreamId as u8 => Ok(Self::StreamId),
x if x == Self::StrictSourceRoute as u8 => Ok(Self::StrictSourceRoute),
x if x == Self::RouterAlert as u8 => Ok(Self::RouterAlert),
_ => Err(BufError::UnexpectedValue),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct EndOfOptionList;
impl EndOfOptionList {
pub const TYPE: Type = Type::EndOfOptionList;
}
impl Codec for EndOfOptionList {
fn encode<W: BufMut>(&self, writer: &mut Cursor<W>, _: ()) -> BufResult<()> {
Self::TYPE.encode(writer, ())
}
fn decode<R: Buf>(reader: &mut Cursor<R>, _: ()) -> BufResult<Self> {
if Type::decode(reader, ())? != Self::TYPE {
return Err(BufError::UnexpectedValue);
}
Ok(Self)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct NoOperation;
impl NoOperation {
pub const TYPE: Type = Type::NoOperation;
}
impl Codec for NoOperation {
fn encode<W: BufMut>(&self, writer: &mut Cursor<W>, _: ()) -> BufResult<()> {
Self::TYPE.encode(writer, ())
}
fn decode<R: Buf>(reader: &mut Cursor<R>, _: ()) -> BufResult<Self> {
if Type::decode(reader, ())? != Self::TYPE {
return Err(BufError::UnexpectedValue);
}
Ok(Self)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct RouterAlert {
pub value: u16,
}
impl RouterAlert {
pub const TYPE: Type = Type::RouterAlert;
}
impl Codec for RouterAlert {
fn encode<W: BufMut>(&self, writer: &mut Cursor<W>, _: ()) -> BufResult<()> {
Self::TYPE.encode(writer, ())?;
4u8.encode(writer, ())?;
self.value.encode(writer, ())
}
fn decode<R: Buf>(reader: &mut Cursor<R>, _: ()) -> BufResult<Self> {
if Type::decode(reader, ())? != Self::TYPE {
return Err(BufError::UnexpectedValue);
}
let _length = u8::decode(reader, ())?;
Ok(Self {
value: u16::decode(reader, ())?,
})
}
}
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct RecordRoute {
pub pointer: u8,
pub data_length: u8,
pub route_data: [u8; 37],
}
impl RecordRoute {
pub const TYPE: Type = Type::RecordRoute;
}
impl Codec for RecordRoute {
fn encode<W: BufMut>(&self, writer: &mut Cursor<W>, _: ()) -> BufResult<()> {
let total_length = 3u8 + self.data_length;
Self::TYPE.encode(writer, ())?;
total_length.encode(writer, ())?;
self.pointer.encode(writer, ())?;
writer.write_slice(&self.route_data[..self.data_length as usize])
}
fn decode<R: Buf>(reader: &mut Cursor<R>, _: ()) -> BufResult<Self> {
if Type::decode(reader, ())? != Self::TYPE {
return Err(BufError::UnexpectedValue);
}
let length = u8::decode(reader, ())?;
if length < 3 {
return Err(BufError::UnexpectedValue);
}
let data_length = length - 3;
if data_length > 37 {
return Err(BufError::UnexpectedValue);
}
let pointer = u8::decode(reader, ())?;
let mut route_data = [0u8; 37];
if data_length > 0 {
reader.read_into(&mut route_data[..data_length as usize])?;
}
Ok(Self {
pointer,
data_length,
route_data,
})
}
}
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct LooseSourceRoute {
pub pointer: u8,
pub data_length: u8,
pub route_data: [u8; 37],
}
impl LooseSourceRoute {
pub const TYPE: Type = Type::LooseSourceRoute;
}
impl Codec for LooseSourceRoute {
fn encode<W: BufMut>(&self, writer: &mut Cursor<W>, _: ()) -> BufResult<()> {
let total_length = 3u8 + self.data_length;
Self::TYPE.encode(writer, ())?;
total_length.encode(writer, ())?;
self.pointer.encode(writer, ())?;
writer.write_slice(&self.route_data[..self.data_length as usize])
}
fn decode<R: Buf>(reader: &mut Cursor<R>, _: ()) -> BufResult<Self> {
if Type::decode(reader, ())? != Self::TYPE {
return Err(BufError::UnexpectedValue);
}
let length = u8::decode(reader, ())?;
if length < 3 {
return Err(BufError::UnexpectedValue);
}
let data_length = length - 3;
if data_length > 37 {
return Err(BufError::UnexpectedValue);
}
let pointer = u8::decode(reader, ())?;
let mut route_data = [0u8; 37];
if data_length > 0 {
reader.read_into(&mut route_data[..data_length as usize])?;
}
Ok(Self {
pointer,
data_length,
route_data,
})
}
}
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct StrictSourceRoute {
pub pointer: u8,
pub data_length: u8,
pub route_data: [u8; 37],
}
impl StrictSourceRoute {
pub const TYPE: Type = Type::StrictSourceRoute;
}
impl Codec for StrictSourceRoute {
fn encode<W: BufMut>(&self, writer: &mut Cursor<W>, _: ()) -> BufResult<()> {
let total_length = 3u8 + self.data_length;
Self::TYPE.encode(writer, ())?;
total_length.encode(writer, ())?;
self.pointer.encode(writer, ())?;
writer.write_slice(&self.route_data[..self.data_length as usize])
}
fn decode<R: Buf>(reader: &mut Cursor<R>, _: ()) -> BufResult<Self> {
if Type::decode(reader, ())? != Self::TYPE {
return Err(BufError::UnexpectedValue);
}
let length = u8::decode(reader, ())?;
if length < 3 {
return Err(BufError::UnexpectedValue);
}
let data_length = length - 3;
if data_length > 37 {
return Err(BufError::UnexpectedValue);
}
let pointer = u8::decode(reader, ())?;
let mut route_data = [0u8; 37];
if data_length > 0 {
reader.read_into(&mut route_data[..data_length as usize])?;
}
Ok(Self {
pointer,
data_length,
route_data,
})
}
}
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct InternetTimestamp {
pub pointer: u8,
pub overflow_and_flags: u8,
pub data_length: u8,
pub data: [u8; 36],
}
impl InternetTimestamp {
pub const TYPE: Type = Type::InternetTimestamp;
}
impl Codec for InternetTimestamp {
fn encode<W: BufMut>(&self, writer: &mut Cursor<W>, _: ()) -> BufResult<()> {
let total_length = 4u8 + self.data_length;
Self::TYPE.encode(writer, ())?;
total_length.encode(writer, ())?;
self.pointer.encode(writer, ())?;
self.overflow_and_flags.encode(writer, ())?;
writer.write_slice(&self.data[..self.data_length as usize])
}
fn decode<R: Buf>(reader: &mut Cursor<R>, _: ()) -> BufResult<Self> {
if Type::decode(reader, ())? != Self::TYPE {
return Err(BufError::UnexpectedValue);
}
let length = u8::decode(reader, ())?;
if length < 4 {
return Err(BufError::UnexpectedValue);
}
let data_length = length - 4;
if data_length > 36 {
return Err(BufError::UnexpectedValue);
}
let pointer = u8::decode(reader, ())?;
let overflow_and_flags = u8::decode(reader, ())?;
let mut data = [0u8; 36];
if data_length > 0 {
reader.read_into(&mut data[..data_length as usize])?;
}
Ok(Self {
pointer,
overflow_and_flags,
data_length,
data,
})
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct StreamId {
pub id: u16,
}
impl StreamId {
pub const TYPE: Type = Type::StreamId;
}
impl Codec for StreamId {
fn encode<W: BufMut>(&self, writer: &mut Cursor<W>, _: ()) -> BufResult<()> {
Self::TYPE.encode(writer, ())?;
4u8.encode(writer, ())?;
self.id.encode(writer, ())
}
fn decode<R: Buf>(reader: &mut Cursor<R>, _: ()) -> BufResult<Self> {
if Type::decode(reader, ())? != Self::TYPE {
return Err(BufError::UnexpectedValue);
}
let _length = u8::decode(reader, ())?;
Ok(Self {
id: u16::decode(reader, ())?,
})
}
}
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Security {
pub data_length: u8,
pub data: [u8; 9],
}
impl Security {
pub const TYPE: Type = Type::Security;
}
impl Codec for Security {
fn encode<W: BufMut>(&self, writer: &mut Cursor<W>, _: ()) -> BufResult<()> {
let total_length = 2u8 + self.data_length;
Self::TYPE.encode(writer, ())?;
total_length.encode(writer, ())?;
writer.write_slice(&self.data[..self.data_length as usize])
}
fn decode<R: Buf>(reader: &mut Cursor<R>, _: ()) -> BufResult<Self> {
if Type::decode(reader, ())? != Self::TYPE {
return Err(BufError::UnexpectedValue);
}
let length = u8::decode(reader, ())?;
if length < 2 {
return Err(BufError::UnexpectedValue);
}
let data_length = length - 2;
if data_length > 9 {
return Err(BufError::UnexpectedValue);
}
let mut data = [0u8; 9];
if data_length > 0 {
reader.read_into(&mut data[..data_length as usize])?;
}
Ok(Self { data_length, data })
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{Codec, Cursor};
use core::fmt::Debug;
fn codec_roundtrip<T: Codec<C> + Debug + Eq, C: Copy>(
etalon_struct: T,
etalon_bytes: &[u8],
context: C,
) {
let mut encoded_bytes = vec![];
{
let writer = &mut Cursor::new(&mut encoded_bytes);
etalon_struct.encode(writer, context).unwrap();
}
assert_eq!(etalon_bytes, &encoded_bytes);
let decoded_struct = {
let reader = &mut Cursor::new(&encoded_bytes);
T::decode(reader, context).unwrap()
};
assert_eq!(etalon_struct, decoded_struct);
}
#[test]
fn end_of_option_list() {
let etalon_bytes = &[0x00];
let etalon_struct = Option::EndOfOptionList(EndOfOptionList);
codec_roundtrip(etalon_struct, etalon_bytes, ());
}
#[test]
fn no_operation() {
let etalon_bytes = &[0x01];
let etalon_struct = Option::NoOperation(NoOperation);
codec_roundtrip(etalon_struct, etalon_bytes, ());
}
#[test]
fn router_alert() {
let etalon_bytes = &[0x94, 0x04, 0x00, 0x00];
let etalon_struct = Option::RouterAlert(RouterAlert { value: 0 });
codec_roundtrip(etalon_struct, etalon_bytes, ());
}
#[test]
fn record_route() {
let etalon_bytes = &[0x07, 0x07, 0x04, 0xc0, 0xa8, 0x00, 0x01];
let mut route_data = [0u8; 37];
route_data[..4].copy_from_slice(&[0xc0, 0xa8, 0x00, 0x01]);
let etalon_struct = Option::RecordRoute(RecordRoute {
pointer: 4,
data_length: 4,
route_data,
});
codec_roundtrip(etalon_struct, etalon_bytes, ());
}
#[test]
fn loose_source_route() {
let etalon_bytes = &[0x83, 0x07, 0x04, 0xc0, 0xa8, 0x00, 0x01];
let mut route_data = [0u8; 37];
route_data[..4].copy_from_slice(&[0xc0, 0xa8, 0x00, 0x01]);
let etalon_struct = Option::LooseSourceRoute(LooseSourceRoute {
pointer: 4,
data_length: 4,
route_data,
});
codec_roundtrip(etalon_struct, etalon_bytes, ());
}
#[test]
fn strict_source_route() {
let etalon_bytes = &[0x89, 0x07, 0x04, 0xc0, 0xa8, 0x00, 0x01];
let mut route_data = [0u8; 37];
route_data[..4].copy_from_slice(&[0xc0, 0xa8, 0x00, 0x01]);
let etalon_struct = Option::StrictSourceRoute(StrictSourceRoute {
pointer: 4,
data_length: 4,
route_data,
});
codec_roundtrip(etalon_struct, etalon_bytes, ());
}
#[test]
fn internet_timestamp() {
let etalon_bytes = &[0x44, 0x08, 0x05, 0x00, 0x00, 0x00, 0x00, 0x01];
let mut data = [0u8; 36];
data[..4].copy_from_slice(&[0x00, 0x00, 0x00, 0x01]);
let etalon_struct = Option::InternetTimestamp(InternetTimestamp {
pointer: 5,
overflow_and_flags: 0,
data_length: 4,
data,
});
codec_roundtrip(etalon_struct, etalon_bytes, ());
}
#[test]
fn stream_id() {
let etalon_bytes = &[0x88, 0x04, 0x12, 0x34];
let etalon_struct = Option::StreamId(StreamId { id: 0x1234 });
codec_roundtrip(etalon_struct, etalon_bytes, ());
}
#[test]
fn security() {
let etalon_bytes = &[0x82, 0x06, 0x00, 0x01, 0x02, 0x03];
let mut data = [0u8; 9];
data[..4].copy_from_slice(&[0x00, 0x01, 0x02, 0x03]);
let etalon_struct = Option::Security(Security {
data_length: 4,
data,
});
codec_roundtrip(etalon_struct, etalon_bytes, ());
}
#[test]
fn options_struct_roundtrip() {
let etalon_bytes = &[0x07, 0x07, 0x04, 0xc0, 0xa8, 0x00, 0x01, 0x00];
let etalon_struct = Options::try_from(etalon_bytes).unwrap();
codec_roundtrip(etalon_struct, etalon_bytes, etalon_bytes.len());
}
#[test]
fn options_iterator() {
let bytes = &[0x07, 0x07, 0x04, 0xc0, 0xa8, 0x00, 0x01, 0x00];
let mut options = Options::try_from(bytes).unwrap();
let opt1 = options.next().unwrap().unwrap();
assert!(matches!(opt1, Option::RecordRoute(rr) if rr.pointer == 4 && rr.data_length == 4));
let opt2 = options.next().unwrap().unwrap();
assert!(matches!(opt2, Option::EndOfOptionList(_)));
assert!(options.next().is_none());
}
}