| use crate::{tcp_option, TcpHeader, TcpOptionElement, TcpOptionWriteError, TcpOptionsIterator}; |
| |
| /// Options present in a TCP header. |
| /// |
| /// # Examples (reading) |
| /// |
| /// The underlying bytes can be accessed via the [`TcpOptions::as_slice`] method: |
| /// |
| /// ``` |
| /// use etherparse::{ |
| /// TcpOptions, |
| /// tcp_option::{KIND_WINDOW_SCALE, LEN_WINDOW_SCALE, KIND_END} |
| /// }; |
| /// |
| /// let tcp_options = TcpOptions::from([ |
| /// KIND_WINDOW_SCALE, LEN_WINDOW_SCALE, 2, KIND_END |
| /// ]); |
| /// |
| /// // `as_slice` allows access to the raw encoded data |
| /// let slice = tcp_options.as_slice(); |
| /// |
| /// assert_eq!( |
| /// slice, |
| /// [KIND_WINDOW_SCALE, LEN_WINDOW_SCALE, 2, KIND_END] |
| /// ); |
| /// ``` |
| /// |
| /// It also possible to iterate over the decoded [`TcpOptionElement`]s |
| /// by calling [`TcpOptions::elements_iter`]: |
| /// |
| /// ``` |
| /// use etherparse::{ |
| /// TcpOptions, |
| /// TcpOptionElement::WindowScale, |
| /// tcp_option::{KIND_WINDOW_SCALE, LEN_WINDOW_SCALE, KIND_END} |
| /// }; |
| /// |
| /// let tcp_options = TcpOptions::from([ |
| /// KIND_WINDOW_SCALE, LEN_WINDOW_SCALE, 2, KIND_END |
| /// ]); |
| /// |
| /// // `elements_iter` allows iteration over the decoded elements |
| /// // and decoding errors |
| /// let mut iter = tcp_options.elements_iter(); |
| /// |
| /// assert_eq!( |
| /// iter.collect::<Vec<_>>(), |
| /// vec![Ok(WindowScale(2))] |
| /// ); |
| /// ``` |
| /// |
| /// # Examples (constructing) |
| /// |
| /// Arrays of type `[u8;4]`, `[u8;8]`, `[u8;12]`, `[u8;16]`, `[u8;20]`, |
| /// `[u8;24]`, `[u8;28]`, `[u8;32]`, `[u8;36]`, `[u8;40]` can directly be |
| /// converted with the `from` or `into` methods to [`TcpOptions`]: |
| /// |
| /// ``` |
| /// use etherparse::TcpOptions; |
| /// |
| /// // static sized arrays of size 4,8,... 40 can directly be converted |
| /// // via `from` or `into` |
| /// let options: TcpOptions = [1,2,3,4].into(); |
| /// |
| /// assert_eq!(&options[..], &[1,2,3,4]); |
| /// ``` |
| /// |
| /// Slices can be converted with `try_from` or `try_into` into [`TcpOptions`]. |
| /// If the len of 40 bytes is exceeded an error is returned and if the |
| /// len is not a multiple of 4 the len is automatically increased to the next |
| /// multiple of 4 value and the data filled up with zeroes (equivalent to the |
| /// TCP END option): |
| /// |
| /// ``` |
| /// use etherparse::TcpOptions; |
| /// { |
| /// let data = [1u8,2,3,4,5,6,7,8]; |
| /// |
| /// // slices can be converted into TcpOptions via `try_from` or `try_into` |
| /// let options: TcpOptions = (&data[..]).try_into().unwrap(); |
| /// |
| /// assert_eq!(options.as_slice(), &data); |
| /// } |
| /// { |
| /// let data = [1u8]; |
| /// |
| /// // len is automatically increased to a multiple of 4 (filled |
| /// // with 0, also known as the END TCP option). |
| /// let options = TcpOptions::try_from(&data[..]).unwrap(); |
| /// |
| /// assert_eq!(options.as_slice(), &[1, 0, 0, 0]); |
| /// } |
| /// { |
| /// use etherparse::TcpOptionWriteError::NotEnoughSpace; |
| /// |
| /// let data = [0u8;41]; // 41 bytes |
| /// |
| /// // slices with a len bigger then 40 cause an error |
| /// let result = TcpOptions::try_from(&data[..]); |
| /// assert_eq!(result, Err(NotEnoughSpace(41))); |
| /// } |
| /// ``` |
| /// |
| /// Slices containing [`TcpOptionElement`]s can also be converted via |
| /// `try_from` or `try_into` as long as the encoded elements are within |
| /// 40 bytes: |
| /// |
| /// ``` |
| /// use etherparse::{ |
| /// TcpOptions, |
| /// tcp_option::{KIND_WINDOW_SCALE, LEN_WINDOW_SCALE, KIND_NOOP, KIND_END}, |
| /// TcpOptionElement::{Noop, WindowScale} |
| /// }; |
| /// |
| /// let elements = [WindowScale(123), Noop, Noop]; |
| /// |
| /// // try_from encodes the options into the "on the wire" format |
| /// let options = TcpOptions::try_from(&elements[..]).unwrap(); |
| /// |
| /// assert_eq!( |
| /// options.as_slice(), |
| /// &[ |
| /// KIND_WINDOW_SCALE, LEN_WINDOW_SCALE, 123, KIND_NOOP, |
| /// KIND_NOOP, KIND_END, KIND_END, KIND_END |
| /// ] |
| /// ); |
| /// ``` |
| #[derive(Clone)] |
| pub struct TcpOptions { |
| /// Number of bytes in the buffer. |
| pub(crate) len: u8, |
| |
| /// Buffer containing the options of the header |
| /// (note that the `len` field defines the actual length). Use |
| /// the options() method if you want to get a slice that has |
| /// the actual length of the options. |
| pub(crate) buf: [u8; 40], |
| } |
| |
| impl TcpOptions { |
| /// Maximum number of bytes that can be part of an TCP options. |
| pub const MAX_LEN: usize = 40; |
| |
| /// Constructs a new empty TcpOptions. |
| #[inline] |
| pub fn new() -> TcpOptions { |
| TcpOptions { |
| len: 0, |
| buf: [0; 40], |
| } |
| } |
| |
| /// Tries to convert an `u8` slice into [`TcpOptions`]. |
| /// |
| /// # Examples |
| /// |
| /// Slices with a length that is a multiple of 4 and a length not |
| /// bigger than 40 can be converted one-to-one: |
| /// |
| /// ``` |
| /// use etherparse::TcpOptions; |
| /// |
| /// let data = [1u8,2,3,4,5,6,7,8]; |
| /// let options = TcpOptions::try_from_slice(&data[..]).unwrap(); |
| /// assert_eq!(options.as_slice(), &data); |
| /// ``` |
| /// |
| /// If the length is not a multiple of 4 it is automatically filled |
| /// up with `0` (value of TCP option END) to the next multiple of 4: |
| /// |
| /// ``` |
| /// use etherparse::TcpOptions; |
| /// { |
| /// let data = [1u8]; |
| /// let options = TcpOptions::try_from(&data[..]).unwrap(); |
| /// // 3 bytes of zero added so the len is a multiple of 4 |
| /// assert_eq!(options.as_slice(), &[1, 0, 0, 0]); |
| /// } |
| /// ``` |
| /// |
| /// In case more than 40 bytes are passed as input an error is returned: |
| /// |
| /// ``` |
| /// use etherparse::{ |
| /// TcpOptions, |
| /// TcpOptionWriteError::NotEnoughSpace |
| /// }; |
| /// |
| /// let data = [0u8;41]; // 41 bytes |
| /// |
| /// // slices with a len bigger then 40 cause an error |
| /// let result = TcpOptions::try_from(&data[..]); |
| /// assert_eq!(result, Err(NotEnoughSpace(41))); |
| /// ``` |
| pub fn try_from_slice(slice: &[u8]) -> Result<TcpOptions, TcpOptionWriteError> { |
| // check length |
| if Self::MAX_LEN < slice.len() { |
| Err(TcpOptionWriteError::NotEnoughSpace(slice.len())) |
| } else { |
| let len = slice.len() as u8; |
| |
| // reset all to zero to ensure padding |
| Ok(TcpOptions { |
| len: ((len >> 2) << 2) |
| + if 0 != len & 0b11 { |
| // NOTE: If the slice length is not a multiple of |
| // 4 the length is automatically increased to be |
| // a multiple of 4 and the data is filled up with |
| // zeroes. |
| 4 |
| } else { |
| 0 |
| }, |
| buf: { |
| let mut buf = [0; 40]; |
| buf[..slice.len()].copy_from_slice(slice); |
| buf |
| }, |
| }) |
| } |
| } |
| |
| /// Tries to convert [`crate::TcpOptionElement`] into serialized |
| /// form as [`TcpOptions`]. |
| /// |
| /// # Example |
| /// |
| /// ``` |
| /// use etherparse::{ |
| /// TcpOptions, |
| /// tcp_option::{KIND_WINDOW_SCALE, LEN_WINDOW_SCALE, KIND_NOOP, KIND_END}, |
| /// TcpOptionElement::{Noop, WindowScale} |
| /// }; |
| /// |
| /// let elements = [WindowScale(123), Noop, Noop]; |
| /// |
| /// // try_from encodes the options into the "on the wire" format |
| /// let options = TcpOptions::try_from_elements(&elements[..]).unwrap(); |
| /// |
| /// assert_eq!( |
| /// options.as_slice(), |
| /// &[ |
| /// KIND_WINDOW_SCALE, LEN_WINDOW_SCALE, 123, KIND_NOOP, |
| /// // padding in form of "KIND_END" (0) is automatically added |
| /// // so the resulting options length is a multiple of 4 |
| /// KIND_NOOP, KIND_END, KIND_END, KIND_END |
| /// ] |
| /// ); |
| /// ``` |
| pub fn try_from_elements( |
| elements: &[TcpOptionElement], |
| ) -> Result<TcpOptions, TcpOptionWriteError> { |
| // calculate the required size of the options |
| use crate::TcpOptionElement::*; |
| let required_len = elements.iter().fold(0, |acc, ref x| { |
| acc + match x { |
| Noop => 1, |
| MaximumSegmentSize(_) => 4, |
| WindowScale(_) => 3, |
| SelectiveAcknowledgementPermitted => 2, |
| SelectiveAcknowledgement(_, rest) => rest.iter().fold(10, |acc2, ref y| match y { |
| None => acc2, |
| Some(_) => acc2 + 8, |
| }), |
| Timestamp(_, _) => 10, |
| } |
| }); |
| |
| if Self::MAX_LEN < required_len { |
| Err(TcpOptionWriteError::NotEnoughSpace(required_len)) |
| } else { |
| // reset the options to null |
| let mut buf = [0u8; TcpOptions::MAX_LEN]; |
| let mut len: usize = 0; |
| |
| // write the options to the buffer |
| use tcp_option::*; |
| for element in elements { |
| match element { |
| Noop => { |
| buf[len] = KIND_NOOP; |
| len += 1; |
| } |
| MaximumSegmentSize(value) => { |
| // determine insertion area |
| let t = &mut buf[len..len + 4]; |
| |
| // insert data |
| let value = value.to_be_bytes(); |
| t[0] = KIND_MAXIMUM_SEGMENT_SIZE; |
| t[1] = 4; |
| t[2] = value[0]; |
| t[3] = value[1]; |
| |
| len += 4; |
| } |
| WindowScale(value) => { |
| // determine insertion area |
| let t = &mut buf[len..len + 3]; |
| |
| // write data |
| t[0] = KIND_WINDOW_SCALE; |
| t[1] = 3; |
| t[2] = *value; |
| |
| len += 3; |
| } |
| SelectiveAcknowledgementPermitted => { |
| // determine insertion area |
| let insert = &mut buf[len..len + 2]; |
| |
| // write data |
| insert[0] = KIND_SELECTIVE_ACK_PERMITTED; |
| insert[1] = 2; |
| |
| len += 2; |
| } |
| SelectiveAcknowledgement(first, rest) => { |
| //write guaranteed data |
| { |
| let t = &mut buf[len..len + 10]; |
| len += 10; |
| |
| t[0] = KIND_SELECTIVE_ACK; |
| //write the length |
| t[1] = rest.iter().fold(10, |acc, ref y| match y { |
| None => acc, |
| Some(_) => acc + 8, |
| }); |
| // write first |
| t[2..6].copy_from_slice(&first.0.to_be_bytes()); |
| t[6..10].copy_from_slice(&first.1.to_be_bytes()); |
| } |
| //write the rest |
| for v in rest { |
| match v { |
| None => {} |
| Some((a, b)) => { |
| // determine insertion area |
| let t = &mut buf[len..len + 8]; |
| |
| // insert |
| t[0..4].copy_from_slice(&a.to_be_bytes()); |
| t[4..8].copy_from_slice(&b.to_be_bytes()); |
| |
| len += 8; |
| } |
| } |
| } |
| } |
| Timestamp(a, b) => { |
| let t = &mut buf[len..len + 10]; |
| |
| t[0] = KIND_TIMESTAMP; |
| t[1] = 10; |
| t[2..6].copy_from_slice(&a.to_be_bytes()); |
| t[6..10].copy_from_slice(&b.to_be_bytes()); |
| |
| len += 10; |
| } |
| } |
| } |
| // set the new data offset |
| if (len > 0) && (0 != len & 0b11) { |
| len = (len & (!0b11)) + 4; |
| } |
| // done |
| Ok(TcpOptions { |
| len: len as u8, |
| buf, |
| }) |
| } |
| } |
| |
| /// The number of 32 bit words in the TCP Header & TCP header options. |
| /// |
| /// This indicates where the data begins relative to the start of an |
| /// TCP header in multiples of 4 bytes. This number is |
| /// present in the `data_offset` field of the header and defines |
| /// the length of the tcp options present. |
| /// |
| /// # Example |
| /// |
| /// ``` |
| /// use etherparse::TcpOptions; |
| /// |
| /// { |
| /// let options = TcpOptions::try_from_slice(&[]).unwrap(); |
| /// // in case there are no options the minimum size of the tcp |
| /// // is returned. |
| /// assert_eq!(5, options.data_offset()); |
| /// } |
| /// { |
| /// let options = TcpOptions::try_from_slice(&[1,2,3,4,5,6,7,8]).unwrap(); |
| /// // otherwise the base TCP header size plus the number of 4 byte |
| /// // words in the options is returned |
| /// assert_eq!(5 + 2, options.data_offset()); |
| /// } |
| /// ``` |
| #[inline] |
| pub fn data_offset(&self) -> u8 { |
| TcpHeader::MIN_DATA_OFFSET + (self.len >> 2) |
| } |
| |
| /// Number of bytes in the buffer as an unsigned 8 bit integer. |
| #[inline] |
| pub fn len_u8(&self) -> u8 { |
| self.len |
| } |
| |
| /// Number of bytes in the buffer. |
| #[inline] |
| pub fn len(&self) -> usize { |
| self.len as usize |
| } |
| |
| /// Returns true if the options contain no elements. |
| #[inline] |
| pub fn is_empty(&self) -> bool { |
| 0 == self.len |
| } |
| |
| /// Slice containing the options. |
| #[inline] |
| pub fn as_slice(&self) -> &[u8] { |
| debug_assert!(self.len <= 40); |
| // SAFETY: Safe as all constructing methods verify len to be less then 40. |
| unsafe { core::slice::from_raw_parts(self.buf.as_ptr(), self.len()) } |
| } |
| |
| /// Mutable slice containing the options. |
| #[inline] |
| pub fn as_mut_slice(&mut self) -> &mut [u8] { |
| debug_assert!(self.len <= 40); |
| // SAFETY: Safe as all constructing methods verify len to be less then 40. |
| unsafe { core::slice::from_raw_parts_mut(self.buf.as_mut_ptr(), self.len()) } |
| } |
| |
| /// Returns an iterator that allows to iterate through the |
| /// decoded option elements. |
| /// |
| /// # Example |
| /// |
| /// ``` |
| /// use etherparse::{ |
| /// TcpOptions, |
| /// TcpOptionElement::{Noop, WindowScale} |
| /// }; |
| /// |
| /// let options = TcpOptions::try_from(&[WindowScale(123), Noop, Noop][..]).unwrap(); |
| /// |
| /// let mut v = Vec::with_capacity(3); |
| /// for re in options.elements_iter() { |
| /// v.push(re); |
| /// } |
| /// assert_eq!(v, vec![Ok(WindowScale(123)), Ok(Noop), Ok(Noop)]); |
| /// ``` |
| #[inline] |
| pub fn elements_iter(&self) -> TcpOptionsIterator { |
| TcpOptionsIterator { |
| options: self.as_slice(), |
| } |
| } |
| } |
| |
| impl Default for TcpOptions { |
| #[inline] |
| fn default() -> Self { |
| Self { |
| len: 0, |
| buf: [0; 40], |
| } |
| } |
| } |
| |
| impl core::cmp::Eq for TcpOptions {} |
| impl PartialEq for TcpOptions { |
| fn eq(&self, other: &Self) -> bool { |
| self.as_slice() == other.as_slice() |
| } |
| } |
| |
| impl<'a> TryFrom<&'a [u8]> for TcpOptions { |
| type Error = TcpOptionWriteError; |
| |
| #[inline] |
| fn try_from(value: &'a [u8]) -> Result<Self, Self::Error> { |
| TcpOptions::try_from_slice(value) |
| } |
| } |
| |
| impl<'a> TryFrom<&'a [TcpOptionElement]> for TcpOptions { |
| type Error = TcpOptionWriteError; |
| |
| #[inline] |
| fn try_from(value: &'a [TcpOptionElement]) -> Result<Self, Self::Error> { |
| TcpOptions::try_from_elements(value) |
| } |
| } |
| |
| impl core::fmt::Debug for TcpOptions { |
| fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { |
| self.elements_iter().fmt(f) |
| } |
| } |
| |
| impl core::hash::Hash for TcpOptions { |
| fn hash<H: core::hash::Hasher>(&self, state: &mut H) { |
| self.as_slice().hash(state); |
| } |
| } |
| |
| impl core::cmp::PartialOrd for TcpOptions { |
| fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> { |
| Some(self.as_slice().cmp(other.as_slice())) |
| } |
| } |
| |
| impl core::cmp::Ord for TcpOptions { |
| fn cmp(&self, other: &Self) -> core::cmp::Ordering { |
| self.as_slice().cmp(other.as_slice()) |
| } |
| } |
| |
| impl core::ops::Deref for TcpOptions { |
| type Target = [u8]; |
| |
| #[inline] |
| fn deref(&self) -> &[u8] { |
| self.as_slice() |
| } |
| } |
| |
| impl AsRef<TcpOptions> for TcpOptions { |
| #[inline] |
| fn as_ref(&self) -> &TcpOptions { |
| self |
| } |
| } |
| |
| impl AsMut<TcpOptions> for TcpOptions { |
| #[inline] |
| fn as_mut(&mut self) -> &mut TcpOptions { |
| self |
| } |
| } |
| |
| impl AsRef<[u8]> for TcpOptions { |
| #[inline] |
| fn as_ref(&self) -> &[u8] { |
| self.as_slice() |
| } |
| } |
| |
| impl AsMut<[u8]> for TcpOptions { |
| #[inline] |
| fn as_mut(&mut self) -> &mut [u8] { |
| self.as_mut_slice() |
| } |
| } |
| |
| macro_rules! from_static_array { |
| ($x:expr) => { |
| impl From<[u8; $x]> for TcpOptions { |
| #[inline] |
| fn from(values: [u8; $x]) -> Self { |
| let mut result = TcpOptions { |
| len: $x, |
| buf: [0; 40], |
| }; |
| let r = result.buf.as_mut_ptr() as *mut [u8; $x]; |
| unsafe { |
| *r = values; |
| } |
| result |
| } |
| } |
| }; |
| } |
| |
| from_static_array!(4); |
| from_static_array!(8); |
| from_static_array!(12); |
| from_static_array!(16); |
| from_static_array!(20); |
| from_static_array!(24); |
| from_static_array!(28); |
| from_static_array!(32); |
| from_static_array!(36); |
| |
| impl From<[u8; 40]> for TcpOptions { |
| fn from(values: [u8; 40]) -> Self { |
| TcpOptions { |
| len: 40, |
| buf: values, |
| } |
| } |
| } |
| |
| #[cfg(test)] |
| mod test { |
| use super::*; |
| use crate::test_gens::tcp_options_any; |
| use core::ops::Deref; |
| use proptest::prelude::*; |
| use std::format; |
| |
| #[test] |
| fn new() { |
| assert_eq!( |
| TcpOptions::new(), |
| TcpOptions { |
| len: 0, |
| buf: [0; 40] |
| } |
| ); |
| } |
| |
| #[test] |
| fn try_from_slice() { |
| let actual = TcpOptions::try_from_slice(&[1, 2, 3, 4][..]); |
| assert_eq!(actual, Ok(TcpOptions::from([1, 2, 3, 4]))); |
| } |
| |
| #[test] |
| fn try_from_elements() { |
| use crate::tcp_option::KIND_NOOP; |
| use crate::TcpOptionElement::Noop; |
| let actual = TcpOptions::try_from_elements(&[Noop, Noop, Noop, Noop][..]); |
| assert_eq!( |
| actual, |
| Ok(TcpOptions::from([ |
| KIND_NOOP, KIND_NOOP, KIND_NOOP, KIND_NOOP |
| ])) |
| ); |
| } |
| |
| proptest! { |
| #[test] |
| fn data_offset( |
| options in tcp_options_any() |
| ) { |
| assert_eq!( |
| (5 + ((options.len as u64) / 4)) as u8, |
| options.data_offset() |
| ); |
| } |
| } |
| proptest! { |
| #[test] |
| fn len( |
| options in tcp_options_any() |
| ) { |
| assert_eq!(options.len(), usize::from(options.len)); |
| } |
| } |
| |
| proptest! { |
| #[test] |
| fn len_u8( |
| options in tcp_options_any() |
| ) { |
| assert_eq!(options.len_u8(), options.len); |
| } |
| } |
| |
| proptest! { |
| #[test] |
| fn is_empty( |
| options in tcp_options_any() |
| ) { |
| assert_eq!(options.is_empty(), 0 == options.len); |
| } |
| } |
| |
| #[test] |
| fn as_slice() { |
| let options = TcpOptions::from([1, 2, 3, 4]); |
| assert_eq!(options.as_slice(), &[1, 2, 3, 4][..]); |
| } |
| |
| #[test] |
| fn as_mut_slice() { |
| let mut options = TcpOptions::from([1, 2, 3, 4]); |
| let r = options.as_mut_slice(); |
| r[0] = 5; |
| assert_eq!(options.as_slice(), &[5, 2, 3, 4][..]); |
| } |
| |
| #[test] |
| fn options_iterator() { |
| let options = TcpOptions::from([1, 2, 3, 4]); |
| assert_eq!( |
| options.elements_iter(), |
| TcpOptionsIterator { |
| options: &[1, 2, 3, 4][..] |
| } |
| ); |
| } |
| |
| #[test] |
| fn default() { |
| let actual: TcpOptions = Default::default(); |
| assert_eq!(0, actual.len); |
| assert_eq!([0u8; 40], actual.buf); |
| } |
| |
| #[test] |
| fn try_from() { |
| // from slice |
| { |
| let actual = TcpOptions::try_from(&[1, 2, 3, 4][..]); |
| assert_eq!(actual, Ok(TcpOptions::from([1, 2, 3, 4]))); |
| } |
| // from elements |
| { |
| use crate::tcp_option::KIND_NOOP; |
| use crate::TcpOptionElement::Noop; |
| let actual = TcpOptions::try_from(&[Noop, Noop, Noop, Noop][..]); |
| assert_eq!( |
| actual, |
| Ok(TcpOptions::from([ |
| KIND_NOOP, KIND_NOOP, KIND_NOOP, KIND_NOOP |
| ])) |
| ); |
| } |
| } |
| |
| #[test] |
| fn debug_fmt() { |
| use crate::tcp_option::KIND_NOOP; |
| let data = [KIND_NOOP, KIND_NOOP, KIND_NOOP, KIND_NOOP]; |
| let options = TcpOptions::from(data.clone()); |
| assert_eq!( |
| format!("{:?}", TcpOptionsIterator { options: &data[..] }), |
| format!("{:?}", options) |
| ); |
| } |
| |
| #[test] |
| fn clone_eq_hash_ord() { |
| let a = TcpOptions::from([1u8, 2, 3, 4]); |
| assert_eq!(a, a.clone()); |
| assert_ne!(a, TcpOptions::from([5u8, 6, 7, 8])); |
| { |
| use core::hash::{Hash, Hasher}; |
| use std::collections::hash_map::DefaultHasher; |
| let a_hash = { |
| let mut hasher = DefaultHasher::new(); |
| a.hash(&mut hasher); |
| hasher.finish() |
| }; |
| let b_hash = { |
| let mut hasher = DefaultHasher::new(); |
| a.hash(&mut hasher); |
| hasher.finish() |
| }; |
| assert_eq!(a_hash, b_hash); |
| } |
| { |
| use core::cmp::Ordering; |
| assert_eq!(a.cmp(&a), Ordering::Equal); |
| } |
| } |
| |
| #[test] |
| pub fn partial_cmp() { |
| use core::cmp::Ordering; |
| let a = TcpOptions::from([1u8, 2, 3, 4]); |
| assert_eq!(a.partial_cmp(&a), Some(Ordering::Equal)); |
| } |
| |
| #[test] |
| fn deref() { |
| let a = TcpOptions::from([1u8, 2, 3, 4]); |
| assert_eq!(a.deref(), &[1u8, 2, 3, 4][..]); |
| } |
| |
| #[test] |
| fn as_ref() { |
| // TcpOptions ref |
| { |
| let a = TcpOptions::from([1u8, 2, 3, 4]); |
| let b: &TcpOptions = a.as_ref(); |
| assert_eq!(b, &TcpOptions::from([1u8, 2, 3, 4])); |
| } |
| // slice ref |
| { |
| let a = TcpOptions::from([1u8, 2, 3, 4]); |
| let b: &[u8] = a.as_ref(); |
| assert_eq!(b, &[1u8, 2, 3, 4]); |
| } |
| } |
| |
| #[test] |
| fn as_mut() { |
| // TcpOptions ref |
| { |
| let mut a = TcpOptions::from([1u8, 2, 3, 4]); |
| let b: &mut TcpOptions = a.as_mut(); |
| *b = TcpOptions::from([5u8, 6, 7, 8]); |
| assert_eq!(a, TcpOptions::from([5u8, 6, 7, 8])); |
| } |
| // slice ref |
| { |
| let mut a = TcpOptions::from([1u8, 2, 3, 4]); |
| let b: &mut [u8] = a.as_mut(); |
| assert_eq!(b, &[1u8, 2, 3, 4]); |
| b[0] = 5; |
| assert_eq!(a, TcpOptions::from([5u8, 2, 3, 4])); |
| } |
| } |
| |
| #[test] |
| fn from() { |
| assert_eq!(TcpOptions::from([1u8, 2, 3, 4]).as_slice(), &[1u8, 2, 3, 4]); |
| assert_eq!( |
| TcpOptions::from([1u8, 2, 3, 4, 5, 6, 7, 8]).as_slice(), |
| &[1u8, 2, 3, 4, 5, 6, 7, 8] |
| ); |
| assert_eq!( |
| TcpOptions::from([1u8, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]).as_slice(), |
| &[1u8, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12] |
| ); |
| assert_eq!( |
| TcpOptions::from([1u8, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]).as_slice(), |
| &[1u8, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16] |
| ); |
| assert_eq!( |
| TcpOptions::from([ |
| 1u8, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20 |
| ]) |
| .as_slice(), |
| &[1u8, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20] |
| ); |
| assert_eq!( |
| TcpOptions::from([ |
| 1u8, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, |
| 23, 24 |
| ]) |
| .as_slice(), |
| &[ |
| 1u8, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, |
| 23, 24 |
| ] |
| ); |
| assert_eq!( |
| TcpOptions::from([ |
| 1u8, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, |
| 23, 24, 25, 26, 27, 28 |
| ]) |
| .as_slice(), |
| &[ |
| 1u8, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, |
| 23, 24, 25, 26, 27, 28 |
| ] |
| ); |
| assert_eq!( |
| TcpOptions::from([ |
| 1u8, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, |
| 23, 24, 25, 26, 27, 28, 29, 30, 31, 32 |
| ]) |
| .as_slice(), |
| &[ |
| 1u8, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, |
| 23, 24, 25, 26, 27, 28, 29, 30, 31, 32 |
| ] |
| ); |
| assert_eq!( |
| TcpOptions::from([ |
| 1u8, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, |
| 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36 |
| ]) |
| .as_slice(), |
| &[ |
| 1u8, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, |
| 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36 |
| ] |
| ); |
| assert_eq!( |
| TcpOptions::from([ |
| 1u8, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, |
| 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40 |
| ]) |
| .as_slice(), |
| &[ |
| 1u8, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, |
| 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40 |
| ] |
| ); |
| } |
| } |