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
8// Centralized automatic definitions based on enabled features & flags
9#[cfg(any(doc, test))]
10impl_non_value![I 8];
11#[cfg(feature = "_char7")]
12impl_non_value![U 8];
13#[cfg(feature = "_char16")]
14impl_non_value![U 16];
15
16/// Implements a `NonValue[I|U]B<V>`.
17///
18/// - `I` or `U` means a signed or unsigned type, respectively.
19/// - `B` represents the bit-size, from [8, 16, 32, 64, 128].
20/// - `V` is the prohibited value in the bit-sized range.
21///
22/// # Example
23/// ```
24/// # use devela::impl_non_value;
25/// impl_non_value![I 8];
26///
27/// assert![NonValueI8::<3>::new(2).is_some()];
28/// assert![NonValueI8::<3>::new(3).is_none()];
29///
30/// assert![NonExtremeI8::new(i8::MIN).is_none()];
31/// assert![NonExtremeI8::new(i8::MAX).is_some()];
32/// assert![NonExtremeI8::new(0).is_some()];
33/// ```
34///
35/// See for example: [`NonValueI8`] and [`NonExtremeI8`].
36//
37// NOTE: can't use doc(cfg) attributes in generated methods.
38#[macro_export]
39#[cfg_attr(cargo_primary_package, doc(hidden))]
40macro_rules! impl_non_value {
41    (
42        // Defines a new signed non-value type. E.g.: impl_non_value![i 32]
43        // would generate NonValueI32 and NonExtremeI32
44        I $bits:literal) => {
45        $crate::impl_non_value![@MIN, "A signed", i, $bits];
46    };
47    (
48        // Defines a new signed non-value type. E.g.: impl_non_value![u 32]
49        // would generate NonValueU32 and NonExtremeU32
50        U $bits:literal) => {
51        $crate::impl_non_value![@MAX, "An unsigned", u, $bits];
52    };
53    (
54     /* private arms */
55     @$XTR:ident, $doc:literal, $s:ident, $b:literal) => {
56        $crate::paste!{
57            $crate::impl_non_value![@
58                [<NonValue $s:upper $b>],   // $name
59                [<NonZero $s:upper $b>],   // $n0
60                [<NonExtreme $s:upper $b>], // $ne
61                $XTR,
62                $doc,
63                [<$s $b>], // $IP
64                $s,
65                $b
66            ];
67        }
68    };
69    (
70    // $name: the full name of the new type. E.g. NonValueI8.
71    // $n0: the full name of the inner NonZero. E.g. NonZeroI8.
72    // $ne: the full name of the new type. E.g. NonExtremeI8.
73    //
74    // $XTR:  the *extreme* value constant for this type. (MIN | MAX).
75    // $doc:  the specific beginning of the documentation.
76    // $IP:   the type of the corresponding integer primitive. E.g. i8
77    // $s:    the sign identifier: i or u.
78    // $b:    the bits of the type, from 8 to 128, or the `size` suffix.
79    @$name:ident, $n0:ident, $ne:ident, $XTR:ident, $doc:literal, $IP:ty, $s:ident, $b:literal)
80        => { $crate::paste! {
81
82        pub use [<__impls_ $name >]::*;
83        #[allow(non_snake_case)]
84        mod [<__impls_ $name >] {
85            #[cfg(all(feature = "dep_bytemuck", feature = "unsafe_niche", not(feature = "safe_num")))]
86            use $crate::_dep::bytemuck::{CheckedBitPattern, NoUninit, PodInOption, ZeroableInOption};
87            // #[cfg(feature = "unsafe_layout")]
88            // use $crate::MemPod;
89            #[cfg(feature = "bit")]
90            use $crate::{BitSized, ByteSized};
91            use $crate::{
92                _core::num::*, iif, unwrap, ConstDefault, FromStr,
93                NumError::{Invalid, Overflow}, NumResult, Debug, Display,
94                FmtResult, Formatter, Binary, Octal, LowerHex, UpperHex,
95            };
96
97            /* definition */
98
99            #[doc = $doc " integer that is known not to equal some specific value." ]
100            ///
101            #[doc = "It has the same memory layout optimization as [`" $n0 "`][core::num::" $n0 "],"]
102            #[doc = " so that `Option<" $name ">` is the same size as `" $name "`."]
103            ///
104            /// # Examples
105            /// ```ignore
106            /// # use devela::NonValueI8;
107            /// assert![NonValueI8::<13>::new(13).is_none()];
108            /// assert![NonValueI8::<13>::new(12).unwrap().get() == 12];
109            /// ```
110            #[doc = "See also [`NonExtreme" $s:upper $b "`]."]
111            #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
112            pub struct $name <const V: $IP>($n0);
113
114            /* aliases */
115
116            #[doc = $doc " integer that is known not to equal its most extreme value ([`"
117                $XTR "`][" $IP "::" $XTR "])."]
118            ///
119            /// Unlike the `NonValue*` types in general, this type alias implements
120            /// the [`Default`] and [`ConstDefault`][crate::code::ConstDefault] traits.
121            pub type [<NonExtreme $s:upper $b>] = $name <{$IP::$XTR}>;
122
123            impl Default for [<NonExtreme $s:upper $b>] {
124                /// # Features
125                /// Makes use of the `unsafe_niche` feature if enabled.
126                #[must_use]
127                fn default() -> Self {
128                    #[cfg(any(feature = "safe_num", not(feature = "unsafe_niche")))]
129                    return [<NonExtreme $s:upper $b>]::new($IP::default()).unwrap();
130
131                    #[cfg(all(not(feature = "safe_num"), feature = "unsafe_niche"))]
132                    // SAFETY: the default primitive value is always 0, and their MAX is never 0.
133                    unsafe { return [<NonExtreme $s:upper $b>]::new_unchecked($IP::default()); }
134                }
135            }
136
137            impl ConstDefault for [<NonExtreme $s:upper $b>] {
138                /// # Features
139                /// Makes use of the `unsafe_niche` feature if enabled.
140                const DEFAULT: Self = {
141                    #[cfg(any(feature = "safe_num", not(feature = "unsafe_niche")))]
142                    if let Some(v) = Self::new($IP::DEFAULT) { v } else { unreachable![] }
143
144                    #[cfg(all(not(feature = "safe_num"), feature = "unsafe_niche"))]
145                    // SAFETY: the default primitive value is always 0, and their MAX is never 0.
146                    unsafe { [<NonExtreme $s:upper $b>]::new_unchecked($IP::DEFAULT) }
147                };
148            }
149
150            impl<const V: $IP> $name<V> {
151                /* constants */
152
153                /// Returns the maximum possible value.
154                pub const MAX: Self = {
155                    if $IP::MAX > V {
156                        unwrap![some Self::new($IP::MAX)]
157                    } else {
158                        unwrap![some Self::new($IP::MAX - 1)]
159                    }
160                };
161                /// Returns the minimum possible value.
162                pub const MIN: Self = {
163                    if $IP::MIN < V {
164                        unwrap![some Self::new($IP::MIN)]
165                    } else {
166                        unwrap![some Self::new($IP::MIN + 1)]
167                    }
168                };
169
170                /// Returns the number of valid values.
171                pub const VALID_VALUES: [<u $b>] = [<u $b>]::MAX;
172
173                /// Returns the number of invalid values.
174                pub const INVALID_VALUES: [<u $b>] = 1;
175
176                /* methods */
177
178                #[doc = "Returns a `" $name "` with the given `value`,"
179                    " if it is not equal to `V`."]
180                #[must_use]
181                pub const fn new(value: $IP) -> Option<Self> {
182                    match [<NonZero $s:upper $b>]::new(value ^ V) {
183                        None => None,
184                        Some(v) => Some(Self(v)),
185                    }
186                }
187
188                #[doc = "Returns a `" $name "` if the given `value`" " if it is not equal to `V`."]
189                ///
190                /// # Panics
191                /// Panics in debug if the given `value` is equal to `V`.
192                /// # Safety
193                /// The given `value` must never be equal to `V`.
194                #[must_use]
195                #[cfg(all(not(feature = "safe_num"), feature = "unsafe_niche"))]
196                pub const unsafe fn new_unchecked(value: $IP) -> Self {
197                    #[cfg(debug_assertions)]
198                    if value == V { panic!("The given value was specifically prohibited.") }
199
200                    // SAFETY: caller must ensure safety
201                    Self(unsafe { [<NonZero $s:upper $b>]::new_unchecked(value ^ V) })
202                }
203
204                /// Returns the value as a primitive type.
205                #[must_use]
206                pub const fn get(&self) -> $IP { self.0.get() ^ V }
207
208                /// Returns `true` if it is equal to the maximum value ([`MAX`][Self::MAX]).
209                #[must_use]
210                pub const fn is_max(&self) -> bool { self.get() == $IP::MAX }
211
212                /// Returns `true` if it is equal to the minimum value ([`MIN`][Self::MIN]).
213                #[must_use]
214                pub const fn is_min(&self) -> bool { self.get() == $IP::MIN }
215
216                /// Checked integer addition. Computes `self + rhs`.
217                ///
218                /// # Errors
219                /// Returns [`Overflow`] if the operations overflows, or
220                /// [`Invalid`] if the result equals the forbidden value `V`.
221                pub const fn checked_add(&self, other: $IP) -> NumResult<Self> {
222                    let res = unwrap![some_ok_or? self.get().checked_add(other), Overflow(None)];
223                    unwrap![some_ok_or Self::new(res), Invalid]
224                }
225                /// Checked integer substration. Computes `self - rhs`.
226                ///
227                /// # Errors
228                /// Returns [`Overflow`] if the operations overflows, or
229                /// [`Invalid`] if the result equals the forbidden value `V`.
230                pub const fn checked_sub(&self, other: $IP) -> NumResult<Self> {
231                    let res = unwrap![some_ok_or? self.get().checked_sub(other), Overflow(None)];
232                    unwrap![some_ok_or Self::new(res), Invalid]
233                }
234
235                /// Strict integer addition. Computes `self + rhs`.
236                ///
237                /// # Panics
238                /// Panics on overflow or if the result equals the forbidden value `V`.
239                pub const fn strict_add(&self, other: $IP) -> Self {
240                    let res = unwrap![some self.get().checked_add(other)];
241                    unwrap![some Self::new(res)]
242                }
243                /// Strict integer substration. Computes `self - rhs`.
244                ///
245                /// # Panics
246                /// Panics on overflow or if the result equals the forbidden value `V`.
247                pub const fn strict_sub(&self, other: $IP) -> Self {
248                    let res = unwrap![some self.get().checked_sub(other)];
249                    unwrap![some Self::new(res)]
250                }
251
252                /// Saturating integer addition. Computes `self + rhs`.
253                ///
254                /// Saturates at the numeric bounds instead of overflowing.
255                /// If the result would equal `V` it will return `V - 1`.
256                pub const fn saturating_add(&self, other: $IP) -> Self {
257                    let res = self.get().saturating_add(other);
258                    unwrap![some Self::new(iif![res == V; res - 1; res])]
259                }
260                /// Saturating integer substration. Computes `self - rhs`.
261                ///
262                /// Saturates at the numeric bounds instead of overflowing.
263                /// If the result would equal `V` it will return `V + 1`.
264                pub const fn saturating_sub(&self, other: $IP) -> Self {
265                    let res = self.get().saturating_sub(other);
266                    unwrap![some Self::new(iif![res == V; res + 1; res])]
267                }
268
269                /// Wraping integer addition. Computes `self + rhs`.
270                ///
271                /// Wraps at the numeric bounds instead of overflowing.
272                /// If the result would equal `V` it will return `V + 1`.
273                pub const fn wrapping_add(&self, other: $IP) -> Self {
274                    let res = self.get().wrapping_add(other);
275                    unwrap![some Self::new(iif![res == V; res + 1; res])]
276                }
277                /// Wraping integer subtraction. Computes `self - rhs`.
278                ///
279                /// Wraps at the numeric bounds instead of overflowing.
280                /// If the result would equal `V` it will return `V - 1`.
281                pub const fn wrapping_sub(&self, other: $IP) -> Self {
282                    let res = self.get().wrapping_sub(other);
283                    unwrap![some Self::new(iif![res == V; res - 1; res])]
284                }
285            }
286
287            /* core impls */
288
289            impl<const V: $IP> Display for $name <V> {
290                fn fmt(&self, f: &mut Formatter) -> FmtResult<()> {
291                    write!(f, "{}", self.get()) } }
292            impl<const V: $IP> Debug for $name <V> {
293                fn fmt(&self, f: &mut Formatter) -> FmtResult<()> {
294                    write!(f, "{}::<{}>({})", stringify!($name), V, self.get()) } }
295            impl<const V: $IP> Binary for $name<V> {
296                fn fmt(&self, f: &mut Formatter) -> FmtResult<()> {
297                    Binary::fmt(&self.get(), f) } }
298            impl<const V: $IP> Octal for $name<V> {
299                fn fmt(&self, f: &mut Formatter) -> FmtResult<()> {
300                    Octal::fmt(&self.get(), f) } }
301            impl<const V: $IP> LowerHex for $name<V> {
302                fn fmt(&self, f: &mut Formatter) -> FmtResult<()> {
303                    LowerHex::fmt(&self.get(), f) } }
304            impl<const V: $IP> UpperHex for $name<V> {
305                fn fmt(&self, f: &mut Formatter) -> FmtResult<()> {
306                    UpperHex::fmt(&self.get(), f) } }
307            impl<const V: $IP> FromStr for $name<V> {
308                type Err = ParseIntError;
309                fn from_str(s: &str) -> Result<Self, Self::Err> {
310                    Self::new($IP::from_str(s)?).ok_or_else(||"".parse::<i32>().unwrap_err()) } }
311
312            /* conversions */
313
314            impl<const V: $IP> From<$name<V>> for $IP {
315                #[must_use]
316                fn from(value: $name<V>) -> $IP { value.get() }
317            }
318
319            impl<const V: $IP> TryFrom<$IP> for $name<V> {
320                type Error = $crate::TryFromIntError;
321
322                /// # Features
323                /// Makes use of the `unsafe_niche` feature if enabled.
324                fn try_from(value: $IP) -> Result<Self, Self::Error> {
325                    // We generate a TryFromIntError by intentionally causing a failed conversion.
326                    #[cfg(any(feature = "safe_num", not(feature = "unsafe_niche")))]
327                    return Self::new(value).ok_or_else(|| i8::try_from(255_u8).unwrap_err());
328
329                    #[cfg(all(not(feature = "safe_num"), feature = "unsafe_niche"))]
330                    return Self::new(value)
331                        .ok_or_else(|| unsafe { i8::try_from(255_u8).unwrap_err_unchecked() });
332                }
333            }
334
335            /* internal impls */
336
337            // BitSized
338            #[cfg(feature = "bit")]
339            impl<const V: $IP> BitSized<{$IP::BYTE_SIZE * 8}> for $name<V> {}
340
341            // NOTE: due to the orphan rule we can't implement MemPod for Option<NonValue*>
342            // #[cfg(feature = "unsafe_layout")]
343            // unsafe impl<const V: $IP> MemPod for Option<$name<V>> {}
344
345            /* external impls*/
346
347            #[cfg(all(feature = "dep_bytemuck", feature = "unsafe_niche", not(feature = "safe_num")))]
348            #[allow(non_snake_case)]
349            mod [<$name $s $b>] {
350                use super::*;
351
352                unsafe impl<const V: $IP> ZeroableInOption for $name<V> {}
353                unsafe impl<const V: $IP> PodInOption for $name<V> {}
354                unsafe impl<const V: $IP> NoUninit for $name<V> {}
355                unsafe impl<const V: $IP> CheckedBitPattern for $name<V> {
356                    type Bits = $IP;
357
358                    fn is_valid_bit_pattern(bits: &Self::Bits) -> bool {
359                        // Since inner repr is NonZero, 0 is the only invalid bit pattern
360                        *bits != 0
361                    }
362                }
363            }
364        }
365    }};
366}
367#[doc(inline)]
368pub use impl_non_value;