Skip to content

Instantly share code, notes, and snippets.

@SteveBate
Created November 14, 2015 17:50
Show Gist options
  • Save SteveBate/7fd8a2bd3551949a4e7e to your computer and use it in GitHub Desktop.
Save SteveBate/7fd8a2bd3551949a4e7e to your computer and use it in GitHub Desktop.
A package to create a unique identifier (UUID)
// Creates a unique identifier
package uuid
import (
"crypto/md5"
"crypto/rand"
"encoding/binary"
"fmt"
"io"
"os"
"sync/atomic"
"time"
)
type UniqueId string
// NewUniqueId returns a new unique UniqueId.
func NewUniqueId() UniqueId {
var b [12]byte
// Timestamp, 4 bytes, big endian
binary.BigEndian.PutUint32(b[:], uint32(time.Now().Unix()))
// Machine, first 3 bytes of md5(hostname)
b[4] = machineId[0]
b[5] = machineId[1]
b[6] = machineId[2]
// Pid, 2 bytes, specs don't specify endianness, but we use big endian.
pid := os.Getpid()
b[7] = byte(pid >> 8)
b[8] = byte(pid)
// Increment, 3 bytes, big endian
i := atomic.AddUint32(&UniqueIdCounter, 1)
b[9] = byte(i >> 16)
b[10] = byte(i >> 8)
b[11] = byte(i)
return UniqueId(b[:])
}
// String returns a string representation of the unique id
func (id UniqueId) String() string {
return fmt.Sprintf("%x", string(id))
}
// UniqueIdCounter is atomically incremented when generating a new UniqueId
// using NewUniqueId() function. It's used as a counter part of an id.
var UniqueIdCounter uint32 = readRandomUint32()
// readRandomUint32 returns a random UniqueIdCounter.
func readRandomUint32() uint32 {
var b [4]byte
_, err := io.ReadFull(rand.Reader, b[:])
if err != nil {
panic(fmt.Errorf("cannot read random object id: %v", err))
}
return uint32((uint32(b[0]) << 0) | (uint32(b[1]) << 8) | (uint32(b[2]) << 16) | (uint32(b[3]) << 24))
}
// machineId stores machine id generated once and used in subsequent calls
// to NewUniqueId function.
var machineId = readMachineId()
// readMachineId generates and returns a machine id.
// If this function fails to get the hostname it will cause a runtime error.
func readMachineId() []byte {
var sum [3]byte
id := sum[:]
hostname, err1 := os.Hostname()
if err1 != nil {
_, err2 := io.ReadFull(rand.Reader, id)
if err2 != nil {
panic(fmt.Errorf("cannot get hostname: %v; %v", err1, err2))
}
return id
}
hw := md5.New()
hw.Write([]byte(hostname))
copy(id, hw.Sum(nil))
return id
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment