forkjo/vendor/github.com/twinj/uuid/state.go

138 lines
3.4 KiB
Go
Raw Normal View History

package uuid
/****************
* Date: 14/02/14
* Time: 7:43 PM
***************/
import (
"bytes"
"log"
seed "math/rand"
"net"
"sync"
)
// **************************************************** State
func SetupCustomStateSaver(pSaver StateSaver) {
state.Lock()
pSaver.Init(&state)
state.init()
state.Unlock()
}
// Holds package information about the current
// state of the UUID generator
type State struct {
// A flag which informs whether to
// randomly create a node id
randomNode bool
// A flag which informs whether to
// randomly create the sequence
randomSequence bool
// the last time UUID was saved
past Timestamp
// the next time the state will be saved
next Timestamp
// the last node which saved a UUID
node []byte
// An iterated value to help ensure different
// values across the same domain
sequence uint16
sync.Mutex
// save state interface
saver StateSaver
}
// Changes the state with current data
// Compares the current found node to the last node stored,
// If they are the same or randomSequence is already set due
// to an earlier read issue then the sequence is randomly generated
// else if there is an issue with the time the sequence is incremented
func (o *State) read(pNow Timestamp, pNode net.HardwareAddr) {
if bytes.Equal([]byte(pNode), o.node) || o.randomSequence {
o.sequence = uint16(seed.Int()) & 0x3FFF
} else if pNow < o.past {
o.sequence++
}
o.past = pNow
o.node = pNode
}
func (o *State) persist() {
if o.saver != nil {
o.saver.Save(o)
}
}
// Initialises the UUID state when the package is first loaded
// it first attempts to decode the file state into State
// if this file does not exist it will create the file and do a flush
// of the random state which gets loaded at package runtime
// second it will attempt to resolve the current hardware address nodeId
// thirdly it will check the state of the clock
func (o *State) init() {
if o.saver != nil {
intfcs, err := net.Interfaces()
if err != nil {
log.Println("uuid.State.init: address error: will generate random node id instead", err)
return
}
a := getHardwareAddress(intfcs)
if a == nil {
log.Println("uuid.State.init: address error: will generate random node id instead", err)
return
}
// Don't use random as we have a real address
o.randomSequence = false
if bytes.Equal([]byte(a), state.node) {
state.sequence++
}
state.node = a
state.randomNode = false
}
}
func getHardwareAddress(pInterfaces []net.Interface) net.HardwareAddr {
for _, inter := range pInterfaces {
// Initially I could multicast out the Flags to get
// whether the interface was up but started failing
if (inter.Flags & (1 << net.FlagUp)) != 0 {
//if inter.Flags.String() != "0" {
if addrs, err := inter.Addrs(); err == nil {
for _, addr := range addrs {
if addr.String() != "0.0.0.0" && !bytes.Equal([]byte(inter.HardwareAddr), make([]byte, len(inter.HardwareAddr))) {
return inter.HardwareAddr
}
}
}
}
}
return nil
}
// *********************************************** StateSaver interface
// Use this interface to setup a custom state saver if you wish to have
// v1 UUIDs based on your node id and constant time.
type StateSaver interface {
// Init is run if Setup() is false
// Init should setup the system to save the state
Init(*State)
// Save saves the state and is called only if const V1Save and
// Setup() is true
Save(*State)
}