Skip to main content

arithmetic_nonmax/
lib.rs

1//! `NonMax` provides integer types that cannot be the maximum value of their underlying primitive type.
2//!
3//! # Memory Optimization
4//!
5//! The main benefit of `NonMax<T>` is that `Option<NonMax<T>>` has the same size as `T`.
6//! This is achieved through Rust's "niche optimization", where the bit pattern of the
7//! maximum value is used to represent `None`.
8//!
9//! | Primitive | `size_of::<T>()` | `size_of::<Option<T>>()` | `size_of::<Option<NonMax<T>>>()` |
10//! |-----------|------------------|--------------------------|---------------------------------|
11//! | `u32`     | 4                | 8                        | **4**                           |
12//! | `i32`     | 4                | 8                        | **4**                           |
13//! | `u8`      | 1                | 2                        | **1**                           |
14
15#![no_std]
16
17use core::convert::TryFrom;
18use core::fmt::{self, Binary, Display, LowerHex, Octal, UpperHex};
19use core::hash::Hash;
20use core::marker::PhantomData;
21use core::num::NonZero;
22use core::ops::{
23    Add, AddAssign, Div, DivAssign, Index, IndexMut, Mul, MulAssign, Rem, RemAssign, Sub, SubAssign,
24};
25
26#[cfg(feature = "alloc")]
27extern crate alloc;
28
29/// Creates a `NonMax` value at compile-time.
30///
31/// This macro checks at compile-time that the provided value is not the maximum
32/// value for its type. If the value is the maximum, the program will fail to compile.
33///
34/// # Examples
35/// ```
36/// # use arithmetic_nonmax::non_max;
37/// let x = non_max!(123u8);
38/// ```
39///
40/// ```compile_fail
41/// # use arithmetic_nonmax::non_max;
42/// let x = non_max!(255u8); // This fails to compile
43/// ```
44///
45/// ```compile_fail
46/// # use arithmetic_nonmax::non_max;
47/// let x = non_max!(127i8); // i8::MAX fails
48/// ```
49///
50/// ```compile_fail
51/// # use arithmetic_nonmax::non_max;
52/// let x = non_max!(u16::MAX); // Explicit MAX fails
53/// ```
54#[macro_export]
55macro_rules! non_max {
56    ($val:expr) => {{
57        const _: () = const {
58            if $val == <_ as $crate::NonMaxItem>::MAX {
59                panic!("provided value is the maximum value for this type");
60            }
61        };
62        unsafe { <_ as $crate::NonMaxItem>::create_nonmax_unchecked($val) }
63    }};
64}
65
66/// Error type returned when a value is the maximum for its type.
67#[derive(Debug, Clone, Copy, PartialEq, Eq)]
68pub struct MaxValueError;
69
70impl Display for MaxValueError {
71    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
72        write!(f, "provided value is the maximum value for this type")
73    }
74}
75
76impl core::error::Error for MaxValueError {}
77
78/// A wrapper type for an integer that cannot be its maximum value.
79///
80/// This type leverages Rust's `NonZero` optimization by mapping the maximum value to zero internally.
81/// As a result, `Option<NonMax<T>>` has the same size as the underlying primitive type `T`.
82///
83/// # Examples
84/// ```
85/// # use arithmetic_nonmax::NonMaxU32;
86/// # use core::mem::size_of;
87/// assert_eq!(size_of::<NonMaxU32>(), 4);
88/// assert_eq!(size_of::<Option<NonMaxU32>>(), 4);
89/// ```
90#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
91pub struct NonMax<T: NonMaxItem>(T::NonZero);
92
93impl<T: NonMaxItem + Copy> NonMax<T> {
94    /// Creates a new `NonMax` if the given value is not the maximum value.
95    ///
96    /// # Examples
97    /// ```
98    /// # use arithmetic_nonmax::NonMaxU8;
99    /// assert!(NonMaxU8::new(254).is_some());
100    /// assert!(NonMaxU8::new(255).is_none());
101    /// ```
102    pub fn new(value: T) -> Option<Self> {
103        Value::new(value).to_inner_repr().to_nonmax()
104    }
105
106    fn to_real_repr(self) -> Value<T, Real> {
107        T::from_nonzero(self.0).to_real_repr()
108    }
109
110    /// Returns the underlying primitive value.
111    ///
112    /// # Examples
113    /// ```
114    /// # use arithmetic_nonmax::NonMaxU32;
115    /// let x = NonMaxU32::new(123).unwrap();
116    /// assert_eq!(x.get(), 123);
117    /// ```
118    pub fn get(&self) -> T {
119        self.to_real_repr().value()
120    }
121}
122
123impl<T: NonMaxItem + Copy + PartialEq> NonMax<T> {
124    /// Returns `true` if this is the minimum value.
125    pub fn is_min(&self) -> bool {
126        self.get() == T::MIN_VALUE
127    }
128
129    /// Returns `true` if this is the maximum possible value for this type.
130    pub fn is_max(&self) -> bool {
131        self.get() == T::MAX_SAFE
132    }
133
134    /// Returns `true` if the value is zero.
135    pub fn is_zero(&self) -> bool {
136        self.get() == T::ZERO_VALUE
137    }
138}
139
140impl<T: NonMaxItem + Copy> NonMax<T> {
141    /// Checked integer addition. Computes `self + rhs`, returning `None` if overflow occurred
142    /// or if the result is the maximum value.
143    pub fn checked_add(self, rhs: Self) -> Option<Self> {
144        self.get().checked_add(rhs.get()).and_then(Self::new)
145    }
146
147    /// Checked integer subtraction. Computes `self - rhs`, returning `None` if overflow occurred
148    /// or if the result is the maximum value.
149    pub fn checked_sub(self, rhs: Self) -> Option<Self> {
150        self.get().checked_sub(rhs.get()).and_then(Self::new)
151    }
152
153    /// Checked integer multiplication. Computes `self * rhs`, returning `None` if overflow occurred
154    /// or if the result is the maximum value.
155    pub fn checked_mul(self, rhs: Self) -> Option<Self> {
156        self.get().checked_mul(rhs.get()).and_then(Self::new)
157    }
158
159    /// Checked integer division. Computes `self / rhs`, returning `None` if the divisor is zero
160    /// or if the result is the maximum value.
161    pub fn checked_div(self, rhs: Self) -> Option<Self> {
162        self.get().checked_div(rhs.get()).and_then(Self::new)
163    }
164
165    /// Checked integer remainder. Computes `self % rhs`, returning `None` if the divisor is zero
166    /// or if the result is the maximum value.
167    pub fn checked_rem(self, rhs: Self) -> Option<Self> {
168        self.get().checked_rem(rhs.get()).and_then(Self::new)
169    }
170
171    /// Checked addition with a primitive value.
172    ///
173    /// # Examples
174    /// ```
175    /// # use arithmetic_nonmax::NonMaxU8;
176    /// let x = NonMaxU8::new(100).unwrap();
177    /// assert_eq!(x.checked_add_val(50).unwrap().get(), 150);
178    /// assert!(x.checked_add_val(155).is_none()); // 255 is MAX
179    /// ```
180    pub fn checked_add_val(self, rhs: T) -> Option<Self> {
181        self.get().checked_add(rhs).and_then(Self::new)
182    }
183
184    /// Checked subtraction with a primitive value.
185    pub fn checked_sub_val(self, rhs: T) -> Option<Self> {
186        self.get().checked_sub(rhs).and_then(Self::new)
187    }
188
189    /// Checked multiplication with a primitive value.
190    pub fn checked_mul_val(self, rhs: T) -> Option<Self> {
191        self.get().checked_mul(rhs).and_then(Self::new)
192    }
193
194    /// Checked division with a primitive value.
195    pub fn checked_div_val(self, rhs: T) -> Option<Self> {
196        self.get().checked_div(rhs).and_then(Self::new)
197    }
198
199    /// Checked remainder with a primitive value.
200    pub fn checked_rem_val(self, rhs: T) -> Option<Self> {
201        self.get().checked_rem(rhs).and_then(Self::new)
202    }
203}
204
205impl<T: NonMaxItem + Copy + Add<Output = T>> Add for NonMax<T> {
206    type Output = Self;
207    fn add(self, rhs: Self) -> Self::Output {
208        self.checked_add(rhs)
209            .expect("attempt to add with overflow or to maximum value")
210    }
211}
212
213impl<T: NonMaxItem + Copy + Add<Output = T>> Add<T> for NonMax<T> {
214    type Output = Self;
215    fn add(self, rhs: T) -> Self::Output {
216        self.checked_add_val(rhs)
217            .expect("attempt to add with overflow or to maximum value")
218    }
219}
220
221impl<T: NonMaxItem + Copy + Add<Output = T>> AddAssign for NonMax<T> {
222    fn add_assign(&mut self, rhs: Self) {
223        *self = *self + rhs;
224    }
225}
226
227impl<T: NonMaxItem + Copy + Add<Output = T>> AddAssign<T> for NonMax<T> {
228    fn add_assign(&mut self, rhs: T) {
229        *self = *self + rhs;
230    }
231}
232
233impl<T: NonMaxItem + Copy + Sub<Output = T>> Sub for NonMax<T> {
234    type Output = Self;
235    fn sub(self, rhs: Self) -> Self::Output {
236        self.checked_sub(rhs)
237            .expect("attempt to subtract with overflow or to maximum value")
238    }
239}
240
241impl<T: NonMaxItem + Copy + Sub<Output = T>> Sub<T> for NonMax<T> {
242    type Output = Self;
243    fn sub(self, rhs: T) -> Self::Output {
244        self.checked_sub_val(rhs)
245            .expect("attempt to subtract with overflow or to maximum value")
246    }
247}
248
249impl<T: NonMaxItem + Copy + Sub<Output = T>> SubAssign for NonMax<T> {
250    fn sub_assign(&mut self, rhs: Self) {
251        *self = *self - rhs;
252    }
253}
254
255impl<T: NonMaxItem + Copy + Sub<Output = T>> SubAssign<T> for NonMax<T> {
256    fn sub_assign(&mut self, rhs: T) {
257        *self = *self - rhs;
258    }
259}
260
261impl<T: NonMaxItem + Copy + Mul<Output = T>> Mul for NonMax<T> {
262    type Output = Self;
263    fn mul(self, rhs: Self) -> Self::Output {
264        self.checked_mul(rhs)
265            .expect("attempt to multiply with overflow or to maximum value")
266    }
267}
268
269impl<T: NonMaxItem + Copy + Mul<Output = T>> Mul<T> for NonMax<T> {
270    type Output = Self;
271    fn mul(self, rhs: T) -> Self::Output {
272        self.checked_mul_val(rhs)
273            .expect("attempt to multiply with overflow or to maximum value")
274    }
275}
276
277impl<T: NonMaxItem + Copy + Mul<Output = T>> MulAssign for NonMax<T> {
278    fn mul_assign(&mut self, rhs: Self) {
279        *self = *self * rhs;
280    }
281}
282
283impl<T: NonMaxItem + Copy + Mul<Output = T>> MulAssign<T> for NonMax<T> {
284    fn mul_assign(&mut self, rhs: T) {
285        *self = *self * rhs;
286    }
287}
288
289impl<T: NonMaxItem + Copy + Div<Output = T>> Div for NonMax<T> {
290    type Output = Self;
291    fn div(self, rhs: Self) -> Self::Output {
292        self.checked_div(rhs)
293            .expect("attempt to divide by zero or to maximum value")
294    }
295}
296
297impl<T: NonMaxItem + Copy + Div<T, Output = T>> Div<T> for NonMax<T> {
298    type Output = Self;
299    fn div(self, rhs: T) -> Self::Output {
300        self.checked_div_val(rhs)
301            .expect("attempt to divide by zero or to maximum value")
302    }
303}
304
305impl<T: NonMaxItem + Copy + Div<Output = T>> DivAssign for NonMax<T> {
306    fn div_assign(&mut self, rhs: Self) {
307        *self = *self / rhs;
308    }
309}
310
311impl<T: NonMaxItem + Copy + Div<T, Output = T>> DivAssign<T> for NonMax<T> {
312    fn div_assign(&mut self, rhs: T) {
313        *self = *self / rhs;
314    }
315}
316
317impl<T: NonMaxItem + Copy + Rem<Output = T>> Rem for NonMax<T> {
318    type Output = Self;
319    fn rem(self, rhs: Self) -> Self::Output {
320        self.checked_rem(rhs)
321            .expect("attempt to calculate remainder by zero or to maximum value")
322    }
323}
324
325impl<T: NonMaxItem + Copy + Rem<T, Output = T>> Rem<T> for NonMax<T> {
326    type Output = Self;
327    fn rem(self, rhs: T) -> Self::Output {
328        self.checked_rem_val(rhs)
329            .expect("attempt to calculate remainder by zero or to maximum value")
330    }
331}
332
333impl<T: NonMaxItem + Copy + Rem<Output = T>> RemAssign for NonMax<T> {
334    fn rem_assign(&mut self, rhs: Self) {
335        *self = *self % rhs;
336    }
337}
338
339impl<T: NonMaxItem + Copy + Rem<T, Output = T>> RemAssign<T> for NonMax<T> {
340    fn rem_assign(&mut self, rhs: T) {
341        *self = *self % rhs;
342    }
343}
344
345impl<T: NonMaxItem + Copy + PartialOrd> PartialOrd for NonMax<T> {
346    fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
347        self.to_real_repr().partial_cmp(&other.to_real_repr())
348    }
349}
350
351impl<T: NonMaxItem + Copy + Ord> Ord for NonMax<T> {
352    fn cmp(&self, other: &Self) -> core::cmp::Ordering {
353        self.to_real_repr().cmp(&other.to_real_repr())
354    }
355}
356
357impl<T: NonMaxItem + Copy + Display> Display for NonMax<T> {
358    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
359        Display::fmt(&self.get(), f)
360    }
361}
362
363impl<T: NonMaxItem + Copy + Binary> Binary for NonMax<T> {
364    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
365        Binary::fmt(&self.get(), f)
366    }
367}
368
369impl<T: NonMaxItem + Copy + Octal> Octal for NonMax<T> {
370    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
371        Octal::fmt(&self.get(), f)
372    }
373}
374
375impl<T: NonMaxItem + Copy + LowerHex> LowerHex for NonMax<T> {
376    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
377        LowerHex::fmt(&self.get(), f)
378    }
379}
380
381impl<T: NonMaxItem + Copy + UpperHex> UpperHex for NonMax<T> {
382    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
383        UpperHex::fmt(&self.get(), f)
384    }
385}
386
387impl<T: NonMaxItem + Copy> Default for NonMax<T> {
388    fn default() -> Self {
389        Self::new(T::ZERO_VALUE).unwrap()
390    }
391}
392
393#[doc(hidden)]
394pub trait NonMaxItem: Sized {
395    type NonZero: Copy + PartialEq + Eq + PartialOrd + Ord + Hash;
396    const MIN_VALUE: Self;
397    const MAX: Self;
398    const MAX_SAFE: Self;
399    const ZERO_VALUE: Self;
400    fn transform(self) -> Self;
401    fn to_nonzero(value: Value<Self, Inner>) -> Option<Self::NonZero>;
402    unsafe fn to_nonzero_unchecked(value: Value<Self, Inner>) -> Self::NonZero;
403    fn from_nonzero(value: Self::NonZero) -> Value<Self, Inner>;
404
405    fn checked_add(self, rhs: Self) -> Option<Self>;
406    fn checked_sub(self, rhs: Self) -> Option<Self>;
407    fn checked_mul(self, rhs: Self) -> Option<Self>;
408    fn checked_div(self, rhs: Self) -> Option<Self>;
409    fn checked_rem(self, rhs: Self) -> Option<Self>;
410
411    /// Creates a `NonMax` from this value without checking.
412    ///
413    /// # Safety
414    /// The value must not be the maximum value.
415    unsafe fn create_nonmax_unchecked(self) -> NonMax<Self>;
416}
417
418macro_rules! impl_non_max_item {
419    ($($t:ty, $name:ident, $doc:expr),*) => {
420        $(
421            impl NonMaxItem for $t {
422                type NonZero = NonZero<$t>;
423                const MIN_VALUE: Self = <$t>::MIN;
424                const MAX: Self = <$t>::MAX;
425                const MAX_SAFE: Self = <$t>::MAX - 1;
426                const ZERO_VALUE: Self = 0;
427                fn transform(self) -> Self {
428                    self ^ <$t>::MAX
429                }
430                fn to_nonzero(value: Value<Self, Inner>) -> Option<Self::NonZero> {
431                    Self::NonZero::new(value.value())
432                }
433                unsafe fn to_nonzero_unchecked(value: Value<Self, Inner>) -> Self::NonZero {
434                    unsafe { Self::NonZero::new_unchecked(value.value()) }
435                }
436                fn from_nonzero(value: Self::NonZero) -> Value<Self, Inner> {
437                    Value::new(value.get())
438                }
439
440                fn checked_add(self, rhs: Self) -> Option<Self> { self.checked_add(rhs) }
441                fn checked_sub(self, rhs: Self) -> Option<Self> { self.checked_sub(rhs) }
442                fn checked_mul(self, rhs: Self) -> Option<Self> { self.checked_mul(rhs) }
443                fn checked_div(self, rhs: Self) -> Option<Self> { self.checked_div(rhs) }
444                fn checked_rem(self, rhs: Self) -> Option<Self> { self.checked_rem(rhs) }
445
446                unsafe fn create_nonmax_unchecked(self) -> NonMax<Self> {
447                    unsafe { NonMax::<$t>::new_unchecked(self) }
448                }
449            }
450
451            impl From<NonMax<$t>> for $t {
452                fn from(value: NonMax<$t>) -> Self {
453                    value.get()
454                }
455            }
456
457            impl TryFrom<$t> for NonMax<$t> {
458                type Error = MaxValueError;
459
460                fn try_from(value: $t) -> Result<Self, Self::Error> {
461                    Self::new(value).ok_or(MaxValueError)
462                }
463            }
464
465            #[doc = $doc]
466            pub type $name = NonMax<$t>;
467
468            impl $name {
469                /// The minimum value for this type.
470                pub const MIN: Self = unsafe { Self(NonZero::new_unchecked(<$t>::MIN ^ <$t>::MAX)) };
471                /// The maximum value for this type.
472                pub const MAX: Self = unsafe { Self(NonZero::new_unchecked((<$t>::MAX - 1) ^ <$t>::MAX)) };
473                /// The zero value for this type.
474                pub const ZERO: Self = unsafe { Self(NonZero::new_unchecked(0 ^ <$t>::MAX)) };
475
476                /// Creates a new `NonMax` without checking the value.
477                ///
478                /// # Safety
479                /// The value must not be the maximum value of the underlying type.
480                pub const unsafe fn new_unchecked(value: $t) -> Self {
481                    Self(unsafe { NonZero::new_unchecked(value ^ <$t>::MAX) })
482                }
483            }
484        )*
485    };
486}
487
488impl_non_max_item!(
489    u8,
490    NonMaxU8,
491    "An unsigned 8-bit integer that cannot be `u8::MAX`.",
492    u16,
493    NonMaxU16,
494    "An unsigned 16-bit integer that cannot be `u16::MAX`.",
495    u32,
496    NonMaxU32,
497    "An unsigned 32-bit integer that cannot be `u32::MAX`.",
498    u64,
499    NonMaxU64,
500    "An unsigned 64-bit integer that cannot be `u64::MAX`.",
501    u128,
502    NonMaxU128,
503    "An unsigned 128-bit integer that cannot be `u128::MAX`.",
504    usize,
505    NonMaxUsize,
506    "An unsigned pointer-sized integer that cannot be `usize::MAX`.",
507    i8,
508    NonMaxI8,
509    "A signed 8-bit integer that cannot be `i8::MAX`.",
510    i16,
511    NonMaxI16,
512    "A signed 16-bit integer that cannot be `i16::MAX`.",
513    i32,
514    NonMaxI32,
515    "A signed 32-bit integer that cannot be `i32::MAX`.",
516    i64,
517    NonMaxI64,
518    "A signed 64-bit integer that cannot be `i64::MAX`.",
519    i128,
520    NonMaxI128,
521    "A signed 128-bit integer that cannot be `i128::MAX`.",
522    isize,
523    NonMaxIsize,
524    "A signed pointer-sized integer that cannot be `isize::MAX`."
525);
526
527#[doc(hidden)]
528#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
529pub struct Real;
530#[doc(hidden)]
531#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
532pub struct Inner;
533
534#[doc(hidden)]
535#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)]
536pub struct Value<T, M> {
537    value: T,
538    _marker: PhantomData<M>,
539}
540
541impl<T: NonMaxItem + Copy, M> Value<T, M> {
542    fn new(value: T) -> Self {
543        Self {
544            value,
545            _marker: PhantomData,
546        }
547    }
548
549    fn value(&self) -> T {
550        self.value
551    }
552}
553
554impl<T: NonMaxItem + Copy> Value<T, Real> {
555    fn to_inner_repr(self) -> Value<T, Inner> {
556        Value::new(T::transform(self.value))
557    }
558}
559
560impl<T: NonMaxItem + Copy + Add<Output = T>> Add for Value<T, Real> {
561    type Output = Self;
562    fn add(self, rhs: Self) -> Self::Output {
563        Self::new(self.value() + rhs.value())
564    }
565}
566
567impl<T: NonMaxItem + Copy + Sub<Output = T>> Sub for Value<T, Real> {
568    type Output = Self;
569    fn sub(self, rhs: Self) -> Self::Output {
570        Self::new(self.value() - rhs.value())
571    }
572}
573
574impl<T: NonMaxItem + Copy + Mul<Output = T>> Mul for Value<T, Real> {
575    type Output = Self;
576    fn mul(self, rhs: Self) -> Self::Output {
577        Self::new(self.value() * rhs.value())
578    }
579}
580
581impl<T: NonMaxItem + Copy + Div<Output = T>> Div for Value<T, Real> {
582    type Output = Self;
583    fn div(self, rhs: Self) -> Self::Output {
584        Self::new(self.value() / rhs.value())
585    }
586}
587
588impl<T: NonMaxItem + Copy + Rem<Output = T>> Rem for Value<T, Real> {
589    type Output = Self;
590    fn rem(self, rhs: Self) -> Self::Output {
591        Self::new(self.value() % rhs.value())
592    }
593}
594
595impl<T: NonMaxItem + Copy> Value<T, Inner> {
596    fn to_real_repr(self) -> Value<T, Real> {
597        Value::new(T::transform(self.value))
598    }
599
600    fn to_nonmax(self) -> Option<NonMax<T>> {
601        T::to_nonzero(self).map(NonMax)
602    }
603}
604
605impl<T: NonMaxItem + Copy + PartialOrd> PartialOrd for Value<T, Real> {
606    fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
607        self.value().partial_cmp(&other.value())
608    }
609}
610
611impl<T: NonMaxItem + Copy + Ord> Ord for Value<T, Real> {
612    fn cmp(&self, other: &Self) -> core::cmp::Ordering {
613        self.value().cmp(&other.value())
614    }
615}
616
617impl<T: NonMaxItem + Copy + Display> Display for Value<T, Real> {
618    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
619        write!(f, "{}", self.value())
620    }
621}
622
623impl<T> Index<NonMaxUsize> for [T] {
624    type Output = T;
625    #[inline]
626    fn index(&self, index: NonMaxUsize) -> &Self::Output {
627        &self[index.get()]
628    }
629}
630
631impl<T> IndexMut<NonMaxUsize> for [T] {
632    #[inline]
633    fn index_mut(&mut self, index: NonMaxUsize) -> &mut Self::Output {
634        &mut self[index.get()]
635    }
636}
637
638#[cfg(feature = "alloc")]
639impl<T> Index<NonMaxUsize> for alloc::vec::Vec<T> {
640    type Output = T;
641    #[inline]
642    fn index(&self, index: NonMaxUsize) -> &Self::Output {
643        &self[index.get()]
644    }
645}
646
647#[cfg(feature = "alloc")]
648impl<T> IndexMut<NonMaxUsize> for alloc::vec::Vec<T> {
649    #[inline]
650    fn index_mut(&mut self, index: NonMaxUsize) -> &mut Self::Output {
651        &mut self[index.get()]
652    }
653}
654
655#[cfg(test)]
656mod tests {
657    extern crate std;
658    use super::*;
659    use core::mem::size_of;
660    use std::collections::HashSet;
661
662    #[test]
663    fn test_hash() {
664        let mut set = HashSet::new();
665        set.insert(NonMaxU32::new(1).unwrap());
666        set.insert(NonMaxU32::new(2).unwrap());
667        set.insert(NonMaxU32::new(1).unwrap());
668
669        assert_eq!(set.len(), 2);
670        assert!(set.contains(&NonMaxU32::new(1).unwrap()));
671    }
672
673    #[test]
674    fn test_sizes() {
675        assert_eq!(size_of::<NonMaxU32>(), 4);
676        assert_eq!(size_of::<Option<NonMaxU32>>(), 4);
677
678        assert_eq!(size_of::<NonMaxI32>(), 4);
679        assert_eq!(size_of::<Option<NonMaxI32>>(), 4);
680
681        assert_eq!(size_of::<NonMaxU8>(), 1);
682        assert_eq!(size_of::<Option<NonMaxU8>>(), 1);
683    }
684
685    #[test]
686    fn test_conversions() {
687        let x = NonMaxU8::try_from(100).unwrap();
688        assert_eq!(u8::from(x), 100);
689
690        let max_val = u8::MAX;
691        assert!(NonMaxU8::try_from(max_val).is_err());
692    }
693
694    #[test]
695    fn test_arithmetic_with_val() {
696        let x = NonMaxU8::new(100).unwrap();
697        let y = x + 50;
698        assert_eq!(u8::from(y), 150);
699
700        let mut z = NonMaxU8::new(10).unwrap();
701        z += 20;
702        assert_eq!(u8::from(z), 30);
703
704        let a = NonMaxU8::new(10).unwrap();
705        let b = a * 5;
706        assert_eq!(u8::from(b), 50);
707
708        let c = NonMaxU8::new(100).unwrap();
709        let d = c / 3;
710        assert_eq!(u8::from(d), 33);
711    }
712
713    #[test]
714    fn test_add_overflow() {
715        let x = NonMaxU8::try_from(250).unwrap();
716        // Now it should return None instead of panicking
717        assert!(x.checked_add_val(10).is_none());
718    }
719
720    #[test]
721    fn test_add_to_max() {
722        let x = NonMaxU8::try_from(250).unwrap();
723        // Result is 255 (MAX), so it should return None
724        assert!(x.checked_add_val(5).is_none());
725    }
726
727    #[test]
728    fn test_signed_integer() {
729        // i8: -128 to 127. MAX is 127.
730        let x = NonMaxI8::try_from(100).unwrap();
731        let y = x + 20;
732        assert_eq!(i8::from(y), 120);
733
734        let z = NonMaxI8::try_from(-50).unwrap();
735        let w = z + 10;
736        assert_eq!(i8::from(w), -40);
737
738        // MIN (-128) is allowed
739        let min_val = NonMaxI8::try_from(i8::MIN).unwrap();
740        assert_eq!(i8::from(min_val), -128);
741    }
742
743    #[test]
744    fn test_signed_overflow() {
745        let x = NonMaxI8::try_from(120).unwrap();
746        // Overflow detected
747        assert!(x.checked_add_val(10).is_none());
748    }
749
750    #[test]
751    fn test_signed_to_max() {
752        let x = NonMaxI8::try_from(120).unwrap();
753        // Result is 127 (MAX), so None
754        assert!(x.checked_add_val(7).is_none());
755    }
756
757    #[test]
758    fn test_formatting() {
759        let x = NonMaxU8::new(254).unwrap();
760        assert_eq!(std::format!("{}", x), "254");
761        assert_eq!(std::format!("{:b}", x), "11111110");
762        assert_eq!(std::format!("{:o}", x), "376");
763        assert_eq!(std::format!("{:x}", x), "fe");
764        assert_eq!(std::format!("{:X}", x), "FE");
765    }
766
767    #[test]
768    fn test_min_max_constants() {
769        assert_eq!(NonMaxU8::MIN.get(), 0);
770        assert_eq!(NonMaxU8::MAX.get(), 254);
771        assert!(NonMaxU8::MIN.is_min());
772        assert!(NonMaxU8::MAX.is_max());
773        assert!(!NonMaxU8::MIN.is_max());
774        assert!(!NonMaxU8::MAX.is_min());
775
776        assert_eq!(NonMaxI8::MIN.get(), -128);
777        assert_eq!(NonMaxI8::MAX.get(), 126);
778    }
779
780    #[test]
781    fn test_zero_constant() {
782        assert_eq!(NonMaxU8::ZERO.get(), 0);
783        assert!(NonMaxU8::ZERO.is_zero());
784        assert_eq!(NonMaxI32::ZERO.get(), 0);
785        assert!(NonMaxI32::ZERO.is_zero());
786    }
787
788    #[test]
789    fn test_non_max_macro() {
790        let x = non_max!(123u8);
791        assert_eq!(x.get(), 123);
792
793        let y = non_max!(456u32);
794        assert_eq!(y.get(), 456);
795
796        let z = non_max!(-10i32);
797        assert_eq!(z.get(), -10);
798    }
799
800    #[test]
801    fn test_indexing() {
802        let v = [1, 2, 3];
803        let idx = NonMaxUsize::new(1).unwrap();
804        assert_eq!(v[idx], 2);
805
806        let mut v_mut = [1, 2, 3];
807        v_mut[idx] = 10;
808        assert_eq!(v_mut[1], 10);
809
810        #[cfg(feature = "alloc")]
811        {
812            let v_vec = std::vec![1, 2, 3];
813            assert_eq!(v_vec[idx], 2);
814
815            let mut v_vec_mut = std::vec![1, 2, 3];
816            v_vec_mut[idx] = 20;
817            assert_eq!(v_vec_mut[1], 20);
818        }
819    }
820}