1#![no_std]
15
16use core::cmp::Ordering;
17use core::fmt;
18use core::ops::{Add, AddAssign, Sub, SubAssign};
19
20pub trait SerialPrimitive: Copy + Ord {
25 const HALF: Self;
31 fn wrapping_add(self, rhs: Self) -> Self;
33 fn wrapping_sub(self, rhs: Self) -> Self;
35}
36
37macro_rules! impl_serial_primitive {
38 ($($t:ty),+ $(,)?) => {$(
39 impl SerialPrimitive for $t {
40 const HALF: Self = 1 << (<$t>::BITS - 1);
41 #[inline]
42 fn wrapping_add(self, rhs: Self) -> Self { <$t>::wrapping_add(self, rhs) }
43 #[inline]
44 fn wrapping_sub(self, rhs: Self) -> Self { <$t>::wrapping_sub(self, rhs) }
45 }
46
47 impl PartialEq<Serial<$t>> for $t {
49 #[inline]
50 fn eq(&self, other: &Serial<$t>) -> bool {
51 *self == other.0
52 }
53 }
54
55 impl PartialOrd<Serial<$t>> for $t {
60 #[inline]
61 fn partial_cmp(&self, other: &Serial<$t>) -> Option<Ordering> {
62 Serial(*self).partial_cmp(other)
63 }
64 }
65 )+};
66}
67
68impl_serial_primitive!(u8, u16, u32, u64, u128, usize);
69
70#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, Default)]
83pub struct Serial<T>(T);
84
85impl<T: SerialPrimitive> Serial<T> {
86 #[inline]
88 pub const fn new(value: T) -> Self {
89 Serial(value)
90 }
91
92 #[inline]
94 pub fn value(self) -> T {
95 self.0
96 }
97
98 #[inline]
103 pub fn distance_to(self, other: Self) -> T {
104 other.0.wrapping_sub(self.0)
105 }
106
107 #[inline]
111 pub fn precedes(self, other: Self) -> bool {
112 matches!(self.partial_cmp(&other), Some(Ordering::Less))
113 }
114}
115
116impl<T: SerialPrimitive + fmt::Display> fmt::Display for Serial<T> {
118 #[inline]
119 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
120 self.0.fmt(f)
121 }
122}
123
124impl<T: SerialPrimitive> PartialEq<T> for Serial<T> {
126 #[inline]
127 fn eq(&self, other: &T) -> bool {
128 self.0 == *other
129 }
130}
131
132impl<T: SerialPrimitive> PartialOrd<T> for Serial<T> {
137 #[inline]
138 fn partial_cmp(&self, other: &T) -> Option<Ordering> {
139 self.partial_cmp(&Serial(*other))
140 }
141}
142
143impl<T: SerialPrimitive> PartialOrd for Serial<T> {
144 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
152 if self.0 == other.0 {
153 return Some(Ordering::Equal);
154 }
155 let d = other.0.wrapping_sub(self.0);
156 if d == T::HALF {
157 None
158 } else if d < T::HALF {
159 Some(Ordering::Less)
160 } else {
161 Some(Ordering::Greater)
162 }
163 }
164}
165
166impl<T: SerialPrimitive> Add<T> for Serial<T> {
167 type Output = Serial<T>;
168 #[inline]
173 fn add(self, rhs: T) -> Serial<T> {
174 debug_assert!(rhs < T::HALF, "addend must be < HALF per RFC 1982");
175 Serial(self.0.wrapping_add(rhs))
176 }
177}
178
179impl<T: SerialPrimitive> Sub<T> for Serial<T> {
180 type Output = Serial<T>;
181 #[inline]
183 fn sub(self, rhs: T) -> Serial<T> {
184 Serial(self.0.wrapping_sub(rhs))
185 }
186}
187
188impl<T: SerialPrimitive> AddAssign<T> for Serial<T> {
189 #[inline]
190 fn add_assign(&mut self, rhs: T) {
191 debug_assert!(rhs < T::HALF, "addend must be < HALF per RFC 1982");
192 self.0 = self.0.wrapping_add(rhs);
193 }
194}
195
196impl<T: SerialPrimitive> SubAssign<T> for Serial<T> {
197 #[inline]
198 fn sub_assign(&mut self, rhs: T) {
199 self.0 = self.0.wrapping_sub(rhs);
200 }
201}
202
203#[cfg(test)]
204mod tests {
205 use super::*;
206
207 #[test]
208 fn add_wraps_around() {
209 let a = Serial::<u8>::new(250);
210 assert_eq!((a + 10).value(), 4);
211 assert_eq!((a + 5).value(), 255);
212 assert_eq!((a + 6).value(), 0);
213 }
214
215 #[test]
216 fn sub_wraps_around() {
217 let a = Serial::<u8>::new(4);
218 assert_eq!((a - 10).value(), 250);
219 assert_eq!((a - 4).value(), 0);
220 assert_eq!((a - 5).value(), 255);
221 }
222
223 #[test]
224 fn add_assign_sub_assign() {
225 let mut a = Serial::<u32>::new(u32::MAX);
226 a += 1;
227 assert_eq!(a.value(), 0);
228 a -= 1;
229 assert_eq!(a.value(), u32::MAX);
230 }
231
232 #[test]
233 fn ordering_basic() {
234 let a = Serial::<u32>::new(1);
235 let b = Serial::<u32>::new(2);
236 assert!(a < b);
237 assert!(b > a);
238 assert_eq!(a, Serial::<u32>::new(1));
239 }
240
241 #[test]
242 fn ordering_wraps_around() {
243 let max = Serial::<u8>::new(u8::MAX);
245 let zero = Serial::<u8>::new(0);
246 assert!(max < zero);
247 assert!(zero > max);
248
249 let a = Serial::<u8>::new(250);
251 let b = Serial::<u8>::new(4);
252 assert!(a < b);
253 }
254
255 #[test]
256 fn ordering_undefined_at_half() {
257 let a = Serial::<u8>::new(0);
259 let b = Serial::<u8>::new(128);
260 assert_eq!(a.partial_cmp(&b), None);
261 assert_eq!(b.partial_cmp(&a), None);
262 assert!(!(a < b));
263 assert!(!(a > b));
264 assert!(!a.precedes(b));
265 }
266
267 #[test]
268 fn distance_to_is_forward_and_nonnegative() {
269 let a = Serial::<u8>::new(250);
270 let b = Serial::<u8>::new(4);
271 assert_eq!(a.distance_to(b), 10);
272 assert_eq!(b.distance_to(a), 246);
273 }
274
275 #[test]
276 fn eq_with_raw_value_both_directions() {
277 let a = Serial::<u32>::new(42);
278 assert!(a == 42);
280 assert!(a != 7);
281 assert!(42u32 == a);
283 assert!(7u32 != a);
284 assert!(Serial::<u8>::new(255) == 255u8);
286 assert!(0u16 != Serial::<u16>::new(1));
287 }
288
289 #[test]
290 fn ord_with_raw_value_both_directions() {
291 let a = Serial::<u8>::new(250);
292 assert!(a < 4u8);
294 assert!(a <= 4u8);
295 assert!(a > 200u8);
296 assert!(4u8 > a);
298 assert!(200u8 < a);
299 assert!(Serial::<u8>::new(u8::MAX) < 0u8);
301 assert!(0u8 > Serial::<u8>::new(u8::MAX));
302 }
303
304 #[test]
305 fn ord_with_raw_value_undefined_at_half() {
306 let a = Serial::<u8>::new(0);
307 assert_eq!(a.partial_cmp(&128u8), None);
309 assert_eq!(128u8.partial_cmp(&a), None);
310 assert!(!(a < 128u8));
311 assert!(!(a > 128u8));
312 }
313
314 #[test]
315 fn works_across_widths() {
316 assert!(Serial::<u16>::new(u16::MAX) < Serial::<u16>::new(0));
317 assert!(Serial::<u64>::new(u64::MAX) < Serial::<u64>::new(0));
318 assert_eq!((Serial::<u128>::new(u128::MAX) + 1).value(), 0);
319 }
320}