1
0
mirror of https://github.com/google/uuid.git synced 2024-11-28 08:49:08 +02:00
uuid/uuid.go

199 lines
4.7 KiB
Go
Raw Normal View History

// Copyright 2016 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.
2011-08-08 21:45:10 +03:00
package uuid
import (
optimize internal parsing function -- avoid unsafe []byte parsing Making xtob take two byte arguments avoids a lot of slicing. This makes the Parse function faster. In addition, because so much slicing is avoiding, duplicating the parse logic to ParseBytes resulted in the function being faster than Parse (<1ns). The BenchmarkParseBytesNative function has been removed (parseBytes was identical to ParseBytes). And a new benchmark, BenchmarkParseBytesUnsafe, has been added to benchmark the old way of parsing []byte (which is slightly slower than Parse and thus the new ParseBytes implementation). benchmark old ns/op new ns/op delta BenchmarkUUID_MarshalJSON-4 685 667 -2.63% BenchmarkUUID_UnmarshalJSON-4 1145 1162 +1.48% BenchmarkParse-4 61.6 56.5 -8.28% BenchmarkParseBytes-4 65.7 55.9 -14.92% BenchmarkParseBytesCopy-4 121 115 -4.96% BenchmarkNew-4 1665 1643 -1.32% BenchmarkUUID_String-4 112 113 +0.89% BenchmarkUUID_URN-4 117 119 +1.71% benchmark old allocs new allocs delta BenchmarkUUID_MarshalJSON-4 4 4 +0.00% BenchmarkUUID_UnmarshalJSON-4 2 2 +0.00% BenchmarkParse-4 0 0 +0.00% BenchmarkParseBytes-4 0 0 +0.00% BenchmarkParseBytesCopy-4 1 1 +0.00% BenchmarkNew-4 1 1 +0.00% BenchmarkUUID_String-4 1 1 +0.00% BenchmarkUUID_URN-4 1 1 +0.00% benchmark old bytes new bytes delta BenchmarkUUID_MarshalJSON-4 248 248 +0.00% BenchmarkUUID_UnmarshalJSON-4 248 248 +0.00% BenchmarkParse-4 0 0 +0.00% BenchmarkParseBytes-4 0 0 +0.00% BenchmarkParseBytesCopy-4 48 48 +0.00% BenchmarkNew-4 16 16 +0.00% BenchmarkUUID_String-4 48 48 +0.00% BenchmarkUUID_URN-4 48 48 +0.00%
2016-02-26 07:36:20 +02:00
"bytes"
2011-08-08 21:45:10 +03:00
"crypto/rand"
"encoding/hex"
"errors"
2011-08-08 21:45:10 +03:00
"fmt"
"io"
"strings"
)
// A UUID is a 128 bit (16 byte) Universal Unique IDentifier as defined in RFC
// 4122.
type UUID [16]byte
2011-08-08 21:45:10 +03:00
// A Version represents a UUID's version.
2011-08-08 21:45:10 +03:00
type Version byte
// A Variant represents a UUID's variant.
2011-08-08 21:45:10 +03:00
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.
2011-08-08 21:45:10 +03:00
)
var rander = rand.Reader // random function
// Parse decodes s into a UUID or returns an error. Both the UUID form of
2011-08-08 21:45:10 +03:00
// xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx and
// urn:uuid:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx are decoded.
func Parse(s string) (UUID, error) {
var uuid UUID
if len(s) != 36 {
if len(s) != 36+9 {
return uuid, fmt.Errorf("invalid UUID length: %d", len(s))
}
2011-08-08 21:45:10 +03:00
if strings.ToLower(s[:9]) != "urn:uuid:" {
return uuid, fmt.Errorf("invalid urn prefix: %q", s[:9])
2011-08-08 21:45:10 +03:00
}
s = s[9:]
}
if s[8] != '-' || s[13] != '-' || s[18] != '-' || s[23] != '-' {
return uuid, errors.New("invalid UUID format")
2011-08-08 21:45:10 +03:00
}
for i, x := range [16]int{
2011-08-08 21:45:10 +03:00
0, 2, 4, 6,
9, 11,
14, 16,
19, 21,
24, 26, 28, 30, 32, 34} {
optimize internal parsing function -- avoid unsafe []byte parsing Making xtob take two byte arguments avoids a lot of slicing. This makes the Parse function faster. In addition, because so much slicing is avoiding, duplicating the parse logic to ParseBytes resulted in the function being faster than Parse (<1ns). The BenchmarkParseBytesNative function has been removed (parseBytes was identical to ParseBytes). And a new benchmark, BenchmarkParseBytesUnsafe, has been added to benchmark the old way of parsing []byte (which is slightly slower than Parse and thus the new ParseBytes implementation). benchmark old ns/op new ns/op delta BenchmarkUUID_MarshalJSON-4 685 667 -2.63% BenchmarkUUID_UnmarshalJSON-4 1145 1162 +1.48% BenchmarkParse-4 61.6 56.5 -8.28% BenchmarkParseBytes-4 65.7 55.9 -14.92% BenchmarkParseBytesCopy-4 121 115 -4.96% BenchmarkNew-4 1665 1643 -1.32% BenchmarkUUID_String-4 112 113 +0.89% BenchmarkUUID_URN-4 117 119 +1.71% benchmark old allocs new allocs delta BenchmarkUUID_MarshalJSON-4 4 4 +0.00% BenchmarkUUID_UnmarshalJSON-4 2 2 +0.00% BenchmarkParse-4 0 0 +0.00% BenchmarkParseBytes-4 0 0 +0.00% BenchmarkParseBytesCopy-4 1 1 +0.00% BenchmarkNew-4 1 1 +0.00% BenchmarkUUID_String-4 1 1 +0.00% BenchmarkUUID_URN-4 1 1 +0.00% benchmark old bytes new bytes delta BenchmarkUUID_MarshalJSON-4 248 248 +0.00% BenchmarkUUID_UnmarshalJSON-4 248 248 +0.00% BenchmarkParse-4 0 0 +0.00% BenchmarkParseBytes-4 0 0 +0.00% BenchmarkParseBytesCopy-4 48 48 +0.00% BenchmarkNew-4 16 16 +0.00% BenchmarkUUID_String-4 48 48 +0.00% BenchmarkUUID_URN-4 48 48 +0.00%
2016-02-26 07:36:20 +02:00
if v, ok := xtob(s[x], s[x+1]); !ok {
return uuid, errors.New("invalid UUID format")
2011-08-08 21:45:10 +03:00
} else {
uuid[i] = v
}
}
return uuid, nil
2011-08-08 21:45:10 +03:00
}
// ParseBytes is like Parse, except it parses a byte slice instead of a string.
func ParseBytes(b []byte) (UUID, error) {
optimize internal parsing function -- avoid unsafe []byte parsing Making xtob take two byte arguments avoids a lot of slicing. This makes the Parse function faster. In addition, because so much slicing is avoiding, duplicating the parse logic to ParseBytes resulted in the function being faster than Parse (<1ns). The BenchmarkParseBytesNative function has been removed (parseBytes was identical to ParseBytes). And a new benchmark, BenchmarkParseBytesUnsafe, has been added to benchmark the old way of parsing []byte (which is slightly slower than Parse and thus the new ParseBytes implementation). benchmark old ns/op new ns/op delta BenchmarkUUID_MarshalJSON-4 685 667 -2.63% BenchmarkUUID_UnmarshalJSON-4 1145 1162 +1.48% BenchmarkParse-4 61.6 56.5 -8.28% BenchmarkParseBytes-4 65.7 55.9 -14.92% BenchmarkParseBytesCopy-4 121 115 -4.96% BenchmarkNew-4 1665 1643 -1.32% BenchmarkUUID_String-4 112 113 +0.89% BenchmarkUUID_URN-4 117 119 +1.71% benchmark old allocs new allocs delta BenchmarkUUID_MarshalJSON-4 4 4 +0.00% BenchmarkUUID_UnmarshalJSON-4 2 2 +0.00% BenchmarkParse-4 0 0 +0.00% BenchmarkParseBytes-4 0 0 +0.00% BenchmarkParseBytesCopy-4 1 1 +0.00% BenchmarkNew-4 1 1 +0.00% BenchmarkUUID_String-4 1 1 +0.00% BenchmarkUUID_URN-4 1 1 +0.00% benchmark old bytes new bytes delta BenchmarkUUID_MarshalJSON-4 248 248 +0.00% BenchmarkUUID_UnmarshalJSON-4 248 248 +0.00% BenchmarkParse-4 0 0 +0.00% BenchmarkParseBytes-4 0 0 +0.00% BenchmarkParseBytesCopy-4 48 48 +0.00% BenchmarkNew-4 16 16 +0.00% BenchmarkUUID_String-4 48 48 +0.00% BenchmarkUUID_URN-4 48 48 +0.00%
2016-02-26 07:36:20 +02:00
var uuid UUID
if len(b) != 36 {
if len(b) != 36+9 {
return uuid, fmt.Errorf("invalid UUID length: %d", len(b))
}
if !bytes.Equal(bytes.ToLower(b[:9]), []byte("urn:uuid:")) {
return uuid, fmt.Errorf("invalid urn prefix: %q", b[:9])
}
b = b[9:]
}
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} {
if v, ok := xtob(b[x], b[x+1]); !ok {
return uuid, errors.New("invalid UUID format")
} else {
uuid[i] = v
}
}
return uuid, nil
2011-08-08 21:45:10 +03:00
}
2017-11-10 22:33:01 +02:00
// 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
}
2011-08-08 21:45:10 +03:00
// 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[:])
2011-08-08 21:45:10 +03:00
}
// 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[:])
2011-08-08 21:45:10 +03:00
}
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:])
2011-08-08 21:45:10 +03:00
}
// Variant returns the variant encoded in uuid.
2011-08-08 21:45:10 +03:00
func (uuid UUID) Variant() Variant {
switch {
case (uuid[8] & 0xc0) == 0x80:
return RFC4122
case (uuid[8] & 0xe0) == 0xc0:
return Microsoft
2011-08-08 21:45:10 +03:00
case (uuid[8] & 0xe0) == 0xe0:
return Future
2011-08-08 21:45:10 +03:00
default:
return Reserved
2011-08-08 21:45:10 +03:00
}
}
// Version returns the version of uuid.
func (uuid UUID) Version() Version {
return Version(uuid[6] >> 4)
2011-08-08 21:45:10 +03:00
}
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"
2011-08-08 21:45:10 +03:00
}
return fmt.Sprintf("BadVariant%d", int(v))
2011-08-08 21:45:10 +03:00
}
2017-07-28 14:14:46 +02:00
// SetRand sets the random number generator to r, which implements io.Reader.
2011-08-08 21:45:10 +03:00
// 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
}