| // Copyright 2015 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 |
| |
| 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 { |
| 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)) |
| } |