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;