Skip to main content

sparkle/
serial.rs

1use std;
2
3const MAX: u32 = 0x_8000_0000;
4
5/// Encapsulates a zone's serial number.
6///
7/// `SerialNumber` uses **sequence space arithmetic**, defined in [RFC
8/// 1982](https://tools.ietf.org/html/rfc1982). Sequence space arithmetic
9/// defines the *addition* and *comparison* operations such that the serial
10/// number wraps on overflow while maintaining an intuitive and well defined
11/// meaning for the operations' results.
12///
13/// # Examples
14///
15/// ```
16/// use sparkle::SerialNumber;
17///
18/// let x = SerialNumber(0);
19/// assert!(x < x + SerialNumber(1));
20///
21/// let x = SerialNumber(0x_ffff_ffff);
22/// assert!(x < x + SerialNumber(1));
23/// ```
24///
25#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
26pub struct SerialNumber(pub u32);
27
28impl SerialNumber {
29    pub fn as_u32(&self) -> u32 {
30        self.0
31    }
32}
33
34impl std::fmt::Display for SerialNumber {
35    fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
36        self.0.fmt(f)
37    }
38}
39
40impl From<SerialNumber> for u32 {
41    fn from(x: SerialNumber) -> Self {
42        x.0
43    }
44}
45
46impl PartialOrd for SerialNumber {
47    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
48
49        const MAX: u32 = 0x_8000_0000;
50
51        let i1 = self.0; // same name used in RFC 1982
52        let i2 = other.0; // same name used in RFC 1982
53
54        if i1 == i2 {
55            Some(std::cmp::Ordering::Equal)
56        } else if (i1 < i2 && i2 - i1 < MAX) || (i1 > i2 && i1 - i2 > MAX) {
57            Some(std::cmp::Ordering::Less)
58        } else if (i1 < i2 && i2 - i1 > MAX) || (i1 > i2 && i1 - i2 < MAX) {
59            Some(std::cmp::Ordering::Greater)
60        } else {
61            debug_assert!((i1 < i2 && i2 - i1 == 0x_8000_0000) || (i1 > i2 && i1 - i2 == 0x_8000_0000));
62
63            // According to RFC 1982, section 3.2, implementations are free to
64            // define any result for this condition.
65            //
66            // > Thus the problem case is left undefined, implementations are
67            // free to return either result, or to flag an error, and users must
68            // take care not to depend on any particular outcome. <
69
70            None
71        }
72    }
73}
74
75impl std::ops::Add for SerialNumber {
76    type Output = Self;
77    fn add(self, other: Self) -> Self::Output {
78        debug_assert!(other.0 < MAX);
79        SerialNumber(self.0.wrapping_add(other.0))
80    }
81}
82
83impl std::ops::AddAssign for SerialNumber {
84    fn add_assign(&mut self, other: Self) {
85        debug_assert!(other.0 < MAX);
86        self.0 = self.0.wrapping_add(other.0);
87    }
88}
89
90#[cfg(test)]
91mod tests {
92    use super::*;
93
94    #[test]
95    fn serial_number_display() {
96        let got = format!("{}", SerialNumber(1234));
97        let expected = "1234";
98        assert_eq!(got, expected);
99    }
100
101    #[test]
102    fn u32_from_serial_number() {
103        let x = SerialNumber(1234);
104        let got = u32::from(x);
105        let expected = 1234;
106        assert_eq!(got, expected);
107    }
108
109    #[test]
110    fn serial_number_partial_cmp() {
111        use std::cmp::Ordering::*;
112
113        let f = |x, y| SerialNumber(x).partial_cmp(&SerialNumber(y));
114
115        // Equality cases.
116
117        assert_eq!(f(0, 0), Some(Equal));
118        assert_eq!(f(1, 1), Some(Equal));
119        assert_eq!(f(0x_7fff_ffff, 0x_7fff_ffff), Some(Equal));
120        assert_eq!(f(0x_8000_0000, 0x_8000_0000), Some(Equal));
121        assert_eq!(f(0x_ffff_ffff, 0x_ffff_ffff), Some(Equal));
122
123        // Unequal-but-defined cases.
124
125        assert_eq!(f(0, 1), Some(Less));
126        assert_eq!(f(1, 0), Some(Greater));
127
128        assert_eq!(f(0, 44), Some(Less));
129        assert_eq!(f(44, 0), Some(Greater));
130
131        assert_eq!(f(0, 100), Some(Less));
132        assert_eq!(f(100, 0), Some(Greater));
133
134        assert_eq!(f(44, 100), Some(Less));
135        assert_eq!(f(100, 44), Some(Greater));
136
137        assert_eq!(f(0x_ffff_ffff, 0x_0000_0000), Some(Less));
138        assert_eq!(f(0x_0000_0000, 0x_ffff_ffff), Some(Greater));
139
140        assert_eq!(f(0x_0000_0000, 0x_7fff_ffff), Some(Less));
141        assert_eq!(f(0x_7fff_ffff, 0x_0000_0000), Some(Greater));
142
143        assert_eq!(f(0x_0000_0001, 0x_8000_0000), Some(Less));
144        assert_eq!(f(0x_8000_0000, 0x_0000_0001), Some(Greater));
145
146        assert_eq!(f(0x_4000_0000, 0x_bfff_ffff), Some(Less));
147        assert_eq!(f(0x_bfff_ffff, 0x_4000_0000), Some(Greater));
148
149        // Undefined cases.
150
151        assert_eq!(f(0x_0000_0000, 0x_8000_0000), None);
152        assert_eq!(f(0x_8000_0000, 0x_0000_0000), None);
153
154        assert_eq!(f(0x_0000_0001, 0x_8000_0001), None);
155        assert_eq!(f(0x_8000_0001, 0x_0000_0001), None);
156
157        assert_eq!(f(0x_4000_0000, 0x_c000_0000), None);
158        assert_eq!(f(0x_c000_0000, 0x_4000_0000), None);
159
160        assert_eq!(f(0x_7fff_ffff, 0x_ffff_ffff), None);
161        assert_eq!(f(0x_ffff_ffff, 0x_7fff_ffff), None);
162    }
163
164    #[test]
165    fn serial_number_add() {
166        let f = |x, y| SerialNumber(x) + SerialNumber(y);
167        assert_eq!(f(0, 0), SerialNumber(0));
168        assert_eq!(f(0, 1), SerialNumber(1));
169        assert_eq!(f(0, 42), SerialNumber(42));
170        assert_eq!(f(0, 0x_7fff_ffff), SerialNumber(0x_7fff_ffff));
171        assert_eq!(f(0x_ffff_ffff, 1), SerialNumber(0));
172        assert_eq!(f(0x_ffff_ffff, 2), SerialNumber(1));
173        assert_eq!(f(0x_ffff_ffff, 0x_7fff_ffff), SerialNumber(0x_7fff_fffe));
174    }
175
176    #[test]
177    fn serial_number_add_assign() {
178        let f = |x, y| {
179            let mut x = SerialNumber(x);
180            x += SerialNumber(y);
181            x
182        };
183        assert_eq!(f(0, 0), SerialNumber(0));
184        assert_eq!(f(0, 1), SerialNumber(1));
185        assert_eq!(f(0, 42), SerialNumber(42));
186        assert_eq!(f(0, 0x_7fff_ffff), SerialNumber(0x_7fff_ffff));
187        assert_eq!(f(0x_ffff_ffff, 1), SerialNumber(0));
188        assert_eq!(f(0x_ffff_ffff, 2), SerialNumber(1));
189        assert_eq!(f(0x_ffff_ffff, 0x_7fff_ffff), SerialNumber(0x_7fff_fffe));
190    }
191}