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 { Some(Self(value)) } else { None }
23    }
24
25    /// Creates a new `PositiveIsize` from a `usize` value.
26    ///
27    /// Returns `None` if `value` is zero or larger than `isize::MAX`.
28    pub const fn new_unsigned(value: usize) -> Option<Self> {
29        match NonZeroUsize::new(value) {
30            Some(v) => Self::new_nonzero_unsigned(v),
31            None => None,
32        }
33    }
34
35    /// Creates a new `PositiveIsize` from a `NonZeroUsize` value.
36    ///
37    /// Returns `None` if `value` is zero or larger than `isize::MAX`.
38    pub const fn new_nonzero_unsigned(value: NonZeroUsize) -> Option<Self> {
39        let value = value.get();
40        if value > (isize::MAX as usize) { None } else { Some(Self(value as isize)) }
41    }
42
43    /// Returns the `isize` value within this `PositiveIsize`.
44    pub const fn get(self) -> isize {
45        let Self(v) = self;
46        v
47    }
48}
49
50impl From<PositiveIsize> for isize {
51    fn from(PositiveIsize(value): PositiveIsize) -> Self {
52        value
53    }
54}
55
56impl From<PositiveIsize> for usize {
57    fn from(PositiveIsize(value): PositiveIsize) -> Self {
58        // Conversion is guaranteed to be infallible because `PositiveIsize`
59        // creation guarantees: a positive `isize` always fits within a `usize`.
60        value as usize
61    }
62}
63
64impl From<PositiveIsize> for NonZeroIsize {
65    fn from(PositiveIsize(value): PositiveIsize) -> Self {
66        // SAFETY: value is guaranteed to be nonzero.
67        unsafe { NonZeroIsize::new_unchecked(value) }
68    }
69}
70
71impl From<PositiveIsize> for NonZeroUsize {
72    fn from(PositiveIsize(value): PositiveIsize) -> Self {
73        // value is guaranteed to be positive.
74        let value = value as usize;
75        // SAFETY: value is guaranteed to be nonzero.
76        unsafe { NonZeroUsize::new_unchecked(value) }
77    }
78}
79
80#[cfg(test)]
81mod tests {
82    use super::*;
83
84    #[test]
85    fn new_positive_isize() {
86        assert_eq!(PositiveIsize::new(0), None);
87        assert_eq!(PositiveIsize::new(-1), None);
88        assert_eq!(PositiveIsize::new(1), Some(PositiveIsize(1)));
89        assert_eq!(PositiveIsize::new(isize::MIN), None);
90        assert_eq!(PositiveIsize::new(isize::MAX), Some(PositiveIsize(isize::MAX)));
91    }
92
93    #[test]
94    fn new_unsigned_positive_isize() {
95        assert_eq!(PositiveIsize::new_unsigned(0), None);
96        assert_eq!(PositiveIsize::new_unsigned(usize::MAX), None);
97        assert_eq!(PositiveIsize::new_unsigned(1), Some(PositiveIsize(1)));
98        let max = usize::try_from(isize::MAX).unwrap();
99        assert_eq!(PositiveIsize::new_unsigned(max), Some(PositiveIsize(isize::MAX)));
100        assert_eq!(PositiveIsize::new_unsigned(max + 1), None);
101    }
102}