mirror of
https://codeberg.org/forgejo/forgejo.git
synced 2024-12-24 13:09:23 -05:00
147 lines
3.7 KiB
Go
147 lines
3.7 KiB
Go
|
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
|
||
|
}
|