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