1#![no_std]
15
16use core::cmp::Ordering;
17use core::ops::{Add, AddAssign, Sub, SubAssign};
18
19pub trait SerialPrimitive: Copy + Ord {
24 const HALF: Self;
30 fn wrapping_add(self, rhs: Self) -> Self;
32 fn wrapping_sub(self, rhs: Self) -> Self;
34}
35
36macro_rules! impl_serial_primitive {
37 ($($t:ty),+ $(,)?) => {$(
38 impl SerialPrimitive for $t {
39 const HALF: Self = 1 << (<$t>::BITS - 1);
40 #[inline]
41 fn wrapping_add(self, rhs: Self) -> Self { <$t>::wrapping_add(self, rhs) }
42 #[inline]
43 fn wrapping_sub(self, rhs: Self) -> Self { <$t>::wrapping_sub(self, rhs) }
44 }
45
46 impl PartialEq<Serial<$t>> for $t {
48 #[inline]
49 fn eq(&self, other: &Serial<$t>) -> bool {
50 *self == other.0
51 }
52 }
53
54 impl PartialOrd<Serial<$t>> for $t {
59 #[inline]
60 fn partial_cmp(&self, other: &Serial<$t>) -> Option<Ordering> {
61 Serial(*self).partial_cmp(other)
62 }
63 }
64 )+};
65}
66
67impl_serial_primitive!(u8, u16, u32, u64, u128, usize);
68
69#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, Default)]
82pub struct Serial<T>(T);
83
84impl<T: SerialPrimitive> Serial<T> {
85 #[inline]
87 pub const fn new(value: T) -> Self {
88 Serial(value)
89 }
90
91 #[inline]
93 pub fn value(self) -> T {
94 self.0
95 }
96
97 #[inline]
102 pub fn distance_to(self, other: Self) -> T {
103 other.0.wrapping_sub(self.0)
104 }
105
106 #[inline]
110 pub fn precedes(self, other: Self) -> bool {
111 matches!(self.partial_cmp(&other), Some(Ordering::Less))
112 }
113}
114
115impl<T: SerialPrimitive> PartialEq<T> for Serial<T> {
117 #[inline]
118 fn eq(&self, other: &T) -> bool {
119 self.0 == *other
120 }
121}
122
123impl<T: SerialPrimitive> PartialOrd<T> for Serial<T> {
128 #[inline]
129 fn partial_cmp(&self, other: &T) -> Option<Ordering> {
130 self.partial_cmp(&Serial(*other))
131 }
132}
133
134impl<T: SerialPrimitive> PartialOrd for Serial<T> {
135 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
143 if self.0 == other.0 {
144 return Some(Ordering::Equal);
145 }
146 let d = other.0.wrapping_sub(self.0);
147 if d == T::HALF {
148 None
149 } else if d < T::HALF {
150 Some(Ordering::Less)
151 } else {
152 Some(Ordering::Greater)
153 }
154 }
155}
156
157impl<T: SerialPrimitive> Add<T> for Serial<T> {
158 type Output = Serial<T>;
159 #[inline]
164 fn add(self, rhs: T) -> Serial<T> {
165 debug_assert!(rhs < T::HALF, "addend must be < HALF per RFC 1982");
166 Serial(self.0.wrapping_add(rhs))
167 }
168}
169
170impl<T: SerialPrimitive> Sub<T> for Serial<T> {
171 type Output = Serial<T>;
172 #[inline]
174 fn sub(self, rhs: T) -> Serial<T> {
175 Serial(self.0.wrapping_sub(rhs))
176 }
177}
178
179impl<T: SerialPrimitive> AddAssign<T> for Serial<T> {
180 #[inline]
181 fn add_assign(&mut self, rhs: T) {
182 debug_assert!(rhs < T::HALF, "addend must be < HALF per RFC 1982");
183 self.0 = self.0.wrapping_add(rhs);
184 }
185}
186
187impl<T: SerialPrimitive> SubAssign<T> for Serial<T> {
188 #[inline]
189 fn sub_assign(&mut self, rhs: T) {
190 self.0 = self.0.wrapping_sub(rhs);
191 }
192}
193
194#[cfg(test)]
195mod tests {
196 use super::*;
197
198 #[test]
199 fn add_wraps_around() {
200 let a = Serial::<u8>::new(250);
201 assert_eq!((a + 10).value(), 4);
202 assert_eq!((a + 5).value(), 255);
203 assert_eq!((a + 6).value(), 0);
204 }
205
206 #[test]
207 fn sub_wraps_around() {
208 let a = Serial::<u8>::new(4);
209 assert_eq!((a - 10).value(), 250);
210 assert_eq!((a - 4).value(), 0);
211 assert_eq!((a - 5).value(), 255);
212 }
213
214 #[test]
215 fn add_assign_sub_assign() {
216 let mut a = Serial::<u32>::new(u32::MAX);
217 a += 1;
218 assert_eq!(a.value(), 0);
219 a -= 1;
220 assert_eq!(a.value(), u32::MAX);
221 }
222
223 #[test]
224 fn ordering_basic() {
225 let a = Serial::<u32>::new(1);
226 let b = Serial::<u32>::new(2);
227 assert!(a < b);
228 assert!(b > a);
229 assert_eq!(a, Serial::<u32>::new(1));
230 }
231
232 #[test]
233 fn ordering_wraps_around() {
234 let max = Serial::<u8>::new(u8::MAX);
236 let zero = Serial::<u8>::new(0);
237 assert!(max < zero);
238 assert!(zero > max);
239
240 let a = Serial::<u8>::new(250);
242 let b = Serial::<u8>::new(4);
243 assert!(a < b);
244 }
245
246 #[test]
247 fn ordering_undefined_at_half() {
248 let a = Serial::<u8>::new(0);
250 let b = Serial::<u8>::new(128);
251 assert_eq!(a.partial_cmp(&b), None);
252 assert_eq!(b.partial_cmp(&a), None);
253 assert!(!(a < b));
254 assert!(!(a > b));
255 assert!(!a.precedes(b));
256 }
257
258 #[test]
259 fn distance_to_is_forward_and_nonnegative() {
260 let a = Serial::<u8>::new(250);
261 let b = Serial::<u8>::new(4);
262 assert_eq!(a.distance_to(b), 10);
263 assert_eq!(b.distance_to(a), 246);
264 }
265
266 #[test]
267 fn eq_with_raw_value_both_directions() {
268 let a = Serial::<u32>::new(42);
269 assert!(a == 42);
271 assert!(a != 7);
272 assert!(42u32 == a);
274 assert!(7u32 != a);
275 assert!(Serial::<u8>::new(255) == 255u8);
277 assert!(0u16 != Serial::<u16>::new(1));
278 }
279
280 #[test]
281 fn ord_with_raw_value_both_directions() {
282 let a = Serial::<u8>::new(250);
283 assert!(a < 4u8);
285 assert!(a <= 4u8);
286 assert!(a > 200u8);
287 assert!(4u8 > a);
289 assert!(200u8 < a);
290 assert!(Serial::<u8>::new(u8::MAX) < 0u8);
292 assert!(0u8 > Serial::<u8>::new(u8::MAX));
293 }
294
295 #[test]
296 fn ord_with_raw_value_undefined_at_half() {
297 let a = Serial::<u8>::new(0);
298 assert_eq!(a.partial_cmp(&128u8), None);
300 assert_eq!(128u8.partial_cmp(&a), None);
301 assert!(!(a < 128u8));
302 assert!(!(a > 128u8));
303 }
304
305 #[test]
306 fn works_across_widths() {
307 assert!(Serial::<u16>::new(u16::MAX) < Serial::<u16>::new(0));
308 assert!(Serial::<u64>::new(u64::MAX) < Serial::<u64>::new(0));
309 assert_eq!((Serial::<u128>::new(u128::MAX) + 1).value(), 0);
310 }
311}