Struct Serial

Source
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 32767th 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

Source

pub const NAN: Self

Special value representing “no serial number”.

By convention, this “number” cannot be increased, or added to.

Source

pub fn is_nan(self) -> bool

Returns true if this number is NAN.

Source

pub fn increase(&mut self)

Increases self with wraparound.

Source

pub fn increase_get(&mut self) -> Self

Increases self with wraparound, and returns a copy.

Source

pub fn get_increase(&mut self) -> Self

Returns a copy of self, and increases self with wraparound.

Source

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.

Source

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.

Source

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.

Source

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.

Source

pub fn partial_cmp(self, other: Self) -> Option<Ordering>

Partial comparison with wraparound.

Returns None if one of the values is NAN.

Based on RFC1982.

Source

pub fn precedes(self, other: Self) -> bool

True if self < other according to RFC1982.

Source

pub fn precedes_or_eq(self, other: Self) -> bool

True if self <= other according to RFC1982.

Source

pub fn succeeds(self, other: Self) -> bool

True if self > other according to RFC1982.

Source

pub fn succeeds_or_eq(self, other: Self) -> bool

True if self >= other according to RFC1982.

Source

pub fn or(self, other: Self) -> Self

Returns self if it’s not NAN, otherwise returns other.

Source

pub fn or_default(self) -> Self

Returns self if it’s not NAN, otherwise returns Serial::default().

Source

pub fn take(&mut self) -> Self

Replaces self with NAN, returning the previous value.

Trait Implementations§

Source§

impl Add<u16> for Serial

Source§

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.

Source§

type Output = Serial

The resulting type after applying the + operator.
Source§

impl Clone for Serial

Source§

fn clone(&self) -> Serial

Returns a copy of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for Serial

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Default for Serial

Source§

fn default() -> Serial

Returns the “default value” for a type. Read more
Source§

impl Hash for Serial

Source§

fn hash<__H: Hasher>(&self, state: &mut __H)

Feeds this value into the given Hasher. Read more
1.3.0 · Source§

fn hash_slice<H>(data: &[Self], state: &mut H)
where H: Hasher, Self: Sized,

Feeds a slice of this type into the given Hasher. Read more
Source§

impl PartialEq for Serial

Source§

fn eq(&self, other: &Serial) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, and should not be overridden without very good reason.
Source§

impl Copy for Serial

Source§

impl Eq for Serial

Source§

impl StructuralPartialEq for Serial

Auto Trait Implementations§

§

impl Freeze for Serial

§

impl RefUnwindSafe for Serial

§

impl Send for Serial

§

impl Sync for Serial

§

impl Unpin for Serial

§

impl UnwindSafe for Serial

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.