1 // Copyright (C) 2013-2018 by Maxim Bublis <b@codemonkey.ru> 2 // 3 // Permission is hereby granted, free of charge, to any person obtaining 4 // a copy of this software and associated documentation files (the 5 // "Software"), to deal in the Software without restriction, including 6 // without limitation the rights to use, copy, modify, merge, publish, 7 // distribute, sublicense, and/or sell copies of the Software, and to 8 // permit persons to whom the Software is furnished to do so, subject to 9 // the following conditions: 10 // 11 // The above copyright notice and this permission notice shall be 12 // included in all copies or substantial portions of the Software. 13 // 14 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 22 // Package uuid provides implementations of the Universally Unique Identifier 23 // (UUID), as specified in RFC-4122 and the Peabody RFC Draft (revision 03). 24 // 25 // RFC-4122[1] provides the specification for versions 1, 3, 4, and 5. The 26 // Peabody UUID RFC Draft[2] provides the specification for the new k-sortable 27 // UUIDs, versions 6 and 7. 28 // 29 // DCE 1.1[3] provides the specification for version 2, but version 2 support 30 // was removed from this package in v4 due to some concerns with the 31 // specification itself. Reading the spec, it seems that it would result in 32 // generating UUIDs that aren't very unique. In having read the spec it seemed 33 // that our implementation did not meet the spec. It also seems to be at-odds 34 // with RFC 4122, meaning we would need quite a bit of special code to support 35 // it. Lastly, there were no Version 2 implementations that we could find to 36 // ensure we were understanding the specification correctly. 37 // 38 // [1] https://tools.ietf.org/html/rfc4122 39 // [2] https://datatracker.ietf.org/doc/html/draft-peabody-dispatch-new-uuid-format-03 40 // [3] http://pubs.opengroup.org/onlinepubs/9696989899/chap5.htm#tagcjh_08_02_01_01 41 package uuid 42 43 import ( 44 "encoding/binary" 45 "encoding/hex" 46 "fmt" 47 "io" 48 "strings" 49 "time" 50 ) 51 52 // Size of a UUID in bytes. 53 const Size = 16 54 55 // UUID is an array type to represent the value of a UUID, as defined in RFC-4122. 56 type UUID [Size]byte 57 58 // UUID versions. 59 const ( 60 _ byte = iota 61 V1 // Version 1 (date-time and MAC address) 62 _ // Version 2 (date-time and MAC address, DCE security version) [removed] 63 V3 // Version 3 (namespace name-based) 64 V4 // Version 4 (random) 65 V5 // Version 5 (namespace name-based) 66 V6 // Version 6 (k-sortable timestamp and random data, field-compatible with v1) [peabody draft] 67 V7 // Version 7 (k-sortable timestamp and random data) [peabody draft] 68 _ // Version 8 (k-sortable timestamp, meant for custom implementations) [peabody draft] [not implemented] 69 ) 70 71 // UUID layout variants. 72 const ( 73 VariantNCS byte = iota 74 VariantRFC4122 75 VariantMicrosoft 76 VariantFuture 77 ) 78 79 // UUID DCE domains. 80 const ( 81 DomainPerson = iota 82 DomainGroup 83 DomainOrg 84 ) 85 86 // Timestamp is the count of 100-nanosecond intervals since 00:00:00.00, 87 // 15 October 1582 within a V1 UUID. This type has no meaning for other 88 // UUID versions since they don't have an embedded timestamp. 89 type Timestamp uint64 90 91 const _100nsPerSecond = 10000000 92 93 // Time returns the UTC time.Time representation of a Timestamp 94 func (t Timestamp) Time() (time.Time, error) { 95 secs := uint64(t) / _100nsPerSecond 96 nsecs := 100 * (uint64(t) % _100nsPerSecond) 97 98 return time.Unix(int64(secs)-(epochStart/_100nsPerSecond), int64(nsecs)), nil 99 } 100 101 // TimestampFromV1 returns the Timestamp embedded within a V1 UUID. 102 // Returns an error if the UUID is any version other than 1. 103 func TimestampFromV1(u UUID) (Timestamp, error) { 104 if u.Version() != 1 { 105 err := fmt.Errorf("uuid: %s is version %d, not version 1", u, u.Version()) 106 return 0, err 107 } 108 109 low := binary.BigEndian.Uint32(u[0:4]) 110 mid := binary.BigEndian.Uint16(u[4:6]) 111 hi := binary.BigEndian.Uint16(u[6:8]) & 0xfff 112 113 return Timestamp(uint64(low) + (uint64(mid) << 32) + (uint64(hi) << 48)), nil 114 } 115 116 // TimestampFromV6 returns the Timestamp embedded within a V6 UUID. This 117 // function returns an error if the UUID is any version other than 6. 118 // 119 // This is implemented based on revision 03 of the Peabody UUID draft, and may 120 // be subject to change pending further revisions. Until the final specification 121 // revision is finished, changes required to implement updates to the spec will 122 // not be considered a breaking change. They will happen as a minor version 123 // releases until the spec is final. 124 func TimestampFromV6(u UUID) (Timestamp, error) { 125 if u.Version() != 6 { 126 return 0, fmt.Errorf("uuid: %s is version %d, not version 6", u, u.Version()) 127 } 128 129 hi := binary.BigEndian.Uint32(u[0:4]) 130 mid := binary.BigEndian.Uint16(u[4:6]) 131 low := binary.BigEndian.Uint16(u[6:8]) & 0xfff 132 133 return Timestamp(uint64(low) + (uint64(mid) << 12) + (uint64(hi) << 28)), nil 134 } 135 136 // String parse helpers. 137 var ( 138 urnPrefix = []byte("urn:uuid:") 139 byteGroups = []int{8, 4, 4, 4, 12} 140 ) 141 142 // Nil is the nil UUID, as specified in RFC-4122, that has all 128 bits set to 143 // zero. 144 var Nil = UUID{} 145 146 // Predefined namespace UUIDs. 147 var ( 148 NamespaceDNS = Must(FromString("6ba7b810-9dad-11d1-80b4-00c04fd430c8")) 149 NamespaceURL = Must(FromString("6ba7b811-9dad-11d1-80b4-00c04fd430c8")) 150 NamespaceOID = Must(FromString("6ba7b812-9dad-11d1-80b4-00c04fd430c8")) 151 NamespaceX500 = Must(FromString("6ba7b814-9dad-11d1-80b4-00c04fd430c8")) 152 ) 153 154 // IsNil returns if the UUID is equal to the nil UUID 155 func (u UUID) IsNil() bool { 156 return u == Nil 157 } 158 159 // Version returns the algorithm version used to generate the UUID. 160 func (u UUID) Version() byte { 161 return u[6] >> 4 162 } 163 164 // Variant returns the UUID layout variant. 165 func (u UUID) Variant() byte { 166 switch { 167 case (u[8] >> 7) == 0x00: 168 return VariantNCS 169 case (u[8] >> 6) == 0x02: 170 return VariantRFC4122 171 case (u[8] >> 5) == 0x06: 172 return VariantMicrosoft 173 case (u[8] >> 5) == 0x07: 174 fallthrough 175 default: 176 return VariantFuture 177 } 178 } 179 180 // Bytes returns a byte slice representation of the UUID. 181 func (u UUID) Bytes() []byte { 182 return u[:] 183 } 184 185 // String returns a canonical RFC-4122 string representation of the UUID: 186 // xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx. 187 func (u UUID) String() string { 188 buf := make([]byte, 36) 189 190 hex.Encode(buf[0:8], u[0:4]) 191 buf[8] = '-' 192 hex.Encode(buf[9:13], u[4:6]) 193 buf[13] = '-' 194 hex.Encode(buf[14:18], u[6:8]) 195 buf[18] = '-' 196 hex.Encode(buf[19:23], u[8:10]) 197 buf[23] = '-' 198 hex.Encode(buf[24:], u[10:]) 199 200 return string(buf) 201 } 202 203 // Format implements fmt.Formatter for UUID values. 204 // 205 // The behavior is as follows: 206 // The 'x' and 'X' verbs output only the hex digits of the UUID, using a-f for 'x' and A-F for 'X'. 207 // The 'v', '+v', 's' and 'q' verbs return the canonical RFC-4122 string representation. 208 // The 'S' verb returns the RFC-4122 format, but with capital hex digits. 209 // The '#v' verb returns the "Go syntax" representation, which is a 16 byte array initializer. 210 // All other verbs not handled directly by the fmt package (like '%p') are unsupported and will return 211 // "%!verb(uuid.UUID=value)" as recommended by the fmt package. 212 func (u UUID) Format(f fmt.State, c rune) { 213 switch c { 214 case 'x', 'X': 215 s := hex.EncodeToString(u.Bytes()) 216 if c == 'X' { 217 s = strings.Map(toCapitalHexDigits, s) 218 } 219 _, _ = io.WriteString(f, s) 220 case 'v': 221 var s string 222 if f.Flag('#') { 223 s = fmt.Sprintf("%#v", [Size]byte(u)) 224 } else { 225 s = u.String() 226 } 227 _, _ = io.WriteString(f, s) 228 case 's', 'S': 229 s := u.String() 230 if c == 'S' { 231 s = strings.Map(toCapitalHexDigits, s) 232 } 233 _, _ = io.WriteString(f, s) 234 case 'q': 235 _, _ = io.WriteString(f, `"`+u.String()+`"`) 236 default: 237 // invalid/unsupported format verb 238 fmt.Fprintf(f, "%%!%c(uuid.UUID=%s)", c, u.String()) 239 } 240 } 241 242 func toCapitalHexDigits(ch rune) rune { 243 // convert a-f hex digits to A-F 244 switch ch { 245 case 'a': 246 return 'A' 247 case 'b': 248 return 'B' 249 case 'c': 250 return 'C' 251 case 'd': 252 return 'D' 253 case 'e': 254 return 'E' 255 case 'f': 256 return 'F' 257 default: 258 return ch 259 } 260 } 261 262 // SetVersion sets the version bits. 263 func (u *UUID) SetVersion(v byte) { 264 u[6] = (u[6] & 0x0f) | (v << 4) 265 } 266 267 // SetVariant sets the variant bits. 268 func (u *UUID) SetVariant(v byte) { 269 switch v { 270 case VariantNCS: 271 u[8] = (u[8]&(0xff>>1) | (0x00 << 7)) 272 case VariantRFC4122: 273 u[8] = (u[8]&(0xff>>2) | (0x02 << 6)) 274 case VariantMicrosoft: 275 u[8] = (u[8]&(0xff>>3) | (0x06 << 5)) 276 case VariantFuture: 277 fallthrough 278 default: 279 u[8] = (u[8]&(0xff>>3) | (0x07 << 5)) 280 } 281 } 282 283 // Must is a helper that wraps a call to a function returning (UUID, error) 284 // and panics if the error is non-nil. It is intended for use in variable 285 // initializations such as 286 // var packageUUID = uuid.Must(uuid.FromString("123e4567-e89b-12d3-a456-426655440000")) 287 func Must(u UUID, err error) UUID { 288 if err != nil { 289 panic(err) 290 } 291 return u 292 } 293