nonmax/
lib.rs

1/*!
2[![GitHub CI Status](https://github.com/LPGhatguy/nonmax/workflows/CI/badge.svg)](https://github.com/LPGhatguy/nonmax/actions)
3[![nonmax on crates.io](https://img.shields.io/crates/v/nonmax.svg)](https://crates.io/crates/nonmax)
4[![nonmax docs](https://img.shields.io/badge/docs-docs.rs-orange.svg)](https://docs.rs/nonmax)
5
6nonmax provides types similar to the std `NonZero*` types, but instead requires
7that their values are not the maximum for their type. This ensures that
8`Option<NonMax*>` is no larger than `NonMax*`.
9
10nonmax supports every type that has a corresponding non-zero variant in the
11standard library:
12
13* `NonMaxI8`
14* `NonMaxI16`
15* `NonMaxI32`
16* `NonMaxI64`
17* `NonMaxI128`
18* `NonMaxIsize`
19* `NonMaxU8`
20* `NonMaxU16`
21* `NonMaxU32`
22* `NonMaxU64`
23* `NonMaxU128`
24* `NonMaxUsize`
25
26## Example
27
28```
29use nonmax::{NonMaxI16, NonMaxU8};
30
31let value = NonMaxU8::new(16).expect("16 should definitely fit in a u8");
32assert_eq!(value.get(), 16);
33assert_eq!(std::mem::size_of_val(&value), 1);
34
35let signed = NonMaxI16::new(i16::min_value()).expect("minimum values are fine");
36assert_eq!(signed.get(), i16::min_value());
37assert_eq!(std::mem::size_of_val(&signed), 2);
38
39let oops = NonMaxU8::new(255);
40assert_eq!(oops, None);
41```
42
43## Features
44
45* `std` (default): implements [`std::error::Error`] for [`ParseIntError`] and
46[`TryFromIntError`]. Disable this feature for
47[`#![no_std]`](https://rust-embedded.github.io/book/intro/no-std.html) support.
48
49## Minimum Supported Rust Version (MSRV)
50
51nonmax supports Rust 1.47.0 and newer. Until this library reaches 1.0,
52changes to the MSRV will require major version bumps. After 1.0, MSRV changes
53will only require minor version bumps, but will need significant justification.
54*/
55
56#![forbid(missing_docs)]
57#![cfg_attr(not(feature = "std"), no_std)]
58
59/// An error type returned when a checked integral type conversion fails (mimics [std::num::TryFromIntError])
60#[derive(Debug, Clone, Copy, PartialEq, Eq)]
61pub struct TryFromIntError(());
62
63#[cfg(feature = "std")]
64impl std::error::Error for TryFromIntError {}
65
66impl core::fmt::Display for TryFromIntError {
67    fn fmt(&self, fmt: &mut core::fmt::Formatter) -> core::fmt::Result {
68        "out of range integral type conversion attempted".fmt(fmt)
69    }
70}
71
72impl From<core::num::TryFromIntError> for TryFromIntError {
73    fn from(_: core::num::TryFromIntError) -> Self {
74        Self(())
75    }
76}
77
78impl From<core::convert::Infallible> for TryFromIntError {
79    fn from(never: core::convert::Infallible) -> Self {
80        match never {}
81    }
82}
83
84/// An error type returned when an integer cannot be parsed (mimics [std::num::ParseIntError])
85#[derive(Clone, Debug, PartialEq, Eq)]
86pub struct ParseIntError(());
87
88#[cfg(feature = "std")]
89impl std::error::Error for ParseIntError {}
90
91impl core::fmt::Display for ParseIntError {
92    fn fmt(&self, fmt: &mut core::fmt::Formatter) -> core::fmt::Result {
93        "unable to parse integer".fmt(fmt)
94    }
95}
96
97impl From<core::num::ParseIntError> for ParseIntError {
98    fn from(_: core::num::ParseIntError) -> Self {
99        Self(())
100    }
101}
102
103// error[E0658]: the `!` type is experimental
104// https://github.com/rust-lang/rust/issues/35121
105// impl From<!> for TryFromIntError { ... }
106
107// https://doc.rust-lang.org/1.47.0/src/core/num/mod.rs.html#31-43
108macro_rules! impl_nonmax_fmt {
109    ( ( $( $Trait: ident ),+ ) for $nonmax: ident ) => {
110        $(
111            impl core::fmt::$Trait for $nonmax {
112                #[inline]
113                fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
114                    core::fmt::$Trait::fmt(&self.get(), f)
115                }
116            }
117        )+
118    };
119}
120
121macro_rules! nonmax {
122    ( common, $nonmax: ident, $non_zero: ident, $primitive: ident ) => {
123        /// An integer that is known not to equal its maximum value.
124        #[derive(Clone, Copy, PartialEq, Eq, Hash)]
125        #[repr(transparent)]
126        pub struct $nonmax(core::num::$non_zero);
127
128        impl $nonmax {
129            /// Creates a new non-max if the given value is not the maximum
130            /// value.
131            #[inline]
132            pub const fn new(value: $primitive) -> Option<Self> {
133                match core::num::$non_zero::new(value ^ $primitive::MAX) {
134                    None => None,
135                    Some(value) => Some(Self(value)),
136                }
137            }
138
139            /// Creates a new non-max without checking the value.
140            ///
141            /// # Safety
142            ///
143            /// The value must not equal the maximum representable value for the
144            /// primitive type.
145            #[inline]
146            pub const unsafe fn new_unchecked(value: $primitive) -> Self {
147                let inner = core::num::$non_zero::new_unchecked(value ^ $primitive::MAX);
148                Self(inner)
149            }
150
151            /// Returns the value as a primitive type.
152            #[inline]
153            pub const fn get(&self) -> $primitive {
154                self.0.get() ^ $primitive::MAX
155            }
156
157            /// Gets non-max with the value zero (0)
158            pub const ZERO: $nonmax = unsafe { Self::new_unchecked(0) };
159
160            /// Gets non-max with the value one (1)
161            pub const ONE: $nonmax = unsafe { Self::new_unchecked(1) };
162
163            /// Gets non-max with maximum possible value (which is maximum of the underlying primitive minus one)
164            pub const MAX: $nonmax = unsafe { Self::new_unchecked($primitive::MAX - 1) };
165        }
166
167        impl Default for $nonmax {
168            fn default() -> Self {
169                unsafe { Self::new_unchecked(0) }
170            }
171        }
172
173        impl From<$nonmax> for $primitive {
174            fn from(value: $nonmax) -> Self {
175                value.get()
176            }
177        }
178
179        impl core::convert::TryFrom<$primitive> for $nonmax {
180            type Error = TryFromIntError;
181            fn try_from(value: $primitive) -> Result<Self, Self::Error> {
182                Self::new(value).ok_or(TryFromIntError(()))
183            }
184        }
185
186        impl core::str::FromStr for $nonmax {
187            type Err = ParseIntError;
188            fn from_str(value: &str) -> Result<Self, Self::Err> {
189                Self::new($primitive::from_str(value)?).ok_or(ParseIntError(()))
190            }
191        }
192
193        impl core::cmp::Ord for $nonmax {
194            fn cmp(&self, other: &Self) -> core::cmp::Ordering {
195                self.get().cmp(&other.get())
196            }
197        }
198        impl core::cmp::PartialOrd for $nonmax {
199            fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
200                Some(self.cmp(other))
201            }
202        }
203
204        // NonZero can implement BitOr (will never 0 a nonzero value) but not BitAnd.
205        // NonMax can implement BitAnd but not BitOr, with some caveats for signed values:
206        // -1 (11...11) & max (01...11) can result in signed max (01...11), so both operands must be nonmax for signed variants
207
208        impl core::ops::BitAnd<$nonmax> for $nonmax {
209            type Output = $nonmax;
210            fn bitand(self, rhs: $nonmax) -> Self::Output {
211                // Safety: since `rhs` is non-max, the result of the
212                // bitwise-and will be non-max regardless of the value of `self`
213                unsafe { $nonmax::new_unchecked(self.get() & rhs.get()) }
214            }
215        }
216
217        impl core::ops::BitAndAssign<$nonmax> for $nonmax {
218            fn bitand_assign(&mut self, rhs: $nonmax) {
219                *self = *self & rhs;
220            }
221        }
222
223        // https://doc.rust-lang.org/1.47.0/src/core/num/mod.rs.html#173-175
224        impl_nonmax_fmt! {
225            (Debug, Display, Binary, Octal, LowerHex, UpperHex) for $nonmax
226        }
227
228        #[cfg(feature = "serde")]
229        impl serde::Serialize for $nonmax {
230            fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
231            where
232                S: serde::Serializer,
233            {
234                self.get().serialize(serializer)
235            }
236        }
237
238        #[cfg(feature = "serde")]
239        impl<'de> serde::Deserialize<'de> for $nonmax {
240            fn deserialize<D>(deserializer: D) -> Result<$nonmax, D::Error>
241            where
242                D: serde::Deserializer<'de>,
243            {
244                let value = $primitive::deserialize(deserializer)?;
245                use core::convert::TryFrom;
246                Self::try_from(value).map_err(serde::de::Error::custom)
247            }
248        }
249
250        #[cfg(test)]
251        mod $primitive {
252            use super::*;
253
254            use core::mem::size_of;
255
256            #[test]
257            fn construct() {
258                let zero = $nonmax::new(0).unwrap();
259                assert_eq!(zero.get(), 0);
260
261                let some = $nonmax::new(19).unwrap();
262                assert_eq!(some.get(), 19);
263
264                let max = $nonmax::new($primitive::MAX);
265                assert_eq!(max, None);
266            }
267
268            #[test]
269            fn sizes_correct() {
270                assert_eq!(size_of::<$primitive>(), size_of::<$nonmax>());
271                assert_eq!(size_of::<$nonmax>(), size_of::<Option<$nonmax>>());
272            }
273
274            #[test]
275            fn convert() {
276                use core::convert::TryFrom;
277                let zero = $nonmax::try_from(0 as $primitive).unwrap();
278                let zero = $primitive::from(zero);
279                assert_eq!(zero, 0);
280
281                $nonmax::try_from($primitive::MAX).unwrap_err();
282            }
283
284            #[test]
285            fn cmp() {
286                let zero = $nonmax::new(0).unwrap();
287                let one = $nonmax::new(1).unwrap();
288                let two = $nonmax::new(2).unwrap();
289                assert!(zero < one);
290                assert!(one < two);
291                assert!(two > one);
292                assert!(one > zero);
293            }
294
295            #[test]
296            fn constants() {
297                let zero = $nonmax::ZERO;
298                let one = $nonmax::ONE;
299                let max = $nonmax::MAX;
300                assert_eq!(zero.get(), 0);
301                assert_eq!(one.get(), 1);
302                assert_eq!(max.get(), $primitive::MAX - 1);
303            }
304
305            #[test]
306            #[cfg(feature = "std")] // to_string
307            fn parse() {
308                for value in [0, 19, $primitive::MAX - 1].iter().copied() {
309                    let string = value.to_string();
310                    let nonmax = string.parse::<$nonmax>().unwrap();
311                    assert_eq!(nonmax.get(), value);
312                }
313                $primitive::MAX.to_string().parse::<$nonmax>().unwrap_err();
314            }
315
316            #[test]
317            #[cfg(feature = "std")] // format!
318            fn fmt() {
319                let zero = $nonmax::new(0).unwrap();
320                let some = $nonmax::new(19).unwrap();
321                let max1 = $nonmax::new($primitive::MAX - 1).unwrap();
322                for value in [zero, some, max1].iter().copied() {
323                    assert_eq!(format!("{}", value.get()), format!("{}", value)); // Display
324                    assert_eq!(format!("{:?}", value.get()), format!("{:?}", value)); // Debug
325                    assert_eq!(format!("{:b}", value.get()), format!("{:b}", value)); // Binary
326                    assert_eq!(format!("{:o}", value.get()), format!("{:o}", value)); // Octal
327                    assert_eq!(format!("{:x}", value.get()), format!("{:x}", value)); // LowerHex
328                    assert_eq!(format!("{:X}", value.get()), format!("{:X}", value)); // UpperHex
329                }
330            }
331
332            #[test]
333            #[cfg(feature = "serde")]
334            fn serde() {
335                for &value in [0, 19, $primitive::MAX - 1].iter() {
336                    let nonmax_value = $nonmax::new(value).unwrap();
337                    let encoded: Vec<u8> = bincode::serialize(&nonmax_value).unwrap();
338                    let decoded: $nonmax = bincode::deserialize(&encoded[..]).unwrap();
339                    assert_eq!(nonmax_value, decoded);
340                }
341            }
342        }
343    };
344
345    ( signed, $nonmax: ident, $non_zero: ident, $primitive: ident ) => {
346        nonmax!(common, $nonmax, $non_zero, $primitive);
347        // Nothing unique to signed versions (yet)
348    };
349
350    ( unsigned, $nonmax: ident, $non_zero: ident, $primitive: ident ) => {
351        nonmax!(common, $nonmax, $non_zero, $primitive);
352
353        impl core::ops::BitAnd<$nonmax> for $primitive {
354            type Output = $nonmax;
355            fn bitand(self, rhs: $nonmax) -> Self::Output {
356                // Safety: since `rhs` is non-max, the result of the
357                // bitwise-and will be non-max regardless of the value of `self`
358                unsafe { $nonmax::new_unchecked(self & rhs.get()) }
359            }
360        }
361
362        impl core::ops::BitAnd<$primitive> for $nonmax {
363            type Output = $nonmax;
364            fn bitand(self, rhs: $primitive) -> Self::Output {
365                // Safety: since `self` is non-max, the result of the
366                // bitwise-and will be non-max regardless of the value of `rhs`
367                unsafe { $nonmax::new_unchecked(self.get() & rhs) }
368            }
369        }
370
371        impl core::ops::BitAndAssign<$primitive> for $nonmax {
372            fn bitand_assign(&mut self, rhs: $primitive) {
373                *self = *self & rhs;
374            }
375        }
376
377        // std doesn't have an equivalent BitAndOr for $nonzero, but this just makes sense
378        impl core::ops::BitAndAssign<$nonmax> for $primitive {
379            fn bitand_assign(&mut self, rhs: $nonmax) {
380                *self = *self & rhs.get();
381            }
382        }
383    };
384}
385
386nonmax!(signed, NonMaxI8, NonZeroI8, i8);
387nonmax!(signed, NonMaxI16, NonZeroI16, i16);
388nonmax!(signed, NonMaxI32, NonZeroI32, i32);
389nonmax!(signed, NonMaxI64, NonZeroI64, i64);
390nonmax!(signed, NonMaxI128, NonZeroI128, i128);
391nonmax!(signed, NonMaxIsize, NonZeroIsize, isize);
392
393nonmax!(unsigned, NonMaxU8, NonZeroU8, u8);
394nonmax!(unsigned, NonMaxU16, NonZeroU16, u16);
395nonmax!(unsigned, NonMaxU32, NonZeroU32, u32);
396nonmax!(unsigned, NonMaxU64, NonZeroU64, u64);
397nonmax!(unsigned, NonMaxU128, NonZeroU128, u128);
398nonmax!(unsigned, NonMaxUsize, NonZeroUsize, usize);
399
400// https://doc.rust-lang.org/1.47.0/src/core/convert/num.rs.html#383-407
401macro_rules! impl_nonmax_from {
402    ( $small: ty, $large: ty ) => {
403        impl From<$small> for $large {
404            #[inline]
405            fn from(small: $small) -> Self {
406                // SAFETY: smaller input type guarantees the value is non-max
407                unsafe { Self::new_unchecked(small.get().into()) }
408            }
409        }
410    };
411}
412
413// Non-max Unsigned -> Non-max Unsigned
414impl_nonmax_from!(NonMaxU8, NonMaxU16);
415impl_nonmax_from!(NonMaxU8, NonMaxU32);
416impl_nonmax_from!(NonMaxU8, NonMaxU64);
417impl_nonmax_from!(NonMaxU8, NonMaxU128);
418impl_nonmax_from!(NonMaxU8, NonMaxUsize);
419impl_nonmax_from!(NonMaxU16, NonMaxU32);
420impl_nonmax_from!(NonMaxU16, NonMaxU64);
421impl_nonmax_from!(NonMaxU16, NonMaxU128);
422impl_nonmax_from!(NonMaxU16, NonMaxUsize);
423impl_nonmax_from!(NonMaxU32, NonMaxU64);
424impl_nonmax_from!(NonMaxU32, NonMaxU128);
425impl_nonmax_from!(NonMaxU64, NonMaxU128);
426
427// Non-max Signed -> Non-max Signed
428impl_nonmax_from!(NonMaxI8, NonMaxI16);
429impl_nonmax_from!(NonMaxI8, NonMaxI32);
430impl_nonmax_from!(NonMaxI8, NonMaxI64);
431impl_nonmax_from!(NonMaxI8, NonMaxI128);
432impl_nonmax_from!(NonMaxI8, NonMaxIsize);
433impl_nonmax_from!(NonMaxI16, NonMaxI32);
434impl_nonmax_from!(NonMaxI16, NonMaxI64);
435impl_nonmax_from!(NonMaxI16, NonMaxI128);
436impl_nonmax_from!(NonMaxI16, NonMaxIsize);
437impl_nonmax_from!(NonMaxI32, NonMaxI64);
438impl_nonmax_from!(NonMaxI32, NonMaxI128);
439impl_nonmax_from!(NonMaxI64, NonMaxI128);
440
441// Non-max Unsigned -> Non-max Signed
442impl_nonmax_from!(NonMaxU8, NonMaxI16);
443impl_nonmax_from!(NonMaxU8, NonMaxI32);
444impl_nonmax_from!(NonMaxU8, NonMaxI64);
445impl_nonmax_from!(NonMaxU8, NonMaxI128);
446impl_nonmax_from!(NonMaxU8, NonMaxIsize);
447impl_nonmax_from!(NonMaxU16, NonMaxI32);
448impl_nonmax_from!(NonMaxU16, NonMaxI64);
449impl_nonmax_from!(NonMaxU16, NonMaxI128);
450impl_nonmax_from!(NonMaxU32, NonMaxI64);
451impl_nonmax_from!(NonMaxU32, NonMaxI128);
452impl_nonmax_from!(NonMaxU64, NonMaxI128);
453
454// https://doc.rust-lang.org/1.47.0/src/core/convert/num.rs.html#383-407
455macro_rules! impl_smaller_from {
456    ( $small: ty, $large: ty ) => {
457        impl From<$small> for $large {
458            #[inline]
459            fn from(small: $small) -> Self {
460                // SAFETY: smaller input type guarantees the value is non-max
461                unsafe { Self::new_unchecked(small.into()) }
462            }
463        }
464    };
465}
466
467// Unsigned -> Non-max Unsigned
468impl_smaller_from!(u8, NonMaxU16);
469impl_smaller_from!(u8, NonMaxU32);
470impl_smaller_from!(u8, NonMaxU64);
471impl_smaller_from!(u8, NonMaxU128);
472impl_smaller_from!(u8, NonMaxUsize);
473impl_smaller_from!(u16, NonMaxU32);
474impl_smaller_from!(u16, NonMaxU64);
475impl_smaller_from!(u16, NonMaxU128);
476impl_smaller_from!(u16, NonMaxUsize);
477impl_smaller_from!(u32, NonMaxU64);
478impl_smaller_from!(u32, NonMaxU128);
479impl_smaller_from!(u64, NonMaxU128);
480
481// Signed -> Non-max Signed
482impl_smaller_from!(i8, NonMaxI16);
483impl_smaller_from!(i8, NonMaxI32);
484impl_smaller_from!(i8, NonMaxI64);
485impl_smaller_from!(i8, NonMaxI128);
486impl_smaller_from!(i8, NonMaxIsize);
487impl_smaller_from!(i16, NonMaxI32);
488impl_smaller_from!(i16, NonMaxI64);
489impl_smaller_from!(i16, NonMaxI128);
490impl_smaller_from!(i16, NonMaxIsize);
491impl_smaller_from!(i32, NonMaxI64);
492impl_smaller_from!(i32, NonMaxI128);
493impl_smaller_from!(i64, NonMaxI128);
494
495// Unsigned -> Non-max Signed
496impl_smaller_from!(u8, NonMaxI16);
497impl_smaller_from!(u8, NonMaxI32);
498impl_smaller_from!(u8, NonMaxI64);
499impl_smaller_from!(u8, NonMaxI128);
500impl_smaller_from!(u8, NonMaxIsize);
501impl_smaller_from!(u16, NonMaxI32);
502impl_smaller_from!(u16, NonMaxI64);
503impl_smaller_from!(u16, NonMaxI128);
504impl_smaller_from!(u32, NonMaxI64);
505impl_smaller_from!(u32, NonMaxI128);
506impl_smaller_from!(u64, NonMaxI128);
507
508#[cfg(test)]
509mod ops {
510    use super::*;
511
512    #[test]
513    fn bitand_unsigned() {
514        for left in 0..=u8::MAX {
515            let nmleft = NonMaxU8::new(left);
516            for right in 0..=u8::MAX {
517                let nmright = NonMaxU8::new(right);
518                let vanilla = left & right;
519
520                if let (Some(nmleft), Some(nmright)) = (nmleft, nmright) {
521                    assert_eq!(vanilla, (nmleft & nmright).get());
522                }
523                if let Some(nmleft) = nmleft {
524                    assert_eq!(vanilla, (nmleft & right).get());
525                }
526                if let Some(nmright) = nmright {
527                    assert_eq!(vanilla, (left & nmright).get());
528                }
529            }
530        }
531    }
532
533    #[test]
534    fn bitand_signed() {
535        for left in i8::MIN..=i8::MAX {
536            let nmleft = NonMaxI8::new(left);
537            for right in i8::MIN..=i8::MAX {
538                let nmright = NonMaxI8::new(right);
539                let vanilla = left & right;
540                if let (Some(nmleft), Some(nmright)) = (nmleft, nmright) {
541                    assert_eq!(vanilla, (nmleft & nmright).get());
542                }
543            }
544        }
545    }
546}