Merge pull request #16 from theory/driver.Valuer
Add support for driver.Valuer.
diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 0000000..a6a98db
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,10 @@
+language: go
+
+go:
+ - 1.4.3
+ - 1.5.3
+ - release
+ - tip
+
+script:
+ - go test -v ./...
diff --git a/README.md b/README.md
index 0c31f01..f023d47 100644
--- a/README.md
+++ b/README.md
@@ -1,9 +1,8 @@
This project was automatically exported from code.google.com/p/go-uuid
-# uuid
+# uuid ![build status](https://travis-ci.org/pborman/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.
-
###### Install
`go get github.com/pborman/uuid`
@@ -12,5 +11,3 @@
Full `go doc` style documentation for the package can be viewed online without installing this package by using the GoDoc site here:
http://godoc.org/github.com/pborman/uuid
-
-
diff --git a/json.go b/json.go
index 760580a..9dda1df 100644
--- a/json.go
+++ b/json.go
@@ -7,17 +7,21 @@
import "errors"
func (u UUID) MarshalJSON() ([]byte, error) {
- if len(u) == 0 {
+ if len(u) != 16 {
return []byte(`""`), nil
}
- return []byte(`"` + u.String() + `"`), nil
+ var js [38]byte
+ js[0] = '"'
+ encodeHex(js[1:], u)
+ js[37] = '"'
+ return js[:], nil
}
func (u *UUID) UnmarshalJSON(data []byte) error {
- if len(data) == 0 || string(data) == `""` {
+ if string(data) == `""` {
return nil
}
- if len(data) < 2 || data[0] != '"' || data[len(data)-1] != '"' {
+ if data[0] != '"' {
return errors.New("invalid UUID format")
}
data = data[1 : len(data)-1]
diff --git a/json_test.go b/json_test.go
index b5eae09..2866b8d 100644
--- a/json_test.go
+++ b/json_test.go
@@ -30,3 +30,32 @@
t.Errorf("got %#v, want %#v", s2, s1)
}
}
+
+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")
+ }
+ for i := 0; i < b.N; i++ {
+ js, err := json.Marshal(x)
+ if err != nil {
+ b.Fatalf("marshal json: %#v (%v)", js, err)
+ }
+ }
+}
+
+func BenchmarkUUID_UnmarshalJSON(b *testing.B) {
+ js := []byte(`{"uuid":"f47ac10b-58cc-0372-8567-0e02b2c3d479"}`)
+ var x *struct {
+ UUID UUID `json:"uuid"`
+ }
+ for i := 0; i < b.N; i++ {
+ err := json.Unmarshal(js, &x)
+ if err != nil {
+ b.Fatalf("marshal json: %#v (%v)", js, err)
+ }
+ }
+}
diff --git a/node.go b/node.go
index dd0a8ac..42d60da 100755
--- a/node.go
+++ b/node.go
@@ -4,9 +4,13 @@
package uuid
-import "net"
+import (
+ "net"
+ "sync"
+)
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
@@ -16,6 +20,8 @@
// derived. The interface "user" is returned if the NodeID was set by
// SetNodeID.
func NodeInterface() string {
+ defer nodeMu.Unlock()
+ nodeMu.Lock()
return ifname
}
@@ -26,6 +32,12 @@
//
// SetNodeInterface never fails when name is "".
func SetNodeInterface(name string) bool {
+ defer nodeMu.Unlock()
+ nodeMu.Lock()
+ return setNodeInterface(name)
+}
+
+func setNodeInterface(name string) bool {
if interfaces == nil {
var err error
interfaces, err = net.Interfaces()
@@ -59,8 +71,10 @@
// NodeID returns a slice of a copy of the current Node ID, setting the Node ID
// if not already set.
func NodeID() []byte {
+ defer nodeMu.Unlock()
+ nodeMu.Lock()
if nodeID == nil {
- SetNodeInterface("")
+ setNodeInterface("")
}
nid := make([]byte, 6)
copy(nid, nodeID)
@@ -71,6 +85,8 @@
// 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
diff --git a/sql_test.go b/sql_test.go
index 8cf695b..1030951 100644
--- a/sql_test.go
+++ b/sql_test.go
@@ -14,7 +14,8 @@
var byteTest []byte = Parse(stringTest)
var badTypeTest int = 6
var invalidTest string = "f47ac10b-58cc-0372-8567-0e02b2c3d4"
- var invalidByteTest []byte = Parse(invalidTest)
+
+ // sunny day tests
var uuid UUID
err := (&uuid).Scan(stringTest)
@@ -32,6 +33,8 @@
t.Fatal(err)
}
+ // bad type tests
+
err = (&uuid).Scan(badTypeTest)
if err == nil {
t.Error("int correctly parsed and shouldn't have")
@@ -40,6 +43,8 @@
t.Error("attempting to parse an int returned an incorrect error message")
}
+ // invalid/incomplete uuids
+
err = (&uuid).Scan(invalidTest)
if err == nil {
t.Error("invalid uuid was parsed without error")
@@ -48,13 +53,37 @@
t.Error("attempting to parse an invalid UUID returned an incorrect error message")
}
- err = (&uuid).Scan(invalidByteTest)
+ err = (&uuid).Scan(byteTest[:len(byteTest)-2])
if err == nil {
t.Error("invalid byte uuid was parsed without error")
}
if !strings.Contains(err.Error(), "invalid UUID") {
t.Error("attempting to parse an invalid byte UUID returned an incorrect error message")
}
+
+ // empty tests
+
+ uuid = nil
+ 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")
+ }
+
+ uuid = nil
+ 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")
+ }
}
func TestValue(t *testing.T) {
diff --git a/time.go b/time.go
index 7ebc9be..eedf242 100755
--- a/time.go
+++ b/time.go
@@ -23,7 +23,7 @@
)
var (
- mu sync.Mutex
+ timeMu sync.Mutex
lasttime uint64 // last time we returned
clock_seq uint16 // clock sequence for this run
@@ -43,8 +43,8 @@
// clock sequence as well as adjusting the clock sequence as needed. An error
// is returned if the current time cannot be determined.
func GetTime() (Time, uint16, error) {
- defer mu.Unlock()
- mu.Lock()
+ defer timeMu.Unlock()
+ timeMu.Lock()
return getTime()
}
@@ -75,8 +75,8 @@
// ClockSequence, GetTime, or NewUUID. (section 4.2.1.1) sequence is generated
// for
func ClockSequence() int {
- defer mu.Unlock()
- mu.Lock()
+ defer timeMu.Unlock()
+ timeMu.Lock()
return clockSequence()
}
@@ -90,8 +90,8 @@
// SetClockSeq sets the clock sequence to the lower 14 bits of seq. Setting to
// -1 causes a new sequence to be generated.
func SetClockSequence(seq int) {
- defer mu.Unlock()
- mu.Lock()
+ defer timeMu.Unlock()
+ timeMu.Lock()
setClockSequence(seq)
}
diff --git a/util.go b/util.go
index de40b10..fc8e052 100644
--- a/util.go
+++ b/util.go
@@ -16,7 +16,7 @@
}
// xvalues returns the value of a byte as a hexadecimal digit or 255.
-var xvalues = []byte{
+var xvalues = [256]byte{
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
diff --git a/uuid.go b/uuid.go
old mode 100755
new mode 100644
index 38e085b..c4482cd
--- a/uuid.go
+++ b/uuid.go
@@ -7,6 +7,7 @@
import (
"bytes"
"crypto/rand"
+ "encoding/hex"
"fmt"
"io"
"strings"
@@ -54,8 +55,8 @@
if s[8] != '-' || s[13] != '-' || s[18] != '-' || s[23] != '-' {
return nil
}
- uuid := make([]byte, 16)
- for i, x := range []int{
+ var uuid [16]byte
+ for i, x := range [16]int{
0, 2, 4, 6,
9, 11,
14, 16,
@@ -67,7 +68,7 @@
uuid[i] = v
}
}
- return uuid
+ return uuid[:]
}
// Equal returns true if uuid1 and uuid2 are equal.
@@ -78,23 +79,36 @@
// String returns the string form of uuid, xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
// , or "" if uuid is invalid.
func (uuid UUID) String() string {
- if uuid == nil || len(uuid) != 16 {
+ if len(uuid) != 16 {
return ""
}
- b := []byte(uuid)
- return fmt.Sprintf("%08x-%04x-%04x-%04x-%012x",
- b[:4], b[4:6], b[6:8], b[8:10], b[10:])
+ 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 {
- if uuid == nil || len(uuid) != 16 {
+ if len(uuid) != 16 {
return ""
}
- b := []byte(uuid)
- return fmt.Sprintf("urn:uuid:%08x-%04x-%04x-%04x-%012x",
- b[:4], b[4:6], b[6:8], b[8:10], b[10:])
+ 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. It returns Invalid if
@@ -113,7 +127,6 @@
default:
return Reserved
}
- panic("unreachable")
}
// Version returns the version of uuid. It returns false if uuid is not
diff --git a/uuid_test.go b/uuid_test.go
old mode 100755
new mode 100644
index 417ebeb..3835cc8
--- a/uuid_test.go
+++ b/uuid_test.go
@@ -112,7 +112,7 @@
t.Errorf("%x: %v: not a stringer", x, v)
} else if s := v.String(); s != tt.name {
v, _ := tt.c.(int)
- t.Errorf("%x: Constant %T:%d gives %q, expected %q\n", x, tt.c, v, s, tt.name)
+ t.Errorf("%x: Constant %T:%d gives %q, expected %q", x, tt.c, v, s, tt.name)
}
}
}
@@ -123,14 +123,14 @@
uuid := NewRandom()
s := uuid.String()
if m[s] {
- t.Errorf("NewRandom returned duplicated UUID %s\n", s)
+ t.Errorf("NewRandom returned duplicated UUID %s", s)
}
m[s] = true
if v, _ := uuid.Version(); v != 4 {
- t.Errorf("Random UUID of version %s\n", v)
+ t.Errorf("Random UUID of version %s", v)
}
if uuid.Variant() != RFC4122 {
- t.Errorf("Random UUID is variant %d\n", uuid.Variant())
+ t.Errorf("Random UUID is variant %d", uuid.Variant())
}
}
}
@@ -140,19 +140,19 @@
for x := 1; x < 32; x++ {
s := New()
if m[s] {
- t.Errorf("New returned duplicated UUID %s\n", 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\n", s)
+ t.Errorf("New returned %q which does not decode", s)
continue
}
if v, _ := uuid.Version(); v != 4 {
- t.Errorf("Random UUID of version %s\n", v)
+ t.Errorf("Random UUID of version %s", v)
}
if uuid.Variant() != RFC4122 {
- t.Errorf("Random UUID is variant %d\n", uuid.Variant())
+ t.Errorf("Random UUID is variant %d", uuid.Variant())
}
}
}
@@ -160,7 +160,7 @@
func clockSeq(t *testing.T, uuid UUID) int {
seq, ok := uuid.ClockSequence()
if !ok {
- t.Fatalf("%s: invalid clock sequence\n", uuid)
+ t.Fatalf("%s: invalid clock sequence", uuid)
}
return seq
}
@@ -179,7 +179,7 @@
uuid2 := NewUUID()
if clockSeq(t, uuid1) != clockSeq(t, uuid2) {
- t.Errorf("clock sequence %d != %d\n", clockSeq(t, uuid1), clockSeq(t, uuid2))
+ t.Errorf("clock sequence %d != %d", clockSeq(t, uuid1), clockSeq(t, uuid2))
}
SetClockSequence(-1)
@@ -192,13 +192,13 @@
uuid2 = NewUUID()
}
if clockSeq(t, uuid1) == clockSeq(t, uuid2) {
- t.Errorf("Duplicate clock sequence %d\n", clockSeq(t, uuid1))
+ t.Errorf("Duplicate clock sequence %d", clockSeq(t, uuid1))
}
SetClockSequence(0x1234)
uuid1 = NewUUID()
if seq := clockSeq(t, uuid1); seq != 0x1234 {
- t.Errorf("%s: expected seq 0x1234 got 0x%04x\n", uuid1, seq)
+ t.Errorf("%s: expected seq 0x1234 got 0x%04x", uuid1, seq)
}
}
@@ -213,15 +213,15 @@
0x5f, 0xfd, 0xce, 0x74, 0xfa, 0xd2,
}
if v := data.String(); v != text {
- t.Errorf("%x: encoded to %s, expected %s\n", data, v, text)
+ t.Errorf("%x: encoded to %s, expected %s", data, v, text)
}
if v := data.URN(); v != urn {
- t.Errorf("%x: urn is %s, expected %s\n", data, v, urn)
+ t.Errorf("%x: urn is %s, expected %s", data, v, urn)
}
uuid := Parse(text)
if !Equal(uuid, data) {
- t.Errorf("%s: decoded to %s, expected %s\n", text, uuid, data)
+ t.Errorf("%s: decoded to %s, expected %s", text, uuid, data)
}
}
@@ -230,30 +230,30 @@
uuid2 := NewUUID()
if Equal(uuid1, uuid2) {
- t.Errorf("%s:duplicate uuid\n", uuid1)
+ t.Errorf("%s:duplicate uuid", uuid1)
}
if v, _ := uuid1.Version(); v != 1 {
- t.Errorf("%s: version %s expected 1\n", uuid1, v)
+ t.Errorf("%s: version %s expected 1", uuid1, v)
}
if v, _ := uuid2.Version(); v != 1 {
- t.Errorf("%s: version %s expected 1\n", uuid2, v)
+ t.Errorf("%s: version %s expected 1", uuid2, v)
}
n1 := uuid1.NodeID()
n2 := uuid2.NodeID()
if !bytes.Equal(n1, n2) {
- t.Errorf("Different nodes %x != %x\n", n1, n2)
+ t.Errorf("Different nodes %x != %x", n1, n2)
}
t1, ok := uuid1.Time()
if !ok {
- t.Errorf("%s: invalid time\n", uuid1)
+ t.Errorf("%s: invalid time", uuid1)
}
t2, ok := uuid2.Time()
if !ok {
- t.Errorf("%s: invalid time\n", uuid2)
+ t.Errorf("%s: invalid time", uuid2)
}
q1, ok := uuid1.ClockSequence()
if !ok {
- t.Errorf("%s: invalid clock sequence\n", uuid1)
+ t.Errorf("%s: invalid clock sequence", uuid1)
}
q2, ok := uuid2.ClockSequence()
if !ok {
@@ -262,11 +262,53 @@
switch {
case t1 == t2 && q1 == q2:
- t.Errorf("time stopped\n")
+ t.Error("time stopped")
case t1 > t2 && q1 == q2:
- t.Errorf("time reversed\n")
+ t.Error("time reversed")
case t1 < t2 && q1 != q2:
- t.Errorf("clock sequence chaned unexpectedly\n")
+ t.Error("clock sequence chaned unexpectedly")
+ }
+}
+
+func TestNode(t *testing.T) {
+ // This test is mostly to make sure we don't leave nodeMu locked.
+ ifname = ""
+ if ni := NodeInterface(); ni != "" {
+ t.Errorf("NodeInterface got %q, want %q", ni, "")
+ }
+ if SetNodeInterface("xyzzy") {
+ t.Error("SetNodeInterface succeeded on a bad interface name")
+ }
+ if !SetNodeInterface("") {
+ t.Error("SetNodeInterface failed")
+ }
+ if ni := NodeInterface(); ni == "" {
+ t.Error("NodeInterface returned an empty string")
+ }
+
+ ni := NodeID()
+ if len(ni) != 6 {
+ t.Errorf("ni got %d bytes, want 6", len(ni))
+ }
+ hasData := false
+ for _, b := range ni {
+ if b != 0 {
+ hasData = true
+ }
+ }
+ if !hasData {
+ t.Error("nodeid is all zeros")
+ }
+
+ id := []byte{1,2,3,4,5,6,7,8}
+ SetNodeID(id)
+ ni = NodeID()
+ if !bytes.Equal(ni, id[:6]) {
+ t.Errorf("got nodeid %v, want %v", ni, id[:6])
+ }
+
+ if ni := NodeInterface(); ni != "user" {
+ t.Errorf("got inteface %q, want %q", ni, "user")
}
}
@@ -284,10 +326,10 @@
t.Errorf("Got time %v, want %v", c, want)
}
} else {
- t.Errorf("%s: bad time\n", uuid)
+ t.Errorf("%s: bad time", uuid)
}
if !bytes.Equal(node, uuid.NodeID()) {
- t.Errorf("Expected node %v got %v\n", node, uuid.NodeID())
+ t.Errorf("Expected node %v got %v", node, uuid.NodeID())
}
}
@@ -295,7 +337,7 @@
uuid := NewMD5(NameSpace_DNS, []byte("python.org")).String()
want := "6fa459ea-ee8a-3ca4-894e-db77e160355e"
if uuid != want {
- t.Errorf("MD5: got %q expected %q\n", uuid, want)
+ t.Errorf("MD5: got %q expected %q", uuid, want)
}
}
@@ -303,7 +345,7 @@
uuid := NewSHA1(NameSpace_DNS, []byte("python.org")).String()
want := "886313e1-3b8a-5372-9b90-0c9aee199e5d"
if uuid != want {
- t.Errorf("SHA1: got %q expected %q\n", uuid, want)
+ t.Errorf("SHA1: got %q expected %q", uuid, want)
}
}
@@ -312,49 +354,49 @@
SetNodeInterface("")
s := NodeInterface()
if s == "" || s == "user" {
- t.Errorf("NodeInterface %q after SetInteface\n", s)
+ t.Errorf("NodeInterface %q after SetInteface", s)
}
node1 := NodeID()
if node1 == nil {
- t.Errorf("NodeID nil after SetNodeInterface\n", s)
+ t.Error("NodeID nil after SetNodeInterface", s)
}
SetNodeID(nid)
s = NodeInterface()
if s != "user" {
- t.Errorf("Expected NodeInterface %q got %q\n", "user", s)
+ t.Errorf("Expected NodeInterface %q got %q", "user", s)
}
node2 := NodeID()
if node2 == nil {
- t.Errorf("NodeID nil after SetNodeID\n", s)
+ t.Error("NodeID nil after SetNodeID", s)
}
if bytes.Equal(node1, node2) {
- t.Errorf("NodeID not changed after SetNodeID\n", s)
+ t.Error("NodeID not changed after SetNodeID", s)
} else if !bytes.Equal(nid, node2) {
- t.Errorf("NodeID is %x, expected %x\n", node2, nid)
+ t.Errorf("NodeID is %x, expected %x", node2, nid)
}
}
func testDCE(t *testing.T, name string, uuid UUID, domain Domain, id uint32) {
if uuid == nil {
- t.Errorf("%s failed\n", name)
+ t.Errorf("%s failed", name)
return
}
if v, _ := uuid.Version(); v != 2 {
- t.Errorf("%s: %s: expected version 2, got %s\n", name, uuid, v)
+ 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\n", name, uuid)
+ t.Errorf("%s: %d: Domain failed", name, uuid)
} else {
- t.Errorf("%s: %s: expected domain %d, got %d\n", name, uuid, domain, v)
+ 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\n", name, uuid)
+ t.Errorf("%s: %d: Id failed", name, uuid)
} else {
- t.Errorf("%s: %s: expected id %d, got %d\n", name, uuid, id, v)
+ t.Errorf("%s: %s: expected id %d, got %d", name, uuid, id, v)
}
}
}
@@ -379,12 +421,51 @@
uuid1 := New()
uuid2 := New()
if uuid1 != uuid2 {
- t.Errorf("execpted duplicates, got %q and %q\n", uuid1, uuid2)
+ t.Errorf("execpted duplicates, got %q and %q", uuid1, uuid2)
}
SetRand(nil)
uuid1 = New()
uuid2 = New()
if uuid1 == uuid2 {
- t.Errorf("unexecpted duplicates, got %q\n", uuid1)
+ t.Errorf("unexecpted duplicates, got %q", uuid1)
+ }
+}
+
+func BenchmarkParse(b *testing.B) {
+ for i := 0; i < b.N; i++ {
+ uuid := Parse("f47ac10b-58cc-0372-8567-0e02b2c3d479")
+ if uuid == nil {
+ b.Fatal("invalid uuid")
+ }
+ }
+}
+
+func BenchmarkNew(b *testing.B) {
+ for i := 0; i < b.N; i++ {
+ New()
+ }
+}
+
+func BenchmarkUUID_String(b *testing.B) {
+ uuid := Parse("f47ac10b-58cc-0372-8567-0e02b2c3d479")
+ if uuid == nil {
+ b.Fatal("invalid uuid")
+ }
+ for i := 0; i < b.N; i++ {
+ if uuid.String() == "" {
+ b.Fatal("invalid uuid")
+ }
+ }
+}
+
+func BenchmarkUUID_URN(b *testing.B) {
+ uuid := Parse("f47ac10b-58cc-0372-8567-0e02b2c3d479")
+ if uuid == nil {
+ b.Fatal("invalid uuid")
+ }
+ for i := 0; i < b.N; i++ {
+ if uuid.URN() == "" {
+ b.Fatal("invalid uuid")
+ }
}
}