netstack3_base/tcp/
seqnum.rs1use core::convert::TryFrom as _;
8use core::num::TryFromIntError;
9use core::ops;
10
11use explicit::ResultExt as _;
12
13#[derive(Debug, PartialEq, Eq, Clone, Copy)]
26pub struct SeqNum(u32);
27
28impl ops::Add<i32> for SeqNum {
29 type Output = SeqNum;
30
31 fn add(self, rhs: i32) -> Self::Output {
32 let Self(lhs) = self;
33 Self(lhs.wrapping_add_signed(rhs))
34 }
35}
36
37impl ops::Sub<i32> for SeqNum {
38 type Output = SeqNum;
39
40 fn sub(self, rhs: i32) -> Self::Output {
41 let Self(lhs) = self;
42 Self(lhs.wrapping_add_signed(rhs.wrapping_neg()))
43 }
44}
45
46impl ops::Add<u32> for SeqNum {
47 type Output = SeqNum;
48
49 fn add(self, rhs: u32) -> Self::Output {
50 let Self(lhs) = self;
51 Self(lhs.wrapping_add(rhs))
52 }
53}
54
55impl ops::Sub<u32> for SeqNum {
56 type Output = SeqNum;
57
58 fn sub(self, rhs: u32) -> Self::Output {
59 let Self(lhs) = self;
60 Self(lhs.wrapping_sub(rhs))
61 }
62}
63
64impl ops::Sub<WindowSize> for SeqNum {
65 type Output = SeqNum;
66
67 fn sub(self, WindowSize(wnd): WindowSize) -> Self::Output {
68 self - i32::try_from(wnd).unwrap()
72 }
73}
74
75impl ops::Add<usize> for SeqNum {
76 type Output = SeqNum;
77
78 fn add(self, rhs: usize) -> Self::Output {
79 self + (rhs as u32)
87 }
88}
89
90impl ops::Sub for SeqNum {
91 type Output = i32;
94
95 fn sub(self, rhs: Self) -> Self::Output {
96 let Self(lhs) = self;
97 let Self(rhs) = rhs;
98 lhs.wrapping_sub(rhs) as i32
108 }
109}
110
111impl From<u32> for SeqNum {
112 fn from(x: u32) -> Self {
113 Self::new(x)
114 }
115}
116
117impl From<SeqNum> for u32 {
118 fn from(x: SeqNum) -> Self {
119 let SeqNum(x) = x;
120 x
121 }
122}
123
124impl SeqNum {
125 pub const fn new(x: u32) -> Self {
127 Self(x)
128 }
129}
130
131impl SeqNum {
132 pub fn before(self, other: SeqNum) -> bool {
136 self - other < 0
137 }
138
139 pub fn after(self, other: SeqNum) -> bool {
143 self - other > 0
144 }
145}
146
147#[derive(Debug, PartialEq, Eq, Clone, Copy, PartialOrd, Ord)]
156pub struct WindowSize(u32);
157
158impl WindowSize {
159 pub const MAX: WindowSize = WindowSize(1 << 30 - 1);
161 pub const ZERO: WindowSize = WindowSize(0);
163
164 pub const DEFAULT: WindowSize = WindowSize(65535);
168
169 pub const fn from_u32(wnd: u32) -> Option<Self> {
173 let WindowSize(max) = Self::MAX;
174 if wnd > max {
175 None
176 } else {
177 Some(Self(wnd))
178 }
179 }
180
181 pub fn saturating_add(self, rhs: u32) -> Self {
183 Self::from_u32(u32::from(self).saturating_add(rhs)).unwrap_or(Self::MAX)
184 }
185
186 pub fn new(wnd: usize) -> Option<Self> {
188 u32::try_from(wnd).ok_checked::<TryFromIntError>().and_then(WindowSize::from_u32)
189 }
190
191 pub fn checked_sub(self, diff: usize) -> Option<Self> {
193 usize::from(self).checked_sub(diff).and_then(Self::new)
199 }
200
201 pub fn saturating_sub(self, diff: usize) -> Self {
204 self.checked_sub(diff).unwrap_or(WindowSize::ZERO)
205 }
206
207 pub fn scale(self) -> WindowScale {
209 let WindowSize(size) = self;
210 let effective_bits = u8::try_from(32 - u32::leading_zeros(size)).unwrap();
211 let scale = WindowScale(effective_bits.saturating_sub(16));
212 scale
213 }
214}
215
216impl ops::Add<WindowSize> for SeqNum {
217 type Output = SeqNum;
218
219 fn add(self, WindowSize(wnd): WindowSize) -> Self::Output {
220 self + wnd
221 }
222}
223
224impl From<WindowSize> for u32 {
225 fn from(WindowSize(wnd): WindowSize) -> Self {
226 wnd
227 }
228}
229
230#[cfg(any(target_pointer_width = "32", target_pointer_width = "64"))]
231impl From<WindowSize> for usize {
232 fn from(WindowSize(wnd): WindowSize) -> Self {
233 wnd as usize
234 }
235}
236
237#[derive(Debug, PartialEq, Eq, Clone, Copy, Default)]
238pub struct WindowScale(u8);
244
245impl WindowScale {
246 pub const MAX: WindowScale = WindowScale(14);
248 pub const ZERO: WindowScale = WindowScale(0);
250
251 pub fn new(ws: u8) -> Option<Self> {
255 (ws <= Self::MAX.get()).then_some(WindowScale(ws))
256 }
257
258 pub fn get(&self) -> u8 {
260 let Self(ws) = self;
261 *ws
262 }
263}
264
265#[derive(Debug, PartialEq, Eq, Clone, Copy)]
266pub struct UnscaledWindowSize(u16);
271
272impl ops::Shl<WindowScale> for UnscaledWindowSize {
273 type Output = WindowSize;
274
275 fn shl(self, WindowScale(scale): WindowScale) -> Self::Output {
276 let UnscaledWindowSize(size) = self;
277 WindowSize::from_u32(u32::from(size) << scale).unwrap()
279 }
280}
281
282impl ops::Shr<WindowScale> for WindowSize {
283 type Output = UnscaledWindowSize;
284
285 fn shr(self, WindowScale(scale): WindowScale) -> Self::Output {
286 let WindowSize(size) = self;
287 UnscaledWindowSize(u16::try_from(size >> scale).unwrap_or(u16::MAX))
288 }
289}
290
291impl From<u16> for UnscaledWindowSize {
292 fn from(value: u16) -> Self {
293 Self(value)
294 }
295}
296
297impl From<UnscaledWindowSize> for u16 {
298 fn from(UnscaledWindowSize(value): UnscaledWindowSize) -> Self {
299 value
300 }
301}
302
303#[cfg(feature = "testutils")]
304mod testutils {
305 use super::*;
306
307 impl UnscaledWindowSize {
308 pub fn from_usize(size: usize) -> Self {
312 UnscaledWindowSize::from(u16::try_from(size).unwrap())
313 }
314
315 pub fn from_u32(size: u32) -> Self {
319 UnscaledWindowSize::from(u16::try_from(size).unwrap())
320 }
321 }
322}
323
324#[cfg(test)]
325mod tests {
326 use alloc::format;
327
328 use proptest::arbitrary::any;
329 use proptest::strategy::{Just, Strategy};
330 use proptest::test_runner::Config;
331 use proptest::{prop_assert, prop_assert_eq, proptest};
332 use proptest_support::failed_seeds_no_std;
333 use test_case::test_case;
334
335 use super::super::segment::MAX_PAYLOAD_AND_CONTROL_LEN;
336 use super::*;
337
338 fn arb_seqnum() -> impl Strategy<Value = SeqNum> {
339 any::<u32>().prop_map(SeqNum::from)
340 }
341
342 fn arb_seqnum_trans_tripple() -> impl Strategy<Value = (SeqNum, SeqNum, SeqNum)> {
345 arb_seqnum().prop_flat_map(|a| {
346 (1..=MAX_PAYLOAD_AND_CONTROL_LEN).prop_flat_map(move |diff_a_b| {
347 let b = a + diff_a_b;
348 (1..=MAX_PAYLOAD_AND_CONTROL_LEN - diff_a_b).prop_flat_map(move |diff_b_c| {
349 let c = b + diff_b_c;
350 (Just(a), Just(b), Just(c))
351 })
352 })
353 })
354 }
355
356 #[test_case(WindowSize::new(1).unwrap() => (UnscaledWindowSize::from(1), WindowScale::default()))]
357 #[test_case(WindowSize::new(65535).unwrap() => (UnscaledWindowSize::from(65535), WindowScale::default()))]
358 #[test_case(WindowSize::new(65536).unwrap() => (UnscaledWindowSize::from(32768), WindowScale::new(1).unwrap()))]
359 #[test_case(WindowSize::new(65537).unwrap() => (UnscaledWindowSize::from(32768), WindowScale::new(1).unwrap()))]
360 fn window_scale(size: WindowSize) -> (UnscaledWindowSize, WindowScale) {
361 let scale = size.scale();
362 (size >> scale, scale)
363 }
364
365 proptest! {
366 #![proptest_config(Config {
367 failure_persistence: failed_seeds_no_std!(),
369 ..Config::default()
370 })]
371
372 #[test]
373 fn seqnum_ord_is_reflexive(a in arb_seqnum()) {
374 prop_assert_eq!(a, a)
375 }
376
377 #[test]
378 fn seqnum_ord_is_total(a in arb_seqnum(), b in arb_seqnum()) {
379 if a == b {
380 prop_assert!(!a.before(b) && !b.before(a))
381 } else {
382 prop_assert!(a.before(b) ^ b.before(a))
383 }
384 }
385
386 #[test]
387 fn seqnum_ord_is_transitive((a, b, c) in arb_seqnum_trans_tripple()) {
388 prop_assert!(a.before(b) && b.before(c) && a.before(c));
389 }
390
391 #[test]
392 fn seqnum_add_positive_greater(a in arb_seqnum(), b in 1..=i32::MAX) {
393 prop_assert!(a.before(a + b))
394 }
395
396 #[test]
397 fn seqnum_add_negative_smaller(a in arb_seqnum(), b in i32::MIN..=-1) {
398 prop_assert!(a.after(a + b))
399 }
400
401 #[test]
402 fn seqnum_sub_positive_smaller(a in arb_seqnum(), b in 1..=i32::MAX) {
403 prop_assert!(a.after(a - b))
404 }
405
406 #[test]
407 fn seqnum_sub_negative_greater(a in arb_seqnum(), b in i32::MIN..=-1) {
408 prop_assert!(a.before(a - b))
409 }
410
411 #[test]
412 fn seqnum_zero_identity(a in arb_seqnum()) {
413 prop_assert_eq!(a, a + 0)
414 }
415
416 #[test]
417 fn seqnum_before_after_inverse(a in arb_seqnum(), b in arb_seqnum()) {
418 prop_assert_eq!(a.after(b), b.before(a))
419 }
420
421 #[test]
422 fn seqnum_wraps_around_at_max_length(a in arb_seqnum()) {
423 prop_assert!(a.before(a + MAX_PAYLOAD_AND_CONTROL_LEN));
424 prop_assert!(a.after(a + MAX_PAYLOAD_AND_CONTROL_LEN + 1));
425 }
426
427 #[test]
428 fn window_size_less_than_or_eq_to_max(wnd in 0..=WindowSize::MAX.0) {
429 prop_assert_eq!(WindowSize::from_u32(wnd), Some(WindowSize(wnd)));
430 }
431
432 #[test]
433 fn window_size_greater_than_max(wnd in WindowSize::MAX.0+1..=u32::MAX) {
434 prop_assert_eq!(WindowSize::from_u32(wnd), None);
435 }
436 }
437}