1
0
mirror of https://github.com/imgproxy/imgproxy.git synced 2025-01-23 11:14:48 +02:00
2018-03-19 14:57:31 +06:00

105 lines
2.2 KiB
Go

package gonanoid
import (
"crypto/rand"
"math"
)
// DefaultsType is the type of the default configuration for Nanoid
type DefaultsType struct {
Alphabet string
Size int
MaskSize int
}
// GetDefaults returns the default configuration for Nanoid
func GetDefaults() *DefaultsType {
return &DefaultsType{
Alphabet: "_~0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ", // len=64
Size: 22,
MaskSize: 5,
}
}
var defaults = GetDefaults()
func initMasks(params ...int) []uint {
var size int
if len(params) == 0 {
size = defaults.MaskSize
} else {
size = params[0]
}
masks := make([]uint, size)
for i := 0; i < size; i++ {
shift := 3 + i
masks[i] = (2 << uint(shift)) - 1
}
return masks
}
func getMask(alphabet string, masks []uint) int {
for i := 0; i < len(masks); i++ {
curr := int(masks[i])
if curr >= len(alphabet)-1 {
return curr
}
}
return 0
}
// Random generates cryptographically strong pseudo-random data.
// The size argument is a number indicating the number of bytes to generate.
func Random(size int) ([]byte, error) {
var randomBytes = make([]byte, size)
_, err := rand.Read(randomBytes)
return randomBytes, err
}
// Generate is a low-level function to change alphabet and ID size.
func Generate(alphabet string, size int) (string, error) {
masks := initMasks(size)
mask := getMask(alphabet, masks)
ceilArg := 1.6 * float64(mask*size) / float64(len(alphabet))
step := int(math.Ceil(ceilArg))
id := make([]byte, size)
bytes := make([]byte, step)
for j := 0; ; {
_, err := rand.Read(bytes)
if err != nil {
return "", err
}
for i := 0; i < step; i++ {
currByte := bytes[i] & byte(mask)
if currByte < byte(len(alphabet)) {
id[j] = alphabet[currByte]
j++
if j == size {
return string(id[:size]), nil
}
}
}
}
}
// Nanoid generates secure URL-friendly unique ID.
func Nanoid(param ...int) (string, error) {
var size int
if len(param) == 0 {
size = defaults.Size
} else {
size = param[0]
}
bytes := make([]byte, size)
_, err := rand.Read(bytes)
if err != nil {
return "", err
}
id := make([]byte, size)
for i := 0; i < size; i++ {
id[i] = defaults.Alphabet[bytes[i]&63]
}
return string(id[:size]), nil
}