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