uuid.go 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174
  1. package xorm
  2. import (
  3. "crypto/md5"
  4. "crypto/rand"
  5. "crypto/sha1"
  6. "encoding/binary"
  7. "fmt"
  8. "hash"
  9. "net"
  10. "time"
  11. )
  12. // The UUID represents Universally Unique IDentifier (which is 128 bit long).
  13. type UUID [16]byte
  14. var (
  15. // NIL is defined in RFC 4122 section 4.1.7.
  16. // The nil UUID is special form of UUID that is specified to have all 128 bits set to zero.
  17. NIL = &UUID{
  18. 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  19. 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  20. }
  21. // NameSpaceDNS assume name to be a fully-qualified domain name.
  22. // Declared in RFC 4122 Appendix C.
  23. NameSpaceDNS = &UUID{
  24. 0x6b, 0xa7, 0xb8, 0x10, 0x9d, 0xad, 0x11, 0xd1,
  25. 0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8,
  26. }
  27. // NameSpaceURL assume name to be a URL.
  28. // Declared in RFC 4122 Appendix C.
  29. NameSpaceURL = &UUID{
  30. 0x6b, 0xa7, 0xb8, 0x11, 0x9d, 0xad, 0x11, 0xd1,
  31. 0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8,
  32. }
  33. // NameSpaceOID assume name to be an ISO OID.
  34. // Declared in RFC 4122 Appendix C.
  35. NameSpaceOID = &UUID{
  36. 0x6b, 0xa7, 0xb8, 0x12, 0x9d, 0xad, 0x11, 0xd1,
  37. 0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8,
  38. }
  39. // NameSpaceX500 assume name to be a X.500 DN (in DER or a text output format).
  40. // Declared in RFC 4122 Appendix C.
  41. NameSpaceX500 = &UUID{
  42. 0x6b, 0xa7, 0xb8, 0x14, 0x9d, 0xad, 0x11, 0xd1,
  43. 0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8,
  44. }
  45. )
  46. // Version of the UUID represents a kind of subtype specifier.
  47. func (u *UUID) Version() int {
  48. return int(binary.BigEndian.Uint16(u[6:8]) >> 12)
  49. }
  50. // String returns the human readable form of the UUID.
  51. func (u *UUID) String() string {
  52. return fmt.Sprintf("%x-%x-%x-%x-%x", u[0:4], u[4:6], u[6:8], u[8:10], u[10:])
  53. }
  54. func (u *UUID) variantRFC4122() {
  55. u[8] = (u[8] & 0x3f) | 0x80
  56. }
  57. // NewV3 creates a new UUID with variant 3 as described in RFC 4122.
  58. // Variant 3 based namespace-uuid and name and MD-5 hash calculation.
  59. func NewV3(namespace *UUID, name []byte) *UUID {
  60. uuid := newByHash(md5.New(), namespace, name)
  61. uuid[6] = (uuid[6] & 0x0f) | 0x30
  62. return uuid
  63. }
  64. func newByHash(hash hash.Hash, namespace *UUID, name []byte) *UUID {
  65. hash.Write(namespace[:])
  66. hash.Write(name[:])
  67. var uuid UUID
  68. copy(uuid[:], hash.Sum(nil)[:16])
  69. uuid.variantRFC4122()
  70. return &uuid
  71. }
  72. type stamp [10]byte
  73. var (
  74. mac []byte
  75. requests chan bool
  76. answers chan stamp
  77. )
  78. const gregorianUnix = 122192928000000000 // nanoseconds between gregorion zero and unix zero
  79. func init() {
  80. mac = make([]byte, 6)
  81. rand.Read(mac)
  82. requests = make(chan bool)
  83. answers = make(chan stamp)
  84. go unique()
  85. i, err := net.Interfaces()
  86. if err != nil {
  87. return
  88. }
  89. for _, d := range i {
  90. if len(d.HardwareAddr) == 6 {
  91. mac = d.HardwareAddr[:6]
  92. return
  93. }
  94. }
  95. }
  96. // NewV1 creates a new UUID with variant 1 as described in RFC 4122.
  97. // Variant 1 is based on hosts MAC address and actual timestamp (as count of 100-nanosecond intervals since
  98. // 00:00:00.00, 15 October 1582 (the date of Gregorian reform to the Christian calendar).
  99. func NewV1() *UUID {
  100. var uuid UUID
  101. requests <- true
  102. s := <-answers
  103. copy(uuid[:4], s[4:])
  104. copy(uuid[4:6], s[2:4])
  105. copy(uuid[6:8], s[:2])
  106. uuid[6] = (uuid[6] & 0x0f) | 0x10
  107. copy(uuid[8:10], s[8:])
  108. copy(uuid[10:], mac)
  109. uuid.variantRFC4122()
  110. return &uuid
  111. }
  112. func unique() {
  113. var (
  114. lastNanoTicks uint64
  115. clockSequence [2]byte
  116. )
  117. rand.Read(clockSequence[:])
  118. for range requests {
  119. var s stamp
  120. nanoTicks := uint64((time.Now().UTC().UnixNano() / 100) + gregorianUnix)
  121. if nanoTicks < lastNanoTicks {
  122. lastNanoTicks = nanoTicks
  123. rand.Read(clockSequence[:])
  124. } else if nanoTicks == lastNanoTicks {
  125. lastNanoTicks = nanoTicks + 1
  126. } else {
  127. lastNanoTicks = nanoTicks
  128. }
  129. binary.BigEndian.PutUint64(s[:], lastNanoTicks)
  130. copy(s[8:], clockSequence[:])
  131. answers <- s
  132. }
  133. }
  134. // NewV4 creates a new UUID with variant 4 as described in RFC 4122. Variant 4 based on pure random bytes.
  135. func NewV4() *UUID {
  136. buf := make([]byte, 16)
  137. rand.Read(buf)
  138. buf[6] = (buf[6] & 0x0f) | 0x40
  139. var uuid UUID
  140. copy(uuid[:], buf[:])
  141. uuid.variantRFC4122()
  142. return &uuid
  143. }
  144. // NewV5 creates a new UUID with variant 5 as described in RFC 4122.
  145. // Variant 5 based namespace-uuid and name and SHA-1 hash calculation.
  146. func NewV5(namespaceUUID *UUID, name []byte) *UUID {
  147. uuid := newByHash(sha1.New(), namespaceUUID, name)
  148. uuid[6] = (uuid[6] & 0x0f) | 0x50
  149. return uuid
  150. }
  151. // NewNamespaceUUID creates a namespace UUID by using the namespace name in the NIL name space.
  152. // This is a different approach as the 4 "standard" namespace UUIDs which are timebased UUIDs (V1).
  153. func NewNamespaceUUID(namespace string) *UUID {
  154. return NewV5(NIL, []byte(namespace))
  155. }