netstack3_base/
num.rs

1// Copyright 2025 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5//! Types and utilities for dealing with numerical values.
6
7use core::num::{NonZeroIsize, NonZeroUsize};
8
9/// An `isize` that is strictly positive (greater than 0).
10///
11/// `PositiveIsize` differs from [`NonZeroUsize`] in that it is guaranteed to
12/// fit in an `isize` (i.e. the maximum value is `isize::MAX` as opposed to
13/// `usize::MAX`).
14#[derive(Copy, Clone, Eq, PartialEq, Debug, Hash, Ord, PartialOrd)]
15pub struct PositiveIsize(isize);
16
17impl PositiveIsize {
18    /// Creates a new `PositiveIsize` from an `isize` value.
19    ///
20    /// Returns `None` if `value` is less than or equal to zero.
21    pub const fn new(value: isize) -> Option<Self> {
22        if value > 0 {
23            Some(Self(value))
24        } else {
25            None
26        }
27    }
28
29    /// Creates a new `PositiveIsize` from a `usize` value.
30    ///
31    /// Returns `None` if `value` is zero or larger than `isize::MAX`.
32    pub const fn new_unsigned(value: usize) -> Option<Self> {
33        match NonZeroUsize::new(value) {
34            Some(v) => Self::new_nonzero_unsigned(v),
35            None => None,
36        }
37    }
38
39    /// Creates a new `PositiveIsize` from a `NonZeroUsize` value.
40    ///
41    /// Returns `None` if `value` is zero or larger than `isize::MAX`.
42    pub const fn new_nonzero_unsigned(value: NonZeroUsize) -> Option<Self> {
43        let value = value.get();
44        if value > (isize::MAX as usize) {
45            None
46        } else {
47            Some(Self(value as isize))
48        }
49    }
50
51    /// Returns the `isize` value within this `PositiveIsize`.
52    pub const fn get(self) -> isize {
53        let Self(v) = self;
54        v
55    }
56}
57
58impl From<PositiveIsize> for isize {
59    fn from(PositiveIsize(value): PositiveIsize) -> Self {
60        value
61    }
62}
63
64impl From<PositiveIsize> for usize {
65    fn from(PositiveIsize(value): PositiveIsize) -> Self {
66        // Conversion is guaranteed to be infallible because `PositiveIsize`
67        // creation guarantees: a positive `isize` always fits within a `usize`.
68        value as usize
69    }
70}
71
72impl From<PositiveIsize> for NonZeroIsize {
73    fn from(PositiveIsize(value): PositiveIsize) -> Self {
74        // SAFETY: value is guaranteed to be nonzero.
75        unsafe { NonZeroIsize::new_unchecked(value) }
76    }
77}
78
79impl From<PositiveIsize> for NonZeroUsize {
80    fn from(PositiveIsize(value): PositiveIsize) -> Self {
81        // value is guaranteed to be positive.
82        let value = value as usize;
83        // SAFETY: value is guaranteed to be nonzero.
84        unsafe { NonZeroUsize::new_unchecked(value) }
85    }
86}
87
88#[cfg(test)]
89mod tests {
90    use super::*;
91
92    #[test]
93    fn new_positive_isize() {
94        assert_eq!(PositiveIsize::new(0), None);
95        assert_eq!(PositiveIsize::new(-1), None);
96        assert_eq!(PositiveIsize::new(1), Some(PositiveIsize(1)));
97        assert_eq!(PositiveIsize::new(isize::MIN), None);
98        assert_eq!(PositiveIsize::new(isize::MAX), Some(PositiveIsize(isize::MAX)));
99    }
100
101    #[test]
102    fn new_unsigned_positive_isize() {
103        assert_eq!(PositiveIsize::new_unsigned(0), None);
104        assert_eq!(PositiveIsize::new_unsigned(usize::MAX), None);
105        assert_eq!(PositiveIsize::new_unsigned(1), Some(PositiveIsize(1)));
106        let max = usize::try_from(isize::MAX).unwrap();
107        assert_eq!(PositiveIsize::new_unsigned(max), Some(PositiveIsize(isize::MAX)));
108        assert_eq!(PositiveIsize::new_unsigned(max + 1), None);
109    }
110}