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