Adding a UUID function that can be used to create sequential but random UUIDs.

Change-Id: Ie550e3c8ee13d7232733d88c8cdcdc12efef52b6
diff --git a/syncbase/key.go b/syncbase/key.go
index 34353af..362631c 100644
--- a/syncbase/key.go
+++ b/syncbase/key.go
@@ -4,7 +4,83 @@
 
 package syncbase
 
+import (
+	"bytes"
+	"crypto/rand"
+	"encoding/base64"
+	"encoding/binary"
+	"fmt"
+	"sync/atomic"
+	"time"
+)
+
+const (
+	// First character of UUID to make the UUID start with a valid identifier character.
+	// Can be used for versioning.
+	uuidPrefix string = "v"
+	// Legal characters of Java identifiers in ASCII-encoding order.
+	uuidCharacterSet string = "$0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz"
+)
+
+var (
+	invocationCounter uint32
+	base64Encoding    *base64.Encoding
+)
+
+func init() {
+	base64Encoding = base64.NewEncoding(uuidCharacterSet).WithPadding(base64.NoPadding)
+}
+
+// Resize string 's' to 'length'. The string will be padded with the first character from the
+// uuidCharacterSet if it is too short, and cut off if its too long.
+func resize(s string, length int) string {
+	switch {
+	case len(s) > length:
+		return s[len(s)-length:]
+	case len(s) == length:
+		return s
+	default:
+		for len(s) < length {
+			return uuidCharacterSet[:1] + s
+		}
+		return s
+	}
+}
+
+// UUID generates a sequential and pseudo-random key that can be used as an ID within
+// Syncbase. The generates IDs are of the format [a-zA-z][a-zA-Z0-9$_]{19}
+//
+// A UUID is made up of four parts:
+// - a prefix which gurantees that the generated UUID is a valid variable name in most programming
+//   languages
+// - a time component which uses nanosecond precision
+// - a counter component which is used to gurantee ordering between calls
+// - a random component which achieves pseude-randomness
 func UUID() string {
-	// TODO(kash): Implement me.
-	return ""
+	buffer := new(bytes.Buffer)
+
+	// Encode the current time in nanos as an base64-charater string.
+	// The maximum encoded length is 11 characters: 2^63/64^11 < 1
+	currentTime := time.Now().UnixNano()
+	binary.Write(buffer, binary.BigEndian, currentTime)
+	uuidTime := base64Encoding.EncodeToString(buffer.Bytes())
+
+	// Increment the global count of invocations and encde as a 3-character base64 string.
+	// Note that overflows here are fine.
+	buffer.Reset()
+	currentInvocation := atomic.AddUint32(&invocationCounter, 1)
+	binary.Write(buffer, binary.BigEndian, currentInvocation)
+	uuidInvocation := base64Encoding.EncodeToString(buffer.Next(4)[1:])
+
+	// Encode 4 bytes of randomness as base64-string (maximum of 6 characters)
+	random := make([]byte, 4)
+	rand.Read(random)
+	uuidRandNumber := base64Encoding.EncodeToString(random)
+
+	return fmt.Sprintf(
+		"%s%s%s%s",
+		resize(uuidPrefix, 1),
+		resize(uuidTime, 11),
+		resize(uuidInvocation, 2),
+		resize(uuidRandNumber, 6))
 }
diff --git a/syncbase/key_test.go b/syncbase/key_test.go
new file mode 100644
index 0000000..3ec9a3a
--- /dev/null
+++ b/syncbase/key_test.go
@@ -0,0 +1,66 @@
+// Copyright 2016 The Vanadium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package syncbase_test
+
+import (
+	"regexp"
+	"sort"
+	"testing"
+
+	"v.io/v23/syncbase"
+)
+
+const (
+	uuidLoopIncovations int = 100
+)
+
+func TestUUIDFormat(t *testing.T) {
+	uuid := syncbase.UUID()
+	regexp := regexp.MustCompile("^v[\\$0-9A-Za-z_]{19}$")
+	if !regexp.MatchString(uuid) {
+		t.Error("Incorrect UUID format")
+	}
+}
+
+func TestUUIDCollisions(t *testing.T) {
+	uuidChannel := make(chan string)
+
+	createUUID := func() {
+		uuidChannel <- syncbase.UUID()
+	}
+
+	for i := 0; i < uuidLoopIncovations; i++ {
+		go createUUID()
+	}
+
+	uuidMap := make(map[string]bool)
+
+	for i := 0; i < uuidLoopIncovations; i++ {
+		uuidMap[<-uuidChannel] = true
+	}
+
+	if len(uuidMap) != uuidLoopIncovations {
+		t.Errorf("UUID collision for %d UUIDs", uuidLoopIncovations-len(uuidMap))
+	}
+}
+
+func TestUUIDSequential(t *testing.T) {
+	unsorted := make([]string, 100)
+	sorted := make([]string, 100)
+
+	for i := 0; i < uuidLoopIncovations; i++ {
+		uuid := syncbase.UUID()
+		unsorted[i] = uuid
+		sorted[i] = uuid
+	}
+
+	sort.Strings(sorted)
+
+	for i := 0; i < uuidLoopIncovations; i++ {
+		if unsorted[i] != sorted[i] {
+			t.Errorf("UUID not sorted at position %d", i)
+		}
+	}
+}