forked from Gitlink/gitea-1120-rc1
138 lines
3.4 KiB
Go
138 lines
3.4 KiB
Go
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)
|
|
}
|
|
|