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

  1. package uuid
  2. /***************
  3. * Date: 14/02/14
  4. * Time: 7:44 PM
  5. ***************/
  6. import (
  7. "crypto/md5"
  8. "crypto/rand"
  9. "crypto/sha1"
  10. "encoding/binary"
  11. "log"
  12. seed "math/rand"
  13. "net"
  14. )
  15. const (
  16. length = 16
  17. // 3F used by RFC4122 although 1F works for all
  18. variantSet = 0x3F
  19. // rather than using 0xC0 we use 0xE0 to retrieve the variant
  20. // The result is the same for all other variants
  21. // 0x80 and 0xA0 are used to identify RFC4122 compliance
  22. variantGet = 0xE0
  23. )
  24. var (
  25. // nodeID is the default Namespace node
  26. nodeId = []byte{
  27. // 00.192.79.212.48.200
  28. 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8,
  29. }
  30. // The following standard UUIDs are for use with V3 or V5 UUIDs.
  31. NamespaceDNS = &Struct{0x6ba7b810, 0x9dad, 0x11d1, 0x80, 0xb4, nodeId, length}
  32. NamespaceURL = &Struct{0x6ba7b811, 0x9dad, 0x11d1, 0x80, 0xb4, nodeId, length}
  33. NamespaceOID = &Struct{0x6ba7b812, 0x9dad, 0x11d1, 0x80, 0xb4, nodeId, length}
  34. NamespaceX500 = &Struct{0x6ba7b814, 0x9dad, 0x11d1, 0x80, 0xb4, nodeId, length}
  35. state State
  36. )
  37. func init() {
  38. seed.Seed((int64(timestamp())^int64(gregorianToUNIXOffset))*0x6ba7b814<<0x6ba7b812 | 1391463463)
  39. state = State{
  40. randomNode: true,
  41. randomSequence: true,
  42. past: Timestamp((1391463463 * 10000000) + (100 * 10) + gregorianToUNIXOffset),
  43. node: nodeId,
  44. sequence: uint16(seed.Int()) & 0x3FFF,
  45. saver: nil,
  46. }
  47. }
  48. // NewV1 will generate a new RFC4122 version 1 UUID
  49. func NewV1() UUID {
  50. state.Lock()
  51. defer state.Unlock()
  52. now := currentUUIDTimestamp()
  53. state.read(now, currentUUIDNodeId())
  54. state.persist()
  55. return formatV1(now, uint16(1), ReservedRFC4122, state.node)
  56. }
  57. // NewV3 will generate a new RFC4122 version 3 UUID
  58. // V3 is based on the MD5 hash of a namespace identifier UUID and
  59. // any type which implements the UniqueName interface for the name.
  60. // For strings and slices cast to a Name type
  61. func NewV3(pNs UUID, pName UniqueName) UUID {
  62. o := new(Array)
  63. // Set all bits to MD5 hash generated from namespace and name.
  64. Digest(o, pNs, pName, md5.New())
  65. o.setRFC4122Variant()
  66. o.setVersion(3)
  67. return o
  68. }
  69. // NewV4 will generate a new RFC4122 version 4 UUID
  70. // A cryptographically secure random UUID.
  71. func NewV4() UUID {
  72. o := new(Array)
  73. // Read random values (or pseudo-randomly) into Array type.
  74. _, err := rand.Read(o[:length])
  75. if err != nil {
  76. panic(err)
  77. }
  78. o.setRFC4122Variant()
  79. o.setVersion(4)
  80. return o
  81. }
  82. // NewV5 will generate a new RFC4122 version 5 UUID
  83. // Generate a UUID based on the SHA-1 hash of a namespace
  84. // identifier and a name.
  85. func NewV5(pNs UUID, pName UniqueName) UUID {
  86. o := new(Array)
  87. Digest(o, pNs, pName, sha1.New())
  88. o.setRFC4122Variant()
  89. o.setVersion(5)
  90. return o
  91. }
  92. // either generates a random node when there is an error or gets
  93. // the pre initialised one
  94. func currentUUIDNodeId() (node net.HardwareAddr) {
  95. if state.randomNode {
  96. b := make([]byte, 16+6)
  97. _, err := rand.Read(b)
  98. if err != nil {
  99. log.Println("UUID.currentUUIDNodeId error:", err)
  100. node = nodeId
  101. return
  102. }
  103. h := sha1.New()
  104. h.Write(b)
  105. binary.Write(h, binary.LittleEndian, state.sequence)
  106. node = h.Sum(nil)[:6]
  107. if err != nil {
  108. log.Println("UUID.currentUUIDNodeId error:", err)
  109. node = nodeId
  110. return
  111. }
  112. // Mark as randomly generated
  113. node[0] |= 0x01
  114. } else {
  115. node = state.node
  116. }
  117. return
  118. }
  119. // Unmarshal data into struct for V1 UUIDs
  120. func formatV1(pNow Timestamp, pVersion uint16, pVariant byte, pNode []byte) UUID {
  121. o := new(Struct)
  122. o.timeLow = uint32(pNow & 0xFFFFFFFF)
  123. o.timeMid = uint16((pNow >> 32) & 0xFFFF)
  124. o.timeHiAndVersion = uint16((pNow >> 48) & 0x0FFF)
  125. o.timeHiAndVersion |= uint16(pVersion << 12)
  126. o.sequenceLow = byte(state.sequence & 0xFF)
  127. o.sequenceHiAndVariant = byte((state.sequence & 0x3F00) >> 8)
  128. o.sequenceHiAndVariant |= pVariant
  129. o.node = pNode
  130. o.size = length
  131. return o
  132. }