From 9e951e1b0753e70359075742c99e505e94e97895 Mon Sep 17 00:00:00 2001 From: Paul Borman Date: Fri, 19 Feb 2016 12:30:25 -0800 Subject: [PATCH] Change a UUID from []byte to [16]byte along with other API changes. --- CONTRIBUTING.md | 10 ++ CONTRIBUTORS | 8 ++ README.md | 5 + dce.go | 34 +++-- doc.go | 10 +- hash.go | 24 ++-- json_test.go | 11 +- marshal.go | 42 ++++++ node.go | 48 +++---- seq_test.go | 4 +- sql.go | 26 ++-- sql_test.go | 26 ++-- time.go | 31 ++--- util.go | 2 +- uuid.go | 97 +++++--------- uuid_test.go | 331 +++++++++++++++++++++++++----------------------- version1.go | 29 +++-- version4.go | 10 +- 18 files changed, 395 insertions(+), 353 deletions(-) create mode 100644 CONTRIBUTING.md create mode 100644 marshal.go diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..04fdf09 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,10 @@ +# How to contribute + +We definitely welcome patches and contribution to this project! + +### Legal requirements + +In order to protect both you and ourselves, you will need to sign the +[Contributor License Agreement](https://cla.developers.google.com/clas). + +You may have already signed it for other Google projects. diff --git a/CONTRIBUTORS b/CONTRIBUTORS index b382a04..b4bb97f 100644 --- a/CONTRIBUTORS +++ b/CONTRIBUTORS @@ -1 +1,9 @@ Paul Borman +bmatsuo +shawnps +theory +jboverfelt +dsymonds +cd1 +wallclockbuilder +dansouza diff --git a/README.md b/README.md index 7e176d9..d5cc534 100644 --- a/README.md +++ b/README.md @@ -3,6 +3,11 @@ This project was automatically exported from code.google.com/p/go-uuid # uuid ![build status](https://travis-ci.org/google/uuid.svg?branch=master) The uuid package generates and inspects UUIDs based on [RFC 412](http://tools.ietf.org/html/rfc4122) and DCE 1.1: Authentication and Security Services. +This package is based on the github.com/pborman/uuid package (previously named +code.google.com/p/go-uuid). It differs from these earlier packages in that +a UUID is a 16 byte array rather than a byte slice. One loss due to this +change is the ability to represent an invalid UUID (vs a NIL UUID). + ###### Install `go get github.com/google/uuid` diff --git a/dce.go b/dce.go index 50a0f2d..8523659 100755 --- a/dce.go +++ b/dce.go @@ -1,4 +1,4 @@ -// Copyright 2011 Google Inc. All rights reserved. +// 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. @@ -29,21 +29,21 @@ const ( // // For a given domain/id pair the same token may be returned for up to // 7 minutes and 10 seconds. -func NewDCESecurity(domain Domain, id uint32) UUID { - uuid := NewUUID() - if uuid != nil { +func NewDCESecurity(domain Domain, id uint32) (UUID, error) { + uuid, err := NewUUID() + if err == nil { uuid[6] = (uuid[6] & 0x0f) | 0x20 // Version 2 uuid[9] = byte(domain) binary.BigEndian.PutUint32(uuid[0:], id) } - return uuid + return uuid, err } // NewDCEPerson returns a DCE Security (Version 2) UUID in the person // domain with the id returned by os.Getuid. // // NewDCEPerson(Person, uint32(os.Getuid())) -func NewDCEPerson() UUID { +func NewDCEPerson() (UUID, error) { return NewDCESecurity(Person, uint32(os.Getuid())) } @@ -51,24 +51,20 @@ func NewDCEPerson() UUID { // domain with the id returned by os.Getgid. // // NewDCEGroup(Group, uint32(os.Getgid())) -func NewDCEGroup() UUID { +func NewDCEGroup() (UUID, error) { return NewDCESecurity(Group, uint32(os.Getgid())) } -// Domain returns the domain for a Version 2 UUID or false. -func (uuid UUID) Domain() (Domain, bool) { - if v, _ := uuid.Version(); v != 2 { - return 0, false - } - return Domain(uuid[9]), true +// Domain returns the domain for a Version 2 UUID. Domains are only defined +// for Version 2 UUIDs. +func (uuid UUID) Domain() Domain { + return Domain(uuid[9]) } -// Id returns the id for a Version 2 UUID or false. -func (uuid UUID) Id() (uint32, bool) { - if v, _ := uuid.Version(); v != 2 { - return 0, false - } - return binary.BigEndian.Uint32(uuid[0:4]), true +// Id returns the id for a Version 2 UUID. Ids are only defined for Vrsion 2 +// UUIDs. +func (uuid UUID) Id() uint32 { + return binary.BigEndian.Uint32(uuid[0:4]) } func (d Domain) String() string { diff --git a/doc.go b/doc.go index d8bd013..5b8a4b9 100755 --- a/doc.go +++ b/doc.go @@ -1,8 +1,12 @@ -// Copyright 2011 Google Inc. All rights reserved. +// 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. -// The uuid package generates and inspects UUIDs. +// Package uuid generates and inspects UUIDs. // -// UUIDs are based on RFC 4122 and DCE 1.1: Authentication and Security Services. +// UUIDs are based on RFC 4122 and DCE 1.1: Authentication and Security +// Services. +// +// A UUID is a 16 byte (128 bit) array. UUIDs may be used as keys to +// maps or compared directly. package uuid diff --git a/hash.go b/hash.go index a0420c1..56db407 100644 --- a/hash.go +++ b/hash.go @@ -1,4 +1,4 @@ -// Copyright 2011 Google Inc. All rights reserved. +// 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. @@ -10,13 +10,13 @@ import ( "hash" ) -// Well known Name Space IDs and UUIDs +// Well known namespace IDs and UUIDs var ( - NameSpace_DNS = Parse("6ba7b810-9dad-11d1-80b4-00c04fd430c8") - NameSpace_URL = Parse("6ba7b811-9dad-11d1-80b4-00c04fd430c8") - NameSpace_OID = Parse("6ba7b812-9dad-11d1-80b4-00c04fd430c8") - NameSpace_X500 = Parse("6ba7b814-9dad-11d1-80b4-00c04fd430c8") - NIL = Parse("00000000-0000-0000-0000-000000000000") + NameSpace_DNS = MustParse("6ba7b810-9dad-11d1-80b4-00c04fd430c8") + NameSpace_URL = MustParse("6ba7b811-9dad-11d1-80b4-00c04fd430c8") + NameSpace_OID = MustParse("6ba7b812-9dad-11d1-80b4-00c04fd430c8") + NameSpace_X500 = MustParse("6ba7b814-9dad-11d1-80b4-00c04fd430c8") + NIL UUID // empty UUID, all zeros ) // NewHash returns a new UUID derived from the hash of space concatenated with @@ -26,18 +26,18 @@ var ( // NewMD5 and NewSHA1. func NewHash(h hash.Hash, space UUID, data []byte, version int) UUID { h.Reset() - h.Write(space) + h.Write(space[:]) h.Write([]byte(data)) s := h.Sum(nil) - uuid := make([]byte, 16) - copy(uuid, s) + var uuid UUID + copy(uuid[:], s) uuid[6] = (uuid[6] & 0x0f) | uint8((version&0xf)<<4) uuid[8] = (uuid[8] & 0x3f) | 0x80 // RFC 4122 variant return uuid } // NewMD5 returns a new MD5 (Version 3) UUID based on the -// supplied name space and data. +// supplied name space and data. It is the same as calling: // // NewHash(md5.New(), space, data, 3) func NewMD5(space UUID, data []byte) UUID { @@ -45,7 +45,7 @@ func NewMD5(space UUID, data []byte) UUID { } // NewSHA1 returns a new SHA1 (Version 5) UUID based on the -// supplied name space and data. +// supplied name space and data. It is the same as calling: // // NewHash(sha1.New(), space, data, 5) func NewSHA1(space UUID, data []byte) UUID { diff --git a/json_test.go b/json_test.go index 2866b8d..4140cee 100644 --- a/json_test.go +++ b/json_test.go @@ -1,4 +1,4 @@ -// Copyright 2014 Google Inc. All rights reserved. +// 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. @@ -10,7 +10,7 @@ import ( "testing" ) -var testUUID = Parse("f47ac10b-58cc-0372-8567-0e02b2c3d479") +var testUUID = MustParse("f47ac10b-58cc-0372-8567-0e02b2c3d479") func TestJSON(t *testing.T) { type S struct { @@ -35,9 +35,10 @@ func BenchmarkUUID_MarshalJSON(b *testing.B) { x := &struct { UUID UUID `json:"uuid"` }{} - x.UUID = Parse("f47ac10b-58cc-0372-8567-0e02b2c3d479") - if x.UUID == nil { - b.Fatal("invalid uuid") + var err error + x.UUID, err = Parse("f47ac10b-58cc-0372-8567-0e02b2c3d479") + if err != nil { + b.Fatal(err) } for i := 0; i < b.N; i++ { js, err := json.Marshal(x) diff --git a/marshal.go b/marshal.go new file mode 100644 index 0000000..435ca7c --- /dev/null +++ b/marshal.go @@ -0,0 +1,42 @@ +// 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. + +package uuid + +import ( + "fmt" + "unsafe" +) + +// MarshalText implements encoding.TextMarshaler. +func (u UUID) MarshalText() ([]byte, error) { + var js [36]byte + encodeHex(js[:], u) + return js[:], nil +} + +// UnmarshalText implements encoding.TextUnmarshaler. +func (u *UUID) UnmarshalText(data []byte) error { + // See comment in ParseBytes why we do this. + // id, err := ParseBytes(data) + id, err := Parse(*(*string)(unsafe.Pointer(&data))) + if err == nil { + *u = id + } + return err +} + +// MarshalBinary implements encoding.BinaryMarshaler. +func (u UUID) MarshalBinary() ([]byte, error) { + return u[:], nil +} + +// UnmarshalBinary implements encoding.BinaryUnmarshaler. +func (u *UUID) UnmarshalBinary(data []byte) error { + if len(data) != 16 { + return fmt.Errorf("invalid UUID (got %d bytes)", len(data)) + } + copy(u[:], data) + return nil +} diff --git a/node.go b/node.go index 42d60da..5f0156a 100755 --- a/node.go +++ b/node.go @@ -1,4 +1,4 @@ -// Copyright 2011 Google Inc. All rights reserved. +// 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. @@ -13,7 +13,8 @@ var ( nodeMu sync.Mutex interfaces []net.Interface // cached list of interfaces ifname string // name of interface being used - nodeID []byte // hardware for version 1 UUIDs + nodeID [6]byte // hardware for version 1 UUIDs + zeroID [6]byte // nodeID with only 0's ) // NodeInterface returns the name of the interface from which the NodeID was @@ -48,10 +49,9 @@ func setNodeInterface(name string) bool { for _, ifs := range interfaces { if len(ifs.HardwareAddr) >= 6 && (name == "" || name == ifs.Name) { - if setNodeID(ifs.HardwareAddr) { - ifname = ifs.Name - return true - } + copy(nodeID[:], ifs.HardwareAddr) + ifname = ifs.Name + return true } } @@ -59,10 +59,7 @@ func setNodeInterface(name string) bool { // does not specify a specific interface generate a random Node ID // (section 4.1.6) if name == "" { - if nodeID == nil { - nodeID = make([]byte, 6) - } - randomBits(nodeID) + randomBits(nodeID[:]) return true } return false @@ -73,35 +70,24 @@ func setNodeInterface(name string) bool { func NodeID() []byte { defer nodeMu.Unlock() nodeMu.Lock() - if nodeID == nil { + if nodeID == zeroID { setNodeInterface("") } - nid := make([]byte, 6) - copy(nid, nodeID) - return nid + nid := nodeID + return nid[:] } // SetNodeID sets the Node ID to be used for Version 1 UUIDs. The first 6 bytes // of id are used. If id is less than 6 bytes then false is returned and the // Node ID is not set. func SetNodeID(id []byte) bool { - defer nodeMu.Unlock() - nodeMu.Lock() - if setNodeID(id) { - ifname = "user" - return true - } - return false -} - -func setNodeID(id []byte) bool { if len(id) < 6 { return false } - if nodeID == nil { - nodeID = make([]byte, 6) - } - copy(nodeID, id) + defer nodeMu.Unlock() + nodeMu.Lock() + copy(nodeID[:], id) + ifname = "user" return true } @@ -111,7 +97,7 @@ func (uuid UUID) NodeID() []byte { if len(uuid) != 16 { return nil } - node := make([]byte, 6) - copy(node, uuid[10:]) - return node + var node [6]byte + copy(node[:], uuid[10:]) + return node[:] } diff --git a/seq_test.go b/seq_test.go index 3b3d143..e340f9d 100644 --- a/seq_test.go +++ b/seq_test.go @@ -1,4 +1,4 @@ -// Copyright 2014 Google Inc. All rights reserved. +// 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. @@ -42,7 +42,7 @@ func TestClockSeqRace(t *testing.T) { select { case <-done: return - case ch <- NewUUID(): + case ch <- MustNewUUID(): } } }() diff --git a/sql.go b/sql.go index d015bfd..528ad0d 100644 --- a/sql.go +++ b/sql.go @@ -1,4 +1,4 @@ -// Copyright 2015 Google Inc. All rights reserved. +// 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. @@ -6,7 +6,6 @@ package uuid import ( "database/sql/driver" - "errors" "fmt" ) @@ -21,14 +20,14 @@ func (uuid *UUID) Scan(src interface{}) error { return nil } - // see uuid.Parse for required string format - parsed := Parse(src.(string)) + // see Parse for required string format + u, err := Parse(src.(string)) - if parsed == nil { - return errors.New("Scan: invalid UUID format") + if err != nil { + return fmt.Errorf("Scan: %v", err) } - *uuid = parsed + *uuid = u case []byte: b := src.([]byte) @@ -39,17 +38,10 @@ func (uuid *UUID) Scan(src interface{}) error { // assumes a simple slice of bytes if 16 bytes // otherwise attempts to parse - if len(b) == 16 { - *uuid = UUID(b) - } else { - u := Parse(string(b)) - - if u == nil { - return errors.New("Scan: invalid UUID format") - } - - *uuid = u + if len(b) != 16 { + return uuid.Scan(string(b)) } + copy((*uuid)[:], b) default: return fmt.Errorf("Scan: unable to scan type %T into UUID", src) diff --git a/sql_test.go b/sql_test.go index 1030951..d53d58c 100644 --- a/sql_test.go +++ b/sql_test.go @@ -1,4 +1,4 @@ -// Copyright 2015 Google Inc. All rights reserved. +// 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. @@ -11,10 +11,13 @@ import ( func TestScan(t *testing.T) { var stringTest string = "f47ac10b-58cc-0372-8567-0e02b2c3d479" - var byteTest []byte = Parse(stringTest) var badTypeTest int = 6 var invalidTest string = "f47ac10b-58cc-0372-8567-0e02b2c3d4" + byteTest := make([]byte, 16) + byteTestUUID := MustParse(stringTest) + copy(byteTest, byteTestUUID[:]) + // sunny day tests var uuid UUID @@ -63,32 +66,35 @@ func TestScan(t *testing.T) { // empty tests - uuid = nil + uuid = UUID{} var emptySlice []byte err = (&uuid).Scan(emptySlice) if err != nil { t.Fatal(err) } - if uuid != nil { - t.Error("UUID was not nil after scanning empty byte slice") + for _, v := range uuid { + if v != 0 { + t.Error("UUID was not nil after scanning empty byte slice") + } } - uuid = nil + uuid = UUID{} var emptyString string err = (&uuid).Scan(emptyString) if err != nil { t.Fatal(err) } - - if uuid != nil { - t.Error("UUID was not nil after scanning empty string") + for _, v := range uuid { + if v != 0 { + t.Error("UUID was not nil after scanning empty byte slice") + } } } func TestValue(t *testing.T) { stringTest := "f47ac10b-58cc-0372-8567-0e02b2c3d479" - uuid := Parse(stringTest) + uuid := MustParse(stringTest) val, _ := uuid.Value() if val != stringTest { t.Error("Value() did not return expected string") diff --git a/time.go b/time.go index eedf242..9cf8624 100755 --- a/time.go +++ b/time.go @@ -1,4 +1,4 @@ -// Copyright 2014 Google Inc. All rights reserved. +// 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. @@ -70,10 +70,9 @@ func getTime() (Time, uint16, error) { // already set. The clock sequence is only used for Version 1 UUIDs. // // The uuid package does not use global static storage for the clock sequence or -// the last time a UUID was generated. Unless SetClockSequence a new random -// clock sequence is generated the first time a clock sequence is requested by -// ClockSequence, GetTime, or NewUUID. (section 4.2.1.1) sequence is generated -// for +// the last time a UUID was generated. Unless SetClockSequence is used, a new +// random clock sequence is generated the first time a clock sequence is +// requested by ClockSequence, GetTime, or NewUUID. (section 4.2.1.1) func ClockSequence() int { defer timeMu.Unlock() timeMu.Lock() @@ -109,24 +108,16 @@ func setClockSequence(seq int) { } // Time returns the time in 100s of nanoseconds since 15 Oct 1582 encoded in -// uuid. It returns false if uuid is not valid. The time is only well defined -// for version 1 and 2 UUIDs. -func (uuid UUID) Time() (Time, bool) { - if len(uuid) != 16 { - return 0, false - } +// uuid. The time is only defined for version 1 and 2 UUIDs. +func (uuid UUID) Time() Time { time := int64(binary.BigEndian.Uint32(uuid[0:4])) time |= int64(binary.BigEndian.Uint16(uuid[4:6])) << 32 time |= int64(binary.BigEndian.Uint16(uuid[6:8])&0xfff) << 48 - return Time(time), true + return Time(time) } -// ClockSequence returns the clock sequence encoded in uuid. It returns false -// if uuid is not valid. The clock sequence is only well defined for version 1 -// and 2 UUIDs. -func (uuid UUID) ClockSequence() (int, bool) { - if len(uuid) != 16 { - return 0, false - } - return int(binary.BigEndian.Uint16(uuid[8:10])) & 0x3fff, true +// ClockSequence returns the clock sequence encoded in uuid. +// The clock sequence is only well defined for version 1 and 2 UUIDs. +func (uuid UUID) ClockSequence() int { + return int(binary.BigEndian.Uint16(uuid[8:10])) & 0x3fff } diff --git a/util.go b/util.go index fc8e052..7eaecde 100644 --- a/util.go +++ b/util.go @@ -1,4 +1,4 @@ -// Copyright 2011 Google Inc. All rights reserved. +// 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. diff --git a/uuid.go b/uuid.go index 82c9e7e..5fd83c0 100644 --- a/uuid.go +++ b/uuid.go @@ -1,40 +1,27 @@ -// Copyright 2011 Google Inc. All rights reserved. +// 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. package uuid import ( - "bytes" "crypto/rand" "encoding/hex" + "errors" "fmt" "io" "strings" + "unsafe" ) -// Array is a pass-by-value UUID that can be used as an effecient key in a map. -type Array [16]byte - -// UUID converts uuid into a slice. -func (uuid Array) UUID() UUID { - return uuid[:] -} - -// String returns the string representation of uuid, -// xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx. -func (uuid Array) String() string { - return uuid.UUID().String() -} - // A UUID is a 128 bit (16 byte) Universal Unique IDentifier as defined in RFC // 4122. -type UUID []byte +type UUID [16]byte -// A Version represents a UUIDs version. +// A Version represents a UUID's version. type Version byte -// A Variant represents a UUIDs variant. +// A Variant represents a UUID's variant. type Variant byte // Constants returned by Variant. @@ -48,28 +35,23 @@ const ( var rander = rand.Reader // random function -// New returns a new random (version 4) UUID as a string. It is a convenience -// function for NewRandom().String(). -func New() string { - return NewRandom().String() -} - -// Parse decodes s into a UUID or returns nil. Both the UUID form of +// Parse decodes s into a UUID or returns an error. Both the UUID form of // xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx and // urn:uuid:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx are decoded. -func Parse(s string) UUID { - if len(s) == 36+9 { +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)) + } if strings.ToLower(s[:9]) != "urn:uuid:" { - return nil + return uuid, fmt.Errorf("invalid urn prefix: %q", s[:9]) } s = s[9:] - } else if len(s) != 36 { - return nil } if s[8] != '-' || s[13] != '-' || s[18] != '-' || s[23] != '-' { - return nil + return uuid, errors.New("invalid UUID format") } - var uuid [16]byte for i, x := range [16]int{ 0, 2, 4, 6, 9, 11, @@ -77,36 +59,35 @@ func Parse(s string) UUID { 19, 21, 24, 26, 28, 30, 32, 34} { if v, ok := xtob(s[x:]); !ok { - return nil + return uuid, errors.New("invalid UUID format") } else { uuid[i] = v } } - return uuid[:] + return uuid, nil } -// Equal returns true if uuid1 and uuid2 are equal. -func Equal(uuid1, uuid2 UUID) bool { - return bytes.Equal(uuid1, uuid2) +// ParseBytes is like Parse, exect it parses a byte slice instead of a string. +func ParseBytes(b []byte) (UUID, error) { + // Parsing a string is actually faster than parsing a byte slice as it + // is cheaper to slice a string. Further, it is not safe to convert + // a string into a byte slice but the opposite direction is. These + // stem from the fact that a byte slice is 3 words while a string + // is only 2 words. + return Parse(*(*string)(unsafe.Pointer(&b))) } -// Array returns an array representation of uuid that can be used as a map key. -// Array panics if uuid is not valid. -func (uuid UUID) Array() Array { - if len(uuid) != 16 { - panic("invalid uuid") +func MustParse(s string) UUID { + u, err := Parse(s) + if err != nil { + panic(err) } - var a Array - copy(a[:], uuid) - return a + return u } // String returns the string form of uuid, xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx // , or "" if uuid is invalid. func (uuid UUID) String() string { - if len(uuid) != 16 { - return "" - } var buf [36]byte encodeHex(buf[:], uuid) return string(buf[:]) @@ -115,9 +96,6 @@ func (uuid UUID) String() string { // 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 { - if len(uuid) != 16 { - return "" - } var buf [36 + 9]byte copy(buf[:], "urn:uuid:") encodeHex(buf[9:], uuid) @@ -136,12 +114,8 @@ func encodeHex(dst []byte, uuid UUID) { hex.Encode(dst[24:], uuid[10:]) } -// Variant returns the variant encoded in uuid. It returns Invalid if -// uuid is invalid. +// Variant returns the variant encoded in uuid. func (uuid UUID) Variant() Variant { - if len(uuid) != 16 { - return Invalid - } switch { case (uuid[8] & 0xc0) == 0x80: return RFC4122 @@ -154,13 +128,10 @@ func (uuid UUID) Variant() Variant { } } -// Version returns the version of uuid. It returns false if uuid is not +// Version returns the version of uuid. // valid. -func (uuid UUID) Version() (Version, bool) { - if len(uuid) != 16 { - return 0, false - } - return Version(uuid[6] >> 4), true +func (uuid UUID) Version() Version { + return Version(uuid[6] >> 4) } func (v Version) String() string { diff --git a/uuid_test.go b/uuid_test.go index cb1cd5c..cc961ad 100644 --- a/uuid_test.go +++ b/uuid_test.go @@ -1,4 +1,4 @@ -// Copyright 2011 Google Inc. All rights reserved. +// 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. @@ -6,6 +6,7 @@ package uuid import ( "bytes" + "errors" "fmt" "os" "strings" @@ -82,26 +83,41 @@ var constants = []struct { } func testTest(t *testing.T, in string, tt test) { - uuid := Parse(in) - if ok := (uuid != nil); ok != tt.isuuid { + uuid, err := Parse(in) + if ok := (err == nil); ok != tt.isuuid { t.Errorf("Parse(%s) got %v expected %v\b", in, ok, tt.isuuid) } - if uuid == nil { + if err != nil { return } if v := uuid.Variant(); v != tt.variant { t.Errorf("Variant(%s) got %d expected %d\b", in, v, tt.variant) } - if v, _ := uuid.Version(); v != tt.version { + if v := uuid.Version(); v != tt.version { t.Errorf("Version(%s) got %d expected %d\b", in, v, tt.version) } } +func testBytes(t *testing.T, in []byte, tt test) { + uuid, err := ParseBytes(in) + if ok := (err == nil); ok != tt.isuuid { + t.Errorf("ParseBytes(%s) got %v expected %v\b", in, ok, tt.isuuid) + } + if err != nil { + return + } + suuid, _ := Parse(string(in)) + if uuid != suuid { + t.Errorf("ParseBytes(%s) got %v expected %v\b", in, uuid, suuid) + } +} + func TestUUID(t *testing.T) { for _, tt := range tests { testTest(t, tt.in, tt) testTest(t, strings.ToUpper(tt.in), tt) + testBytes(t, []byte(tt.in), tt) } } @@ -120,13 +136,13 @@ func TestConstants(t *testing.T) { func TestRandomUUID(t *testing.T) { m := make(map[string]bool) for x := 1; x < 32; x++ { - uuid := NewRandom() + uuid := New() s := uuid.String() if m[s] { t.Errorf("NewRandom returned duplicated UUID %s", s) } m[s] = true - if v, _ := uuid.Version(); v != 4 { + if v := uuid.Version(); v != 4 { t.Errorf("Random UUID of version %s", v) } if uuid.Variant() != RFC4122 { @@ -136,19 +152,19 @@ func TestRandomUUID(t *testing.T) { } func TestNew(t *testing.T) { - m := make(map[string]bool) + m := make(map[UUID]bool) for x := 1; x < 32; x++ { s := New() if m[s] { t.Errorf("New returned duplicated UUID %s", s) } m[s] = true - uuid := Parse(s) - if uuid == nil { - t.Errorf("New returned %q which does not decode", s) + uuid, err := Parse(s.String()) + if err != nil { + t.Errorf("New.String() returned %q which does not decode", s) continue } - if v, _ := uuid.Version(); v != 4 { + if v := uuid.Version(); v != 4 { t.Errorf("Random UUID of version %s", v) } if uuid.Variant() != RFC4122 { @@ -157,14 +173,6 @@ func TestNew(t *testing.T) { } } -func clockSeq(t *testing.T, uuid UUID) int { - seq, ok := uuid.ClockSequence() - if !ok { - t.Fatalf("%s: invalid clock sequence", uuid) - } - return seq -} - func TestClockSeq(t *testing.T) { // Fake time.Now for this test to return a monotonically advancing time; restore it at end. defer func(orig func() time.Time) { timeNow = orig }(timeNow) @@ -175,29 +183,44 @@ func TestClockSeq(t *testing.T) { } SetClockSequence(-1) - uuid1 := NewUUID() - uuid2 := NewUUID() + uuid1, err := NewUUID() + if err != nil { + t.Fatalf("could not create UUID: %v", err) + } + uuid2, err := NewUUID() + if err != nil { + t.Fatalf("could not create UUID: %v", err) + } - if clockSeq(t, uuid1) != clockSeq(t, uuid2) { - t.Errorf("clock sequence %d != %d", clockSeq(t, uuid1), clockSeq(t, uuid2)) + if s1, s2 := uuid1.ClockSequence(), uuid2.ClockSequence(); s1 != s2 { + t.Errorf("clock sequence %d != %d", s1, s2) } SetClockSequence(-1) - uuid2 = NewUUID() + uuid2, err = NewUUID() + if err != nil { + t.Fatalf("could not create UUID: %v", err) + } // Just on the very off chance we generated the same sequence // two times we try again. - if clockSeq(t, uuid1) == clockSeq(t, uuid2) { + if uuid1.ClockSequence() == uuid2.ClockSequence() { SetClockSequence(-1) - uuid2 = NewUUID() + uuid2, err = NewUUID() + if err != nil { + t.Fatalf("could not create UUID: %v", err) + } } - if clockSeq(t, uuid1) == clockSeq(t, uuid2) { - t.Errorf("Duplicate clock sequence %d", clockSeq(t, uuid1)) + if s1, s2 := uuid1.ClockSequence(), uuid2.ClockSequence(); s1 == s2 { + t.Errorf("Duplicate clock sequence %d", s1) } SetClockSequence(0x1234) - uuid1 = NewUUID() - if seq := clockSeq(t, uuid1); seq != 0x1234 { + uuid1, err = NewUUID() + if err != nil { + t.Fatalf("could not create UUID: %v", err) + } + if seq := uuid1.ClockSequence(); seq != 0x1234 { t.Errorf("%s: expected seq 0x1234 got 0x%04x", uuid1, seq) } } @@ -219,23 +242,32 @@ func TestCoding(t *testing.T) { t.Errorf("%x: urn is %s, expected %s", data, v, urn) } - uuid := Parse(text) - if !Equal(uuid, data) { + uuid, err := Parse(text) + if err != nil { + t.Errorf("Parse returned unexpected error %v", err) + } + if data != data { t.Errorf("%s: decoded to %s, expected %s", text, uuid, data) } } func TestVersion1(t *testing.T) { - uuid1 := NewUUID() - uuid2 := NewUUID() + uuid1, err := NewUUID() + if err != nil { + t.Fatalf("could not create UUID: %v", err) + } + uuid2, err := NewUUID() + if err != nil { + t.Fatalf("could not create UUID: %v", err) + } - if Equal(uuid1, uuid2) { + if uuid1 == uuid2 { t.Errorf("%s:duplicate uuid", uuid1) } - if v, _ := uuid1.Version(); v != 1 { + if v := uuid1.Version(); v != 1 { t.Errorf("%s: version %s expected 1", uuid1, v) } - if v, _ := uuid2.Version(); v != 1 { + if v := uuid2.Version(); v != 1 { t.Errorf("%s: version %s expected 1", uuid2, v) } n1 := uuid1.NodeID() @@ -243,22 +275,10 @@ func TestVersion1(t *testing.T) { if !bytes.Equal(n1, n2) { t.Errorf("Different nodes %x != %x", n1, n2) } - t1, ok := uuid1.Time() - if !ok { - t.Errorf("%s: invalid time", uuid1) - } - t2, ok := uuid2.Time() - if !ok { - t.Errorf("%s: invalid time", uuid2) - } - q1, ok := uuid1.ClockSequence() - if !ok { - t.Errorf("%s: invalid clock sequence", uuid1) - } - q2, ok := uuid2.ClockSequence() - if !ok { - t.Errorf("%s: invalid clock sequence", uuid2) - } + t1 := uuid1.Time() + t2 := uuid2.Time() + q1 := uuid1.ClockSequence() + q2 := uuid2.ClockSequence() switch { case t1 == t2 && q1 == q2: @@ -315,18 +335,17 @@ func TestNode(t *testing.T) { func TestNodeAndTime(t *testing.T) { // Time is February 5, 1998 12:30:23.136364800 AM GMT - uuid := Parse("7d444840-9dc0-11d1-b245-5ffdce74fad2") + uuid, err := Parse("7d444840-9dc0-11d1-b245-5ffdce74fad2") + if err != nil { + t.Fatalf("Parser returned unexpected error %v", err) + } node := []byte{0x5f, 0xfd, 0xce, 0x74, 0xfa, 0xd2} - ts, ok := uuid.Time() - if ok { - c := time.Unix(ts.UnixTime()) - want := time.Date(1998, 2, 5, 0, 30, 23, 136364800, time.UTC) - if !c.Equal(want) { - t.Errorf("Got time %v, want %v", c, want) - } - } else { - t.Errorf("%s: bad time", uuid) + ts := uuid.Time() + c := time.Unix(ts.UnixTime()) + want := time.Date(1998, 2, 5, 0, 30, 23, 136364800, time.UTC) + if !c.Equal(want) { + t.Errorf("Got time %v, want %v", c, want) } if !bytes.Equal(node, uuid.NodeID()) { t.Errorf("Expected node %v got %v", node, uuid.NodeID()) @@ -376,35 +395,30 @@ func TestNodeID(t *testing.T) { } } -func testDCE(t *testing.T, name string, uuid UUID, domain Domain, id uint32) { - if uuid == nil { - t.Errorf("%s failed", name) +func testDCE(t *testing.T, name string, uuid UUID, err error, domain Domain, id uint32) { + if err != nil { + t.Errorf("%s failed: %v", name, err) return } - if v, _ := uuid.Version(); v != 2 { + if v := uuid.Version(); v != 2 { t.Errorf("%s: %s: expected version 2, got %s", name, uuid, v) return } - if v, ok := uuid.Domain(); !ok || v != domain { - if !ok { - t.Errorf("%s: %d: Domain failed", name, uuid) - } else { - t.Errorf("%s: %s: expected domain %d, got %d", name, uuid, domain, v) - } + if v := uuid.Domain(); v != domain { + t.Errorf("%s: %s: expected domain %d, got %d", name, uuid, domain, v) } - if v, ok := uuid.Id(); !ok || v != id { - if !ok { - t.Errorf("%s: %d: Id failed", name, uuid) - } else { - t.Errorf("%s: %s: expected id %d, got %d", name, uuid, id, v) - } + if v := uuid.Id(); v != id { + t.Errorf("%s: %s: expected id %d, got %d", name, uuid, id, v) } } func TestDCE(t *testing.T) { - testDCE(t, "NewDCESecurity", NewDCESecurity(42, 12345678), 42, 12345678) - testDCE(t, "NewDCEPerson", NewDCEPerson(), Person, uint32(os.Getuid())) - testDCE(t, "NewDCEGroup", NewDCEGroup(), Group, uint32(os.Getgid())) + uuid, err := NewDCESecurity(42, 12345678) + testDCE(t, "NewDCESecurity", uuid, err, 42, 12345678) + uuid, err = NewDCEPerson() + testDCE(t, "NewDCEPerson", uuid, err, Person, uint32(os.Getuid())) + uuid, err = NewDCEGroup() + testDCE(t, "NewDCEGroup", uuid, err, Group, uint32(os.Getgid())) } type badRand struct{} @@ -431,45 +445,86 @@ func TestBadRand(t *testing.T) { } } -func TestUUID_Array(t *testing.T) { - expect := Array{ - 0xf4, 0x7a, 0xc1, 0x0b, - 0x58, 0xcc, - 0x03, 0x72, - 0x85, 0x67, - 0x0e, 0x02, 0xb2, 0xc3, 0xd4, 0x79, - } - uuid := Parse("f47ac10b-58cc-0372-8567-0e02b2c3d479") - if uuid == nil { - t.Fatal("invalid uuid") - } - if uuid.Array() != expect { - t.Fatal("invalid array") +var asString = "f47ac10b-58cc-0372-8567-0e02b2c3d479" +var asBytes = []byte(asString) + +func BenchmarkParse(b *testing.B) { + for i := 0; i < b.N; i++ { + _, err := Parse(asString) + if err != nil { + b.Fatal(err) + } } } -func TestArray_UUID(t *testing.T) { - array := Array{ - 0xf4, 0x7a, 0xc1, 0x0b, - 0x58, 0xcc, - 0x03, 0x72, - 0x85, 0x67, - 0x0e, 0x02, 0xb2, 0xc3, 0xd4, 0x79, +func BenchmarkParseBytes(b *testing.B) { + for i := 0; i < b.N; i++ { + _, err := ParseBytes(asBytes) + if err != nil { + b.Fatal(err) + } } - expect := Parse("f47ac10b-58cc-0372-8567-0e02b2c3d479") - if expect == nil { - t.Fatal("invalid uuid") +} + +// parseBytesCopy is to benchmark not using unsafe. +func parseBytesCopy(b []byte) (UUID, error) { + return Parse(string(b)) +} + + +// xtobb converts the the first two hex bytes of x into a byte. +func xtobb(x []byte) (byte, bool) { + b1 := xvalues[x[0]] + b2 := xvalues[x[1]] + return (b1 << 4) | b2, b1 != 255 && b2 != 255 +} + +// parseBytes is the same as Parse, but with byte slices. It demonstrates +// that it is faster to convert the byte slice into a string and then parse +// than to parse the byte slice directly. +func parseBytes(s []byte) (UUID, error) { + var uuid UUID + if len(s) != 36 { + if len(s) != 36+9 { + return uuid, fmt.Errorf("invalid UUID length: %d", len(s)) + } + if !bytes.Equal(bytes.ToLower(s[:9]), []byte("urn:uuid:")) { + return uuid, fmt.Errorf("invalid urn prefix: %q", s[:9]) + } + s = s[9:] + } + 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} { + if v, ok := xtobb(s[x:]); !ok { + return uuid, errors.New("invalid UUID format") + } else { + uuid[i] = v + } } - if !bytes.Equal(array.UUID(), expect) { - t.Fatal("invalid uuid") + return uuid, nil +} + +func BenchmarkParseBytesNative(b *testing.B) { + for i := 0; i < b.N; i++ { + _, err := parseBytes(asBytes) + if err != nil { + b.Fatal(err) + } } } -func BenchmarkParse(b *testing.B) { +func BenchmarkParseBytesCopy(b *testing.B) { for i := 0; i < b.N; i++ { - uuid := Parse("f47ac10b-58cc-0372-8567-0e02b2c3d479") - if uuid == nil { - b.Fatal("invalid uuid") + _, err := parseBytesCopy(asBytes) + if err != nil { + b.Fatal(err) } } } @@ -481,9 +536,9 @@ func BenchmarkNew(b *testing.B) { } func BenchmarkUUID_String(b *testing.B) { - uuid := Parse("f47ac10b-58cc-0372-8567-0e02b2c3d479") - if uuid == nil { - b.Fatal("invalid uuid") + uuid, err := Parse("f47ac10b-58cc-0372-8567-0e02b2c3d479") + if err != nil { + b.Fatal(err) } for i := 0; i < b.N; i++ { if uuid.String() == "" { @@ -493,9 +548,9 @@ func BenchmarkUUID_String(b *testing.B) { } func BenchmarkUUID_URN(b *testing.B) { - uuid := Parse("f47ac10b-58cc-0372-8567-0e02b2c3d479") - if uuid == nil { - b.Fatal("invalid uuid") + uuid, err := Parse("f47ac10b-58cc-0372-8567-0e02b2c3d479") + if err != nil { + b.Fatal(err) } for i := 0; i < b.N; i++ { if uuid.URN() == "" { @@ -503,41 +558,3 @@ func BenchmarkUUID_URN(b *testing.B) { } } } - -func BenchmarkUUID_Array(b *testing.B) { - expect := Array{ - 0xf4, 0x7a, 0xc1, 0x0b, - 0x58, 0xcc, - 0x03, 0x72, - 0x85, 0x67, - 0x0e, 0x02, 0xb2, 0xc3, 0xd4, 0x79, - } - uuid := Parse("f47ac10b-58cc-0372-8567-0e02b2c3d479") - if uuid == nil { - b.Fatal("invalid uuid") - } - for i := 0; i < b.N; i++ { - if uuid.Array() != expect { - b.Fatal("invalid array") - } - } -} - -func BenchmarkArray_UUID(b *testing.B) { - array := Array{ - 0xf4, 0x7a, 0xc1, 0x0b, - 0x58, 0xcc, - 0x03, 0x72, - 0x85, 0x67, - 0x0e, 0x02, 0xb2, 0xc3, 0xd4, 0x79, - } - expect := Parse("f47ac10b-58cc-0372-8567-0e02b2c3d479") - if expect == nil { - b.Fatal("invalid uuid") - } - for i := 0; i < b.N; i++ { - if !bytes.Equal(array.UUID(), expect) { - b.Fatal("invalid uuid") - } - } -} diff --git a/version1.go b/version1.go index 0127eac..17c018a 100644 --- a/version1.go +++ b/version1.go @@ -1,4 +1,4 @@ -// Copyright 2011 Google Inc. All rights reserved. +// 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. @@ -14,18 +14,21 @@ import ( // be set NewUUID returns nil. If clock sequence has not been set by // SetClockSequence then it will be set automatically. If GetTime fails to // return the current NewUUID returns nil. -func NewUUID() UUID { - if nodeID == nil { - SetNodeInterface("") +// +// In most cases, New should be used. +func NewUUID() (UUID, error) { + nodeMu.Lock() + if nodeID == zeroID { + setNodeInterface("") } + nodeMu.Unlock() + var uuid UUID now, seq, err := GetTime() if err != nil { - return nil + return uuid, err } - uuid := make([]byte, 16) - time_low := uint32(now & 0xffffffff) time_mid := uint16((now >> 32) & 0xffff) time_hi := uint16((now >> 48) & 0x0fff) @@ -35,7 +38,17 @@ func NewUUID() UUID { binary.BigEndian.PutUint16(uuid[4:], time_mid) binary.BigEndian.PutUint16(uuid[6:], time_hi) binary.BigEndian.PutUint16(uuid[8:], seq) - copy(uuid[10:], nodeID) + copy(uuid[10:], nodeID[:]) + + return uuid, nil +} +// MustNewUUID returns the Verison 1 UUID from calling NewUUID, or panics +// if NewUUID fails. +func MustNewUUID() UUID { + uuid, err := NewUUID() + if err != nil { + panic(err) + } return uuid } diff --git a/version4.go b/version4.go index b3d4a36..913d019 100644 --- a/version4.go +++ b/version4.go @@ -1,10 +1,10 @@ -// Copyright 2011 Google Inc. All rights reserved. +// 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. package uuid -// Random returns a Random (Version 4) UUID or panics. +// New returns a Random (Version 4) UUID or panics. // // The strength of the UUIDs is based on the strength of the crypto/rand // package. @@ -16,9 +16,9 @@ package uuid // means the probability is about 0.00000000006 (6 × 10−11), // equivalent to the odds of creating a few tens of trillions of UUIDs in a // year and having one duplicate. -func NewRandom() UUID { - uuid := make([]byte, 16) - randomBits([]byte(uuid)) +func New() UUID { + var uuid UUID + randomBits([]byte(uuid[:])) uuid[6] = (uuid[6] & 0x0f) | 0x40 // Version 4 uuid[8] = (uuid[8] & 0x3f) | 0x80 // Variant is 10 return uuid