seqid.gno

2.78 Kb ยท 91 lines
 1// Package seqid provides a simple way to have sequential IDs which will be
 2// ordered correctly when inserted in an AVL tree.
 3//
 4// Sample usage:
 5//
 6//	var id seqid.ID
 7//	var users avl.Tree
 8//
 9//	func NewUser() {
10//		users.Set(id.Next().String(), &User{ ... })
11//	}
12package seqid
13
14import (
15	"encoding/binary"
16
17	"gno.land/p/demo/cford32"
18)
19
20// An ID is a simple sequential ID generator.
21type ID uint64
22
23// Next advances the ID i.
24// It will panic if increasing ID would overflow.
25func (i *ID) Next() ID {
26	next, ok := i.TryNext()
27	if !ok {
28		panic("seqid: next ID overflows uint64")
29	}
30	return next
31}
32
33const maxID ID = 1<<64 - 1
34
35// TryNext increases i by 1 and returns its value.
36// It returns true if successful, or false if the increment would result in
37// an overflow.
38func (i *ID) TryNext() (ID, bool) {
39	if *i == maxID {
40		// Addition will overflow.
41		return 0, false
42	}
43	*i++
44	return *i, true
45}
46
47// Binary returns a big-endian binary representation of the ID,
48// suitable to be used as an AVL key.
49func (i ID) Binary() string {
50	buf := make([]byte, 8)
51	binary.BigEndian.PutUint64(buf, uint64(i))
52	return string(buf)
53}
54
55// String encodes i using cford32's compact encoding. For more information,
56// see the documentation for package [gno.land/p/demo/cford32].
57//
58// The result of String will be a 7-byte string for IDs [0,2^34), and a
59// 13-byte string for all values following that. All generated string IDs
60// follow the same lexicographic order as their number values; that is, for any
61// two IDs (x, y) such that x < y, x.String() < y.String().
62// As such, this string representation is suitable to be used as an AVL key.
63func (i ID) String() string {
64	return string(cford32.PutCompact(uint64(i)))
65}
66
67// FromBinary creates a new ID from the given string, expected to be a binary
68// big-endian encoding of an ID (such as that of [ID.Binary]).
69// The second return value is true if the conversion was successful.
70func FromBinary(b string) (ID, bool) {
71	if len(b) != 8 {
72		return 0, false
73	}
74	return ID(binary.BigEndian.Uint64([]byte(b))), true
75}
76
77// FromString creates a new ID from the given string, expected to be a string
78// representation using cford32, such as that returned by [ID.String].
79//
80// The encoding scheme used by cford32 allows the same ID to have many
81// different representations (though the one returned by [ID.String] is only
82// one, deterministic and safe to be used in AVL). The encoding scheme is
83// "human-centric" and is thus case insensitive, and maps some ambiguous
84// characters to be the same, ie. L = I = 1, O = 0. For this reason, when
85// parsing user input to retrieve a key (encoded as a string), always sanitize
86// it first using FromString, then run String(), instead of using the user's
87// input directly.
88func FromString(b string) (ID, error) {
89	n, err := cford32.Uint64([]byte(b))
90	return ID(n), err
91}