PageRenderTime 204ms CodeModel.GetById 18ms RepoModel.GetById 0ms app.codeStats 0ms

/uuid.go

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