| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293 |
- package timebased
- import (
- "crypto/rand"
- "encoding/binary"
- "net"
- "sync"
- "time"
- "github.com/golang-plus/uuid/internal"
- "github.com/golang-plus/errors"
- )
- const (
- // Intervals bewteen 1/1/1970 and 15/10/1582 (Julain days of 1 Jan 1970 - Julain days of 15 Oct 1582) * 100-Nanoseconds Per Day
- intervals = (2440587 - 2299160) * 86400 * 10000000
- )
- var (
- lastGenerated time.Time // last generated time
- clockSequence uint16 // clock sequence for same tick
- nodeID []byte // node id (MAC Address)
- locker sync.Mutex // global lock
- )
- // NewUUID returns a new time-based uuid.
- func NewUUID() ([]byte, error) {
- // Get and release a global lock
- locker.Lock()
- defer locker.Unlock()
- uuid := make([]byte, 16)
- // get timestamp
- now := time.Now().UTC()
- timestamp := uint64(now.UnixNano()/100) + intervals // get timestamp
- if !now.After(lastGenerated) {
- clockSequence++ // last generated time known, then just increment clock sequence
- } else {
- b := make([]byte, 2)
- _, err := rand.Read(b)
- if err != nil {
- return nil, errors.Wrap(err, "could not generate clock sequence")
- }
- clockSequence = uint16(int(b[0])<<8 | int(b[1])) // set to a random value (network byte order)
- }
- lastGenerated = now // remember the last generated time
- timeLow := uint32(timestamp & 0xffffffff)
- timeMiddle := uint16((timestamp >> 32) & 0xffff)
- timeHigh := uint16((timestamp >> 48) & 0xfff)
- // network byte order(BigEndian)
- binary.BigEndian.PutUint32(uuid[0:], timeLow)
- binary.BigEndian.PutUint16(uuid[4:], timeMiddle)
- binary.BigEndian.PutUint16(uuid[6:], timeHigh)
- binary.BigEndian.PutUint16(uuid[8:], clockSequence)
- // get node id(mac address)
- if nodeID == nil {
- interfaces, err := net.Interfaces()
- if err != nil {
- return nil, errors.Wrap(err, "could not get network interfaces")
- }
- for _, i := range interfaces {
- if len(i.HardwareAddr) >= 6 {
- nodeID = make([]byte, 6)
- copy(nodeID, i.HardwareAddr)
- break
- }
- }
- if nodeID == nil {
- nodeID = make([]byte, 6)
- _, err := rand.Read(nodeID)
- if err != nil {
- return nil, errors.Wrap(err, "could not generate node id")
- }
- }
- }
- copy(uuid[10:], nodeID)
- // set version(v1)
- internal.SetVersion(uuid, internal.VersionTimeBased)
- // set layout(RFC4122)
- internal.SetLayout(uuid, internal.LayoutRFC4122)
- return uuid, nil
- }
|