• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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