| // Copyright 2018 Google Inc. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| package uuid |
| |
| import ( |
| "bytes" |
| "crypto/rand" |
| "encoding/hex" |
| "errors" |
| "fmt" |
| "io" |
| "strings" |
| "sync" |
| ) |
| |
| // A UUID is a 128 bit (16 byte) Universal Unique IDentifier as defined in RFC |
| // 4122. |
| type UUID [16]byte |
| |
| // A Version represents a UUID's version. |
| type Version byte |
| |
| // A Variant represents a UUID's variant. |
| type Variant byte |
| |
| // Constants returned by Variant. |
| const ( |
| Invalid = Variant(iota) // Invalid UUID |
| RFC4122 // The variant specified in RFC4122 |
| Reserved // Reserved, NCS backward compatibility. |
| Microsoft // Reserved, Microsoft Corporation backward compatibility. |
| Future // Reserved for future definition. |
| ) |
| |
| const randPoolSize = 16 * 16 |
| |
| var ( |
| rander = rand.Reader // random function |
| poolEnabled = false |
| poolMu sync.Mutex |
| poolPos = randPoolSize // protected with poolMu |
| pool [randPoolSize]byte // protected with poolMu |
| ) |
| |
| type invalidLengthError struct{ len int } |
| |
| func (err invalidLengthError) Error() string { |
| return fmt.Sprintf("invalid UUID length: %d", err.len) |
| } |
| |
| // IsInvalidLengthError is matcher function for custom error invalidLengthError |
| func IsInvalidLengthError(err error) bool { |
| _, ok := err.(invalidLengthError) |
| return ok |
| } |
| |
| // Parse decodes s into a UUID or returns an error if it cannot be parsed. Both |
| // the standard UUID forms defined in RFC 4122 |
| // (xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx and |
| // urn:uuid:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx) are decoded. In addition, |
| // Parse accepts non-standard strings such as the raw hex encoding |
| // xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx and 38 byte "Microsoft style" encodings, |
| // e.g. {xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}. Only the middle 36 bytes are |
| // examined in the latter case. Parse should not be used to validate strings as |
| // it parses non-standard encodings as indicated above. |
| func Parse(s string) (UUID, error) { |
| var uuid UUID |
| switch len(s) { |
| // xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx |
| case 36: |
| |
| // urn:uuid:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx |
| case 36 + 9: |
| if !strings.EqualFold(s[:9], "urn:uuid:") { |
| return uuid, fmt.Errorf("invalid urn prefix: %q", s[:9]) |
| } |
| s = s[9:] |
| |
| // {xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx} |
| case 36 + 2: |
| s = s[1:] |
| |
| // xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx |
| case 32: |
| var ok bool |
| for i := range uuid { |
| uuid[i], ok = xtob(s[i*2], s[i*2+1]) |
| if !ok { |
| return uuid, errors.New("invalid UUID format") |
| } |
| } |
| return uuid, nil |
| default: |
| return uuid, invalidLengthError{len(s)} |
| } |
| // s is now at least 36 bytes long |
| // it must be of the form xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx |
| if s[8] != '-' || s[13] != '-' || s[18] != '-' || s[23] != '-' { |
| return uuid, errors.New("invalid UUID format") |
| } |
| for i, x := range [16]int{ |
| 0, 2, 4, 6, |
| 9, 11, |
| 14, 16, |
| 19, 21, |
| 24, 26, 28, 30, 32, 34, |
| } { |
| v, ok := xtob(s[x], s[x+1]) |
| if !ok { |
| return uuid, errors.New("invalid UUID format") |
| } |
| uuid[i] = v |
| } |
| return uuid, nil |
| } |
| |
| // ParseBytes is like Parse, except it parses a byte slice instead of a string. |
| func ParseBytes(b []byte) (UUID, error) { |
| var uuid UUID |
| switch len(b) { |
| case 36: // xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx |
| case 36 + 9: // urn:uuid:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx |
| if !bytes.EqualFold(b[:9], []byte("urn:uuid:")) { |
| return uuid, fmt.Errorf("invalid urn prefix: %q", b[:9]) |
| } |
| b = b[9:] |
| case 36 + 2: // {xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx} |
| b = b[1:] |
| case 32: // xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx |
| var ok bool |
| for i := 0; i < 32; i += 2 { |
| uuid[i/2], ok = xtob(b[i], b[i+1]) |
| if !ok { |
| return uuid, errors.New("invalid UUID format") |
| } |
| } |
| return uuid, nil |
| default: |
| return uuid, invalidLengthError{len(b)} |
| } |
| // s is now at least 36 bytes long |
| // it must be of the form xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx |
| if b[8] != '-' || b[13] != '-' || b[18] != '-' || b[23] != '-' { |
| return uuid, errors.New("invalid UUID format") |
| } |
| for i, x := range [16]int{ |
| 0, 2, 4, 6, |
| 9, 11, |
| 14, 16, |
| 19, 21, |
| 24, 26, 28, 30, 32, 34, |
| } { |
| v, ok := xtob(b[x], b[x+1]) |
| if !ok { |
| return uuid, errors.New("invalid UUID format") |
| } |
| uuid[i] = v |
| } |
| return uuid, nil |
| } |
| |
| // MustParse is like Parse but panics if the string cannot be parsed. |
| // It simplifies safe initialization of global variables holding compiled UUIDs. |
| func MustParse(s string) UUID { |
| uuid, err := Parse(s) |
| if err != nil { |
| panic(`uuid: Parse(` + s + `): ` + err.Error()) |
| } |
| return uuid |
| } |
| |
| // FromBytes creates a new UUID from a byte slice. Returns an error if the slice |
| // does not have a length of 16. The bytes are copied from the slice. |
| func FromBytes(b []byte) (uuid UUID, err error) { |
| err = uuid.UnmarshalBinary(b) |
| return uuid, err |
| } |
| |
| // Must returns uuid if err is nil and panics otherwise. |
| func Must(uuid UUID, err error) UUID { |
| if err != nil { |
| panic(err) |
| } |
| return uuid |
| } |
| |
| // Validate returns an error if s is not a properly formatted UUID in one of the following formats: |
| // xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx |
| // urn:uuid:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx |
| // xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx |
| // {xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx} |
| // It returns an error if the format is invalid, otherwise nil. |
| func Validate(s string) error { |
| switch len(s) { |
| // Standard UUID format |
| case 36: |
| |
| // UUID with "urn:uuid:" prefix |
| case 36 + 9: |
| if !strings.EqualFold(s[:9], "urn:uuid:") { |
| return fmt.Errorf("invalid urn prefix: %q", s[:9]) |
| } |
| s = s[9:] |
| |
| // UUID enclosed in braces |
| case 36 + 2: |
| if s[0] != '{' || s[len(s)-1] != '}' { |
| return fmt.Errorf("invalid bracketed UUID format") |
| } |
| s = s[1 : len(s)-1] |
| |
| // UUID without hyphens |
| case 32: |
| for i := 0; i < len(s); i += 2 { |
| _, ok := xtob(s[i], s[i+1]) |
| if !ok { |
| return errors.New("invalid UUID format") |
| } |
| } |
| |
| default: |
| return invalidLengthError{len(s)} |
| } |
| |
| // Check for standard UUID format |
| if len(s) == 36 { |
| if s[8] != '-' || s[13] != '-' || s[18] != '-' || s[23] != '-' { |
| return errors.New("invalid UUID format") |
| } |
| for _, x := range []int{0, 2, 4, 6, 9, 11, 14, 16, 19, 21, 24, 26, 28, 30, 32, 34} { |
| if _, ok := xtob(s[x], s[x+1]); !ok { |
| return errors.New("invalid UUID format") |
| } |
| } |
| } |
| |
| return nil |
| } |
| |
| // String returns the string form of uuid, xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx |
| // , or "" if uuid is invalid. |
| func (uuid UUID) String() string { |
| var buf [36]byte |
| encodeHex(buf[:], uuid) |
| return string(buf[:]) |
| } |
| |
| // URN returns the RFC 2141 URN form of uuid, |
| // urn:uuid:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx, or "" if uuid is invalid. |
| func (uuid UUID) URN() string { |
| var buf [36 + 9]byte |
| copy(buf[:], "urn:uuid:") |
| encodeHex(buf[9:], uuid) |
| return string(buf[:]) |
| } |
| |
| func encodeHex(dst []byte, uuid UUID) { |
| hex.Encode(dst, uuid[:4]) |
| dst[8] = '-' |
| hex.Encode(dst[9:13], uuid[4:6]) |
| dst[13] = '-' |
| hex.Encode(dst[14:18], uuid[6:8]) |
| dst[18] = '-' |
| hex.Encode(dst[19:23], uuid[8:10]) |
| dst[23] = '-' |
| hex.Encode(dst[24:], uuid[10:]) |
| } |
| |
| // Variant returns the variant encoded in uuid. |
| func (uuid UUID) Variant() Variant { |
| switch { |
| case (uuid[8] & 0xc0) == 0x80: |
| return RFC4122 |
| case (uuid[8] & 0xe0) == 0xc0: |
| return Microsoft |
| case (uuid[8] & 0xe0) == 0xe0: |
| return Future |
| default: |
| return Reserved |
| } |
| } |
| |
| // Version returns the version of uuid. |
| func (uuid UUID) Version() Version { |
| return Version(uuid[6] >> 4) |
| } |
| |
| func (v Version) String() string { |
| if v > 15 { |
| return fmt.Sprintf("BAD_VERSION_%d", v) |
| } |
| return fmt.Sprintf("VERSION_%d", v) |
| } |
| |
| func (v Variant) String() string { |
| switch v { |
| case RFC4122: |
| return "RFC4122" |
| case Reserved: |
| return "Reserved" |
| case Microsoft: |
| return "Microsoft" |
| case Future: |
| return "Future" |
| case Invalid: |
| return "Invalid" |
| } |
| return fmt.Sprintf("BadVariant%d", int(v)) |
| } |
| |
| // SetRand sets the random number generator to r, which implements io.Reader. |
| // If r.Read returns an error when the package requests random data then |
| // a panic will be issued. |
| // |
| // Calling SetRand with nil sets the random number generator to the default |
| // generator. |
| func SetRand(r io.Reader) { |
| if r == nil { |
| rander = rand.Reader |
| return |
| } |
| rander = r |
| } |
| |
| // EnableRandPool enables internal randomness pool used for Random |
| // (Version 4) UUID generation. The pool contains random bytes read from |
| // the random number generator on demand in batches. Enabling the pool |
| // may improve the UUID generation throughput significantly. |
| // |
| // Since the pool is stored on the Go heap, this feature may be a bad fit |
| // for security sensitive applications. |
| // |
| // Both EnableRandPool and DisableRandPool are not thread-safe and should |
| // only be called when there is no possibility that New or any other |
| // UUID Version 4 generation function will be called concurrently. |
| func EnableRandPool() { |
| poolEnabled = true |
| } |
| |
| // DisableRandPool disables the randomness pool if it was previously |
| // enabled with EnableRandPool. |
| // |
| // Both EnableRandPool and DisableRandPool are not thread-safe and should |
| // only be called when there is no possibility that New or any other |
| // UUID Version 4 generation function will be called concurrently. |
| func DisableRandPool() { |
| poolEnabled = false |
| defer poolMu.Unlock() |
| poolMu.Lock() |
| poolPos = randPoolSize |
| } |
| |
| // UUIDs is a slice of UUID types. |
| type UUIDs []UUID |
| |
| // Strings returns a string slice containing the string form of each UUID in uuids. |
| func (uuids UUIDs) Strings() []string { |
| var uuidStrs = make([]string, len(uuids)) |
| for i, uuid := range uuids { |
| uuidStrs[i] = uuid.String() |
| } |
| return uuidStrs |
| } |