Skip to main content

arithmetic_nonmax/
lib.rs

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