uuid.go 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178
  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) WithoutDashString() string {
  55. return fmt.Sprintf("%x", u[:])
  56. }
  57. func (u *UUID) variantRFC4122() {
  58. u[8] = (u[8] & 0x3f) | 0x80
  59. }
  60. // NewV3 creates a new UUID with variant 3 as described in RFC 4122.
  61. // Variant 3 based namespace-uuid and name and MD-5 hash calculation.
  62. func NewV3(namespace *UUID, name []byte) *UUID {
  63. uuid := newByHash(md5.New(), namespace, name)
  64. uuid[6] = (uuid[6] & 0x0f) | 0x30
  65. return uuid
  66. }
  67. func newByHash(hash hash.Hash, namespace *UUID, name []byte) *UUID {
  68. hash.Write(namespace[:])
  69. hash.Write(name[:])
  70. var uuid UUID
  71. copy(uuid[:], hash.Sum(nil)[:16])
  72. uuid.variantRFC4122()
  73. return &uuid
  74. }
  75. type stamp [10]byte
  76. var (
  77. mac []byte
  78. requests chan bool
  79. answers chan stamp
  80. )
  81. const gregorianUnix = 122192928000000000 // nanoseconds between gregorion zero and unix zero
  82. func init() {
  83. mac = make([]byte, 6)
  84. rand.Read(mac)
  85. requests = make(chan bool)
  86. answers = make(chan stamp)
  87. go unique()
  88. i, err := net.Interfaces()
  89. if err != nil {
  90. return
  91. }
  92. for _, d := range i {
  93. if len(d.HardwareAddr) == 6 {
  94. mac = d.HardwareAddr[:6]
  95. return
  96. }
  97. }
  98. }
  99. // NewV1 creates a new UUID with variant 1 as described in RFC 4122.
  100. // Variant 1 is based on hosts MAC address and actual timestamp (as count of 100-nanosecond intervals since
  101. // 00:00:00.00, 15 October 1582 (the date of Gregorian reform to the Christian calendar).
  102. func NewV1() *UUID {
  103. var uuid UUID
  104. requests <- true
  105. s := <-answers
  106. copy(uuid[:4], s[4:])
  107. copy(uuid[4:6], s[2:4])
  108. copy(uuid[6:8], s[:2])
  109. uuid[6] = (uuid[6] & 0x0f) | 0x10
  110. copy(uuid[8:10], s[8:])
  111. copy(uuid[10:], mac)
  112. uuid.variantRFC4122()
  113. return &uuid
  114. }
  115. func unique() {
  116. var (
  117. lastNanoTicks uint64
  118. clockSequence [2]byte
  119. )
  120. rand.Read(clockSequence[:])
  121. for range requests {
  122. var s stamp
  123. nanoTicks := uint64((time.Now().UTC().UnixNano() / 100) + gregorianUnix)
  124. if nanoTicks < lastNanoTicks {
  125. lastNanoTicks = nanoTicks
  126. rand.Read(clockSequence[:])
  127. } else if nanoTicks == lastNanoTicks {
  128. lastNanoTicks = nanoTicks + 1
  129. } else {
  130. lastNanoTicks = nanoTicks
  131. }
  132. binary.BigEndian.PutUint64(s[:], lastNanoTicks)
  133. copy(s[8:], clockSequence[:])
  134. answers <- s
  135. }
  136. }
  137. // NewV4 creates a new UUID with variant 4 as described in RFC 4122. Variant 4 based on pure random bytes.
  138. func NewV4() *UUID {
  139. buf := make([]byte, 16)
  140. rand.Read(buf)
  141. buf[6] = (buf[6] & 0x0f) | 0x40
  142. var uuid UUID
  143. copy(uuid[:], buf[:])
  144. uuid.variantRFC4122()
  145. return &uuid
  146. }
  147. // NewV5 creates a new UUID with variant 5 as described in RFC 4122.
  148. // Variant 5 based namespace-uuid and name and SHA-1 hash calculation.
  149. func NewV5(namespaceUUID *UUID, name []byte) *UUID {
  150. uuid := newByHash(sha1.New(), namespaceUUID, name)
  151. uuid[6] = (uuid[6] & 0x0f) | 0x50
  152. return uuid
  153. }
  154. // NewNamespaceUUID creates a namespace UUID by using the namespace name in the NIL name space.
  155. // This is a different approach as the 4 "standard" namespace UUIDs which are timebased UUIDs (V1).
  156. func NewNamespaceUUID(namespace string) *UUID {
  157. return NewV5(NIL, []byte(namespace))
  158. }