serial-num [![Latest Version]][crates.io] [![Documentation]][docs.rs] ![License] [Latest Version]: https://img.shields.io/crates/v/serial-num [crates.io]: https://crates.io/crates/serial-num [Documentation]: https://img.shields.io/docsrs/serial-num [docs.rs]: https://docs.rs/serial-num/latest/serial_num/ [License]: https://img.shields.io/crates/l/serial-num

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.

```toml [dependencies] serial-num = "0.5"

or with additional features:

[dependencies] serial-num = { version = "0.5", features = ["serde"] } ```

The following feature flags implement additional traits for the Serial type: * arbitrary: derives [arbitrary]'s Arbitrary (⚠️ incompatible with no_std) * bincode: derives [bincode]'s Decode/Encode * bitcode: derives [bitcode]'s Decode/Encode (⚠️ incompatible with no_std) * 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 * speedy: derives [speedy]'s Readable/Writable (⚠️ incompatible with no_std)

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


Usage

Simple example

```rust 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.increaseget(); // increase, then copy let y = b.getincrease(); // copy, then increase c.increase();

assert!(y < x); asserteq!(-1i16, y.diff(x)); // "diff()" is signed asserteq!(1u16, y.dist(x)); // "dist()" is unsigned

// addition is the same as calling "increase()" n times asserteq!(y + 1u16, x); ```

Wraparound example

```rust 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

```rust 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 asserteq!(Serial::NAN, nan + 1u16);

// distance between two NAN values is zero asserteq!(0u16, nan.dist(nan)); asserteq!(0i16, nan.diff(nan));

// distance and difference of non-NAN to NAN is the maximum distance asserteq!(32767u16, default.dist(nan)); asserteq!(32767u16, nan.dist(default)); asserteq!(32767i16, default.diff(nan)); asserteq!(32767i16, nan.diff(default));

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


Changelog

[0.5.0] - 2023-07-28

[0.4.0] - 2023-05-05

[0.3.1] - 2023-04-28

[0.3.0] - 2023-04-28

[0.2.0] - 2023-04-27

[0.1.1] - 2023-01-06


License

Licensed under either of Apache License, Version 2.0 or MIT license at your option.


Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in this crate by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.