github.com/mymmsc/gox@v1.3.33/util/uuid/generator.go (about)

     1  // Copyright (C) 2013-2018 by Maxim Bublis <b@codemonkey.ru>
     2  //
     3  // Permission is hereby granted, free of charge, to any person obtaining
     4  // a copy of this software and associated documentation files (the
     5  // "Software"), to deal in the Software without restriction, including
     6  // without limitation the rights to use, copy, modify, merge, publish,
     7  // distribute, sublicense, and/or sell copies of the Software, and to
     8  // permit persons to whom the Software is furnished to do so, subject to
     9  // the following conditions:
    10  //
    11  // The above copyright notice and this permission notice shall be
    12  // included in all copies or substantial portions of the Software.
    13  //
    14  // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
    15  // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
    16  // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
    17  // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
    18  // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
    19  // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
    20  // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
    21  
    22  package uuid
    23  
    24  import (
    25  	"crypto/md5"
    26  	"crypto/rand"
    27  	"crypto/sha1"
    28  	"encoding/binary"
    29  	"hash"
    30  	"net"
    31  	"os"
    32  	"sync"
    33  	"time"
    34  )
    35  
    36  // Difference in 100-nanosecond intervals between
    37  // UUID epoch (October 15, 1582) and Unix epoch (January 1, 1970).
    38  const epochStart = 122192928000000000
    39  
    40  var (
    41  	global = newDefaultGenerator()
    42  
    43  	epochFunc = unixTimeFunc
    44  	posixUID  = uint32(os.Getuid())
    45  	posixGID  = uint32(os.Getgid())
    46  )
    47  
    48  // NewV1 returns UUID based on current timestamp and MAC address.
    49  func NewV1() UUID {
    50  	return global.NewV1()
    51  }
    52  
    53  // NewV2 returns DCE Security UUID based on POSIX UID/GID.
    54  func NewV2(domain byte) UUID {
    55  	return global.NewV2(domain)
    56  }
    57  
    58  // NewV3 returns UUID based on MD5 hash of namespace UUID and name.
    59  func NewV3(ns UUID, name string) UUID {
    60  	return global.NewV3(ns, name)
    61  }
    62  
    63  // NewV4 returns random generated UUID.
    64  func NewV4() UUID {
    65  	return global.NewV4()
    66  }
    67  
    68  // NewV5 returns UUID based on SHA-1 hash of namespace UUID and name.
    69  func NewV5(ns UUID, name string) UUID {
    70  	return global.NewV5(ns, name)
    71  }
    72  
    73  // Generator provides interface for generating UUIDs.
    74  type Generator interface {
    75  	NewV1() UUID
    76  	NewV2(domain byte) UUID
    77  	NewV3(ns UUID, name string) UUID
    78  	NewV4() UUID
    79  	NewV5(ns UUID, name string) UUID
    80  }
    81  
    82  // Default generator implementation.
    83  type generator struct {
    84  	storageOnce  sync.Once
    85  	storageMutex sync.Mutex
    86  
    87  	lastTime      uint64
    88  	clockSequence uint16
    89  	hardwareAddr  [6]byte
    90  }
    91  
    92  func newDefaultGenerator() Generator {
    93  	return &generator{}
    94  }
    95  
    96  // NewV1 returns UUID based on current timestamp and MAC address.
    97  func (g *generator) NewV1() UUID {
    98  	u := UUID{}
    99  
   100  	timeNow, clockSeq, hardwareAddr := g.getStorage()
   101  
   102  	binary.BigEndian.PutUint32(u[0:], uint32(timeNow))
   103  	binary.BigEndian.PutUint16(u[4:], uint16(timeNow>>32))
   104  	binary.BigEndian.PutUint16(u[6:], uint16(timeNow>>48))
   105  	binary.BigEndian.PutUint16(u[8:], clockSeq)
   106  
   107  	copy(u[10:], hardwareAddr)
   108  
   109  	u.SetVersion(V1)
   110  	u.SetVariant(VariantRFC4122)
   111  
   112  	return u
   113  }
   114  
   115  // NewV2 returns DCE Security UUID based on POSIX UID/GID.
   116  func (g *generator) NewV2(domain byte) UUID {
   117  	u := UUID{}
   118  
   119  	timeNow, clockSeq, hardwareAddr := g.getStorage()
   120  
   121  	switch domain {
   122  	case DomainPerson:
   123  		binary.BigEndian.PutUint32(u[0:], posixUID)
   124  	case DomainGroup:
   125  		binary.BigEndian.PutUint32(u[0:], posixGID)
   126  	}
   127  
   128  	binary.BigEndian.PutUint16(u[4:], uint16(timeNow>>32))
   129  	binary.BigEndian.PutUint16(u[6:], uint16(timeNow>>48))
   130  	binary.BigEndian.PutUint16(u[8:], clockSeq)
   131  	u[9] = domain
   132  
   133  	copy(u[10:], hardwareAddr)
   134  
   135  	u.SetVersion(V2)
   136  	u.SetVariant(VariantRFC4122)
   137  
   138  	return u
   139  }
   140  
   141  // NewV3 returns UUID based on MD5 hash of namespace UUID and name.
   142  func (g *generator) NewV3(ns UUID, name string) UUID {
   143  	u := newFromHash(md5.New(), ns, name)
   144  	u.SetVersion(V3)
   145  	u.SetVariant(VariantRFC4122)
   146  
   147  	return u
   148  }
   149  
   150  // NewV4 returns random generated UUID.
   151  func (g *generator) NewV4() UUID {
   152  	u := UUID{}
   153  	g.safeRandom(u[:])
   154  	u.SetVersion(V4)
   155  	u.SetVariant(VariantRFC4122)
   156  
   157  	return u
   158  }
   159  
   160  // NewV5 returns UUID based on SHA-1 hash of namespace UUID and name.
   161  func (g *generator) NewV5(ns UUID, name string) UUID {
   162  	u := newFromHash(sha1.New(), ns, name)
   163  	u.SetVersion(V5)
   164  	u.SetVariant(VariantRFC4122)
   165  
   166  	return u
   167  }
   168  
   169  func (g *generator) initStorage() {
   170  	g.initClockSequence()
   171  	g.initHardwareAddr()
   172  }
   173  
   174  func (g *generator) initClockSequence() {
   175  	buf := make([]byte, 2)
   176  	g.safeRandom(buf)
   177  	g.clockSequence = binary.BigEndian.Uint16(buf)
   178  }
   179  
   180  func (g *generator) initHardwareAddr() {
   181  	interfaces, err := net.Interfaces()
   182  	if err == nil {
   183  		for _, iface := range interfaces {
   184  			if len(iface.HardwareAddr) >= 6 {
   185  				copy(g.hardwareAddr[:], iface.HardwareAddr)
   186  				return
   187  			}
   188  		}
   189  	}
   190  
   191  	// Initialize hardwareAddr randomly in case
   192  	// of real network interfaces absence
   193  	g.safeRandom(g.hardwareAddr[:])
   194  
   195  	// Set multicast bit as recommended in RFC 4122
   196  	g.hardwareAddr[0] |= 0x01
   197  }
   198  
   199  func (g *generator) safeRandom(dest []byte) {
   200  	if _, err := rand.Read(dest); err != nil {
   201  		panic(err)
   202  	}
   203  }
   204  
   205  // Returns UUID v1/v2 storage state.
   206  // Returns epoch timestamp, clock sequence, and hardware address.
   207  func (g *generator) getStorage() (uint64, uint16, []byte) {
   208  	g.storageOnce.Do(g.initStorage)
   209  
   210  	g.storageMutex.Lock()
   211  	defer g.storageMutex.Unlock()
   212  
   213  	timeNow := epochFunc()
   214  	// Clock changed backwards since last UUID generation.
   215  	// Should increase clock sequence.
   216  	if timeNow <= g.lastTime {
   217  		g.clockSequence++
   218  	}
   219  	g.lastTime = timeNow
   220  
   221  	return timeNow, g.clockSequence, g.hardwareAddr[:]
   222  }
   223  
   224  // Returns difference in 100-nanosecond intervals between
   225  // UUID epoch (October 15, 1582) and current time.
   226  // This is default epoch calculation function.
   227  func unixTimeFunc() uint64 {
   228  	return epochStart + uint64(time.Now().UnixNano()/100)
   229  }
   230  
   231  // Returns UUID based on hashing of namespace UUID and name.
   232  func newFromHash(h hash.Hash, ns UUID, name string) UUID {
   233  	u := UUID{}
   234  	h.Write(ns[:])
   235  	h.Write([]byte(name))
   236  	copy(u[:], h.Sum(nil))
   237  
   238  	return u
   239  }