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.
 
 
 

174 lines
4.7 KiB

  1. // This package provides immutable UUID structs and the functions
  2. // NewV3, NewV4, NewV5 and Parse() for generating versions 3, 4
  3. // and 5 UUIDs as specified in RFC 4122.
  4. //
  5. // Copyright (C) 2011 by Krzysztof Kowalik <chris@nu7hat.ch>
  6. package uuid
  7. import (
  8. "crypto/md5"
  9. "crypto/rand"
  10. "crypto/sha1"
  11. "encoding/hex"
  12. "errors"
  13. "fmt"
  14. "hash"
  15. "regexp"
  16. )
  17. // The UUID reserved variants.
  18. const (
  19. ReservedNCS byte = 0x80
  20. ReservedRFC4122 byte = 0x40
  21. ReservedMicrosoft byte = 0x20
  22. ReservedFuture byte = 0x00
  23. )
  24. // The following standard UUIDs are for use with NewV3() or NewV5().
  25. var (
  26. NamespaceDNS, _ = ParseHex("6ba7b810-9dad-11d1-80b4-00c04fd430c8")
  27. NamespaceURL, _ = ParseHex("6ba7b811-9dad-11d1-80b4-00c04fd430c8")
  28. NamespaceOID, _ = ParseHex("6ba7b812-9dad-11d1-80b4-00c04fd430c8")
  29. NamespaceX500, _ = ParseHex("6ba7b814-9dad-11d1-80b4-00c04fd430c8")
  30. )
  31. // Pattern used to parse hex string representation of the UUID.
  32. // FIXME: do something to consider both brackets at one time,
  33. // current one allows to parse string with only one opening
  34. // or closing bracket.
  35. const hexPattern = "^(urn\\:uuid\\:)?\\{?([a-z0-9]{8})-([a-z0-9]{4})-" +
  36. "([1-5][a-z0-9]{3})-([a-z0-9]{4})-([a-z0-9]{12})\\}?$"
  37. var re = regexp.MustCompile(hexPattern)
  38. // A UUID representation compliant with specification in
  39. // RFC 4122 document.
  40. type UUID [16]byte
  41. // ParseHex creates a UUID object from given hex string
  42. // representation. Function accepts UUID string in following
  43. // formats:
  44. //
  45. // uuid.ParseHex("6ba7b814-9dad-11d1-80b4-00c04fd430c8")
  46. // uuid.ParseHex("{6ba7b814-9dad-11d1-80b4-00c04fd430c8}")
  47. // uuid.ParseHex("urn:uuid:6ba7b814-9dad-11d1-80b4-00c04fd430c8")
  48. //
  49. func ParseHex(s string) (u *UUID, err error) {
  50. md := re.FindStringSubmatch(s)
  51. if md == nil {
  52. err = errors.New("Invalid UUID string")
  53. return
  54. }
  55. hash := md[2] + md[3] + md[4] + md[5] + md[6]
  56. b, err := hex.DecodeString(hash)
  57. if err != nil {
  58. return
  59. }
  60. u = new(UUID)
  61. copy(u[:], b)
  62. return
  63. }
  64. // Parse creates a UUID object from given bytes slice.
  65. func Parse(b []byte) (u *UUID, err error) {
  66. if len(b) != 16 {
  67. err = errors.New("Given slice is not valid UUID sequence")
  68. return
  69. }
  70. u = new(UUID)
  71. copy(u[:], b)
  72. return
  73. }
  74. // Generate a UUID based on the MD5 hash of a namespace identifier
  75. // and a name.
  76. func NewV3(ns *UUID, name []byte) (u *UUID, err error) {
  77. if ns == nil {
  78. err = errors.New("Invalid namespace UUID")
  79. return
  80. }
  81. u = new(UUID)
  82. // Set all bits to MD5 hash generated from namespace and name.
  83. u.setBytesFromHash(md5.New(), ns[:], name)
  84. u.setVariant(ReservedRFC4122)
  85. u.setVersion(3)
  86. return
  87. }
  88. // Generate a random UUID.
  89. func NewV4() (u *UUID, err error) {
  90. u = new(UUID)
  91. // Set all bits to randomly (or pseudo-randomly) chosen values.
  92. _, err = rand.Read(u[:])
  93. if err != nil {
  94. return
  95. }
  96. u.setVariant(ReservedRFC4122)
  97. u.setVersion(4)
  98. return
  99. }
  100. // Generate a UUID based on the SHA-1 hash of a namespace identifier
  101. // and a name.
  102. func NewV5(ns *UUID, name []byte) (u *UUID, err error) {
  103. u = new(UUID)
  104. // Set all bits to truncated SHA1 hash generated from namespace
  105. // and name.
  106. u.setBytesFromHash(sha1.New(), ns[:], name)
  107. u.setVariant(ReservedRFC4122)
  108. u.setVersion(5)
  109. return
  110. }
  111. // Generate a MD5 hash of a namespace and a name, and copy it to the
  112. // UUID slice.
  113. func (u *UUID) setBytesFromHash(hash hash.Hash, ns, name []byte) {
  114. hash.Write(ns[:])
  115. hash.Write(name)
  116. copy(u[:], hash.Sum([]byte{})[:16])
  117. }
  118. // Set the two most significant bits (bits 6 and 7) of the
  119. // clock_seq_hi_and_reserved to zero and one, respectively.
  120. func (u *UUID) setVariant(v byte) {
  121. switch v {
  122. case ReservedNCS:
  123. u[8] = (u[8] | ReservedNCS) & 0xBF
  124. case ReservedRFC4122:
  125. u[8] = (u[8] | ReservedRFC4122) & 0x7F
  126. case ReservedMicrosoft:
  127. u[8] = (u[8] | ReservedMicrosoft) & 0x3F
  128. }
  129. }
  130. // Variant returns the UUID Variant, which determines the internal
  131. // layout of the UUID. This will be one of the constants: RESERVED_NCS,
  132. // RFC_4122, RESERVED_MICROSOFT, RESERVED_FUTURE.
  133. func (u *UUID) Variant() byte {
  134. if u[8]&ReservedNCS == ReservedNCS {
  135. return ReservedNCS
  136. } else if u[8]&ReservedRFC4122 == ReservedRFC4122 {
  137. return ReservedRFC4122
  138. } else if u[8]&ReservedMicrosoft == ReservedMicrosoft {
  139. return ReservedMicrosoft
  140. }
  141. return ReservedFuture
  142. }
  143. // Set the four most significant bits (bits 12 through 15) of the
  144. // time_hi_and_version field to the 4-bit version number.
  145. func (u *UUID) setVersion(v byte) {
  146. u[6] = (u[6] & 0xF) | (v << 4)
  147. }
  148. // Version returns a version number of the algorithm used to
  149. // generate the UUID sequence.
  150. func (u *UUID) Version() uint {
  151. return uint(u[6] >> 4)
  152. }
  153. // Returns unparsed version of the generated UUID sequence.
  154. func (u *UUID) String() string {
  155. return fmt.Sprintf("%x-%x-%x-%x-%x", u[0:4], u[4:6], u[6:8], u[8:10], u[10:])
  156. }