You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

146 lines
3.7 KiB

package uuid
/***************
* Date: 14/02/14
* Time: 7:44 PM
***************/
import (
"crypto/md5"
"crypto/rand"
"crypto/sha1"
"encoding/binary"
"log"
seed "math/rand"
"net"
)
const (
length = 16
// 3F used by RFC4122 although 1F works for all
variantSet = 0x3F
// rather than using 0xC0 we use 0xE0 to retrieve the variant
// The result is the same for all other variants
// 0x80 and 0xA0 are used to identify RFC4122 compliance
variantGet = 0xE0
)
var (
// nodeID is the default Namespace node
nodeId = []byte{
// 00.192.79.212.48.200
0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8,
}
// The following standard UUIDs are for use with V3 or V5 UUIDs.
NamespaceDNS = &Struct{0x6ba7b810, 0x9dad, 0x11d1, 0x80, 0xb4, nodeId, length}
NamespaceURL = &Struct{0x6ba7b811, 0x9dad, 0x11d1, 0x80, 0xb4, nodeId, length}
NamespaceOID = &Struct{0x6ba7b812, 0x9dad, 0x11d1, 0x80, 0xb4, nodeId, length}
NamespaceX500 = &Struct{0x6ba7b814, 0x9dad, 0x11d1, 0x80, 0xb4, nodeId, length}
state State
)
func init() {
seed.Seed((int64(timestamp())^int64(gregorianToUNIXOffset))*0x6ba7b814<<0x6ba7b812 | 1391463463)
state = State{
randomNode: true,
randomSequence: true,
past: Timestamp((1391463463 * 10000000) + (100 * 10) + gregorianToUNIXOffset),
node: nodeId,
sequence: uint16(seed.Int()) & 0x3FFF,
saver: nil,
}
}
// NewV1 will generate a new RFC4122 version 1 UUID
func NewV1() UUID {
state.Lock()
defer state.Unlock()
now := currentUUIDTimestamp()
state.read(now, currentUUIDNodeId())
state.persist()
return formatV1(now, uint16(1), ReservedRFC4122, state.node)
}
// NewV3 will generate a new RFC4122 version 3 UUID
// V3 is based on the MD5 hash of a namespace identifier UUID and
// any type which implements the UniqueName interface for the name.
// For strings and slices cast to a Name type
func NewV3(pNs UUID, pName UniqueName) UUID {
o := new(Array)
// Set all bits to MD5 hash generated from namespace and name.
Digest(o, pNs, pName, md5.New())
o.setRFC4122Variant()
o.setVersion(3)
return o
}
// NewV4 will generate a new RFC4122 version 4 UUID
// A cryptographically secure random UUID.
func NewV4() UUID {
o := new(Array)
// Read random values (or pseudo-randomly) into Array type.
_, err := rand.Read(o[:length])
if err != nil {
panic(err)
}
o.setRFC4122Variant()
o.setVersion(4)
return o
}
// NewV5 will generate a new RFC4122 version 5 UUID
// Generate a UUID based on the SHA-1 hash of a namespace
// identifier and a name.
func NewV5(pNs UUID, pName UniqueName) UUID {
o := new(Array)
Digest(o, pNs, pName, sha1.New())
o.setRFC4122Variant()
o.setVersion(5)
return o
}
// either generates a random node when there is an error or gets
// the pre initialised one
func currentUUIDNodeId() (node net.HardwareAddr) {
if state.randomNode {
b := make([]byte, 16+6)
_, err := rand.Read(b)
if err != nil {
log.Println("UUID.currentUUIDNodeId error:", err)
node = nodeId
return
}
h := sha1.New()
h.Write(b)
binary.Write(h, binary.LittleEndian, state.sequence)
node = h.Sum(nil)[:6]
if err != nil {
log.Println("UUID.currentUUIDNodeId error:", err)
node = nodeId
return
}
// Mark as randomly generated
node[0] |= 0x01
} else {
node = state.node
}
return
}
// Unmarshal data into struct for V1 UUIDs
func formatV1(pNow Timestamp, pVersion uint16, pVariant byte, pNode []byte) UUID {
o := new(Struct)
o.timeLow = uint32(pNow & 0xFFFFFFFF)
o.timeMid = uint16((pNow >> 32) & 0xFFFF)
o.timeHiAndVersion = uint16((pNow >> 48) & 0x0FFF)
o.timeHiAndVersion |= uint16(pVersion << 12)
o.sequenceLow = byte(state.sequence & 0xFF)
o.sequenceHiAndVariant = byte((state.sequence & 0x3F00) >> 8)
o.sequenceHiAndVariant |= pVariant
o.node = pNode
o.size = length
return o
}