blob: 8c2720b0025614473d76a85e4a26af3f32631f05 [file] [log] [blame]
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
]
);
}
}