-
-
Notifications
You must be signed in to change notification settings - Fork 61
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #58 from anatoly-kussul/optimizations
Some optimizations
- Loading branch information
Showing
3 changed files
with
50 additions
and
72 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 |
---|---|---|
@@ -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 | ||
} |
This file was deleted.
Oops, something went wrong.
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