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