serial_num/
lib.rs

1#![doc = include_str!("crate.md")]
2#![doc = include_str!("examples.md")]
3#![cfg_attr(
4    not(any(test, feature = "arbitrary", feature = "bitcode", feature = "speedy",)),
5    no_std
6)]
7
8#[cfg(test)]
9mod tests;
10
11#[cfg(all(test, not(miri), feature = "arbitrary"))]
12mod tests_prop;
13
14#[cfg(kani)]
15mod tests_kani;
16
17#[cfg(all(test, not(miri)))]
18mod tests_readme;
19
20use core::cmp::Ordering;
21use core::ops::Add;
22
23/// Two-byte serial number with wraparound.
24///
25/// A serial number is an identifier assigned incrementally to an item.
26/// In many cases, you can use a `u32` or `u64` and call it
27/// a day, without having to worry about overflow. The niche benefit of this type
28/// is that it only uses the space of a `u16`, with the problem of overflow solved
29/// by wraparound.
30///
31/// This is an "opaque" type, similar to `Instants`.
32/// Serial numbers get their significance when being compare to one another,
33/// but there is no method to get the "inner counter". Another similarity
34/// is that there is no "maximum" serial number, since every
35/// serial number has a successor.
36///
37/// The window used for comparing two serial numbers is half of the number space,
38/// `(u16::MAX-1)/2 = 32767`. If two serial numbers are within that window, we simply compare
39/// the numbers as you normally would. If we compare numbers that do not fit into
40/// that window, like `5` and `65000`, the comparison is flipped, and we say `65000 < 5`.
41/// This is based on the assumption that we got to `5` by increasing `65000` beyond
42/// the point of wraparound at `u16::MAX-1 = 65534`. The assumption only holds if the items you
43/// assign serial numbers to have a short enough lifetime. The ordering of items in your state
44/// will get messed up if there is an item that is the `32767`th successor of another item.
45///
46/// The final value in the number space, `u16::MAX`, is reserved for the special
47/// [`NAN`](Self::NAN) value. This is done to save space - you don't need to wrap
48/// this type in an `Option` if only some items are assigned a serial number.
49#[doc = include_str!("examples.md")]
50#[must_use]
51#[repr(transparent)]
52#[derive(Debug, Copy, Clone, Default, PartialEq, Eq, Hash)]
53#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
54#[cfg_attr(feature = "bincode", derive(bincode::Decode, bincode::Encode))]
55#[cfg_attr(feature = "bitcode", derive(bitcode::Decode, bitcode::Encode))]
56#[cfg_attr(
57    feature = "borsh",
58    derive(borsh::BorshDeserialize, borsh::BorshSerialize)
59)]
60#[cfg_attr(feature = "bytemuck", derive(bytemuck::Pod, bytemuck::Zeroable))]
61#[cfg_attr(
62    feature = "postcard",
63    derive(
64        postcard::experimental::max_size::MaxSize,
65        postcard::experimental::schema::Schema
66    )
67)]
68#[cfg_attr(
69    feature = "rkyv",
70    derive(rkyv::Archive, rkyv::Deserialize, rkyv::Serialize),
71    archive(compare(PartialEq)),
72    archive_attr(derive(Clone, Copy, Debug))
73)]
74#[cfg_attr(feature = "rkyv-safe", archive(check_bytes))]
75#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
76#[cfg_attr(feature = "speedy", derive(speedy::Readable, speedy::Writable))]
77pub struct Serial(u16);
78
79const NAN_U16: u16 = u16::MAX;
80const NAN_U32: u32 = 65_535;
81const MAX_U16: u16 = u16::MAX - 1;
82const MID_I32: i32 = 32_767;
83const MID_U16: u16 = 32_767;
84
85impl Serial {
86    /// Special value representing "no serial number".
87    ///
88    /// By convention, this "number" cannot be increased, or added to.
89    pub const NAN: Self = Self(NAN_U16);
90
91    /// Returns `true` if this number is [`NAN`](Self::NAN).
92    #[inline]
93    #[must_use]
94    pub fn is_nan(self) -> bool {
95        self == Self::NAN
96    }
97
98    /// Increases `self` with wraparound.
99    #[inline]
100    pub fn increase(&mut self) {
101        if self.is_nan() {
102            return;
103        }
104        if self.0 < MAX_U16 {
105            self.0 += 1;
106        } else {
107            self.0 = 0; // wraparound
108        }
109    }
110
111    /// Increases `self` with wraparound, and returns a copy.
112    #[inline]
113    pub fn increase_get(&mut self) -> Self {
114        self.increase();
115        *self
116    }
117
118    /// Returns a copy of `self`, and increases `self` with wraparound.
119    #[inline]
120    pub fn get_increase(&mut self) -> Self {
121        let num = *self;
122        self.increase();
123        num
124    }
125
126    /// Distance with wraparound.
127    ///
128    /// For the signed difference, use [`Self::diff()`].
129    ///
130    /// If one of the number is [`NAN`](Self::NAN), the maximum distance of `32767` is returned.
131    /// If both are [`NAN`](Self::NAN), we say the distance is `0`.
132    #[inline]
133    #[must_use]
134    pub fn dist(self, other: Self) -> u16 {
135        if self.is_nan() && other.is_nan() {
136            return 0;
137        }
138        if self.is_nan() || other.is_nan() {
139            return MID_U16; // max distance
140        }
141        if self.0 == other.0 {
142            return 0;
143        }
144
145        let min = self.min(other);
146        let max = self.max(other);
147
148        if min.0 < max.0 {
149            max.0 - min.0
150        } else {
151            // min is less, but has higher number
152            //  => sum these distances: min->MAX + 0->max + MAX->0
153            MAX_U16 - min.0 + max.0 + 1
154        }
155    }
156
157    /// Difference with wraparound.
158    ///
159    /// If `self < other`, the result is negative,
160    /// and if `self > other`, the result is positive.
161    ///
162    /// For the unsigned distance, use [`Self::dist()`].
163    ///
164    /// If one of the number is [`NAN`](Self::NAN), the maximum difference of `(-)32767`
165    /// is returned. If both are [`NAN`](Self::NAN), we say the difference is `0`.
166    #[inline]
167    #[must_use]
168    pub fn diff(self, other: Self) -> i16 {
169        let dist = self.dist(other);
170        if self.precedes(other) {
171            -(dist as i16)
172        } else {
173            dist as i16
174        }
175    }
176
177    /// Compares and returns the smaller of two numbers.
178    ///
179    /// The returned number is the "predecessor" of the other.
180    ///
181    /// If one number is [`NAN`](Self::NAN), then the other is returned.
182    #[inline]
183    pub fn min(self, other: Self) -> Self {
184        match self.partial_cmp(other) {
185            Some(Ordering::Less) => self,
186            Some(_) => other,
187            None if self.is_nan() => other,
188            None => self,
189        }
190    }
191
192    /// Compares and returns the larger of two numbers.
193    ///
194    /// The returned number is the "successor" of the other.
195    ///
196    /// If one number is [`NAN`](Self::NAN), then the other is returned.
197    #[inline]
198    pub fn max(self, other: Self) -> Self {
199        match self.partial_cmp(other) {
200            Some(Ordering::Greater) => self,
201            Some(_) => other,
202            None if self.is_nan() => other,
203            None => self,
204        }
205    }
206
207    /// Partial comparison with wraparound.
208    ///
209    /// Returns `None` if one of the values is [`NAN`](Self::NAN).
210    ///
211    /// Based on [RFC1982].
212    ///
213    /// [RFC1982]: https://www.rfc-editor.org/rfc/rfc1982#section-3.2
214    #[inline]
215    #[must_use]
216    pub fn partial_cmp(self, other: Self) -> Option<Ordering> {
217        if self.is_nan() || other.is_nan() {
218            return None;
219        }
220        if self.0 == other.0 {
221            return Some(Ordering::Equal);
222        }
223
224        let a = i32::from(self.0);
225        let b = i32::from(other.0);
226
227        // a < b if either:
228        //  - b has the greater number and is within our window
229        //  - a has the greater number and is outside our window
230        if (b > a && b - a <= MID_I32) || (a > b && a - b > MID_I32) {
231            Some(Ordering::Less)
232        } else {
233            Some(Ordering::Greater)
234        }
235    }
236
237    /// `True` if `self < other` according to [RFC1982].
238    ///
239    /// [RFC1982]: https://www.rfc-editor.org/rfc/rfc1982#section-3.2
240    #[inline]
241    #[must_use]
242    pub fn precedes(self, other: Self) -> bool {
243        self.partial_cmp(other) == Some(Ordering::Less)
244    }
245
246    /// `True` if `self <= other` according to [RFC1982].
247    ///
248    /// [RFC1982]: https://www.rfc-editor.org/rfc/rfc1982#section-3.2
249    #[inline]
250    #[must_use]
251    pub fn precedes_or_eq(self, other: Self) -> bool {
252        match self.partial_cmp(other) {
253            Some(Ordering::Less | Ordering::Equal) => true,
254            Some(Ordering::Greater) | None => false,
255        }
256    }
257
258    /// `True` if `self > other` according to [RFC1982].
259    ///
260    /// [RFC1982]: https://www.rfc-editor.org/rfc/rfc1982#section-3.2
261    #[inline]
262    #[must_use]
263    pub fn succeeds(self, other: Self) -> bool {
264        self.partial_cmp(other) == Some(Ordering::Greater)
265    }
266
267    /// `True` if `self >= other` according to [RFC1982].
268    ///
269    /// [RFC1982]: https://www.rfc-editor.org/rfc/rfc1982#section-3.2
270    #[inline]
271    #[must_use]
272    pub fn succeeds_or_eq(self, other: Self) -> bool {
273        match self.partial_cmp(other) {
274            Some(Ordering::Greater | Ordering::Equal) => true,
275            Some(Ordering::Less) | None => false,
276        }
277    }
278
279    /// Returns `self` if it's not `NAN`, otherwise returns `other`.
280    #[inline]
281    pub fn or(self, other: Self) -> Self {
282        if self.is_nan() {
283            other
284        } else {
285            self
286        }
287    }
288
289    /// Returns `self` if it's not `NAN`, otherwise returns `Serial::default()`.
290    #[inline]
291    pub fn or_default(self) -> Self {
292        if self.is_nan() {
293            Self::default()
294        } else {
295            self
296        }
297    }
298
299    /// Replaces `self` with `NAN`, returning the previous value.
300    #[inline]
301    pub fn take(&mut self) -> Self {
302        core::mem::replace(self, Self::NAN)
303    }
304}
305
306impl Add<u16> for Serial {
307    type Output = Serial;
308
309    /// Addition with wraparound.
310    ///
311    /// You can add any `u16` to the serial number, but be aware that due to the wraparound
312    /// semantics, adding more than `(u16::MAX-1)/2 = 32767` leads to a result that is
313    /// _less_ than `self`. Adding `u16::MAX` will wraparound to the same value.
314    ///
315    /// If `self.is_nan()`, then the returned serial number is also [`NAN`](Self::NAN).
316    #[inline]
317    fn add(self, rhs: u16) -> Self::Output {
318        if self.is_nan() {
319            return self;
320        }
321        let n = (u32::from(self.0) + u32::from(rhs)) % (NAN_U32);
322        Self(n as u16)
323    }
324}