Generating a CUID (Collision-resistant Unique Identifier) in Go can be achieved by following the core principles of CUID generation: timestamp, counter, machine fingerprint, and randomness. Here is an implementation in Go:
Steps to Generate a CUID in Go
- Timestamp: Get the current timestamp in milliseconds.
- Counter: Use a counter to handle multiple CUIDs generated in a short period.
- Fingerprint: Generate a machine-specific fingerprint.
- Randomness: Add random characters to further reduce the risk of collisions.
- Base36 Encoding: Encode the components using Base36 for readability.
Example Code
package main
import (
"crypto/md5"
"encoding/hex"
"fmt"
"math/rand"
"net"
"os"
"strings"
"sync"
"time"
)
var (
counter int
mutex sync.Mutex
)
const base36Chars = "0123456789abcdefghijklmnopqrstuvwxyz"
func encodeBase36(value int64) string {
if value == 0 {
return "0"
}
result := ""
for value > 0 {
remainder := value % 36
result = string(base36Chars[remainder]) + result
value /= 36
}
return result
}
func getMachineFingerprint() string {
hostname, err := os.Hostname()
if err != nil {
hostname = "unknown"
}
hash := md5.Sum([]byte(hostname))
return hex.EncodeToString(hash[:])[:4]
}
func getRandomString(length int) string {
rand.Seed(time.Now().UnixNano())
result := make([]byte, length)
for i := 0; i < length; i++ {
result[i] = base36Chars[rand.Intn(36)]
}
return string(result)
}
func generateCUID() string {
now := time.Now().UnixNano() / int64(time.Millisecond)
mutex.Lock()
if now == int64(counter) {
counter++
} else {
counter = 0
}
counterValue := counter
mutex.Unlock()
timestamp := encodeBase36(now)
counterStr := encodeBase36(int64(counterValue))
fingerprint := getMachineFingerprint()
randomStr := getRandomString(4)
return fmt.Sprintf("c%s%s%s%s", timestamp, counterStr, fingerprint, randomStr)
}
func main() {
fmt.Println(generateCUID())
}
Explanation
Base36 Encoding:
- The
encodeBase36
function converts a decimal number to a Base36 encoded string.
- The
Timestamp:
now := time.Now().UnixNano() / int64(time.Millisecond)
gets the current timestamp in milliseconds.
Counter:
- A counter is used to handle multiple CUIDs generated within the same millisecond. The counter is protected by a mutex to ensure thread safety.
Machine Fingerprint:
getMachineFingerprint
generates a fingerprint using the MD5 hash of the machine's hostname.
Random String:
getRandomString
generates a random string of specified length using Base36 characters.
Combine Components:
- The
generateCUID
function combines the timestamp, counter, fingerprint, and random parts to form the final CUID.
- The
Summary
This Go implementation of CUID generation uses available functions and libraries to handle timestamps, hashing, randomness, and thread-safe counters. This approach ensures that the generated CUIDs are unique, readable, and collision-resistant, making them suitable for various applications.