-
Notifications
You must be signed in to change notification settings - Fork 4
/
Copy pathgouid.go
81 lines (72 loc) · 2.01 KB
/
gouid.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
// Package guoid provides cryptographically secure unique identifiers
// of type string and type []byte.
//
// On Linux, FreeBSD, Dragonfly and Solaris, getrandom(2) is used if
// available, /dev/urandom otherwise.
// On OpenBSD and macOS, getentropy(2) is used.
// On other Unix-like systems, /dev/urandom is used.
// On Windows systems, the RtlGenRandom API is used.
// On Wasm, the Web Crypto API is used.
package gouid
import (
"crypto/rand"
"encoding/hex"
"encoding/json"
"unsafe"
)
// GOUID is a byte slice.
type GOUID []byte
// Charsets for string gouids.
var (
// Cryptographically secure charsets should include N characters,
// where N is a factor of 256 (2, 4, 8, 16, 32, 64, 128, 256)
Secure32Char = []byte("abcdefghijklmnopqrstuvwxyz012345")
Secure64Char = []byte("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-")
)
// String returns a string with the given size made up of
// characters from the given charset. Ids are made with
// cryptographically secure random bytes. The length of
// charset must not exceed 256.
func String(size int, charset []byte) string {
b := make([]byte, size)
randBytes(b)
charCnt := byte(len(charset))
for i := range b {
b[i] = charset[b[i]%charCnt]
}
return *(*string)(unsafe.Pointer(&b))
}
// Bytes returns cryptographically secure random bytes.
func Bytes(size int) GOUID {
b := make([]byte, size)
randBytes(b)
return b
}
// MarshalJSON hex encodes the gouid.
func (g GOUID) MarshalJSON() ([]byte, error) {
return json.Marshal(g.String())
}
// String implements the Stringer interface.
func (g GOUID) String() string {
return hex.EncodeToString(g)
}
// UnmarshalJSON decodes a hex encoded string into a gouid.
func (g *GOUID) UnmarshalJSON(data []byte) error {
var x string
err := json.Unmarshal(data, &x)
if err == nil {
str, e := hex.DecodeString(x)
*g = GOUID([]byte(str))
err = e
}
return err
}
func randBytes(buf []byte) {
var n int
var err error
for n < len(buf) && err == nil {
var nn int
nn, err = rand.Reader.Read(buf[n:])
n += nn
}
}