Платформа ЦРНП "Мирокод" для разработки проектов
https://git.mirocod.ru
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
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 |
|
}
|
|
|