528 lines
14 KiB
Go
528 lines
14 KiB
Go
package uuid
|
|
|
|
import (
|
|
"bytes"
|
|
"crypto/md5"
|
|
"crypto/rand"
|
|
"crypto/sha1"
|
|
"encoding/binary"
|
|
"fmt"
|
|
"hash"
|
|
"log"
|
|
mrand "math/rand"
|
|
"net"
|
|
"os"
|
|
"sync"
|
|
"time"
|
|
)
|
|
|
|
var (
|
|
once = new(sync.Once)
|
|
generator = newGenerator(nil)
|
|
)
|
|
|
|
func init() {
|
|
seed := time.Now().UTC().UnixNano()
|
|
b := [8]byte{}
|
|
_, err := rand.Read(b[:])
|
|
if err == nil {
|
|
seed += int64(binary.BigEndian.Uint64(b[:]))
|
|
}
|
|
mrand.Seed(seed)
|
|
|
|
}
|
|
|
|
// Random provides a random number generator which reads into the given []byte, the package
|
|
// uses crypto/rand.Read by default. You can supply your own implementation or downgrade it
|
|
// to the match/rand package.
|
|
//
|
|
// The function is used by V4 UUIDs and for setting up V1 and V2 UUIDs in the
|
|
// Generator Init or Register* functions.
|
|
type Random func([]byte) (int, error)
|
|
|
|
// Next provides the next Timestamp value to be used by the next V1 or V2 UUID.
|
|
// The default uses the uuid.spinner which spins at a resolution of
|
|
// 100ns ticks and provides a spin resolution redundancy of 1024
|
|
// cycles. This ensures that the system is not too quick when
|
|
// generating V1 or V2 UUIDs. Each system requires a tuned Resolution to
|
|
// enhance performance.
|
|
type Next func() Timestamp
|
|
|
|
// Identifier provides the Node to be used during the life of a
|
|
// uuid.Generator. If it cannot be determined nil should be returned, the
|
|
// package will then provide a node identifier provided by the Random function.
|
|
// The default generator gets a MAC address from the first interface that is 'up' checking
|
|
// net.FlagUp.
|
|
type Identifier func() Node
|
|
|
|
// HandleRandomError provides the ability to manage a serious error that may be
|
|
// caused by accessing the standard crypto/rand library or the supplied uuid/Random
|
|
// function. Due to the rarity of this occurrence the error is swallowed by the
|
|
// uuid/NewV4 function which relies heavily on random numbers, the package will
|
|
// panic instead if an error occurs.
|
|
//
|
|
// You can change this behaviour by passing in your own uuid/HandleError
|
|
// function to a custom Generator. This function can attempt to fix the random
|
|
// number generator. If your uuid/HandleError returns true the generator will
|
|
// attempt to generate another V4 uuid. If another error occurs the function
|
|
// will return a fallback v4 uuid generated from the less random math/rand standard library.
|
|
//
|
|
// Waiting for system entropy may be all that is required in the initial error.
|
|
// If something more serious has occurred, handle appropriately using this function.
|
|
type HandleRandomError func([]byte, int, error) error
|
|
|
|
// Generator is used to create and monitor the running of V1 and V2, and V4
|
|
// UUIDs. It can be setup to take custom implementations for Timestamp, Node
|
|
// and Random number retrieval by providing those functions as required.
|
|
// You can also supply a uuid/Saver implementation for saving the state of the generator
|
|
// and you can also provide an error policy for V4 UUIDs and possible errors in the random
|
|
// number generator.
|
|
type Generator struct {
|
|
// Access to the store needs to be maintained
|
|
sync.Mutex
|
|
|
|
// Once ensures that the generator is only setup and initialised once.
|
|
// This will occur either when you explicitly call the
|
|
// uuid.Generator.Init function or when a V1 or V2 id is generated.
|
|
sync.Once
|
|
|
|
// Store contains the current values being used by the Generator.
|
|
*Store
|
|
|
|
// Identifier as per the type Identifier func() Node
|
|
Identifier
|
|
|
|
// HandleRandomError as per the type HandleError func(error) bool
|
|
HandleRandomError
|
|
|
|
// Next as per the type Next func() Timestamp
|
|
Next
|
|
|
|
// Random as per the type Random func([]byte) (int, error)
|
|
Random
|
|
|
|
// Saver provides a non-volatile store to save the state of the
|
|
// generator, the default is nil which will cause the timestamp
|
|
// clock sequence to populate with random data. You can register your
|
|
// own saver by using the uuid.RegisterSaver function or by creating
|
|
// your own uuid.Generator instance.
|
|
// UUIDs.
|
|
Saver
|
|
|
|
*log.Logger
|
|
}
|
|
|
|
// GeneratorConfig allows you to setup a new uuid.Generator using uuid.NewGenerator or RegisterGenerator. You can supply your own
|
|
// implementations for the random number generator Random, Identifier and Timestamp retrieval. You can also
|
|
// adjust the resolution of the default Timestamp spinner and supply your own
|
|
// error handler for crypto/rand failures.
|
|
type GeneratorConfig struct {
|
|
Saver
|
|
Next
|
|
Resolution uint
|
|
Identifier
|
|
Random
|
|
HandleRandomError
|
|
*log.Logger
|
|
}
|
|
|
|
// NewGenerator will create a new uuid.Generator with the given functions.
|
|
func NewGenerator(config *GeneratorConfig) (*Generator, error) {
|
|
return onceDo(newGenerator(config))
|
|
}
|
|
|
|
func onceDo(gen *Generator) (*Generator, error) {
|
|
var err error
|
|
gen.Do(func() {
|
|
err = gen.init()
|
|
if err != nil {
|
|
gen = nil
|
|
}
|
|
})
|
|
return gen, err
|
|
}
|
|
|
|
func newGenerator(config *GeneratorConfig) (gen *Generator) {
|
|
if config == nil {
|
|
config = new(GeneratorConfig)
|
|
}
|
|
gen = new(Generator)
|
|
if config.Next == nil {
|
|
if config.Resolution == 0 {
|
|
config.Resolution = defaultSpinResolution
|
|
}
|
|
gen.Next = (&spinner{
|
|
Resolution: config.Resolution,
|
|
Count: 0,
|
|
Timestamp: Now(),
|
|
now: Now,
|
|
}).next
|
|
} else {
|
|
gen.Next = config.Next
|
|
}
|
|
if config.Identifier == nil {
|
|
gen.Identifier = findFirstHardwareAddress
|
|
} else {
|
|
gen.Identifier = config.Identifier
|
|
}
|
|
if config.Random == nil {
|
|
gen.Random = rand.Read
|
|
} else {
|
|
gen.Random = config.Random
|
|
}
|
|
if config.HandleRandomError == nil {
|
|
gen.HandleRandomError = gen.runHandleError
|
|
} else {
|
|
gen.HandleRandomError = config.HandleRandomError
|
|
}
|
|
if config.Logger == nil {
|
|
gen.Logger = log.New(os.Stderr, "uuid: ", log.LstdFlags)
|
|
} else {
|
|
gen.Logger = config.Logger
|
|
}
|
|
gen.Saver = config.Saver
|
|
gen.Store = new(Store)
|
|
return
|
|
}
|
|
|
|
// RegisterGenerator will set the package generator with the given configuration
|
|
// Like uuid.Init this can only be called once. Any subsequent calls will have no
|
|
// effect. If you call this you do not need to call uuid.Init.
|
|
func RegisterGenerator(config *GeneratorConfig) (err error) {
|
|
notOnce := true
|
|
once.Do(func() {
|
|
generator, err = NewGenerator(config)
|
|
notOnce = false
|
|
return
|
|
})
|
|
if notOnce {
|
|
panic("uuid: Register* methods cannot be called more than once.")
|
|
}
|
|
return
|
|
}
|
|
|
|
func (o *Generator) read() {
|
|
|
|
// Save the state (current timestamp, clock sequence, and node ID)
|
|
// back to the stable store
|
|
if o.Saver != nil {
|
|
defer o.save()
|
|
}
|
|
|
|
// Obtain a lock
|
|
o.Lock()
|
|
defer o.Unlock()
|
|
|
|
// Get the current time as a 60-bit count of 100-nanosecond intervals
|
|
// since 00:00:00.00, 15 October 1582.
|
|
now := o.Next()
|
|
|
|
// If the last timestamp is later than
|
|
// the current timestamp, increment the clock sequence value.
|
|
if now <= o.Timestamp {
|
|
o.Sequence++
|
|
}
|
|
|
|
// Update the timestamp
|
|
o.Timestamp = now
|
|
}
|
|
|
|
func (o *Generator) init() error {
|
|
// From a system-wide shared stable store (e.g., a file), read the
|
|
// UUID generator state: the values of the timestamp, clock sequence,
|
|
// and node ID used to generate the last UUID.
|
|
var (
|
|
storage Store
|
|
err error
|
|
)
|
|
|
|
o.Lock()
|
|
defer o.Unlock()
|
|
|
|
if o.Saver != nil {
|
|
storage, err = o.Read()
|
|
if err != nil {
|
|
o.Saver = nil
|
|
}
|
|
}
|
|
|
|
// Get the current time as a 60-bit count of 100-nanosecond intervals
|
|
// since 00:00:00.00, 15 October 1582.
|
|
now := o.Next()
|
|
|
|
// Get the current node id
|
|
node := o.Identifier()
|
|
|
|
if node == nil {
|
|
o.Println("address error generating random node id")
|
|
|
|
node = make([]byte, 6)
|
|
n, err := o.Random(node)
|
|
if err != nil {
|
|
o.Printf("could not read random bytes into node - read [%d] %s", n, err)
|
|
return err
|
|
}
|
|
// Mark as randomly generated
|
|
node[0] |= 0x01
|
|
}
|
|
|
|
// If the state was unavailable (e.g., non-existent or corrupted), or
|
|
// the saved node ID is different than the current node ID, generate
|
|
// a random clock sequence value.
|
|
if o.Saver == nil || !bytes.Equal(storage.Node, node) {
|
|
|
|
// 4.1.5. Clock Sequence https://www.ietf.org/rfc/rfc4122.txt
|
|
//
|
|
// For UUID version 1, the clock sequence is used to help avoid
|
|
// duplicates that could arise when the clock is set backwards in time
|
|
// or if the node ID changes.
|
|
//
|
|
// If the clock is set backwards, or might have been set backwards
|
|
// (e.g., while the system was powered off), and the UUID generator can
|
|
// not be sure that no UUIDs were generated with timestamps larger than
|
|
// the value to which the clock was set, then the clock sequence has to
|
|
// be changed. If the previous value of the clock sequence is known, it
|
|
// can just be incremented; otherwise it should be set to a random or
|
|
// high-quality pseudo-random value.
|
|
|
|
// The clock sequence MUST be originally (i.e., once in the lifetime of
|
|
// a system) initialized to a random number to minimize the correlation
|
|
// across systems. This provides maximum protection against node
|
|
// identifiers that may move or switch from system to system rapidly.
|
|
// The initial value MUST NOT be correlated to the node identifier.
|
|
b := make([]byte, 2)
|
|
n, err := o.Random(b)
|
|
if err == nil {
|
|
storage.Sequence = Sequence(binary.BigEndian.Uint16(b))
|
|
o.Printf("initialised random sequence [%d]", storage.Sequence)
|
|
|
|
} else {
|
|
o.Printf("could not read random bytes into sequence - read [%d] %s", n, err)
|
|
return err
|
|
}
|
|
} else if now < storage.Timestamp {
|
|
// If the state was available, but the saved timestamp is later than
|
|
// the current timestamp, increment the clock sequence value.
|
|
storage.Sequence++
|
|
}
|
|
|
|
storage.Timestamp = now
|
|
storage.Node = node
|
|
|
|
o.Store = &storage
|
|
|
|
return nil
|
|
}
|
|
|
|
func (o *Generator) save() {
|
|
func(state *Generator) {
|
|
if state.Saver != nil {
|
|
state.Lock()
|
|
defer state.Unlock()
|
|
state.Save(*state.Store)
|
|
}
|
|
}(o)
|
|
}
|
|
|
|
// NewV1 generates a new RFC4122 version 1 UUID based on a 60 bit timestamp and
|
|
// node id.
|
|
func (o *Generator) NewV1() UUID {
|
|
o.read()
|
|
id := UUID{}
|
|
|
|
makeUuid(&id,
|
|
uint32(o.Timestamp),
|
|
uint16(o.Timestamp>>32),
|
|
uint16(o.Timestamp>>48),
|
|
uint16(o.Sequence),
|
|
o.Node)
|
|
|
|
id.setRFC4122Version(VersionOne)
|
|
return id
|
|
}
|
|
|
|
// ReadV1 will read a slice of UUIDs. Be careful with the set amount.
|
|
func (o *Generator) ReadV1(ids []UUID) {
|
|
for i := range ids {
|
|
ids[i] = o.NewV1()
|
|
}
|
|
}
|
|
|
|
// BulkV1 will return a slice of V1 UUIDs. Be careful with the set amount.
|
|
func (o *Generator) BulkV1(amount int) []UUID {
|
|
ids := make([]UUID, amount)
|
|
o.ReadV1(ids)
|
|
return ids
|
|
}
|
|
|
|
// NewV2 generates a new DCE version 2 UUID based on a 60 bit timestamp, node id
|
|
// and the id of the given Id type.
|
|
func (o *Generator) NewV2(idType SystemId) UUID {
|
|
o.read()
|
|
|
|
id := UUID{}
|
|
|
|
var osId uint32
|
|
|
|
switch idType {
|
|
case SystemIdUser:
|
|
osId = uint32(os.Getuid())
|
|
case SystemIdGroup:
|
|
osId = uint32(os.Getgid())
|
|
case SystemIdEffectiveUser:
|
|
osId = uint32(os.Geteuid())
|
|
case SystemIdEffectiveGroup:
|
|
osId = uint32(os.Getegid())
|
|
case SystemIdCallerProcess:
|
|
osId = uint32(os.Getpid())
|
|
case SystemIdCallerProcessParent:
|
|
osId = uint32(os.Getppid())
|
|
}
|
|
|
|
makeUuid(&id,
|
|
osId,
|
|
uint16(o.Timestamp>>32),
|
|
uint16(o.Timestamp>>48),
|
|
uint16(o.Sequence),
|
|
o.Node)
|
|
|
|
id[9] = byte(idType)
|
|
id.setRFC4122Version(VersionTwo)
|
|
return id
|
|
}
|
|
|
|
// NewV3 generates a new RFC4122 version 3 UUID based on the MD5 hash of a
|
|
// namespace UUID namespace Implementation UUID and one or more unique names.
|
|
func (o *Generator) NewV3(namespace Implementation, names ...interface{}) UUID {
|
|
id := UUID{}
|
|
id.unmarshal(digest(md5.New(), namespace.Bytes(), names...))
|
|
id.setRFC4122Version(VersionThree)
|
|
return id
|
|
}
|
|
|
|
func digest(hash hash.Hash, name []byte, names ...interface{}) []byte {
|
|
for _, v := range names {
|
|
switch t := v.(type) {
|
|
case string:
|
|
name = append(name, t...)
|
|
continue
|
|
case []byte:
|
|
name = append(name, t...)
|
|
continue
|
|
case *string:
|
|
name = append(name, (*t)...)
|
|
continue
|
|
}
|
|
if s, ok := v.(fmt.Stringer); ok {
|
|
name = append(name, s.String()...)
|
|
continue
|
|
}
|
|
panic(fmt.Sprintf("uuid: does not support type [%T] as a name for hashed UUIDs.", v))
|
|
}
|
|
hash.Write(name)
|
|
return hash.Sum(nil)
|
|
}
|
|
|
|
// NewV4 generates a cryptographically secure random RFC4122 version 4 UUID. If there is an error with the random
|
|
// number generator this will
|
|
func (o *Generator) NewV4() (id UUID) {
|
|
o.v4(&id)
|
|
return
|
|
}
|
|
|
|
// ReadV4 will read into a slice of UUIDs. Be careful with the set amount.
|
|
// Note: V4 UUIDs require sufficient entropy from the generator.
|
|
// If n == len(ids) err will be nil.
|
|
func (o *Generator) ReadV4(ids []UUID) {
|
|
for i := range ids {
|
|
id := UUID{}
|
|
o.v4(&id)
|
|
ids[i] = id
|
|
continue
|
|
}
|
|
return
|
|
}
|
|
|
|
// BulkV4 will return a slice of V4 UUIDs. Be careful with the set amount.
|
|
// Note: V4 UUIDs require sufficient entropy from the generator.
|
|
// If n == len(ids) err will be nil.
|
|
func (o *Generator) BulkV4(amount int) []UUID {
|
|
ids := make([]UUID, amount)
|
|
o.ReadV4(ids)
|
|
return ids
|
|
}
|
|
|
|
func (o *Generator) v4(id *UUID) {
|
|
n, err := o.Random(id[:])
|
|
if err != nil {
|
|
o.Printf("there was an error getting random bytes [%s]", err)
|
|
if err = o.HandleRandomError(id[:], n, err); err != nil {
|
|
panic(fmt.Sprintf("random number error - %s", err))
|
|
}
|
|
}
|
|
id.setRFC4122Version(VersionFour)
|
|
}
|
|
|
|
|
|
// NewV5 generates an RFC4122 version 5 UUID based on the SHA-1 hash of a
|
|
// namespace Implementation UUID and one or more unique names.
|
|
func (o *Generator) NewV5(namespace Implementation, names ...interface{}) UUID {
|
|
id := UUID{}
|
|
id.unmarshal(digest(sha1.New(), namespace.Bytes(), names...))
|
|
id.setRFC4122Version(VersionFive)
|
|
return id
|
|
}
|
|
|
|
// NewHash generate a UUID based on the given hash implementation. The hash will
|
|
// be of the given names. The version will be set to 0 for Unknown and the
|
|
// variant will be set to VariantFuture.
|
|
func (o *Generator) NewHash(hash hash.Hash, names ...interface{}) UUID {
|
|
id := UUID{}
|
|
id.unmarshal(digest(hash, []byte{}, names...))
|
|
id[versionIndex] &= 0x0f
|
|
id[versionIndex] |= uint8(0 << 4)
|
|
id[variantIndex] &= variantSet
|
|
id[variantIndex] |= VariantFuture
|
|
return id
|
|
}
|
|
|
|
func makeUuid(id *UUID, low uint32, mid, hiAndV, seq uint16, node Node) {
|
|
|
|
id[0] = byte(low >> 24)
|
|
id[1] = byte(low >> 16)
|
|
id[2] = byte(low >> 8)
|
|
id[3] = byte(low)
|
|
|
|
id[4] = byte(mid >> 8)
|
|
id[5] = byte(mid)
|
|
|
|
id[6] = byte(hiAndV >> 8)
|
|
id[7] = byte(hiAndV)
|
|
|
|
id[8] = byte(seq >> 8)
|
|
id[9] = byte(seq)
|
|
|
|
copy(id[10:], node)
|
|
}
|
|
|
|
func findFirstHardwareAddress() (node Node) {
|
|
interfaces, err := net.Interfaces()
|
|
if err == nil {
|
|
for _, i := range interfaces {
|
|
if i.Flags&net.FlagUp != 0 && bytes.Compare(i.HardwareAddr, nil) != 0 {
|
|
// Don't use random as we have a real address
|
|
node = Node(i.HardwareAddr)
|
|
break
|
|
}
|
|
}
|
|
}
|
|
return
|
|
}
|
|
|
|
func (o *Generator) runHandleError(id []byte, n int, err error) error {
|
|
o.Lock()
|
|
mrand.Read(id)
|
|
o.Unlock()
|
|
return nil
|
|
}
|