github.com/artisanhe/tools@v1.0.1-0.20210607022958-19a8fef2eb04/misc/unique_str/rand_id.go (about) 1 package unique_str 2 3 import ( 4 "math/rand" 5 "sync" 6 "time" 7 ) 8 9 const ( 10 // Modeled after base64 web-safe chars, but ordered by ASCII. 11 pushChars = "-0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz" 12 ) 13 14 var ( 15 // Timestamp of last push, used to prevent local collisions if you push twice in one ms. 16 lastPushTimeMs int64 17 // We generate 72-bits of randomness which get turned into 12 characters and appended to the 18 // timestamp to prevent collisions with other clients. We store the last characters we 19 // generated because in the event of a collision, we'll use those same characters except 20 // "incremented" by one. 21 lastRandChars [12]int 22 mu sync.Mutex 23 rnd *rand.Rand 24 ) 25 26 func init() { 27 // seed to get randomness 28 rnd = rand.New(rand.NewSource(time.Now().UnixNano())) 29 } 30 31 func genRandPart() { 32 for i := 0; i < len(lastRandChars); i++ { 33 lastRandChars[i] = rnd.Intn(64) 34 } 35 } 36 37 // New creates a new random, unique id len >12 38 func New() string { 39 var id [4 + 12]byte 40 mu.Lock() 41 timeMs := time.Now().UTC().UnixNano() / 1e6 42 if timeMs == lastPushTimeMs { 43 // increment lastRandChars 44 for i := 0; i < 12; i++ { 45 lastRandChars[i]++ 46 if lastRandChars[i] < 64 { 47 break 48 } 49 // increment the next byte 50 lastRandChars[i] = 0 51 } 52 } else { 53 genRandPart() 54 } 55 lastPushTimeMs = timeMs 56 // put random as the second part 57 for i := 0; i < 12; i++ { 58 id[15-i] = pushChars[lastRandChars[i]] 59 } 60 mu.Unlock() 61 62 // put current time at the beginning 63 for i := 3; i >= 0; i-- { 64 n := int(timeMs % 64) 65 id[i] = pushChars[n] 66 timeMs = timeMs / 64 67 } 68 return string(id[:]) 69 }