1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
//! [`NonMaxU32`], a 32-bit type that can represent any value except [`u32::MAX`].
//!
//! Naga would like `Option<Handle<T>>` to be a 32-bit value, which means we
//! need to exclude some index value for use in representing [`None`]. We could
//! have [`Handle`] store a [`NonZeroU32`], but zero is a very useful value for
//! indexing. We could have a [`Handle`] store a value one greater than its index,
//! but it turns out that it's not uncommon to want to work with [`Handle`]s'
//! indices, so that bias of 1 becomes more visible than one would like.
//!
//! This module defines the type [`NonMaxU32`], for which `Option<NonMaxU32>` is
//! still a 32-bit value, but which is directly usable as a [`Handle`] index
//! type. It still uses a bias of 1 under the hood, but that fact is isolated
//! within the implementation.
//!
//! [`Handle`]: crate::arena::Handle
//! [`NonZeroU32`]: std::num::NonZeroU32
#![allow(dead_code)]

use std::num::NonZeroU32;

/// An unsigned 32-bit value known not to be [`u32::MAX`].
///
/// A `NonMaxU32` value can represent any value in the range `0 .. u32::MAX -
/// 1`, and an `Option<NonMaxU32>` is still a 32-bit value. In other words,
/// `NonMaxU32` is just like [`NonZeroU32`], except that a different value is
/// missing from the full `u32` range.
///
/// Since zero is a very useful value in indexing, `NonMaxU32` is more useful
/// for representing indices than [`NonZeroU32`].
///
/// `NonMaxU32` values and `Option<NonMaxU32>` values both occupy 32 bits.
///
/// # Serialization and Deserialization
///
/// When the appropriate Cargo features are enabled, `NonMaxU32` implements
/// [`serde::Serialize`] and [`serde::Deserialize`] in the natural way, as the
/// integer value it represents. For example, serializing
/// `NonMaxU32::new(0).unwrap()` as JSON or RON yields the string `"0"`. This is
/// the case despite `NonMaxU32`'s implementation, described below.
///
/// # Implementation
///
/// Although this should not be observable to its users, a `NonMaxU32` whose
/// value is `n` is a newtype around a [`NonZeroU32`] whose value is `n + 1`.
/// This way, the range of values that `NonMaxU32` can represent, `0..=u32::MAX
/// - 1`, is mapped to the range `1..=u32::MAX`, which is the range that
/// [`NonZeroU32`] can represent. (And conversely, since [`u32`] addition wraps
/// around, the value unrepresentable in `NonMaxU32`, [`u32::MAX`], becomes the
/// value unrepresentable in [`NonZeroU32`], `0`.)
///
/// [`NonZeroU32`]: std::num::NonZeroU32
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
pub struct NonMaxU32(NonZeroU32);

impl NonMaxU32 {
    /// Construct a [`NonMaxU32`] whose value is `n`, if possible.
    pub const fn new(n: u32) -> Option<Self> {
        // If `n` is `u32::MAX`, then `n.wrapping_add(1)` is `0`,
        // so `NonZeroU32::new` returns `None` in exactly the case
        // where we must return `None`.
        match NonZeroU32::new(n.wrapping_add(1)) {
            Some(non_zero) => Some(NonMaxU32(non_zero)),
            None => None,
        }
    }

    /// Return the value of `self` as a [`u32`].
    pub const fn get(self) -> u32 {
        self.0.get() - 1
    }

    /// Construct a [`NonMaxU32`] whose value is `n`.
    ///
    /// # Safety
    ///
    /// The value of `n` must not be [`u32::MAX`].
    pub const unsafe fn new_unchecked(n: u32) -> NonMaxU32 {
        NonMaxU32(unsafe { NonZeroU32::new_unchecked(n + 1) })
    }

    /// Construct a [`NonMaxU32`] whose value is `index`.
    ///
    /// # Safety
    ///
    /// - The value of `index` must be strictly less than [`u32::MAX`].
    pub const unsafe fn from_usize_unchecked(index: usize) -> Self {
        NonMaxU32(unsafe { NonZeroU32::new_unchecked(index as u32 + 1) })
    }

    pub fn checked_add(self, n: u32) -> Option<Self> {
        // Adding `n` to `self` produces `u32::MAX` if and only if
        // adding `n` to `self.0` produces `0`. So we can simply
        // call `NonZeroU32::checked_add` and let its check for zero
        // determine whether our add would have produced `u32::MAX`.
        Some(NonMaxU32(self.0.checked_add(n)?))
    }
}

impl std::fmt::Debug for NonMaxU32 {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        self.get().fmt(f)
    }
}

impl std::fmt::Display for NonMaxU32 {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        self.get().fmt(f)
    }
}

#[cfg(feature = "serialize")]
impl serde::Serialize for NonMaxU32 {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: serde::Serializer,
    {
        serializer.serialize_u32(self.get())
    }
}

#[cfg(feature = "deserialize")]
impl<'de> serde::Deserialize<'de> for NonMaxU32 {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: serde::Deserializer<'de>,
    {
        // Defer to `u32`'s `Deserialize` implementation.
        let n = <u32 as serde::Deserialize>::deserialize(deserializer)?;

        // Constrain the range of the value further.
        NonMaxU32::new(n).ok_or_else(|| {
            <D::Error as serde::de::Error>::invalid_value(
                serde::de::Unexpected::Unsigned(n as u64),
                &"a value no less than 0 and no greater than 4294967294 (2^32 - 2)",
            )
        })
    }
}

#[test]
fn size() {
    use core::mem::size_of;
    assert_eq!(size_of::<Option<NonMaxU32>>(), size_of::<u32>());
}