Port short uuid code to Go
This commit is contained in:
parent
a22ba9f739
commit
ff2ff9c04f
91
tools/utils/short-uuid.go
Normal file
91
tools/utils/short-uuid.go
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
// License: GPLv3 Copyright: 2022, Kovid Goyal, <kovid at kovidgoyal.net>
|
||||||
|
|
||||||
|
package utils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"math"
|
||||||
|
"math/big"
|
||||||
|
|
||||||
|
"github.com/ALTree/bigfloat"
|
||||||
|
"github.com/google/uuid"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
ESCAPE_CODE_SAFE_ALPHABET = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~ "
|
||||||
|
HUMAN_ALPHABET = "23456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||||
|
)
|
||||||
|
|
||||||
|
func num_to_string(number *big.Int, alphabet []rune, alphabet_len *big.Int, pad_to_length int) string {
|
||||||
|
var zero, digit big.Int
|
||||||
|
if number.Sign() < 0 {
|
||||||
|
*number = zero
|
||||||
|
}
|
||||||
|
capacity := 64
|
||||||
|
if pad_to_length > capacity {
|
||||||
|
capacity = pad_to_length
|
||||||
|
}
|
||||||
|
ans := make([]rune, 0, capacity)
|
||||||
|
for number.Cmp(&zero) == 1 {
|
||||||
|
number.DivMod(number, alphabet_len, &digit)
|
||||||
|
ans = append(ans, alphabet[digit.Uint64()])
|
||||||
|
}
|
||||||
|
al := len(ans)
|
||||||
|
if pad_to_length > -1 && al < pad_to_length {
|
||||||
|
ans = ans[:pad_to_length]
|
||||||
|
for i := al; i < pad_to_length; i++ {
|
||||||
|
ans[i] = alphabet[0]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return string(ans)
|
||||||
|
}
|
||||||
|
|
||||||
|
func get_padding_length(alphabet_len int) int {
|
||||||
|
bi := big.NewInt(2)
|
||||||
|
bi = bi.Exp(bi, big.NewInt(128), nil)
|
||||||
|
bb := new(big.Float).SetPrec(256)
|
||||||
|
bb.SetInt(bi)
|
||||||
|
log_al := bigfloat.Log(big.NewFloat(float64(alphabet_len)).SetPrec(256))
|
||||||
|
log_b := bigfloat.Log(bb)
|
||||||
|
res := new(big.Float).SetPrec(256)
|
||||||
|
res = res.Quo(log_b, log_al)
|
||||||
|
val, _ := res.Float64()
|
||||||
|
return int(math.Ceil(val))
|
||||||
|
}
|
||||||
|
|
||||||
|
type ShortUUID struct {
|
||||||
|
alphabet []rune
|
||||||
|
alphabet_len big.Int
|
||||||
|
pad_to_length int
|
||||||
|
}
|
||||||
|
|
||||||
|
func CreateShortUUID(alphabet string) *ShortUUID {
|
||||||
|
if alphabet == "" {
|
||||||
|
alphabet = HUMAN_ALPHABET
|
||||||
|
}
|
||||||
|
var ans = ShortUUID{
|
||||||
|
alphabet: []rune(alphabet),
|
||||||
|
}
|
||||||
|
ans.pad_to_length = get_padding_length(len(ans.alphabet))
|
||||||
|
ans.alphabet_len.SetUint64(uint64(len(ans.alphabet)))
|
||||||
|
return &ans
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *ShortUUID) Uuid4() (string, error) {
|
||||||
|
b, err := uuid.NewRandom()
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
bb, err := b.MarshalBinary()
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
var bi big.Int
|
||||||
|
bi.SetBytes(bb)
|
||||||
|
return num_to_string(&bi, self.alphabet, &self.alphabet_len, self.pad_to_length), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var HumanUUID *ShortUUID
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
HumanUUID = CreateShortUUID(HUMAN_ALPHABET)
|
||||||
|
}
|
||||||
28
tools/utils/short-uuid_test.go
Normal file
28
tools/utils/short-uuid_test.go
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
// License: GPLv3 Copyright: 2022, Kovid Goyal, <kovid at kovidgoyal.net>
|
||||||
|
|
||||||
|
package utils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"math/big"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestShortUUID(t *testing.T) {
|
||||||
|
if HumanUUID.pad_to_length != 22 {
|
||||||
|
t.Fatalf("pad length for human UUID is %d not %d", HumanUUID.pad_to_length, 22)
|
||||||
|
}
|
||||||
|
u, err := HumanUUID.Uuid4()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if len(u) != 22 {
|
||||||
|
t.Fatalf("uuid4 %s has unexpected length: %d", u, len(u))
|
||||||
|
}
|
||||||
|
|
||||||
|
b := big.NewInt(int64(1234567890123456789))
|
||||||
|
q := num_to_string(b, HumanUUID.alphabet, &HumanUUID.alphabet_len, HumanUUID.pad_to_length)
|
||||||
|
const expected = "bzT6LtUjw4422222222222"
|
||||||
|
if q != expected {
|
||||||
|
t.Fatalf("unexpected short human serialization: %s != %s", q, expected)
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user