naga/
non_max_u32.rs

1//! [`NonMaxU32`], a 32-bit type that can represent any value except [`u32::MAX`].
2//!
3//! Naga would like `Option<Handle<T>>` to be a 32-bit value, which means we
4//! need to exclude some index value for use in representing [`None`]. We could
5//! have [`Handle`] store a [`NonZeroU32`], but zero is a very useful value for
6//! indexing. We could have a [`Handle`] store a value one greater than its index,
7//! but it turns out that it's not uncommon to want to work with [`Handle`]s'
8//! indices, so that bias of 1 becomes more visible than one would like.
9//!
10//! This module defines the type [`NonMaxU32`], for which `Option<NonMaxU32>` is
11//! still a 32-bit value, but which is directly usable as a [`Handle`] index
12//! type. It still uses a bias of 1 under the hood, but that fact is isolated
13//! within the implementation.
14//!
15//! [`Handle`]: crate::arena::Handle
16//! [`NonZeroU32`]: core::num::NonZeroU32
17#![allow(dead_code)]
18
19use core::num::NonZeroU32;
20
21/// An unsigned 32-bit value known not to be [`u32::MAX`].
22///
23/// A `NonMaxU32` value can represent any value in the range `0 .. u32::MAX -
24/// 1`, and an `Option<NonMaxU32>` is still a 32-bit value. In other words,
25/// `NonMaxU32` is just like [`NonZeroU32`], except that a different value is
26/// missing from the full `u32` range.
27///
28/// Since zero is a very useful value in indexing, `NonMaxU32` is more useful
29/// for representing indices than [`NonZeroU32`].
30///
31/// `NonMaxU32` values and `Option<NonMaxU32>` values both occupy 32 bits.
32///
33/// # Serialization and Deserialization
34///
35/// When the appropriate Cargo features are enabled, `NonMaxU32` implements
36/// [`serde::Serialize`] and [`serde::Deserialize`] in the natural way, as the
37/// integer value it represents. For example, serializing
38/// `NonMaxU32::new(0).unwrap()` as JSON or RON yields the string `"0"`. This is
39/// the case despite `NonMaxU32`'s implementation, described below.
40///
41/// # Implementation
42///
43/// Although this should not be observable to its users, a `NonMaxU32` whose
44/// value is `n` is a newtype around a [`NonZeroU32`] whose value is `n + 1`.
45/// This way, the range of values that `NonMaxU32` can represent,
46/// `0..=u32::MAX - 1`, is mapped to the range `1..=u32::MAX`, which is the
47/// range that /// [`NonZeroU32`] can represent. (And conversely, since
48/// [`u32`] addition wraps around, the value unrepresentable in `NonMaxU32`,
49/// [`u32::MAX`], becomes the value unrepresentable in [`NonZeroU32`], `0`.)
50///
51/// [`NonZeroU32`]: core::num::NonZeroU32
52#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
53#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
54pub struct NonMaxU32(NonZeroU32);
55
56impl NonMaxU32 {
57    /// Construct a [`NonMaxU32`] whose value is `n`, if possible.
58    pub const fn new(n: u32) -> Option<Self> {
59        // If `n` is `u32::MAX`, then `n.wrapping_add(1)` is `0`,
60        // so `NonZeroU32::new` returns `None` in exactly the case
61        // where we must return `None`.
62        match NonZeroU32::new(n.wrapping_add(1)) {
63            Some(non_zero) => Some(NonMaxU32(non_zero)),
64            None => None,
65        }
66    }
67
68    /// Return the value of `self` as a [`u32`].
69    pub const fn get(self) -> u32 {
70        self.0.get() - 1
71    }
72
73    /// Construct a [`NonMaxU32`] whose value is `n`.
74    ///
75    /// # Safety
76    ///
77    /// The value of `n` must not be [`u32::MAX`].
78    pub const unsafe fn new_unchecked(n: u32) -> NonMaxU32 {
79        NonMaxU32(unsafe { NonZeroU32::new_unchecked(n + 1) })
80    }
81
82    /// Construct a [`NonMaxU32`] whose value is `index`.
83    ///
84    /// # Safety
85    ///
86    /// - The value of `index` must be strictly less than [`u32::MAX`].
87    pub const unsafe fn from_usize_unchecked(index: usize) -> Self {
88        NonMaxU32(unsafe { NonZeroU32::new_unchecked(index as u32 + 1) })
89    }
90
91    pub fn checked_add(self, n: u32) -> Option<Self> {
92        // Adding `n` to `self` produces `u32::MAX` if and only if
93        // adding `n` to `self.0` produces `0`. So we can simply
94        // call `NonZeroU32::checked_add` and let its check for zero
95        // determine whether our add would have produced `u32::MAX`.
96        Some(NonMaxU32(self.0.checked_add(n)?))
97    }
98}
99
100impl core::fmt::Debug for NonMaxU32 {
101    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
102        self.get().fmt(f)
103    }
104}
105
106impl core::fmt::Display for NonMaxU32 {
107    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
108        self.get().fmt(f)
109    }
110}
111
112#[cfg(feature = "serialize")]
113impl serde::Serialize for NonMaxU32 {
114    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
115    where
116        S: serde::Serializer,
117    {
118        serializer.serialize_u32(self.get())
119    }
120}
121
122#[cfg(feature = "deserialize")]
123impl<'de> serde::Deserialize<'de> for NonMaxU32 {
124    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
125    where
126        D: serde::Deserializer<'de>,
127    {
128        // Defer to `u32`'s `Deserialize` implementation.
129        let n = <u32 as serde::Deserialize>::deserialize(deserializer)?;
130
131        // Constrain the range of the value further.
132        NonMaxU32::new(n).ok_or_else(|| {
133            <D::Error as serde::de::Error>::invalid_value(
134                serde::de::Unexpected::Unsigned(n as u64),
135                &"a value no less than 0 and no greater than 4294967294 (2^32 - 2)",
136            )
137        })
138    }
139}
140
141#[test]
142fn size() {
143    assert_eq!(size_of::<Option<NonMaxU32>>(), size_of::<u32>());
144}