lightyear_utils/
wrapping_id.rs

1//! u16 that wraps around when it reaches the maximum value
2pub trait WrappedId {
3    // used for sequence buffers
4    /// returns self % total
5    fn rem(&self, total: usize) -> usize;
6}
7
8pub use paste::paste;
9
10// macro_rules! wrapping_id {
11//     ($struct_name:ident) => {
12//         wrapping_id_impl!($struct_name, u16, i16, i32);
13//     }; // ($struct_name:ident, u32) => {
14//        //     wrapping_id_impl!($struct_name, u32, i32, i64);
15//        // };
16// }
17//
18/// Index that wraps around 65536
19// TODO: we don't want serialize this with gamma!
20#[macro_export]
21macro_rules! wrapping_id {
22    ($struct_name:ident) => {
23        use lightyear_utils::wrapping_id::paste;
24        paste! {
25        mod [<$struct_name:lower _module>] {
26            use serde::{Deserialize, Serialize};
27            use core::ops::{Add, AddAssign, Deref, Sub};
28            use core::cmp::Ordering;
29            use bevy_reflect::Reflect;
30            use lightyear_serde::{SerializationError, reader::{Reader, ReadInteger}, writer::WriteInteger, ToBytes};
31            use lightyear_utils::wrapping_id::{wrapping_diff, WrappedId};
32
33            // define the struct
34            #[derive(Serialize, Deserialize, Clone, Copy, Debug, Eq, Hash, PartialEq, Default, Reflect
35            )]
36            pub struct $struct_name(pub u16);
37
38            impl ToBytes for $struct_name {
39                fn bytes_len(&self) -> usize {
40                    2
41                }
42
43                fn to_bytes(&self, buffer: &mut impl WriteInteger) -> Result<(), SerializationError> {
44                    Ok(buffer.write_u16(self.0)?)
45                }
46
47                fn from_bytes(buffer: &mut Reader) -> Result<Self, SerializationError>
48                where
49                    Self: Sized,
50                {
51                    Ok(Self(buffer.read_u16()?))
52                }
53            }
54
55            impl WrappedId for $struct_name {
56                 fn rem(&self, total: usize) -> usize {
57                     (self.0 as usize) % total
58                 }
59            }
60
61            /// Derive deref so that we don't have to write packet_id.0 in most cases
62            impl Deref for $struct_name {
63                type Target = u16;
64                fn deref(&self) -> &Self::Target {
65                    &self.0
66                }
67            }
68
69            impl Ord for $struct_name {
70                fn cmp(&self, other: &Self) -> Ordering {
71                    match wrapping_diff(self.0, other.0) {
72                        0 => Ordering::Equal,
73                        x if x > 0 => Ordering::Less,
74                        x if x < 0 => Ordering::Greater,
75                        _ => unreachable!(),
76                    }
77                }
78            }
79
80            impl PartialOrd for $struct_name {
81                fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
82                    Some(self.cmp(other))
83                }
84            }
85
86            impl Sub for $struct_name {
87                type Output = i16;
88
89                fn sub(self, rhs: Self) -> Self::Output {
90                    wrapping_diff(rhs.0, self.0)
91                }
92            }
93
94            impl Sub<u16> for $struct_name {
95                type Output = Self;
96
97                fn sub(self, rhs: u16) -> Self::Output {
98                    // NOTE: this is only valid for small diffs, it doesn't
99                    // handle wrapping correctly with `wrapping_diff`
100                    Self(self.0.wrapping_sub(rhs))
101                }
102            }
103
104
105            impl Add for $struct_name {
106                type Output = Self;
107
108                fn add(self, rhs: Self) -> Self::Output {
109                    Self(self.0.wrapping_add(rhs.0))
110                }
111            }
112
113            impl AddAssign<u16> for $struct_name {
114                fn add_assign(&mut self, rhs: u16) {
115                    self.0 = self.0.wrapping_add(rhs);
116                }
117            }
118            // impl Add<u16> for $struct_name {
119            //     type Output = Self;
120            //
121            //     fn add(self, rhs: u16) -> Self::Output {
122            //         Self(self.0.wrapping_add(rhs))
123            //     }
124            // }
125
126            impl Add<i16> for $struct_name {
127                type Output = Self;
128
129                fn add(self, rhs: i16) -> Self::Output {
130                    Self(self.0.wrapping_add_signed(rhs))
131                }
132            }
133        }
134        pub use [<$struct_name:lower _module>]::$struct_name;
135        }
136    };
137}
138
139/// Retrieves the wrapping difference of b-a.
140/// Wraps around 32768
141///
142/// # Examples
143///
144/// ```
145/// use lightyear_utils::wrapping_id::wrapping_diff;
146/// assert_eq!(wrapping_diff(1, 2), 1);
147/// assert_eq!(wrapping_diff(2, 1), -1);
148/// assert_eq!(wrapping_diff(65535, 0), 1);
149/// assert_eq!(wrapping_diff(0, 65535), -1);
150/// assert_eq!(wrapping_diff(0, 32767), 32767);
151/// assert_eq!(wrapping_diff(0, 32768), -32768);
152/// ```
153pub fn wrapping_diff(a: u16, b: u16) -> i16 {
154    b.wrapping_sub(a) as i16
155}
156
157#[cfg(test)]
158mod wrapping_diff_tests {
159    use super::wrapping_diff;
160
161    #[test]
162    fn simple() {
163        let a: u16 = 10;
164        let b: u16 = 12;
165
166        let result = wrapping_diff(a, b);
167
168        assert_eq!(result, 2);
169    }
170
171    #[test]
172    fn simple_backwards() {
173        let a: u16 = 10;
174        let b: u16 = 12;
175
176        let result = wrapping_diff(b, a);
177
178        assert_eq!(result, -2);
179    }
180
181    #[test]
182    fn max_wrap() {
183        let a: u16 = u16::MAX;
184        let b: u16 = a.wrapping_add(2);
185
186        let result = wrapping_diff(a, b);
187
188        assert_eq!(result, 2);
189    }
190
191    #[test]
192    fn min_wrap() {
193        let a: u16 = 0;
194        let b: u16 = a.wrapping_sub(2);
195
196        let result = wrapping_diff(a, b);
197
198        assert_eq!(result, -2);
199    }
200
201    #[test]
202    fn max_wrap_backwards() {
203        let a: u16 = u16::MAX;
204        let b: u16 = a.wrapping_add(2);
205
206        let result = wrapping_diff(b, a);
207
208        assert_eq!(result, -2);
209    }
210
211    #[test]
212    fn min_wrap_backwards() {
213        let a: u16 = 0;
214        let b: u16 = a.wrapping_sub(2);
215
216        let result = wrapping_diff(b, a);
217
218        assert_eq!(result, 2);
219    }
220
221    #[test]
222    fn medium_min_wrap() {
223        let diff: u16 = u16::MAX / 2;
224        let a: u16 = 0;
225        let b: u16 = a.wrapping_sub(diff);
226
227        let result = i32::from(wrapping_diff(a, b));
228
229        assert_eq!(result, -i32::from(diff));
230    }
231
232    #[test]
233    fn medium_min_wrap_backwards() {
234        let diff: u16 = u16::MAX / 2;
235        let a: u16 = 0;
236        let b: u16 = a.wrapping_sub(diff);
237
238        let result = i32::from(wrapping_diff(b, a));
239
240        assert_eq!(result, i32::from(diff));
241    }
242
243    #[test]
244    fn medium_max_wrap() {
245        let diff: u16 = u16::MAX / 2;
246        let a: u16 = u16::MAX;
247        let b: u16 = a.wrapping_add(diff);
248
249        let result = i32::from(wrapping_diff(a, b));
250
251        assert_eq!(result, i32::from(diff));
252    }
253
254    #[test]
255    fn medium_max_wrap_backwards() {
256        let diff: u16 = u16::MAX / 2;
257        let a: u16 = u16::MAX;
258        let b: u16 = a.wrapping_add(diff);
259
260        let result = i32::from(wrapping_diff(b, a));
261
262        assert_eq!(result, -i32::from(diff));
263    }
264}