#numbers #serial #ack #udp #rfc-1982

不使用std serial-num

带环绕的序列号类型

13个版本 (破坏性)

0.10.0 2024年7月16日
0.8.0 2023年10月20日
0.5.0 2023年7月28日
0.1.1 2023年1月6日

#643 in 网络编程

每月25次下载

MIT/Apache

23KB
202

此crate提供带环绕功能的两个字节的序列号。

发行说明

序列号是分配给项目的递增标识符。在许多情况下,您可以使用u32u64并结束,无需担心溢出。此类型的独特优势是它只使用与u16相同的空间,通过环绕解决了溢出问题。

使用场景之一是在使用序列号签名网络数据包时,能够使用包含比使用u32u64更多序列号的确认数据包进行响应。特别是对于UDP这样的协议,你可以在ack包中放入更多的数字,这会得到更多的冗余,也就更有可能确认所有接收到的数据包。

由于数字空间有限,最近分配的序列号可能与非常旧的序列号重复,但不会与其他最近分配的序列号重复。为了避免与这些非唯一数字产生歧义,RFC 1982 "序列号算术"为涉及此类序列号的计算定义了特殊的规则。


[dependencies]
serial-num = "0.10"

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

功能标志

以下功能标志实现了对Serial类型的额外特性

  • arbitrary:派生arbitraryArbitrary(⚠️需要std
  • bincode:派生bincodeDecode/Encode
  • bitcode:派生bitcodeDecode/Encode(⚠️需要std
  • borsh:派生borshBorshDeserialize/BorshSerialize
  • bytemuck:推导 bytemuckPod/Zeroable
  • postcard:推导 postcardSchema/MaxSize
  • rkyv:推导 rkyvArchive/Deserialize/Serialize
  • rkyv-safe:额外启用 rkyv 的安全 API
  • serde:推导 serdeDeserialize/Serialize
  • speedy:推导 speedyReadable/Writable(⚠️ 需要 std

用法

简单示例

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);

环绕示例

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)

NAN

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));

依赖

~0–1MB
~20K SLoC