{"id":17946778,"url":"https://github.com/timwie/serial-num","last_synced_at":"2026-02-18T01:02:18.097Z","repository":{"id":65176619,"uuid":"585737706","full_name":"timwie/serial-num","owner":"timwie","description":"Small serial number type with wraparound","archived":false,"fork":false,"pushed_at":"2024-10-03T15:41:09.000Z","size":293,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2024-10-29T07:07:32.359Z","etag":null,"topics":["ack","rfc-1982","rust","serial-number"],"latest_commit_sha":null,"homepage":"https://crates.io/crates/serial-num","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/timwie.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE-APACHE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2023-01-06T00:05:39.000Z","updated_at":"2024-10-03T15:41:13.000Z","dependencies_parsed_at":"2023-11-23T22:21:42.590Z","dependency_job_id":"4d8aecdf-a906-4788-82cc-d7c0b8b7ddab","html_url":"https://github.com/timwie/serial-num","commit_stats":{"total_commits":6,"total_committers":1,"mean_commits":6.0,"dds":0.0,"last_synced_commit":"9efb36b4e0eb1d65fccf64b1a85f73f2ac8481a2"},"previous_names":[],"tags_count":13,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/timwie%2Fserial-num","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/timwie%2Fserial-num/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/timwie%2Fserial-num/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/timwie%2Fserial-num/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/timwie","download_url":"https://codeload.github.com/timwie/serial-num/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":245348323,"owners_count":20600626,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["ack","rfc-1982","rust","serial-number"],"created_at":"2024-10-29T07:07:34.082Z","updated_at":"2026-02-18T01:02:18.057Z","avatar_url":"https://github.com/timwie.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"This crate offers a two-byte serial number with wraparound.\n\n[Release notes](https://github.com/timwie/serial-num/blob/main/RELEASES.md)\n\nA serial number is an identifier assigned incrementally to an item.\nIn many cases, you can use a `u32` or `u64` and call it\na day, without having to worry about overflow. The niche benefit of this type\nis that it only uses the space of a `u16`, with the problem of overflow solved\nby wraparound.\n\nOne scenario where this is useful, is when signing network packets with\na serial number, and being able to respond with an [ack](https://en.wikipedia.org/wiki/Acknowledgement_(data_networks)) packet\nthat contains a larger amount of serial numbers than when using `u32` or `u64`.\nEspecially with a protocol like UDP, the more numbers you can fit into that ack packet,\nthe more redundancy you get, and the more likely is it that all received packets are also successfully acknowledged.\n\nDue to the limited number space, recently allocated serial numbers may\nduplicate very old serial numbers, but not other recently allocated serial numbers.\nTo avoid ambiguity with these non-unique numbers, [RFC 1982 \"Serial Number Arithmetic\"](https://datatracker.ietf.org/doc/html/rfc1982),\ndefines special rules for calculations involving these kinds of serial numbers. \n\n\u003cbr\u003e\n\n```toml\n[dependencies]\nserial-num = \"0.10\"\n\n# or with additional features:\n[dependencies]\nserial-num = { version = \"0.10\", features = [\"serde\"] }\n```\n\n\u003cbr\u003e\n\n## Feature Flags\nThe following feature flags implement additional traits for the `Serial` type:\n* `arbitrary`: derives [arbitrary]'s `Arbitrary`\n* `bincode`: derives [bincode]'s `Decode/Encode`\n* `bitcode`: derives [bitcode]'s `Decode/Encode`\n* `borsh`: derives [borsh]'s `BorshDeserialize/BorshSerialize`\n* `bytemuck`: derives [bytemuck]'s `Pod/Zeroable`\n* `postcard`: derives [postcard]'s `Schema/MaxSize`\n* `proptest`: derives [proptest]'s `Arbitrary`\n* `rkyv`: derives [rkyv]'s `Archive/Deserialize/Serialize`\n* `rkyv-safe`: additionally enables [rkyv]’s safe API\n* `serde`: derives [serde]'s `Deserialize/Serialize`\n* `speedy`: derives [speedy]'s `Readable/Writable`\n\n[arbitrary]: https://crates.io/crates/arbitrary\n[bincode]: https://crates.io/crates/bincode\n[bitcode]: https://crates.io/crates/bitcode\n[borsh]: https://crates.io/crates/borsh\n[bytemuck]: https://crates.io/crates/bytemuck\n[postcard]: https://crates.io/crates/postcard\n[proptest]: https://crates.io/crates/proptest\n[rkyv]: https://crates.io/crates/rkyv\n[serde]: https://crates.io/crates/serde\n[speedy]: https://crates.io/crates/speedy\n\n\u003cbr\u003e\n\n## Usage\n### Simple example\n```rust\nuse serial_num::Serial;\n\n// the default is a reference point - not serial number \"zero\"\nlet mut a = Serial::default();\nlet mut b = Serial::default();\nlet mut c = Serial::default();\n\n// three ways to increase\nlet x = a.increase_get(); // increase, then copy\nlet y = b.get_increase(); // copy, then increase\nc.increase();\n\nassert!(y.precedes(x));\nassert_eq!(-1_i16, y.diff(x)); // \"diff()\" is signed\nassert_eq!(1_u16, y.dist(x)); // \"dist()\" is unsigned\n\n// addition is the same as calling \"increase()\" n times\nassert_eq!(y + 1_u16, x);\n```\n\n### Wraparound example\n```rust\nuse serial_num::Serial;\n\n// a serial number can be increased indefinitely\nlet mut x = Serial::default();\nfor _ in 0..u16::MAX {\n    x.increase();\n}\nlet x = x + u16::MAX + u16::MAX + u16::MAX;\n\n// comparison is trivial as long as two serial numbers have\n// a distance of less than half of our number space (32767).\nlet a = Serial::default() + 5;\nlet b = Serial::default() + 32000;\nassert!(a.precedes(b)); // 5th successor \u003c 32000th successor\n\n// but: the comparison flips if the distance is larger\nlet a = Serial::default() + 5;\nlet b = Serial::default() + 65000;\nassert!(a.succeeds(b)); // 5th successor \u003e 65000th successor\n\n// this means that you get the right ordering as long as\n// you compare one serial number at most with one that\n// is its 32767th successor:\nlet num = Serial::default();\nassert!(num.precedes(num + 32767));     //  0 \u003c 32767 (still intuitive)\nassert!(num.succeeds(num + 32768));     //  0 \u003e 32768 (flip #1)\nassert!(num.succeeds(num + 65534));     //  0 \u003e 65534\nassert!(num == num + 65535);            // 0 == 65535 (due to same internal representation)\nassert!(num.precedes(num + 65535 + 1)); //  0 \u003c 65536 (flip #2)\n```\n\n### The `NAN` value\n```rust\nuse serial_num::Serial;\n\n// \"NAN\" exists to have value representing \"no serial number\",\n// since it saves encoding space vs wrapping Serial in an Option.\nlet nan = Serial::NAN;\nlet num = Serial::default();\n\n// you can check whether a serial number is NAN\nassert!(nan.is_nan());\n\n// NAN cannot be increased\nassert_eq!(Serial::NAN, nan + 1_u16);\n\n// distance between two NAN values is zero\nassert_eq!(0_u16, nan.dist(nan));\nassert_eq!(0_i16, nan.diff(nan));\n\n// distance and difference of non-NAN to NAN is the maximum distance\nassert_eq!(32_767_u16, num.dist(nan));\nassert_eq!(32_767_u16, nan.dist(num));\nassert_eq!(32_767_i16, num.diff(nan));\nassert_eq!(32_767_i16, nan.diff(num));\n\n// partial ordering does not include the NAN value\nassert_eq!(None, nan.partial_cmp(num));\nassert!(!nan.precedes(num) \u0026\u0026 !nan.succeeds(num));\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftimwie%2Fserial-num","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftimwie%2Fserial-num","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftimwie%2Fserial-num/lists"}