github.com/mymmsc/gox@v1.3.33/util/uuid/generator.go (about) 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 23 24 import ( 25 "crypto/md5" 26 "crypto/rand" 27 "crypto/sha1" 28 "encoding/binary" 29 "hash" 30 "net" 31 "os" 32 "sync" 33 "time" 34 ) 35 36 // Difference in 100-nanosecond intervals between 37 // UUID epoch (October 15, 1582) and Unix epoch (January 1, 1970). 38 const epochStart = 122192928000000000 39 40 var ( 41 global = newDefaultGenerator() 42 43 epochFunc = unixTimeFunc 44 posixUID = uint32(os.Getuid()) 45 posixGID = uint32(os.Getgid()) 46 ) 47 48 // NewV1 returns UUID based on current timestamp and MAC address. 49 func NewV1() UUID { 50 return global.NewV1() 51 } 52 53 // NewV2 returns DCE Security UUID based on POSIX UID/GID. 54 func NewV2(domain byte) UUID { 55 return global.NewV2(domain) 56 } 57 58 // NewV3 returns UUID based on MD5 hash of namespace UUID and name. 59 func NewV3(ns UUID, name string) UUID { 60 return global.NewV3(ns, name) 61 } 62 63 // NewV4 returns random generated UUID. 64 func NewV4() UUID { 65 return global.NewV4() 66 } 67 68 // NewV5 returns UUID based on SHA-1 hash of namespace UUID and name. 69 func NewV5(ns UUID, name string) UUID { 70 return global.NewV5(ns, name) 71 } 72 73 // Generator provides interface for generating UUIDs. 74 type Generator interface { 75 NewV1() UUID 76 NewV2(domain byte) UUID 77 NewV3(ns UUID, name string) UUID 78 NewV4() UUID 79 NewV5(ns UUID, name string) UUID 80 } 81 82 // Default generator implementation. 83 type generator struct { 84 storageOnce sync.Once 85 storageMutex sync.Mutex 86 87 lastTime uint64 88 clockSequence uint16 89 hardwareAddr [6]byte 90 } 91 92 func newDefaultGenerator() Generator { 93 return &generator{} 94 } 95 96 // NewV1 returns UUID based on current timestamp and MAC address. 97 func (g *generator) NewV1() UUID { 98 u := UUID{} 99 100 timeNow, clockSeq, hardwareAddr := g.getStorage() 101 102 binary.BigEndian.PutUint32(u[0:], uint32(timeNow)) 103 binary.BigEndian.PutUint16(u[4:], uint16(timeNow>>32)) 104 binary.BigEndian.PutUint16(u[6:], uint16(timeNow>>48)) 105 binary.BigEndian.PutUint16(u[8:], clockSeq) 106 107 copy(u[10:], hardwareAddr) 108 109 u.SetVersion(V1) 110 u.SetVariant(VariantRFC4122) 111 112 return u 113 } 114 115 // NewV2 returns DCE Security UUID based on POSIX UID/GID. 116 func (g *generator) NewV2(domain byte) UUID { 117 u := UUID{} 118 119 timeNow, clockSeq, hardwareAddr := g.getStorage() 120 121 switch domain { 122 case DomainPerson: 123 binary.BigEndian.PutUint32(u[0:], posixUID) 124 case DomainGroup: 125 binary.BigEndian.PutUint32(u[0:], posixGID) 126 } 127 128 binary.BigEndian.PutUint16(u[4:], uint16(timeNow>>32)) 129 binary.BigEndian.PutUint16(u[6:], uint16(timeNow>>48)) 130 binary.BigEndian.PutUint16(u[8:], clockSeq) 131 u[9] = domain 132 133 copy(u[10:], hardwareAddr) 134 135 u.SetVersion(V2) 136 u.SetVariant(VariantRFC4122) 137 138 return u 139 } 140 141 // NewV3 returns UUID based on MD5 hash of namespace UUID and name. 142 func (g *generator) NewV3(ns UUID, name string) UUID { 143 u := newFromHash(md5.New(), ns, name) 144 u.SetVersion(V3) 145 u.SetVariant(VariantRFC4122) 146 147 return u 148 } 149 150 // NewV4 returns random generated UUID. 151 func (g *generator) NewV4() UUID { 152 u := UUID{} 153 g.safeRandom(u[:]) 154 u.SetVersion(V4) 155 u.SetVariant(VariantRFC4122) 156 157 return u 158 } 159 160 // NewV5 returns UUID based on SHA-1 hash of namespace UUID and name. 161 func (g *generator) NewV5(ns UUID, name string) UUID { 162 u := newFromHash(sha1.New(), ns, name) 163 u.SetVersion(V5) 164 u.SetVariant(VariantRFC4122) 165 166 return u 167 } 168 169 func (g *generator) initStorage() { 170 g.initClockSequence() 171 g.initHardwareAddr() 172 } 173 174 func (g *generator) initClockSequence() { 175 buf := make([]byte, 2) 176 g.safeRandom(buf) 177 g.clockSequence = binary.BigEndian.Uint16(buf) 178 } 179 180 func (g *generator) initHardwareAddr() { 181 interfaces, err := net.Interfaces() 182 if err == nil { 183 for _, iface := range interfaces { 184 if len(iface.HardwareAddr) >= 6 { 185 copy(g.hardwareAddr[:], iface.HardwareAddr) 186 return 187 } 188 } 189 } 190 191 // Initialize hardwareAddr randomly in case 192 // of real network interfaces absence 193 g.safeRandom(g.hardwareAddr[:]) 194 195 // Set multicast bit as recommended in RFC 4122 196 g.hardwareAddr[0] |= 0x01 197 } 198 199 func (g *generator) safeRandom(dest []byte) { 200 if _, err := rand.Read(dest); err != nil { 201 panic(err) 202 } 203 } 204 205 // Returns UUID v1/v2 storage state. 206 // Returns epoch timestamp, clock sequence, and hardware address. 207 func (g *generator) getStorage() (uint64, uint16, []byte) { 208 g.storageOnce.Do(g.initStorage) 209 210 g.storageMutex.Lock() 211 defer g.storageMutex.Unlock() 212 213 timeNow := epochFunc() 214 // Clock changed backwards since last UUID generation. 215 // Should increase clock sequence. 216 if timeNow <= g.lastTime { 217 g.clockSequence++ 218 } 219 g.lastTime = timeNow 220 221 return timeNow, g.clockSequence, g.hardwareAddr[:] 222 } 223 224 // Returns difference in 100-nanosecond intervals between 225 // UUID epoch (October 15, 1582) and current time. 226 // This is default epoch calculation function. 227 func unixTimeFunc() uint64 { 228 return epochStart + uint64(time.Now().UnixNano()/100) 229 } 230 231 // Returns UUID based on hashing of namespace UUID and name. 232 func newFromHash(h hash.Hash, ns UUID, name string) UUID { 233 u := UUID{} 234 h.Write(ns[:]) 235 h.Write([]byte(name)) 236 copy(u[:], h.Sum(nil)) 237 238 return u 239 }