blob: aa1dba3a28434ee436b4ce7595d463fbfbb2ee01 [file] [log] [blame]
// 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 uniqueid defines functions that are likely to generate
// globally unique identifiers. We want to be able to generate many
// Ids quickly, so we make a time/space tradeoff. We reuse the same
// random data many times with a counter appended. Note: these Ids are
// NOT useful as a security mechanism as they will be predictable.
package uniqueid
import (
"crypto/rand"
"encoding/binary"
"fmt"
"strings"
"sync"
)
var random = RandomGenerator{}
func (id Id) String() string {
return fmt.Sprintf("0x%x", [16]byte(id))
}
// Valid returns true if the given Id is valid.
func Valid(id Id) bool {
return id != Id{}
}
func FromHexString(s string) (Id, error) {
var id Id
var slice []byte
if strings.HasPrefix(s, "0x") {
s = s[2:]
}
if _, err := fmt.Sscanf(s, "%x", &slice); err != nil {
return id, err
}
if len(slice) != len(id) {
// Ideally we would generate a verror error here, but Go
// complains about the import cycle: verror, vtrace, and
// uniqueid. In most languages the linker would just pull in
// all three implementations, but Go conflates implementations
// and their interfaces, so cannot be sure that this isn't an
// interface definition cycle, and thus gives up.
return id, fmt.Errorf("Cannot convert %s to Id, size mismatch.", s)
}
copy(id[:], slice)
return id, nil
}
// A RandomGenerator can generate random Ids.
// The zero value of RandomGenerator is ready to use.
type RandomGenerator struct {
mu sync.Mutex
id Id
count uint16
}
// NewId produces a new probably unique identifier.
func (g *RandomGenerator) NewID() (Id, error) {
g.mu.Lock()
defer g.mu.Unlock()
if g.count > 0x7fff || g.count == uint16(0) {
g.count = 0
// Either the generator is uninitialized or the counter
// has wrapped. We need a new random prefix.
if _, err := rand.Read(g.id[:14]); err != nil {
return Id{}, err
}
}
binary.BigEndian.PutUint16(g.id[14:], g.count)
g.count++
g.id[14] |= 0x80 // Use this bit as a reserved bit (set to 1) to support future format changes.
return g.id, nil
}
// Random produces a new probably unique identifier using the RandomGenerator.
func Random() (Id, error) {
return random.NewID()
}