serial-num 0.4.0

Serial number type with wraparound
Documentation

Latest Version Documentation License

This crate offers a 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.

[dependencies]
serial-num = "0.3"

# or with additional features:
[dependencies]
serial-num = { version = "0.3", features = ["serde"] }

The following feature flags implement additional traits for the Serial type:

  • arbitrary: derives arbitrary's Arbitrary
  • bincode: derives bincode's Decode/Encode
  • borsh: derives borsh's BorshDeserialize/BorshSerialize
  • rkyv: derives rkyv's Archive/Deserialize/Serialize
  • rkyv-safe: additionally enables rkyv’s safe API
  • serde: derives serde's Deserialize/Serialize

The Minimum Supported Rust Version (MSRV) for this crate is 1.66.0.

Usage

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 < 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 < 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 > 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.

// a real use case of this is to sign UDP packets with
// a serial number. this would allow you to restore the
// order of packets at the receiver as long as you never
// look at more than the 32767 last packets (which
// should be much more than you need).

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 default = 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, default.dist(nan));
assert_eq!(32_767_u16, nan.dist(default));
assert_eq!(32_767_i16, default.diff(nan));
assert_eq!(32_767_i16, nan.diff(default));

// partial ordering does not include the NAN value
assert_eq!(None, nan.partial_cmp(&default));
assert!(!(nan < default) && !(nan >= default));

Changelog

0.4.0 - 2023-05-05

  • Set MSRV to 1.66.0
  • Add some #[must_use] attributes

0.3.1 - 2023-04-28

  • Add documentation to Cargo.toml
  • Fix outdated README

0.3.0 - 2023-04-28

  • Set MSRV to 1.63.0
  • Add borsh feature
  • Add rkyv and rkyv-safe features
  • Add arbitrary feature

0.2.0 - 2023-04-27

  • Set MSRV to 1.60.0
  • Up bincode to ^2.0.0-rc.3
  • Improved documentation

0.1.1 - 2023-01-06

  • Disabled the std features of bincode/serde to enable no_std support.

License