Added Base32 and ParseBase32 methods.
These methods are nearly identical to the base58 methods, except that these use the z-base-32 character set.
This commit is contained in:
parent
7d434bc4d8
commit
0516e7e5cf
56
snowflake.go
56
snowflake.go
@ -20,6 +20,10 @@ const (
|
|||||||
nodeShift uint8 = stepBits
|
nodeShift uint8 = stepBits
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const encodeBase32Map = "ybndrfg8ejkmcpqxot1uwisza345h769"
|
||||||
|
|
||||||
|
var decodeBase32Map [256]byte
|
||||||
|
|
||||||
const encodeBase58Map = "123456789abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ"
|
const encodeBase58Map = "123456789abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ"
|
||||||
|
|
||||||
var decodeBase58Map [256]byte
|
var decodeBase58Map [256]byte
|
||||||
@ -41,11 +45,22 @@ func init() {
|
|||||||
for i := 0; i < len(encodeBase58Map); i++ {
|
for i := 0; i < len(encodeBase58Map); i++ {
|
||||||
decodeBase58Map[encodeBase58Map[i]] = byte(i)
|
decodeBase58Map[encodeBase58Map[i]] = byte(i)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for i := 0; i < len(encodeBase32Map); i++ {
|
||||||
|
decodeBase32Map[i] = 0xFF
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 0; i < len(encodeBase32Map); i++ {
|
||||||
|
decodeBase32Map[encodeBase32Map[i]] = byte(i)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ErrInvalidBase58 is returned by ParseBase58 when given an invalid []byte
|
// ErrInvalidBase58 is returned by ParseBase58 when given an invalid []byte
|
||||||
var ErrInvalidBase58 = errors.New("invalid base58")
|
var ErrInvalidBase58 = errors.New("invalid base58")
|
||||||
|
|
||||||
|
// ErrInvalidBase32 is returned by ParseBase32 when given an invalid []byte
|
||||||
|
var ErrInvalidBase32 = errors.New("invalid base32")
|
||||||
|
|
||||||
// Epoch is set to the twitter snowflake epoch of 2006-03-21:20:50:14 GMT
|
// Epoch is set to the twitter snowflake epoch of 2006-03-21:20:50:14 GMT
|
||||||
// You may customize this to set a different epoch for your application.
|
// You may customize this to set a different epoch for your application.
|
||||||
var Epoch int64 = 1288834974657
|
var Epoch int64 = 1288834974657
|
||||||
@ -128,6 +143,47 @@ func (f ID) Base36() string {
|
|||||||
return strconv.FormatInt(int64(f), 36)
|
return strconv.FormatInt(int64(f), 36)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Base32 uses the z-base-32 character set but encodes and decodes similar
|
||||||
|
// to base58, allowing it to create an even smaller result string.
|
||||||
|
// NOTE: There are many different base32 implementations so becareful when
|
||||||
|
// doing any interoperation interop with other packages.
|
||||||
|
func (f ID) Base32() string {
|
||||||
|
|
||||||
|
if f < 32 {
|
||||||
|
return string(encodeBase32Map[f])
|
||||||
|
}
|
||||||
|
|
||||||
|
b := make([]byte, 0, 12)
|
||||||
|
for f >= 32 {
|
||||||
|
b = append(b, encodeBase32Map[f%32])
|
||||||
|
f /= 32
|
||||||
|
}
|
||||||
|
b = append(b, encodeBase32Map[f])
|
||||||
|
|
||||||
|
for x, y := 0, len(b)-1; x < y; x, y = x+1, y-1 {
|
||||||
|
b[x], b[y] = b[y], b[x]
|
||||||
|
}
|
||||||
|
|
||||||
|
return string(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParseBase32 parses a base32 []byte into a snowflake ID
|
||||||
|
// NOTE: There are many different base32 implementations so becareful when
|
||||||
|
// doing any interoperation interop with other packages.
|
||||||
|
func ParseBase32(b []byte) (ID, error) {
|
||||||
|
|
||||||
|
var id int64
|
||||||
|
|
||||||
|
for i := range b {
|
||||||
|
if decodeBase32Map[b[i]] == 0xFF {
|
||||||
|
return -1, ErrInvalidBase32
|
||||||
|
}
|
||||||
|
id = id*32 + int64(decodeBase32Map[b[i]])
|
||||||
|
}
|
||||||
|
|
||||||
|
return ID(id), nil
|
||||||
|
}
|
||||||
|
|
||||||
// Base58 returns a base58 string of the snowflake ID
|
// Base58 returns a base58 string of the snowflake ID
|
||||||
func (f ID) Base58() string {
|
func (f ID) Base58() string {
|
||||||
|
|
||||||
|
@ -52,6 +52,49 @@ func TestUnmarshalJSON(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestBase32(t *testing.T) {
|
||||||
|
|
||||||
|
node, _ := NewNode(1)
|
||||||
|
|
||||||
|
for i := 0; i < 100; i++ {
|
||||||
|
|
||||||
|
sf := node.Generate()
|
||||||
|
b32i := sf.Base32()
|
||||||
|
psf, err := ParseBase32([]byte(b32i))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if sf != psf {
|
||||||
|
t.Fatal("Parsed does not match String.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkParseBase32(b *testing.B) {
|
||||||
|
|
||||||
|
node, _ := NewNode(1)
|
||||||
|
sf := node.Generate()
|
||||||
|
b32i := sf.Base32()
|
||||||
|
|
||||||
|
b.ReportAllocs()
|
||||||
|
|
||||||
|
b.ResetTimer()
|
||||||
|
for n := 0; n < b.N; n++ {
|
||||||
|
ParseBase32([]byte(b32i))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
func BenchmarkBase32(b *testing.B) {
|
||||||
|
|
||||||
|
node, _ := NewNode(1)
|
||||||
|
sf := node.Generate()
|
||||||
|
|
||||||
|
b.ReportAllocs()
|
||||||
|
|
||||||
|
b.ResetTimer()
|
||||||
|
for n := 0; n < b.N; n++ {
|
||||||
|
sf.Base32()
|
||||||
|
}
|
||||||
|
}
|
||||||
func TestBase58(t *testing.T) {
|
func TestBase58(t *testing.T) {
|
||||||
|
|
||||||
node, _ := NewNode(1)
|
node, _ := NewNode(1)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user