github.com/qioalice/ekago/v3@v3.3.2-0.20221202205325-5c262d586ee4/ekatyp/uuid_private.go (about) 1 // Copyright © 2020. All rights reserved. 2 // Author: Ilya Stroy. 3 // Contacts: iyuryevich@pm.me, https://github.com/qioalice 4 // License: https://opensource.org/licenses/MIT 5 6 // Copyright (C) 2013-2018 by Maxim Bublis <b@codemonkey.ru> 7 // 8 // Permission is hereby granted, free of charge, to any person obtaining 9 // a copy of this software and associated documentation files (the 10 // "Software"), to deal in the Software without restriction, including 11 // without limitation the rights to use, copy, modify, merge, publish, 12 // distribute, sublicense, and/or sell copies of the Software, and to 13 // permit persons to whom the Software is furnished to do so, subject to 14 // the following conditions: 15 // 16 // The above copyright notice and this permission notice shall be 17 // included in all copies or substantial portions of the Software. 18 // 19 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 20 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 21 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 22 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 23 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 24 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 25 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 26 27 package ekatyp 28 29 import ( 30 "bytes" 31 "crypto/md5" 32 "crypto/rand" 33 "crypto/sha1" 34 "encoding/binary" 35 "encoding/hex" 36 "fmt" 37 "hash" 38 "io" 39 "net" 40 "sync" 41 "time" 42 43 "github.com/qioalice/ekago/v3/ekasys" 44 ) 45 46 //noinspection GoSnakeCaseUsage (Intellij IDEA suppress snake case warning). 47 type ( 48 // _UUID_Generator provides interface for generating UUIDs. 49 _UUID_Generator interface { 50 NewV1() (UUID, error) 51 NewV2(domain byte) (UUID, error) 52 NewV3(ns UUID, name string) UUID 53 NewV4() (UUID, error) 54 NewV5(ns UUID, name string) UUID 55 } 56 57 // Default generator implementation. 58 _T_UUID_RFC4122_Generator struct { 59 storageMutex sync.Mutex 60 61 hwAddr [6]byte 62 hwAddrErr error 63 64 clockSequence uint16 65 clockSequenceErr error 66 67 rand io.Reader 68 69 lastTime uint64 70 } 71 ) 72 73 //noinspection GoSnakeCaseUsage (Intellij IDEA suppress snake case warning). 74 const ( 75 _UUID_SIZE = 16 // size of a UUID in bytes. 76 77 // Difference in 100-nanosecond intervals between 78 // UUID epoch (October 15, 1582) and Unix epoch (January 1, 1970). 79 _UUID_EPOCH_START = 122192928000000000 80 ) 81 82 //noinspection GoSnakeCaseUsage (Intellij IDEA suppress snake case warning). 83 var ( 84 _UUID_RFC4122_Generator = newRFC4122Generator(rand.Reader) 85 86 // String parse helpers. 87 _UUID_URN_Prefix = []byte("urn:uuid:") 88 _UUID_ByteGroups = []int{8, 4, 4, 4, 12} 89 90 _UUID_JSON_NULL = []byte("null") 91 ) 92 93 // hexEncodeTo encodes UUID as hex to dest with canonical string representation: 94 // xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx (w/o quotes). Requires: len(dest) >= 36. 95 // Returns dest. 96 func (u UUID) hexEncodeTo(dest []byte) []byte { 97 98 dest[8] = '-' 99 dest[13] = '-' 100 dest[18] = '-' 101 dest[23] = '-' 102 103 hex.Encode(dest[0:8], u[0:4]) 104 hex.Encode(dest[9:13], u[4:6]) 105 hex.Encode(dest[14:18], u[6:8]) 106 hex.Encode(dest[19:23], u[8:10]) 107 hex.Encode(dest[24:], u[10:]) 108 109 return dest 110 } 111 112 // jsonMarshal returns canonical string representation of UUID with double quotes: 113 // "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx". 114 func (u UUID) jsonMarshal() []byte { 115 116 buf := make([]byte, 38) 117 118 buf[0] = '"' 119 buf[37] = '"' 120 121 u.hexEncodeTo(buf[1:37]) 122 123 return buf 124 } 125 126 // decodeCanonical decodes UUID string in format 127 // "6ba7b810-9dad-11d1-80b4-00c04fd430c8". 128 func (u *UUID) decodeCanonical(t []byte) (err error) { 129 if t[8] != '-' || t[13] != '-' || t[18] != '-' || t[23] != '-' { 130 return fmt.Errorf("uuid: incorrect UUID format %s", t) 131 } 132 133 src := t[:] 134 dst := u[:] 135 136 for i, byteGroup := range _UUID_ByteGroups { 137 if i > 0 { 138 src = src[1:] // skip dash 139 } 140 _, err = hex.Decode(dst[:byteGroup/2], src[:byteGroup]) 141 if err != nil { 142 return 143 } 144 src = src[byteGroup:] 145 dst = dst[byteGroup/2:] 146 } 147 148 return 149 } 150 151 // decodeHashLike decodes UUID string in format 152 // "6ba7b8109dad11d180b400c04fd430c8". 153 func (u *UUID) decodeHashLike(t []byte) (err error) { 154 src := t[:] 155 dst := u[:] 156 157 if _, err = hex.Decode(dst, src); err != nil { 158 return err 159 } 160 return 161 } 162 163 // decodeBraced decodes UUID string in format 164 // "{6ba7b810-9dad-11d1-80b4-00c04fd430c8}" or in format 165 // "{6ba7b8109dad11d180b400c04fd430c8}". 166 func (u *UUID) decodeBraced(t []byte) (err error) { 167 l := len(t) 168 169 if t[0] != '{' || t[l-1] != '}' { 170 return fmt.Errorf("uuid: incorrect UUID format %s", t) 171 } 172 173 return u.decodePlain(t[1 : l-1]) 174 } 175 176 // decodeURN decodes UUID string in format 177 // "urn:uuid:6ba7b810-9dad-11d1-80b4-00c04fd430c8" or in format 178 // "urn:uuid:6ba7b8109dad11d180b400c04fd430c8". 179 func (u *UUID) decodeURN(t []byte) (err error) { 180 total := len(t) 181 182 urn_uuid_prefix := t[:9] 183 184 if !bytes.Equal(urn_uuid_prefix, _UUID_URN_Prefix) { 185 return fmt.Errorf("uuid: incorrect UUID format: %s", t) 186 } 187 188 return u.decodePlain(t[9:total]) 189 } 190 191 // decodePlain decodes UUID string in canonical format 192 // "6ba7b810-9dad-11d1-80b4-00c04fd430c8" or in hash-like format 193 // "6ba7b8109dad11d180b400c04fd430c8". 194 func (u *UUID) decodePlain(t []byte) (err error) { 195 switch len(t) { 196 case 32: 197 return u.decodeHashLike(t) 198 case 36: 199 return u.decodeCanonical(t) 200 default: 201 return fmt.Errorf("uuid: incorrrect UUID length: %s", t) 202 } 203 } 204 205 // NewV1 returns UUID based on current timestamp and MAC address. 206 func (g *_T_UUID_RFC4122_Generator) NewV1() (UUID, error) { 207 u := UUID{} 208 209 timeNow, clockSeq, err := g.getClockSequence() 210 if err != nil { 211 return _UUID_NULL, err 212 } 213 binary.BigEndian.PutUint32(u[0:], uint32(timeNow)) 214 binary.BigEndian.PutUint16(u[4:], uint16(timeNow>>32)) 215 binary.BigEndian.PutUint16(u[6:], uint16(timeNow>>48)) 216 binary.BigEndian.PutUint16(u[8:], clockSeq) 217 218 if g.hwAddrErr != nil { 219 return _UUID_NULL, err 220 } 221 copy(u[10:], g.hwAddr[:]) 222 223 u.SetVersion(UUID_V1) 224 u.SetVariant(UUID_VARIANT_RFC4122) 225 226 return u, nil 227 } 228 229 // NewV2 returns DCE Security UUID based on POSIX UID/GID. 230 func (g *_T_UUID_RFC4122_Generator) NewV2(domain byte) (UUID, error) { 231 u, err := g.NewV1() 232 if err != nil { 233 return _UUID_NULL, err 234 } 235 236 switch domain { 237 case UUID_DOMAIN_PERSON: 238 binary.BigEndian.PutUint32(u[:], ekasys.PosixCachedUid()) 239 case UUID_DOMAIN_GROUP: 240 binary.BigEndian.PutUint32(u[:], ekasys.PosixCachedGid()) 241 } 242 243 u[9] = domain 244 245 u.SetVersion(UUID_V2) 246 u.SetVariant(UUID_VARIANT_RFC4122) 247 248 return u, nil 249 } 250 251 // NewV3 returns UUID based on MD5 hash of namespace UUID and name. 252 func (g *_T_UUID_RFC4122_Generator) NewV3(ns UUID, name string) UUID { 253 u := g.newFromHash(md5.New(), ns, name) 254 u.SetVersion(UUID_V3) 255 u.SetVariant(UUID_VARIANT_RFC4122) 256 257 return u 258 } 259 260 // NewV4 returns random generated UUID. 261 func (g *_T_UUID_RFC4122_Generator) NewV4() (UUID, error) { 262 u := UUID{} 263 if _, err := io.ReadFull(g.rand, u[:]); err != nil { 264 return _UUID_NULL, err 265 } 266 u.SetVersion(UUID_V4) 267 u.SetVariant(UUID_VARIANT_RFC4122) 268 269 return u, nil 270 } 271 272 // NewV5 returns UUID based on SHA-1 hash of namespace UUID and name. 273 func (g *_T_UUID_RFC4122_Generator) NewV5(ns UUID, name string) UUID { 274 u := g.newFromHash(sha1.New(), ns, name) 275 u.SetVersion(UUID_V5) 276 u.SetVariant(UUID_VARIANT_RFC4122) 277 278 return u 279 } 280 281 // Returns epoch and clock sequence. 282 func (g *_T_UUID_RFC4122_Generator) getClockSequence() (uint64, uint16, error) { 283 284 if g.clockSequenceErr != nil { 285 return 0, 0, g.clockSequenceErr 286 } 287 288 g.storageMutex.Lock() 289 defer g.storageMutex.Unlock() 290 291 timeNow := _UUID_EPOCH_START + uint64(time.Now().UnixNano()/100) 292 // Clock didn't change since last UUID generation. 293 // Should increase clock sequence. 294 if timeNow <= g.lastTime { 295 g.clockSequence++ 296 } 297 g.lastTime = timeNow 298 299 return timeNow, g.clockSequence, nil 300 } 301 302 // Returns UUID based on hashing of namespace UUID and name. 303 func (_ *_T_UUID_RFC4122_Generator) newFromHash(h hash.Hash, ns UUID, name string) UUID { 304 u := UUID{} 305 h.Write(ns[:]) 306 h.Write([]byte(name)) 307 copy(u[:], h.Sum(nil)) 308 309 return u 310 } 311 312 // newRFC4122Generator creates, initializes and returns an RFC4122 UUID generator. 313 func newRFC4122Generator(randReader io.Reader) _UUID_Generator { 314 315 var ( 316 g _T_UUID_RFC4122_Generator 317 ifaces []net.Interface 318 ) 319 320 g.rand = randReader 321 322 ifaces, g.hwAddrErr = net.Interfaces() 323 // no need to check err because if err != nil, ifaces is empty 324 325 for _, iface := range ifaces { 326 if len(iface.HardwareAddr) >= 6 { 327 copy(g.hwAddr[:], iface.HardwareAddr) 328 } 329 } 330 331 // do not overwrite g.hwAddrErr 332 // but update it if g.hwAddr has not been set after the loop above 333 if g.hwAddrErr == nil && bytes.Equal(g.hwAddr[:], make([]byte, 6)) { 334 g.hwAddrErr = fmt.Errorf("uuid: no HW address found") 335 } 336 337 // Initialize g.hwAddr randomly in case of real network interfaces absence. 338 if g.hwAddrErr != nil { 339 if _, err := io.ReadFull(g.rand, g.hwAddr[:]); err == nil { 340 g.hwAddr[0] |= 0x01 // Set multicast bit as recommended by RFC 4122 341 g.hwAddrErr = nil // Ignore all previous errors 342 } else { 343 g.hwAddrErr = fmt.Errorf("uuid: no HW address found and failed to mock it: %s", err.Error()) 344 } 345 } 346 347 buf := make([]byte, 2) 348 if _, err := io.ReadFull(g.rand, buf); err == nil { 349 g.clockSequence = binary.BigEndian.Uint16(buf) 350 } else { 351 g.clockSequenceErr = fmt.Errorf("uuid: failed to generate clock sequence: %s", err.Error()) 352 } 353 354 return &g 355 }