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;