• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 use crate::err::ValueTooBigError;
2 
3 /// The fragment offset is a 13 bit unsigned integer indicating the stating
4 /// position of the payload of a packet relative to the originally fragmented
5 /// packet payload.
6 ///
7 /// This value can be present in an [`crate::Ipv4Header`] or an
8 /// [`crate::Ipv6FragmentHeader`].
9 ///
10 /// # Example Usage:
11 ///
12 /// ```
13 /// use etherparse::IpFragOffset;
14 ///
15 /// // try into
16 /// {
17 ///     let frag_offset: IpFragOffset = 123.try_into().unwrap();
18 ///     assert_eq!(frag_offset.value(), 123);
19 ///
20 ///     // fragment offset can always be converted back to an u16
21 ///     let value: u16 = frag_offset.into();
22 ///     assert_eq!(123, value);
23 /// }
24 ///
25 /// // via try_new
26 /// {
27 ///     let frag_offset = IpFragOffset::try_new(123).unwrap();
28 ///     assert_eq!(frag_offset.value(), 123);
29 ///
30 ///     // note that only 13 bit numbers are allowed (meaning
31 ///     // 0b0001_1111_1111_1111 is the maximum allowed value)
32 ///     use etherparse::err::{ValueTooBigError, ValueType};
33 ///     assert_eq!(
34 ///         IpFragOffset::try_new(IpFragOffset::MAX_U16 + 1),
35 ///         Err(ValueTooBigError{
36 ///             actual: IpFragOffset::MAX_U16 + 1,
37 ///             max_allowed: IpFragOffset::MAX_U16,
38 ///             value_type: ValueType::IpFragmentOffset,
39 ///         })
40 ///     );
41 /// }
42 ///
43 /// // via new_unchecked
44 /// {
45 ///     // in case you are sure the number does not exceed the max
46 ///     // you can use the unsafe new_unchecked function
47 ///     let frag_offset = unsafe {
48 ///         // please make sure that the value is not greater than IpFragOffset::MAX_U16
49 ///         // before calling this method
50 ///         IpFragOffset::new_unchecked(123)
51 ///     };
52 ///     assert_eq!(frag_offset.value(), 123);
53 /// }
54 /// ```
55 #[derive(Copy, Clone, Default, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
56 pub struct IpFragOffset(u16);
57 
58 impl IpFragOffset {
59     /// IpFragOffset with value 0.
60     pub const ZERO: IpFragOffset = IpFragOffset(0);
61 
62     /// Maximum value of an IP fragmentation offset.
63     pub const MAX_U16: u16 = 0b0001_1111_1111_1111;
64 
65     /// Tries to create an [`IpFragOffset`] and checks that the passed value
66     /// is smaller or equal than [`IpFragOffset::MAX_U16`] (13 bit unsigned integer).
67     ///
68     /// In case the passed value is bigger then what can be represented in an 13 bit
69     /// integer an error is returned. Otherwise an `Ok` containing the [`IpFragOffset`].
70     ///
71     /// ```
72     /// use etherparse::IpFragOffset;
73     ///
74     /// let frag_offset = IpFragOffset::try_new(123).unwrap();
75     /// assert_eq!(frag_offset.value(), 123);
76     ///
77     /// // if a number that can not be represented in an 13 bit integer
78     /// // gets passed in an error is returned
79     /// use etherparse::err::{ValueTooBigError, ValueType};
80     /// assert_eq!(
81     ///     IpFragOffset::try_new(IpFragOffset::MAX_U16 + 1),
82     ///     Err(ValueTooBigError{
83     ///         actual: IpFragOffset::MAX_U16 + 1,
84     ///         max_allowed: IpFragOffset::MAX_U16,
85     ///         value_type: ValueType::IpFragmentOffset,
86     ///     })
87     /// );
88     /// ```
89     #[inline]
try_new(value: u16) -> Result<IpFragOffset, ValueTooBigError<u16>>90     pub const fn try_new(value: u16) -> Result<IpFragOffset, ValueTooBigError<u16>> {
91         use crate::err::ValueType::IpFragmentOffset;
92         if value <= IpFragOffset::MAX_U16 {
93             Ok(IpFragOffset(value))
94         } else {
95             Err(ValueTooBigError {
96                 actual: value,
97                 max_allowed: IpFragOffset::MAX_U16,
98                 value_type: IpFragmentOffset,
99             })
100         }
101     }
102 
103     /// Creates an [`IpFragOffset`] without checking that the value
104     /// is smaller or equal than [`IpFragOffset::MAX_U16`] (13 bit unsigned integer).
105     /// The caller must guarantee that `value <= IpFragOffset::MAX_U16`.
106     ///
107     /// # Safety
108     ///
109     /// `value` must be smaller or equal than [`IpFragOffset::MAX_U16`]
110     /// otherwise the behavior of functions or data structures relying
111     /// on this pre-requirement is undefined.
112     #[inline]
new_unchecked(value: u16) -> IpFragOffset113     pub const unsafe fn new_unchecked(value: u16) -> IpFragOffset {
114         debug_assert!(value <= IpFragOffset::MAX_U16);
115         IpFragOffset(value)
116     }
117 
118     /// Returns the underlying unsigned 13 bit value as an `u16` value.
119     #[inline]
value(self) -> u16120     pub const fn value(self) -> u16 {
121         self.0
122     }
123 
124     /// Returns the offset in bytes (offset raw value multiplied by 8).
125     #[inline]
byte_offset(self) -> u16126     pub const fn byte_offset(self) -> u16 {
127         self.0 << 3
128     }
129 }
130 
131 impl core::fmt::Display for IpFragOffset {
132     #[inline]
fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result133     fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
134         self.0.fmt(f)
135     }
136 }
137 
138 impl From<IpFragOffset> for u16 {
139     #[inline]
from(value: IpFragOffset) -> Self140     fn from(value: IpFragOffset) -> Self {
141         value.0
142     }
143 }
144 
145 impl TryFrom<u16> for IpFragOffset {
146     type Error = ValueTooBigError<u16>;
147 
148     #[inline]
try_from(value: u16) -> Result<Self, Self::Error>149     fn try_from(value: u16) -> Result<Self, Self::Error> {
150         use crate::err::ValueType::IpFragmentOffset;
151         if value <= IpFragOffset::MAX_U16 {
152             Ok(IpFragOffset(value))
153         } else {
154             Err(Self::Error {
155                 actual: value,
156                 max_allowed: IpFragOffset::MAX_U16,
157                 value_type: IpFragmentOffset,
158             })
159         }
160     }
161 }
162 
163 #[cfg(test)]
164 mod test {
165     use super::*;
166     use core::hash::{Hash, Hasher};
167     use proptest::prelude::*;
168     use std::format;
169 
170     #[test]
derived_traits()171     fn derived_traits() {
172         // copy & clone
173         {
174             let a = IpFragOffset(123);
175             let b = a;
176             assert_eq!(a, b);
177             assert_eq!(a.clone(), a);
178         }
179 
180         // default
181         {
182             let actual: IpFragOffset = Default::default();
183             assert_eq!(actual.value(), 0);
184         }
185 
186         // debug
187         {
188             let a = IpFragOffset(123);
189             assert_eq!(format!("{:?}", a), format!("IpFragOffset(123)"));
190         }
191 
192         // ord & partial ord
193         {
194             use core::cmp::Ordering;
195             let a = IpFragOffset(123);
196             let b = a;
197             assert_eq!(a.cmp(&b), Ordering::Equal);
198             assert_eq!(a.partial_cmp(&b), Some(Ordering::Equal));
199         }
200 
201         // hash
202         {
203             use std::collections::hash_map::DefaultHasher;
204             let a = {
205                 let mut hasher = DefaultHasher::new();
206                 IpFragOffset(123).hash(&mut hasher);
207                 hasher.finish()
208             };
209             let b = {
210                 let mut hasher = DefaultHasher::new();
211                 IpFragOffset(123).hash(&mut hasher);
212                 hasher.finish()
213             };
214             assert_eq!(a, b);
215         }
216     }
217 
218     proptest! {
219         #[test]
220         fn try_new(
221             valid_value in 0..=0b0001_1111_1111_1111u16,
222             invalid_value in 0b0010_0000_0000_0000u16..=u16::MAX
223         ) {
224             use crate::err::{ValueType, ValueTooBigError};
225             assert_eq!(
226                 valid_value,
227                 IpFragOffset::try_new(valid_value).unwrap().value()
228             );
229             assert_eq!(
230                 IpFragOffset::try_new(invalid_value).unwrap_err(),
231                 ValueTooBigError{
232                     actual: invalid_value,
233                     max_allowed: 0b0001_1111_1111_1111,
234                     value_type:  ValueType::IpFragmentOffset
235                 }
236             );
237         }
238     }
239 
240     proptest! {
241         #[test]
242         fn try_from(
243             valid_value in 0..=0b0001_1111_1111_1111u16,
244             invalid_value in 0b0010_0000_0000_0000u16..=u16::MAX
245         ) {
246             use crate::err::{ValueType, ValueTooBigError};
247             // try_into
248             {
249                 let actual: IpFragOffset = valid_value.try_into().unwrap();
250                 assert_eq!(actual.value(), valid_value);
251 
252                 let err: Result<IpFragOffset, ValueTooBigError<u16>> = invalid_value.try_into();
253                 assert_eq!(
254                     err.unwrap_err(),
255                     ValueTooBigError{
256                         actual: invalid_value,
257                         max_allowed: 0b0001_1111_1111_1111,
258                         value_type:  ValueType::IpFragmentOffset
259                     }
260                 );
261             }
262             // try_from
263             {
264                 assert_eq!(
265                     IpFragOffset::try_from(valid_value).unwrap().value(),
266                     valid_value
267                 );
268 
269                 assert_eq!(
270                     IpFragOffset::try_from(invalid_value).unwrap_err(),
271                     ValueTooBigError{
272                         actual: invalid_value,
273                         max_allowed: 0b0001_1111_1111_1111,
274                         value_type:  ValueType::IpFragmentOffset
275                     }
276                 );
277             }
278         }
279     }
280 
281     proptest! {
282         #[test]
283         fn new_unchecked(valid_value in 0..=0b0001_1111_1111_1111u16) {
284             assert_eq!(
285                 valid_value,
286                 unsafe {
287                     IpFragOffset::new_unchecked(valid_value).value()
288                 }
289             );
290         }
291     }
292 
293     proptest! {
294         #[test]
295         fn fmt(valid_value in 0..=0b0001_1111_1111_1111u16) {
296             assert_eq!(format!("{}", IpFragOffset(valid_value)), format!("{}", valid_value));
297         }
298     }
299 
300     proptest! {
301         #[test]
302         fn from(valid_value in 0..=0b0001_1111_1111_1111u16,) {
303             let frag_offset = IpFragOffset::try_new(valid_value).unwrap();
304             let actual: u16 = frag_offset.into();
305             assert_eq!(actual, valid_value);
306         }
307     }
308 }
309