Skip to content

Commit

Permalink
Merge pull request #58 from anatoly-kussul/optimizations
Browse files Browse the repository at this point in the history
Some optimizations
  • Loading branch information
lithammer authored Nov 29, 2024
2 parents f1aeca9 + 3cea18c commit d3d4af7
Show file tree
Hide file tree
Showing 3 changed files with 50 additions and 72 deletions.
107 changes: 49 additions & 58 deletions base57.go
Original file line number Diff line number Diff line change
@@ -1,91 +1,82 @@
package shortuuid

import (
"encoding/binary"
"fmt"
"math"
"math/big"
"strings"

"github.com/google/uuid"
"math/bits"
"strings"
)

type base57 struct {
// alphabet is the character set to construct the UUID from.
alphabet alphabet
}

const (
strLen = 22
alphabetLen = 57
)

// Encode encodes uuid.UUID into a string using the most significant bits (MSB)
// first according to the alphabet.
func (b base57) Encode(u uuid.UUID) string {
var num big.Int
num.SetString(strings.Replace(u.String(), "-", "", 4), 16)

// Calculate encoded length.
length := math.Ceil(math.Log(math.Pow(2, 128)) / math.Log(float64(b.alphabet.Length())))

return b.numToString(&num, int(length))
}

// Decode decodes a string according to the alphabet into a uuid.UUID. If s is
// too short, its most significant bits (MSB) will be padded with 0 (zero).
func (b base57) Decode(u string) (uuid.UUID, error) {
str, err := b.stringToNum(u)
if err != nil {
return uuid.Nil, err
num := uint128{
binary.BigEndian.Uint64(u[8:]),
binary.BigEndian.Uint64(u[:8]),
}
return uuid.Parse(str)
}

// numToString converts a number a string using the given alphabet.
func (b *base57) numToString(number *big.Int, padToLen int) string {
var (
out []rune
digit *big.Int
)
var outIndexes [strLen]uint64

alphaLen := big.NewInt(b.alphabet.Length())

zero := new(big.Int)
for number.Cmp(zero) > 0 {
number, digit = new(big.Int).DivMod(number, alphaLen, new(big.Int))
out = append(out, b.alphabet.chars[digit.Int64()])
for i := strLen - 1; num.Hi > 0 || num.Lo > 0; i-- {
num, outIndexes[i] = num.quoRem64(alphabetLen)
}

if padToLen > 0 {
remainder := math.Max(float64(padToLen-len(out)), 0)
out = append(out, []rune(strings.Repeat(string(b.alphabet.chars[0]), int(remainder)))...)
var sb strings.Builder
sb.Grow(strLen)
for i := 0; i < strLen; i++ {
sb.WriteRune(b.alphabet.chars[outIndexes[i]])
}

reverse(out)

return string(out)
return sb.String()
}

// stringToNum converts a string a number using the given alphabet.
func (b *base57) stringToNum(s string) (string, error) {
n := big.NewInt(0)
// Decode decodes a string according to the alphabet into a uuid.UUID. If s is
// too short, its most significant bits (MSB) will be padded with 0 (zero).
func (b base57) Decode(s string) (u uuid.UUID, err error) {
var n uint128
var index int64

for _, char := range s {
n.Mul(n, big.NewInt(b.alphabet.Length()))

index, err := b.alphabet.Index(char)
index, err = b.alphabet.Index(char)
if err != nil {
return "", err
return
}
n, err = n.mulAdd64(alphabetLen, uint64(index))
if err != nil {
return
}

n.Add(n, big.NewInt(index))
}
binary.BigEndian.PutUint64(u[:8], n.Hi)
binary.BigEndian.PutUint64(u[8:], n.Lo)
return
}

if n.BitLen() > 128 {
return "", fmt.Errorf("number is out of range (need a 128-bit value)")
}
type uint128 struct {
Lo, Hi uint64
}

return fmt.Sprintf("%032x", n), nil
func (u uint128) quoRem64(v uint64) (q uint128, r uint64) {
q.Hi, r = bits.Div64(0, u.Hi, v)
q.Lo, r = bits.Div64(r, u.Lo, v)
return
}

// reverse reverses a inline.
func reverse(a []rune) {
for i, j := 0, len(a)-1; i < j; i, j = i+1, j-1 {
a[i], a[j] = a[j], a[i]
func (u uint128) mulAdd64(m uint64, a uint64) (uint128, error) {
hi, lo := bits.Mul64(u.Lo, m)
p0, p1 := bits.Mul64(u.Hi, m)
lo, c0 := bits.Add64(lo, a, 0)
hi, c1 := bits.Add64(hi, p1, c0)
if p0 != 0 || c1 != 0 {
return uint128{}, fmt.Errorf("number is out of range (need a 128-bit value)")
}
return uint128{lo, hi}, nil
}
13 changes: 0 additions & 13 deletions base57_test.go

This file was deleted.

2 changes: 1 addition & 1 deletion shortuuid_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,6 @@ func BenchmarkEncoding(b *testing.B) {

func BenchmarkDecoding(b *testing.B) {
for i := 0; i < b.N; i++ {
_, _ = DefaultEncoder.Decode("c3eeb3e6-e577-4de2-b5bb-08371196b453")
_, _ = DefaultEncoder.Decode("nUfojcH2M5j9j3Tk5A8mf7")
}
}

0 comments on commit d3d4af7

Please sign in to comment.