2019-04-15 15:36:10 +00:00
|
|
|
// Package base62 provides utilities for working with base62 strings.
|
|
|
|
// base62 strings will only contain characters: 0-9, a-z, A-Z
|
|
|
|
package base62
|
|
|
|
|
|
|
|
import (
|
2019-10-17 17:33:00 +00:00
|
|
|
"crypto/rand"
|
|
|
|
"io"
|
2019-11-07 16:54:34 +00:00
|
|
|
|
|
|
|
uuid "github.com/hashicorp/go-uuid"
|
2019-04-15 15:36:10 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
const charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
|
|
|
|
const csLen = byte(len(charset))
|
|
|
|
|
|
|
|
// Random generates a random string using base-62 characters.
|
|
|
|
// Resulting entropy is ~5.95 bits/character.
|
|
|
|
func Random(length int) (string, error) {
|
2019-10-17 17:33:00 +00:00
|
|
|
return RandomWithReader(length, rand.Reader)
|
|
|
|
}
|
|
|
|
|
|
|
|
// RandomWithReader generates a random string using base-62 characters and a given reader.
|
|
|
|
// Resulting entropy is ~5.95 bits/character.
|
|
|
|
func RandomWithReader(length int, reader io.Reader) (string, error) {
|
2019-04-15 15:36:10 +00:00
|
|
|
if length == 0 {
|
|
|
|
return "", nil
|
|
|
|
}
|
|
|
|
output := make([]byte, 0, length)
|
|
|
|
|
|
|
|
// Request a bit more than length to reduce the chance
|
|
|
|
// of needing more than one batch of random bytes
|
|
|
|
batchSize := length + length/4
|
|
|
|
|
|
|
|
for {
|
2019-10-17 17:33:00 +00:00
|
|
|
buf, err := uuid.GenerateRandomBytesWithReader(batchSize, reader)
|
2019-04-15 15:36:10 +00:00
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, b := range buf {
|
|
|
|
// Avoid bias by using a value range that's a multiple of 62
|
|
|
|
if b < (csLen * 4) {
|
|
|
|
output = append(output, charset[b%csLen])
|
|
|
|
|
|
|
|
if len(output) == length {
|
|
|
|
return string(output), nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|