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.

296 lines
7.7 KiB

  1. // This package provides RFC4122 UUIDs.
  2. //
  3. // NewV1, NewV3, NewV4, NewV5, for generating versions 1, 3, 4
  4. // and 5 UUIDs as specified in RFC-4122.
  5. //
  6. // New([]byte), unsafe; NewHex(string); and Parse(string) for
  7. // creating UUIDs from existing data.
  8. //
  9. // The original version was from Krzysztof Kowalik <chris@nu7hat.ch>
  10. // Unfortunately, that version was non compliant with RFC4122.
  11. // I forked it but have since heavily redesigned it.
  12. //
  13. // The example code in the specification was also used as reference
  14. // for design.
  15. //
  16. // Copyright (C) 2014 twinj@github.com 2014 MIT style licence
  17. package uuid
  18. /****************
  19. * Date: 31/01/14
  20. * Time: 3:35 PM
  21. ***************/
  22. import (
  23. "encoding"
  24. "encoding/hex"
  25. "errors"
  26. "fmt"
  27. "hash"
  28. "regexp"
  29. "strings"
  30. "bytes"
  31. )
  32. const (
  33. ReservedNCS byte = 0x00
  34. ReservedRFC4122 byte = 0x80 // or and A0 if masked with 1F
  35. ReservedMicrosoft byte = 0xC0
  36. ReservedFuture byte = 0xE0
  37. TakeBack byte = 0xF0
  38. )
  39. const (
  40. // Pattern used to parse string representation of the UUID.
  41. // Current one allows to parse string where only one opening
  42. // or closing bracket or any of the hyphens are optional.
  43. // It is only used to extract the main bytes to create a UUID,
  44. // so these imperfections are of no consequence.
  45. hexPattern = `^(urn\:uuid\:)?[\{(\[]?([A-Fa-f0-9]{8})-?([A-Fa-f0-9]{4})-?([1-5][A-Fa-f0-9]{3})-?([A-Fa-f0-9]{4})-?([A-Fa-f0-9]{12})[\]\})]?$`
  46. )
  47. var (
  48. parseUUIDRegex = regexp.MustCompile(hexPattern)
  49. format string
  50. )
  51. func init() {
  52. SwitchFormat(CleanHyphen)
  53. }
  54. // ****************************************************** UUID
  55. // The main interface for UUIDs
  56. // Each implementation must also implement the UniqueName interface
  57. type UUID interface {
  58. encoding.BinaryMarshaler
  59. encoding.BinaryUnmarshaler
  60. // Marshals the UUID bytes or data
  61. Bytes() (data []byte)
  62. // Organises data into a new UUID
  63. Unmarshal(pData []byte)
  64. // Size is used where different implementations require
  65. // different sizes. Should return the number of bytes in
  66. // the implementation.
  67. // Enables unmarshal and Bytes to screen for size
  68. Size() int
  69. // Version returns a version number of the algorithm used
  70. // to generate the UUID.
  71. // This may may behave independently across non RFC4122 UUIDs
  72. Version() int
  73. // Variant returns the UUID Variant
  74. // This will be one of the constants:
  75. // ReservedRFC4122,
  76. // ReservedMicrosoft,
  77. // ReservedFuture,
  78. // ReservedNCS.
  79. // This may behave differently across non RFC4122 UUIDs
  80. Variant() byte
  81. // UUID can be used as a Name within a namespace
  82. // Is simply just a String() string method
  83. // Returns a formatted version of the UUID.
  84. String() string
  85. }
  86. // New creates a UUID from a slice of bytes.
  87. // Truncates any bytes past the default length of 16
  88. // Will panic if data slice is too small.
  89. func New(pData []byte) UUID {
  90. o := new(Array)
  91. o.Unmarshal(pData[:length])
  92. return o
  93. }
  94. // Creates a UUID from a hex string
  95. // Will panic if hex string is invalid - will panic even with hyphens and brackets
  96. // Expects a clean string use Parse otherwise.
  97. func NewHex(pUuid string) UUID {
  98. bytes, err := hex.DecodeString(pUuid)
  99. if err != nil {
  100. panic(err)
  101. }
  102. return New(bytes)
  103. }
  104. // Parse creates a UUID from a valid string representation.
  105. // Accepts UUID string in following formats:
  106. // 6ba7b8149dad11d180b400c04fd430c8
  107. // 6ba7b814-9dad-11d1-80b4-00c04fd430c8
  108. // {6ba7b814-9dad-11d1-80b4-00c04fd430c8}
  109. // urn:uuid:6ba7b814-9dad-11d1-80b4-00c04fd430c8
  110. // [6ba7b814-9dad-11d1-80b4-00c04fd430c8]
  111. //
  112. func Parse(pUUID string) (UUID, error) {
  113. md := parseUUIDRegex.FindStringSubmatch(pUUID)
  114. if md == nil {
  115. return nil, errors.New("uuid.Parse: invalid string")
  116. }
  117. return NewHex(md[2] + md[3] + md[4] + md[5] + md[6]), nil
  118. }
  119. // Digest a namespace UUID and a UniqueName, which then marshals to
  120. // a new UUID
  121. func Digest(o, pNs UUID, pName UniqueName, pHash hash.Hash) {
  122. // Hash writer never returns an error
  123. pHash.Write(pNs.Bytes())
  124. pHash.Write([]byte(pName.String()))
  125. o.Unmarshal(pHash.Sum(nil)[:o.Size()])
  126. }
  127. // Function provides a safe way to unmarshal bytes into an
  128. // existing UUID.
  129. // Checks for length.
  130. func UnmarshalBinary(o UUID, pData []byte) error {
  131. if len(pData) != o.Size() {
  132. return errors.New("uuid.UnmarshalBinary: invalid length")
  133. }
  134. o.Unmarshal(pData)
  135. return nil
  136. }
  137. // ********************************************** UUID Names
  138. // A UUID Name is a simple string which implements UniqueName
  139. // which satisfies the Stringer interface.
  140. type Name string
  141. // Returns the name as a string. Satisfies the Stringer interface.
  142. func (o Name) String() string {
  143. return string(o)
  144. }
  145. // NewName will create a unique name from several sources
  146. func NewName(salt string, pNames ...UniqueName) UniqueName {
  147. var s string
  148. for _, s2 := range pNames {
  149. s += s2.String()
  150. }
  151. return Name(s + salt)
  152. }
  153. // UniqueName is a Stinger interface
  154. // Made for easy passing of IPs, URLs, the several Address types,
  155. // Buffers and any other type which implements Stringer
  156. // string, []byte types and Hash sums will need to be cast to
  157. // the Name type or some other type which implements
  158. // Stringer or UniqueName
  159. type UniqueName interface {
  160. // Many go types implement this method for use with printing
  161. // Will convert the current type to its native string format
  162. String() string
  163. }
  164. // ********************************************** UUID Printing
  165. // A Format is a pattern used by the stringer interface with which to print
  166. // the UUID.
  167. type Format string
  168. const (
  169. Clean Format = "%x%x%x%x%x%x"
  170. Curly Format = "{%x%x%x%x%x%x}"
  171. Bracket Format = "(%x%x%x%x%x%x)"
  172. // This is the default format.
  173. CleanHyphen Format = "%x-%x-%x-%x%x-%x"
  174. CurlyHyphen Format = "{%x-%x-%x-%x%x-%x}"
  175. BracketHyphen Format = "(%x-%x-%x-%x%x-%x)"
  176. GoIdFormat Format = "[%X-%X-%x-%X%X-%x]"
  177. )
  178. // Gets the current default format pattern
  179. func GetFormat() string {
  180. return format
  181. }
  182. // Switches the default printing format for ALL UUID strings
  183. // A valid format will have 6 groups if the supplied Format does not
  184. func SwitchFormat(pFormat Format) {
  185. form := string(pFormat)
  186. if strings.Count(form, "%") != 6 {
  187. panic(errors.New("uuid.switchFormat: invalid formatting"))
  188. }
  189. format = form
  190. }
  191. // Same as SwitchFormat but will make it uppercase
  192. func SwitchFormatUpperCase(pFormat Format) {
  193. form := strings.ToUpper(string(pFormat))
  194. SwitchFormat(Format(form))
  195. }
  196. // Compares whether each UUID is the same
  197. func Equal(p1 UUID, p2 UUID) bool {
  198. return bytes.Equal(p1.Bytes(), p2.Bytes())
  199. }
  200. // Format a UUID into a human readable string which matches the given Format
  201. // Use this for one time formatting when setting the default using SwitchFormat
  202. // is overkill.
  203. func Formatter(pUUID UUID, pFormat Format) string {
  204. form := string(pFormat)
  205. if strings.Count(form, "%") != 6 {
  206. panic(errors.New("uuid.Formatter: invalid formatting"))
  207. }
  208. return formatter(pUUID, form)
  209. }
  210. // ********************************************** UUID Versions
  211. type UUIDVersion int
  212. const (
  213. NONE UUIDVersion = iota
  214. RFC4122v1
  215. DunnoYetv2
  216. RFC4122v3
  217. RFC4122v4
  218. RFC4122v5
  219. )
  220. // *************************************************** Helpers
  221. // Retrieves the variant from the given byte
  222. func variant(pVariant byte) byte {
  223. switch pVariant & variantGet {
  224. case ReservedRFC4122, 0xA0:
  225. return ReservedRFC4122
  226. case ReservedMicrosoft:
  227. return ReservedMicrosoft
  228. case ReservedFuture:
  229. return ReservedFuture
  230. }
  231. return ReservedNCS
  232. }
  233. // not strictly required
  234. func setVariant(pByte *byte, pVariant byte) {
  235. switch pVariant {
  236. case ReservedRFC4122:
  237. *pByte &= variantSet
  238. case ReservedFuture, ReservedMicrosoft:
  239. *pByte &= 0x1F
  240. case ReservedNCS:
  241. *pByte &= 0x7F
  242. default:
  243. panic(errors.New("uuid.setVariant: invalid variant mask"))
  244. }
  245. *pByte |= pVariant
  246. }
  247. // format a UUID into a human readable string
  248. func formatter(pUUID UUID, pFormat string) string {
  249. b := pUUID.Bytes()
  250. return fmt.Sprintf(pFormat, b[0:4], b[4:6], b[6:8], b[8:9], b[9:10], b[10:pUUID.Size()])
  251. }