pub struct Serial(/* private fields */);
Expand description
Two-byte serial number with wraparound.
A serial number is an identifier assigned incrementally to an item.
In many cases, you can use a u32
or u64
and call it
a day, without having to worry about overflow. The niche benefit of this type
is that it only uses the space of a u16
, with the problem of overflow solved
by wraparound.
This is an “opaque” type, similar to Instants
.
Serial numbers get their significance when being compare to one another,
but there is no method to get the “inner counter”. Another similarity
is that there is no “maximum” serial number, since every
serial number has a successor.
The window used for comparing two serial numbers is half of the number space,
(u16::MAX-1)/2 = 32767
. If two serial numbers are within that window, we simply compare
the numbers as you normally would. If we compare numbers that do not fit into
that window, like 5
and 65000
, the comparison is flipped, and we say 65000 < 5
.
This is based on the assumption that we got to 5
by increasing 65000
beyond
the point of wraparound at u16::MAX-1 = 65534
. The assumption only holds if the items you
assign serial numbers to have a short enough lifetime. The ordering of items in your state
will get messed up if there is an item that is the 32767
th successor of another item.
The final value in the number space, u16::MAX
, is reserved for the special
NAN
value. This is done to save space - you don’t need to wrap
this type in an Option
if only some items are assigned a serial number.
§Simple example
use serial_num::Serial;
// the default is a reference point - not serial number "zero"
let mut a = Serial::default();
let mut b = Serial::default();
let mut c = Serial::default();
// three ways to increase
let x = a.increase_get(); // increase, then copy
let y = b.get_increase(); // copy, then increase
c.increase();
assert!(y.precedes(x));
assert_eq!(-1_i16, y.diff(x)); // "diff()" is signed
assert_eq!(1_u16, y.dist(x)); // "dist()" is unsigned
// addition is the same as calling "increase()" n times
assert_eq!(y + 1_u16, x);
§Wraparound example
use serial_num::Serial;
// a serial number can be increased indefinitely
let mut x = Serial::default();
for _ in 0..u16::MAX {
x.increase();
}
let x = x + u16::MAX + u16::MAX + u16::MAX;
// comparison is trivial as long as two serial numbers have
// a distance of less than half of our number space (32767).
let a = Serial::default() + 5;
let b = Serial::default() + 32000;
assert!(a.precedes(b)); // 5th successor < 32000th successor
// but: the comparison flips if the distance is larger
let a = Serial::default() + 5;
let b = Serial::default() + 65000;
assert!(a.succeeds(b)); // 5th successor > 65000th successor
// this means that you get the right ordering as long as
// you compare one serial number at most with one that
// is its 32767th successor:
let num = Serial::default();
assert!(num.precedes(num + 32767)); // 0 < 32767 (still intuitive)
assert!(num.succeeds(num + 32768)); // 0 > 32768 (flip #1)
assert!(num.succeeds(num + 65534)); // 0 > 65534
assert!(num == num + 65535); // 0 == 65535 (due to same internal representation)
assert!(num.precedes(num + 65535 + 1)); // 0 < 65536 (flip #2)
§The NAN
value
use serial_num::Serial;
// "NAN" exists to have value representing "no serial number",
// since it saves encoding space vs wrapping Serial in an Option.
let nan = Serial::NAN;
let num = Serial::default();
// you can check whether a serial number is NAN
assert!(nan.is_nan());
// NAN cannot be increased
assert_eq!(Serial::NAN, nan + 1_u16);
// distance between two NAN values is zero
assert_eq!(0_u16, nan.dist(nan));
assert_eq!(0_i16, nan.diff(nan));
// distance and difference of non-NAN to NAN is the maximum distance
assert_eq!(32_767_u16, num.dist(nan));
assert_eq!(32_767_u16, nan.dist(num));
assert_eq!(32_767_i16, num.diff(nan));
assert_eq!(32_767_i16, nan.diff(num));
// partial ordering does not include the NAN value
assert_eq!(None, nan.partial_cmp(num));
assert!(!nan.precedes(num) && !nan.succeeds(num));
Implementations§
Source§impl Serial
impl Serial
Sourcepub const NAN: Self
pub const NAN: Self
Special value representing “no serial number”.
By convention, this “number” cannot be increased, or added to.
Sourcepub fn increase_get(&mut self) -> Self
pub fn increase_get(&mut self) -> Self
Increases self
with wraparound, and returns a copy.
Sourcepub fn get_increase(&mut self) -> Self
pub fn get_increase(&mut self) -> Self
Returns a copy of self
, and increases self
with wraparound.
Sourcepub fn dist(self, other: Self) -> u16
pub fn dist(self, other: Self) -> u16
Distance with wraparound.
For the signed difference, use Self::diff()
.
If one of the number is NAN
, the maximum distance of 32767
is returned.
If both are NAN
, we say the distance is 0
.
Sourcepub fn diff(self, other: Self) -> i16
pub fn diff(self, other: Self) -> i16
Difference with wraparound.
If self < other
, the result is negative,
and if self > other
, the result is positive.
For the unsigned distance, use Self::dist()
.
If one of the number is NAN
, the maximum difference of (-)32767
is returned. If both are NAN
, we say the difference is 0
.
Sourcepub fn min(self, other: Self) -> Self
pub fn min(self, other: Self) -> Self
Compares and returns the smaller of two numbers.
The returned number is the “predecessor” of the other.
If one number is NAN
, then the other is returned.
Sourcepub fn max(self, other: Self) -> Self
pub fn max(self, other: Self) -> Self
Compares and returns the larger of two numbers.
The returned number is the “successor” of the other.
If one number is NAN
, then the other is returned.
Sourcepub fn partial_cmp(self, other: Self) -> Option<Ordering>
pub fn partial_cmp(self, other: Self) -> Option<Ordering>
Sourcepub fn precedes_or_eq(self, other: Self) -> bool
pub fn precedes_or_eq(self, other: Self) -> bool
True
if self <= other
according to RFC1982.
Sourcepub fn succeeds_or_eq(self, other: Self) -> bool
pub fn succeeds_or_eq(self, other: Self) -> bool
True
if self >= other
according to RFC1982.
Sourcepub fn or_default(self) -> Self
pub fn or_default(self) -> Self
Returns self
if it’s not NAN
, otherwise returns Serial::default()
.
Trait Implementations§
Source§impl Add<u16> for Serial
impl Add<u16> for Serial
Source§fn add(self, rhs: u16) -> Self::Output
fn add(self, rhs: u16) -> Self::Output
Addition with wraparound.
You can add any u16
to the serial number, but be aware that due to the wraparound
semantics, adding more than (u16::MAX-1)/2 = 32767
leads to a result that is
less than self
. Adding u16::MAX
will wraparound to the same value.
If self.is_nan()
, then the returned serial number is also NAN
.