devela/num/niche/mem/
non_value.rs

1// devela::num::niche::non_value
2//
3//! Creates const generic customizable wrappers over the `NonZero` primitives.
4//!
5//! Always available for internal use.
6//
7
8use crate::items;
9
10impl_non_value![U 8, u8];
11impl_non_value![U 16, u16];
12impl_non_value![U 32, u32];
13impl_non_value![U 64, u64];
14impl_non_value![U 128, u128];
15
16impl_non_value![I 8, i8];
17impl_non_value![I 16, i16];
18impl_non_value![I 32, i32];
19impl_non_value![I 64, i64];
20impl_non_value![I 128, i128];
21
22#[cfg(target_pointer_width = "8")]
23items! { impl_non_value![U 8, usize]; impl_non_value![I 8, isize]; }
24#[cfg(target_pointer_width = "16")]
25items! { impl_non_value![U 16, usize]; impl_non_value![I 16, isize]; }
26#[cfg(target_pointer_width = "32")]
27items! { impl_non_value![U 32, usize]; impl_non_value![I 32, isize]; }
28#[cfg(target_pointer_width = "64")]
29items! { impl_non_value![U 64, usize]; impl_non_value![I 64, isize]; }
30#[cfg(target_pointer_width = "128")]
31items! { impl_non_value![U 128, usize]; impl_non_value![I 128, isize]; }
32
33/// Implements a `NonValue[I|U]B<V>`.
34///
35/// - `I` or `U` means a signed or unsigned type, respectively.
36/// - `B` represents the bit-size, from [8, 16, 32, 64, 128].
37/// - `V` is the prohibited value in the bit-sized range.
38///
39/// # Example
40/// ```
41/// # use devela::{NonValueI8, NonValueU8, NonExtremeI8};
42///
43/// assert![NonValueI8::<3>::new(2).is_some()];
44/// assert![NonValueI8::<3>::new(3).is_none()];
45///
46/// assert![NonExtremeI8::new(i8::MIN).is_none()];
47/// assert![NonExtremeI8::new(i8::MAX).is_some()];
48/// assert![NonExtremeI8::new(0).is_some()];
49/// ```
50///
51/// See for example: [`NonValueI8`] and [`NonExtremeI8`].
52//
53// NOTE: can't use doc(cfg) attributes in generated methods.
54macro_rules! impl_non_value {
55    // Defines a new unsigned non-value type.
56    // E.g.: impl_non_value![U 32, u32] would generate NonValueU32 and NonExtremeU32
57    (I $bits:literal, $IP:ty) => { impl_non_value![@MIN, "A signed", i, $bits, $IP]; };
58    (U $bits:literal, $IP:ty) => { impl_non_value![@MAX, "An unsigned", u, $bits, $IP]; };
59    (@$XTR:ident, $doc:literal, $s:ident, $b:literal, $IP:ty) => {
60        $crate::paste!{
61            impl_non_value![@
62                [<NonValue $IP:camel>],   // $name
63                [<NonZero $IP:camel>],    // $n0
64                [<NonExtreme $IP:camel>], // $ne
65                $XTR,
66                $doc,
67                $IP,
68                $s,
69                $b
70            ];
71        }
72    };
73    (
74    // $name: the full name of the new type. E.g. NonValueI8.
75    // $n0: the full name of the inner NonZero. E.g. NonZeroI8.
76    // $ne: the full name of the new type. E.g. NonExtremeI8.
77    //
78    // $XTR:  the *extreme* value constant for this type. (MIN | MAX).
79    // $doc:  the specific beginning of the documentation.
80    // $IP:   the type of the corresponding integer primitive. E.g. i8
81    // $s:    the sign identifier: i or u.
82    // $b:    the bits of the type, from 8 to 128, or the `size` suffix.
83    @$name:ident, $n0:ident, $ne:ident, $XTR:ident, $doc:literal, $IP:ty, $s:ident, $b:literal)
84        => { $crate::paste! {
85
86        pub use [<__impls_ $name >]::*;
87        #[allow(non_snake_case)]
88        mod [<__impls_ $name >] {
89            #[cfg(all(feature = "dep_bytemuck", feature = "unsafe_niche", not(feature = "safe_num")))]
90            use $crate::_dep::bytemuck::{CheckedBitPattern, NoUninit, PodInOption, ZeroableInOption};
91            #[cfg(feature = "unsafe_layout")]
92            use $crate::MemPod;
93
94            #[cfg(feature = "bit")]
95            use $crate::{BitSized, ByteSized};
96            use $crate::{
97                _core::num::*, is, unwrap, ConstDefault, FromStr,
98                NumError::{Invalid, Overflow}, NumResult, Debug, Display,
99                FmtResult, Formatter, Binary, Octal, LowerHex, UpperHex,
100            };
101
102            /* definition */
103
104            #[doc = $crate::TAG_NUM!()]
105            #[doc = $crate::TAG_NICHE!()]
106            #[doc = $doc " integer that is known not to equal some specific value." ]
107            ///
108            #[doc = "It has the same memory layout optimization as [`" $n0 "`][core::num::" $n0 "],"]
109            #[doc = " so that `Option<" $name ">` is the same size as `" $name "`."]
110            ///
111            /// # Examples
112            /// ```ignore
113            /// # use devela::NonValueI8;
114            /// assert![NonValueI8::<13>::new(13).is_none()];
115            /// assert![NonValueI8::<13>::new(12).unwrap().get() == 12];
116            /// ```
117            #[doc = "See also [`" $ne "`]."]
118            #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
119            pub struct $name <const V: $IP>(pub(in crate::num::niche::mem) $n0);
120
121            /* aliases */
122
123            #[doc = $crate::TAG_NUM!()]
124            #[doc = $crate::TAG_NICHE!()]
125            #[doc = $doc " integer that is known not to equal its most extreme value ([`"
126                $XTR "`][" $IP "::" $XTR "])."]
127            ///
128            /// Unlike the `NonValue*` types in general, this type alias implements
129            /// the [`Default`] and [`ConstDefault`][crate::code::ConstDefault] traits.
130            pub type $ne = $name <{$IP::$XTR}>;
131
132            impl Default for $ne {
133                /// # Features
134                /// Makes use of the `unsafe_niche` feature if enabled.
135                fn default() -> Self {
136                    #[cfg(any(feature = "safe_num", not(feature = "unsafe_niche")))]
137                    return $ne::new($IP::default()).unwrap();
138
139                    #[cfg(all(not(feature = "safe_num"), feature = "unsafe_niche"))]
140                    // SAFETY: the default primitive value is always 0, and their MAX is never 0.
141                    unsafe { return $ne::new_unchecked($IP::default()); }
142                }
143            }
144
145            impl ConstDefault for $ne {
146                /// # Features
147                /// Makes use of the `unsafe_niche` feature if enabled.
148                const DEFAULT: Self = {
149                    #[cfg(any(feature = "safe_num", not(feature = "unsafe_niche")))]
150                    if let Some(v) = Self::new($IP::DEFAULT) { v } else { unreachable![] }
151
152                    #[cfg(all(not(feature = "safe_num"), feature = "unsafe_niche"))]
153                    // SAFETY: the default primitive value is always 0, and their MAX is never 0.
154                    unsafe { $ne::new_unchecked($IP::DEFAULT) }
155                };
156            }
157
158            impl<const V: $IP> $name<V> {
159                /* constants */
160
161                /// Returns the maximum possible value.
162                pub const MAX: Self = {
163                    if $IP::MAX > V {
164                        unwrap![some Self::new($IP::MAX)]
165                    } else {
166                        unwrap![some Self::new($IP::MAX - 1)]
167                    }
168                };
169                /// Returns the minimum possible value.
170                pub const MIN: Self = {
171                    if $IP::MIN < V {
172                        unwrap![some Self::new($IP::MIN)]
173                    } else {
174                        unwrap![some Self::new($IP::MIN + 1)]
175                    }
176                };
177
178                /// Returns the number of valid values.
179                pub const VALID_VALUES: $IP = $IP::MAX;
180
181                /// Returns the number of invalid values.
182                pub const INVALID_VALUES: $IP = 1;
183
184                /* methods */
185
186                #[doc = "Returns a `" $name "` with the given `value`,"
187                    " if it is not equal to `V`."]
188                #[must_use]
189                pub const fn new(value: $IP) -> Option<Self> {
190                    if $IP::MIN == 0 && V == $IP::MAX { // unsigned::MAX optimization
191                        if value == V { return None; }
192                        is![let Some(nz) = $n0::new(!value); Some(Self(nz)); None]
193                    } else { // default case: XOR
194                        // NOTE: `i*::MIN` uses `LEA`-optimized `value ^ MIN`
195                        // (equivalent to `value.wrapping_sub(MIN)`).
196                        is![let Some(nz) = $n0::new(value ^ V); Some(Self(nz)); None]
197                    }
198                }
199                /// Creates a non-value integer, automatically converting the prohibited value `V`
200                /// to the closest valid number (`V-1` for most cases, `V+1` for `MIN`).
201                ///
202                /// # Guarantees
203                /// - For unsigned `MAX`: `V-1` → stored as `!(V-1)`
204                /// - For signed `MIN`: `V+1` → stored as `(V+1) ^ MIN`
205                /// - All others: `V-1` → stored as `(V-1) ^ V`
206                ///
207                /// # Features
208                /// - Can use the `unsafe_niche` feature internally.
209                ///
210                /// # Example
211                /// ```
212                /// # use devela::{NonValueI8, NonValueU8};
213                /// let x = assert_eq![NonValueU8::<255>::new_lossy(255).get(), 254];
214                /// let y = assert_eq![NonValueI8::<-128>::new_lossy(-128).get(), -127];
215                /// ```
216                #[must_use]
217                pub const fn new_lossy(value: $IP) -> Self {
218                    let transformed = if $IP::MIN == 0 && V == $IP::MAX { // unsigned MAX case
219                        let transformed = if value == V { V - 1 } else { value };
220                        !transformed
221                    } else { // For MIN: choose MIN+1, for others: V-1
222                        let transformed = is![value == V; is![V == $IP::MIN; V + 1; V - 1]; value];
223                        transformed ^ V
224                    };
225
226                    #[cfg(any(feature = "safe_num", not(feature = "unsafe_niche")))]
227                    { Self(unwrap![some $n0::new(transformed)]) }
228
229                    #[cfg(all(not(feature = "safe_num"), feature = "unsafe_niche"))]
230                    // SAFETY: We make sure the transformed value != 0
231                    unsafe {
232                        const { // compile-time verification:
233                            if $IP::MIN == 0 && V == $IP::MAX {
234                                assert!(!(V - 1) != 0); // unsigned MAX case
235                            } else if V == $IP::MIN {
236                                assert!((V + 1) ^ V != 0); // signed MIN case
237                            } else {
238                                assert!((V - 1) ^ V != 0); // all others
239                            }
240                        }
241                        Self($n0::new_unchecked(transformed))
242                    }
243                }
244
245                #[doc = "Returns a `" $name "` if the given `value`" " if it is not equal to `V`."]
246                ///
247                /// # Panics
248                /// Panics in debug if the given `value` is equal to `V`.
249                /// # Safety
250                /// The given `value` must never be equal to `V`.
251                #[cfg(all(not(feature = "safe_num"), feature = "unsafe_niche"))]
252                pub const unsafe fn new_unchecked(value: $IP) -> Self {
253                    #[cfg(debug_assertions)]
254                    if value == V { panic!("The given value was specifically prohibited.") }
255
256                    // SAFETY: caller must ensure safety
257                    Self(unsafe { $n0::new_unchecked(value ^ V) })
258                }
259
260                /// Returns the value as a primitive type.
261                #[must_use]
262                pub const fn get(&self) -> $IP {
263                    if $IP::MIN == 0 && V == $IP::MAX { // unsigned::MAX optimization
264                        !self.0.get()
265                    } else {
266                        self.0.get() ^ V
267                    }
268                }
269
270                /// Returns `true` if it is equal to the maximum value ([`MAX`][Self::MAX]).
271                #[must_use]
272                pub const fn is_max(&self) -> bool { self.get() == $IP::MAX }
273
274                /// Returns `true` if it is equal to the minimum value ([`MIN`][Self::MIN]).
275                #[must_use]
276                pub const fn is_min(&self) -> bool { self.get() == $IP::MIN }
277
278                /// Checked integer addition. Computes `self + rhs`.
279                ///
280                /// # Errors
281                /// Returns [`Overflow`] if the operations overflows, or
282                /// [`Invalid`] if the result equals the forbidden value `V`.
283                pub const fn checked_add(&self, other: $IP) -> NumResult<Self> {
284                    let res = unwrap![some_ok_or? self.get().checked_add(other), Overflow(None)];
285                    unwrap![some_ok_or Self::new(res), Invalid]
286                }
287                /// Checked integer substration. Computes `self - rhs`.
288                ///
289                /// # Errors
290                /// Returns [`Overflow`] if the operations overflows, or
291                /// [`Invalid`] if the result equals the forbidden value `V`.
292                pub const fn checked_sub(&self, other: $IP) -> NumResult<Self> {
293                    let res = unwrap![some_ok_or? self.get().checked_sub(other), Overflow(None)];
294                    unwrap![some_ok_or Self::new(res), Invalid]
295                }
296
297                /// Strict integer addition. Computes `self + rhs`.
298                ///
299                /// # Panics
300                /// Panics on overflow or if the result equals the forbidden value `V`.
301                pub const fn strict_add(&self, other: $IP) -> Self {
302                    let res = unwrap![some self.get().checked_add(other)];
303                    unwrap![some Self::new(res)]
304                }
305                /// Strict integer substration. Computes `self - rhs`.
306                ///
307                /// # Panics
308                /// Panics on overflow or if the result equals the forbidden value `V`.
309                pub const fn strict_sub(&self, other: $IP) -> Self {
310                    let res = unwrap![some self.get().checked_sub(other)];
311                    unwrap![some Self::new(res)]
312                }
313
314                /// Saturating integer addition. Computes `self + rhs`.
315                ///
316                /// Saturates at the numeric bounds instead of overflowing.
317                /// If the result would equal `V` it will return `V - 1`.
318                pub const fn saturating_add(&self, other: $IP) -> Self {
319                    let res = self.get().saturating_add(other);
320                    unwrap![some Self::new(is![res == V; res - 1; res])]
321                }
322                /// Saturating integer substration. Computes `self - rhs`.
323                ///
324                /// Saturates at the numeric bounds instead of overflowing.
325                /// If the result would equal `V` it will return `V + 1`.
326                pub const fn saturating_sub(&self, other: $IP) -> Self {
327                    let res = self.get().saturating_sub(other);
328                    unwrap![some Self::new(is![res == V; res + 1; res])]
329                }
330
331                /// Wraping integer addition. Computes `self + rhs`.
332                ///
333                /// Wraps at the numeric bounds instead of overflowing.
334                /// If the result would equal `V` it will return `V + 1`.
335                pub const fn wrapping_add(&self, other: $IP) -> Self {
336                    let res = self.get().wrapping_add(other);
337                    unwrap![some Self::new(is![res == V; res + 1; res])]
338                }
339                /// Wraping integer subtraction. Computes `self - rhs`.
340                ///
341                /// Wraps at the numeric bounds instead of overflowing.
342                /// If the result would equal `V` it will return `V - 1`.
343                pub const fn wrapping_sub(&self, other: $IP) -> Self {
344                    let res = self.get().wrapping_sub(other);
345                    unwrap![some Self::new(is![res == V; res - 1; res])]
346                }
347            }
348
349            /* core impls */
350
351            impl<const V: $IP> Display for $name <V> {
352                fn fmt(&self, f: &mut Formatter) -> FmtResult<()> {
353                    write!(f, "{}", self.get()) } }
354            impl<const V: $IP> Debug for $name <V> {
355                fn fmt(&self, f: &mut Formatter) -> FmtResult<()> {
356                    write!(f, "{}::<{}>({})", stringify!($name), V, self.get()) } }
357            impl<const V: $IP> Binary for $name<V> {
358                fn fmt(&self, f: &mut Formatter) -> FmtResult<()> {
359                    Binary::fmt(&self.get(), f) } }
360            impl<const V: $IP> Octal for $name<V> {
361                fn fmt(&self, f: &mut Formatter) -> FmtResult<()> {
362                    Octal::fmt(&self.get(), f) } }
363            impl<const V: $IP> LowerHex for $name<V> {
364                fn fmt(&self, f: &mut Formatter) -> FmtResult<()> {
365                    LowerHex::fmt(&self.get(), f) } }
366            impl<const V: $IP> UpperHex for $name<V> {
367                fn fmt(&self, f: &mut Formatter) -> FmtResult<()> {
368                    UpperHex::fmt(&self.get(), f) } }
369            impl<const V: $IP> FromStr for $name<V> {
370                type Err = ParseIntError;
371                fn from_str(s: &str) -> Result<Self, Self::Err> {
372                    Self::new($IP::from_str(s)?).ok_or_else(||"".parse::<i32>().unwrap_err()) } }
373
374            /* conversions */
375
376            impl<const V: $IP> From<$name<V>> for $IP {
377                fn from(value: $name<V>) -> $IP { value.get() }
378            }
379
380            impl<const V: $IP> TryFrom<$IP> for $name<V> {
381                type Error = $crate::TryFromIntError;
382
383                /// # Features
384                /// Makes use of the `unsafe_niche` feature if enabled.
385                fn try_from(value: $IP) -> Result<Self, Self::Error> {
386                    // We generate a TryFromIntError by intentionally causing a failed conversion.
387                    #[cfg(any(feature = "safe_num", not(feature = "unsafe_niche")))]
388                    return Self::new(value).ok_or_else(|| i8::try_from(255_u8).unwrap_err());
389
390                    #[cfg(all(not(feature = "safe_num"), feature = "unsafe_niche"))]
391                    return Self::new(value)
392                        .ok_or_else(|| unsafe { i8::try_from(255_u8).unwrap_err_unchecked() });
393                }
394            }
395
396            /* internal impls */
397
398            // BitSized
399            #[cfg(feature = "bit")]
400            impl<const V: $IP> BitSized<{$IP::BYTE_SIZE * 8}> for $name<V> {}
401
402            #[cfg(feature = "unsafe_layout")]
403            unsafe impl<const V: $IP> MemPod for Option<$name<V>> {}
404
405            // // MAYBE: THINK
406            // // - could not have a one,two or three
407            // impl NumConst for $name<V> {
408            //     type Num = $IP;
409            //     const NUM_ZERO: Option<$IP> = $ZERO;
410            //     const NUM_ONE: $IP = $ONE;
411            //     const NUM_TWO: $IP = $TWO;
412            //     const NUM_THREE: $IP = $THREE;
413            //     const NUM_NEG_ONE: Option<$IP> = $NEG_ONE;
414            //     const NUM_MIN: $IP = <$IP>::MIN;
415            //     const NUM_MIN_POSITIVE: Option<$IP> = $MIN_POS;
416            //     const NUM_MAX: $IP = <$IP>::MAX;
417            //     const NUM_MAX_NEGATIVE: Option<$IP> = $MAX_NEG;
418            //     const NUM_MAX_POWER_OF_TWO: Option<$IP> = $MAX_POW2;
419            // }
420
421            /* external impls*/
422
423            #[cfg(all(feature = "dep_bytemuck", feature = "unsafe_niche", not(feature = "safe_num")))]
424            #[allow(non_snake_case)]
425            mod [<$name $s $b>] {
426                use super::*;
427
428                unsafe impl<const V: $IP> ZeroableInOption for $name<V> {}
429                unsafe impl<const V: $IP> PodInOption for $name<V> {}
430                unsafe impl<const V: $IP> NoUninit for $name<V> {}
431                unsafe impl<const V: $IP> CheckedBitPattern for $name<V> {
432                    type Bits = $IP;
433
434                    fn is_valid_bit_pattern(bits: &Self::Bits) -> bool {
435                        // Since inner repr is NonZero, 0 is the only invalid bit pattern
436                        *bits != 0
437                    }
438                }
439            }
440        }
441    }};
442}
443use impl_non_value;