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  }