diff --git a/README.md b/README.md index fb664ff..0e749d0 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,3 @@ # idgen -Simple ID generation. \ No newline at end of file +Simple ID generation. diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..9340d09 --- /dev/null +++ b/go.mod @@ -0,0 +1,3 @@ +module git.crumpinton.com/lib/idgen + +go 1.25.1 diff --git a/idgen.go b/idgen.go new file mode 100644 index 0000000..c95a1ef --- /dev/null +++ b/idgen.go @@ -0,0 +1,53 @@ +package idgen + +import ( + "crypto/rand" + "encoding/base32" + "sync" + "time" +) + +// Creates a new, random token. +func NewToken() string { + buf := make([]byte, 20) + _, err := rand.Read(buf) + if err != nil { + panic(err) + } + return base32.StdEncoding.EncodeToString(buf) +} + +var ( + lock sync.Mutex + ts int64 = time.Now().Unix() + counter int64 = 1 + counterMax int64 = 1 << 20 +) + +// NextID can generate ~1M ints per second for a given nodeID. +// +// nodeID must be less than 64. +func NextID(nodeID int64) int64 { + lock.Lock() + defer lock.Unlock() + + tt := time.Now().Unix() + if tt > ts { + ts = tt + counter = 1 + } else { + counter++ + if counter == counterMax { + panic("Too many IDs.") + } + } + + return (ts << 26) + (nodeID << 20) + counter +} + +func SplitID(id int64) (unixTime, nodeID, counter int64) { + counter = id & (0x00000000000FFFFF) + nodeID = (id >> 20) & (0x000000000000003F) + unixTime = id >> 26 + return +} diff --git a/idgen_test.go b/idgen_test.go new file mode 100644 index 0000000..822f6d7 --- /dev/null +++ b/idgen_test.go @@ -0,0 +1,19 @@ +package idgen + +import ( + "log" + "testing" +) + +func BenchmarkNext(b *testing.B) { + for i := 0; i < b.N; i++ { + NextID(0) + } +} + +func TestNextID(t *testing.T) { + id := NextID(32) + + a, b, c := SplitID(id) + log.Print(a, b, c) +}