-
Notifications
You must be signed in to change notification settings - Fork 383
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(examples): add p/demo/seqid (#1378)
A very simple ID generation package, designed to be used in combination with `avl.Tree`s to push values in order. The name was originally `seqid` (sequential IDs), but after saying it a few times I realised it was close to "squid" and probably would be more fun if I named it that way ;) There's another piece of functionality that I want to add, which is a way to create simple base32-encoded IDs. This depends on #1290. These would also guarantee alphabetical ordering, so a list of them can be easily sorted and you'd get it in the same order they were created. They would likely be 13 characters long, but I'm also thinking of making a compact version which works from [0,2^35) which is 7 chracters, and then smoothly transitions over to the 13 characters version when the ID is reached. (I've experience with both base64 and base32 encoded IDs as 64-bit numbers, as I used both systems. The advantage of base32 is that it makes IDs case insensitive, all the while being at most 13 bytes instead of 11 for base64.) In GnoChess, we used simple sequential IDs combined with [`zeroPad9`](https://github.com/gnolang/gnochess/blob/7e841191a4a0a94c0d46bc977458bd6b757eab5e/realm/chess.gno#L287-L296) to create IDs which were both readable and sortable. I want to make a more "canonical" solution to this which does not have a upper limit at 1 billion entries.
- Loading branch information
Showing
4 changed files
with
137 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
# seqid | ||
|
||
``` | ||
package seqid // import "gno.land/p/demo/seqid" | ||
Package seqid provides a simple way to have sequential IDs which will be ordered | ||
correctly when inserted in an AVL tree. | ||
Sample usage: | ||
var id seqid.ID | ||
var users avl.Tree | ||
func NewUser() { | ||
users.Set(id.Next().Binary(), &User{ ... }) | ||
} | ||
TYPES | ||
type ID uint64 | ||
An ID is a simple sequential ID generator. | ||
func FromBinary(b string) (ID, bool) | ||
FromBinary creates a new ID from the given string. | ||
func (i ID) Binary() string | ||
Binary returns a big-endian binary representation of the ID, suitable to be | ||
used as an AVL key. | ||
func (i *ID) Next() ID | ||
Next advances the ID i. It will panic if increasing ID would overflow. | ||
func (i *ID) TryNext() (ID, bool) | ||
TryNext increases i by 1 and returns its value. It returns true if | ||
successful, or false if the increment would result in an overflow. | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
module gno.land/p/demo/seqid |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
// Package seqid provides a simple way to have sequential IDs which will be | ||
// ordered correctly when inserted in an AVL tree. | ||
// | ||
// Sample usage: | ||
// | ||
// var id seqid.ID | ||
// var users avl.Tree | ||
// | ||
// func NewUser() { | ||
// users.Set(id.Next().Binary(), &User{ ... }) | ||
// } | ||
package seqid | ||
|
||
import "encoding/binary" | ||
|
||
// An ID is a simple sequential ID generator. | ||
type ID uint64 | ||
|
||
// Next advances the ID i. | ||
// It will panic if increasing ID would overflow. | ||
func (i *ID) Next() ID { | ||
next, ok := i.TryNext() | ||
if !ok { | ||
panic("seqid: next ID overflows uint64") | ||
} | ||
return next | ||
} | ||
|
||
const maxID ID = 1<<64 - 1 | ||
|
||
// TryNext increases i by 1 and returns its value. | ||
// It returns true if successful, or false if the increment would result in | ||
// an overflow. | ||
func (i *ID) TryNext() (ID, bool) { | ||
if *i == maxID { | ||
// Addition will overflow. | ||
return 0, false | ||
} | ||
*i++ | ||
return *i, true | ||
} | ||
|
||
// Binary returns a big-endian binary representation of the ID, | ||
// suitable to be used as an AVL key. | ||
func (i ID) Binary() string { | ||
buf := make([]byte, 8) | ||
binary.BigEndian.PutUint64(buf, uint64(i)) | ||
return string(buf) | ||
} | ||
|
||
// FromBinary creates a new ID from the given string. | ||
func FromBinary(b string) (ID, bool) { | ||
if len(b) != 8 { | ||
return 0, false | ||
} | ||
return ID(binary.BigEndian.Uint64([]byte(b))), true | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
package seqid | ||
|
||
import ( | ||
"fmt" | ||
"strings" | ||
"testing" | ||
) | ||
|
||
func TestID(t *testing.T) { | ||
var i ID | ||
|
||
for j := 0; j < 100; j++ { | ||
i.Next() | ||
} | ||
if i != 100 { | ||
t.Fatalf("invalid: wanted %d got %d", 100, i) | ||
} | ||
} | ||
|
||
func TestID_Overflow(t *testing.T) { | ||
i := ID(maxID) | ||
|
||
defer func() { | ||
err := recover() | ||
if !strings.Contains(fmt.Sprint(err), "next ID overflows") { | ||
t.Errorf("did not overflow") | ||
} | ||
}() | ||
|
||
i.Next() | ||
} | ||
|
||
func TestID_Binary(t *testing.T) { | ||
var i ID | ||
prev := i.Binary() | ||
|
||
for j := 0; j < 1000; j++ { | ||
cur := i.Next().Binary() | ||
if cur <= prev { | ||
t.Fatalf("cur %x <= prev %x", cur, prev) | ||
} | ||
} | ||
} |