...

Source file src/github.com/gofrs/uuid/generator.go

Documentation: github.com/gofrs/uuid

     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  	"fmt"
    30  	"hash"
    31  	"io"
    32  	"net"
    33  	"sync"
    34  	"time"
    35  )
    36  
    37  // Difference in 100-nanosecond intervals between
    38  // UUID epoch (October 15, 1582) and Unix epoch (January 1, 1970).
    39  const epochStart = 122192928000000000
    40  
    41  type epochFunc func() time.Time
    42  
    43  // HWAddrFunc is the function type used to provide hardware (MAC) addresses.
    44  type HWAddrFunc func() (net.HardwareAddr, error)
    45  
    46  // DefaultGenerator is the default UUID Generator used by this package.
    47  var DefaultGenerator Generator = NewGen()
    48  
    49  // NewV1 returns a UUID based on the current timestamp and MAC address.
    50  func NewV1() (UUID, error) {
    51  	return DefaultGenerator.NewV1()
    52  }
    53  
    54  // NewV3 returns a UUID based on the MD5 hash of the namespace UUID and name.
    55  func NewV3(ns UUID, name string) UUID {
    56  	return DefaultGenerator.NewV3(ns, name)
    57  }
    58  
    59  // NewV4 returns a randomly generated UUID.
    60  func NewV4() (UUID, error) {
    61  	return DefaultGenerator.NewV4()
    62  }
    63  
    64  // NewV5 returns a UUID based on SHA-1 hash of the namespace UUID and name.
    65  func NewV5(ns UUID, name string) UUID {
    66  	return DefaultGenerator.NewV5(ns, name)
    67  }
    68  
    69  // NewV6 returns a k-sortable UUID based on a timestamp and 48 bits of
    70  // pseudorandom data. The timestamp in a V6 UUID is the same as V1, with the bit
    71  // order being adjusted to allow the UUID to be k-sortable.
    72  //
    73  // This is implemented based on revision 03 of the Peabody UUID draft, and may
    74  // be subject to change pending further revisions. Until the final specification
    75  // revision is finished, changes required to implement updates to the spec will
    76  // not be considered a breaking change. They will happen as a minor version
    77  // releases until the spec is final.
    78  func NewV6() (UUID, error) {
    79  	return DefaultGenerator.NewV6()
    80  }
    81  
    82  // NewV7 returns a k-sortable UUID based on the current millisecond precision
    83  // UNIX epoch and 74 bits of pseudorandom data.
    84  //
    85  // This is implemented based on revision 03 of the Peabody UUID draft, and may
    86  // be subject to change pending further revisions. Until the final specification
    87  // revision is finished, changes required to implement updates to the spec will
    88  // not be considered a breaking change. They will happen as a minor version
    89  // releases until the spec is final.
    90  func NewV7() (UUID, error) {
    91  	return DefaultGenerator.NewV7()
    92  }
    93  
    94  // Generator provides an interface for generating UUIDs.
    95  type Generator interface {
    96  	NewV1() (UUID, error)
    97  	NewV3(ns UUID, name string) UUID
    98  	NewV4() (UUID, error)
    99  	NewV5(ns UUID, name string) UUID
   100  	NewV6() (UUID, error)
   101  	NewV7() (UUID, error)
   102  }
   103  
   104  // Gen is a reference UUID generator based on the specifications laid out in
   105  // RFC-4122 and DCE 1.1: Authentication and Security Services. This type
   106  // satisfies the Generator interface as defined in this package.
   107  //
   108  // For consumers who are generating V1 UUIDs, but don't want to expose the MAC
   109  // address of the node generating the UUIDs, the NewGenWithHWAF() function has been
   110  // provided as a convenience. See the function's documentation for more info.
   111  //
   112  // The authors of this package do not feel that the majority of users will need
   113  // to obfuscate their MAC address, and so we recommend using NewGen() to create
   114  // a new generator.
   115  type Gen struct {
   116  	clockSequenceOnce sync.Once
   117  	hardwareAddrOnce  sync.Once
   118  	storageMutex      sync.Mutex
   119  
   120  	rand io.Reader
   121  
   122  	epochFunc     epochFunc
   123  	hwAddrFunc    HWAddrFunc
   124  	lastTime      uint64
   125  	clockSequence uint16
   126  	hardwareAddr  [6]byte
   127  }
   128  
   129  // interface check -- build will fail if *Gen doesn't satisfy Generator
   130  var _ Generator = (*Gen)(nil)
   131  
   132  // NewGen returns a new instance of Gen with some default values set. Most
   133  // people should use this.
   134  func NewGen() *Gen {
   135  	return NewGenWithHWAF(defaultHWAddrFunc)
   136  }
   137  
   138  // NewGenWithHWAF builds a new UUID generator with the HWAddrFunc provided. Most
   139  // consumers should use NewGen() instead.
   140  //
   141  // This is used so that consumers can generate their own MAC addresses, for use
   142  // in the generated UUIDs, if there is some concern about exposing the physical
   143  // address of the machine generating the UUID.
   144  //
   145  // The Gen generator will only invoke the HWAddrFunc once, and cache that MAC
   146  // address for all the future UUIDs generated by it. If you'd like to switch the
   147  // MAC address being used, you'll need to create a new generator using this
   148  // function.
   149  func NewGenWithHWAF(hwaf HWAddrFunc) *Gen {
   150  	return &Gen{
   151  		epochFunc:  time.Now,
   152  		hwAddrFunc: hwaf,
   153  		rand:       rand.Reader,
   154  	}
   155  }
   156  
   157  // NewV1 returns a UUID based on the current timestamp and MAC address.
   158  func (g *Gen) NewV1() (UUID, error) {
   159  	u := UUID{}
   160  
   161  	timeNow, clockSeq, err := g.getClockSequence()
   162  	if err != nil {
   163  		return Nil, err
   164  	}
   165  	binary.BigEndian.PutUint32(u[0:], uint32(timeNow))
   166  	binary.BigEndian.PutUint16(u[4:], uint16(timeNow>>32))
   167  	binary.BigEndian.PutUint16(u[6:], uint16(timeNow>>48))
   168  	binary.BigEndian.PutUint16(u[8:], clockSeq)
   169  
   170  	hardwareAddr, err := g.getHardwareAddr()
   171  	if err != nil {
   172  		return Nil, err
   173  	}
   174  	copy(u[10:], hardwareAddr)
   175  
   176  	u.SetVersion(V1)
   177  	u.SetVariant(VariantRFC4122)
   178  
   179  	return u, nil
   180  }
   181  
   182  // NewV3 returns a UUID based on the MD5 hash of the namespace UUID and name.
   183  func (g *Gen) NewV3(ns UUID, name string) UUID {
   184  	u := newFromHash(md5.New(), ns, name)
   185  	u.SetVersion(V3)
   186  	u.SetVariant(VariantRFC4122)
   187  
   188  	return u
   189  }
   190  
   191  // NewV4 returns a randomly generated UUID.
   192  func (g *Gen) NewV4() (UUID, error) {
   193  	u := UUID{}
   194  	if _, err := io.ReadFull(g.rand, u[:]); err != nil {
   195  		return Nil, err
   196  	}
   197  	u.SetVersion(V4)
   198  	u.SetVariant(VariantRFC4122)
   199  
   200  	return u, nil
   201  }
   202  
   203  // NewV5 returns a UUID based on SHA-1 hash of the namespace UUID and name.
   204  func (g *Gen) NewV5(ns UUID, name string) UUID {
   205  	u := newFromHash(sha1.New(), ns, name)
   206  	u.SetVersion(V5)
   207  	u.SetVariant(VariantRFC4122)
   208  
   209  	return u
   210  }
   211  
   212  // NewV6 returns a k-sortable UUID based on a timestamp and 48 bits of
   213  // pseudorandom data. The timestamp in a V6 UUID is the same as V1, with the bit
   214  // order being adjusted to allow the UUID to be k-sortable.
   215  //
   216  // This is implemented based on revision 03 of the Peabody UUID draft, and may
   217  // be subject to change pending further revisions. Until the final specification
   218  // revision is finished, changes required to implement updates to the spec will
   219  // not be considered a breaking change. They will happen as a minor version
   220  // releases until the spec is final.
   221  func (g *Gen) NewV6() (UUID, error) {
   222  	var u UUID
   223  
   224  	if _, err := io.ReadFull(g.rand, u[10:]); err != nil {
   225  		return Nil, err
   226  	}
   227  
   228  	timeNow, clockSeq, err := g.getClockSequence()
   229  	if err != nil {
   230  		return Nil, err
   231  	}
   232  
   233  	binary.BigEndian.PutUint32(u[0:], uint32(timeNow>>28))   // set time_high
   234  	binary.BigEndian.PutUint16(u[4:], uint16(timeNow>>12))   // set time_mid
   235  	binary.BigEndian.PutUint16(u[6:], uint16(timeNow&0xfff)) // set time_low (minus four version bits)
   236  	binary.BigEndian.PutUint16(u[8:], clockSeq&0x3fff)       // set clk_seq_hi_res (minus two variant bits)
   237  
   238  	u.SetVersion(V6)
   239  	u.SetVariant(VariantRFC4122)
   240  
   241  	return u, nil
   242  }
   243  
   244  // getClockSequence returns the epoch and clock sequence for V1 and V6 UUIDs.
   245  func (g *Gen) getClockSequence() (uint64, uint16, error) {
   246  	var err error
   247  	g.clockSequenceOnce.Do(func() {
   248  		buf := make([]byte, 2)
   249  		if _, err = io.ReadFull(g.rand, buf); err != nil {
   250  			return
   251  		}
   252  		g.clockSequence = binary.BigEndian.Uint16(buf)
   253  	})
   254  	if err != nil {
   255  		return 0, 0, err
   256  	}
   257  
   258  	g.storageMutex.Lock()
   259  	defer g.storageMutex.Unlock()
   260  
   261  	timeNow := g.getEpoch()
   262  	// Clock didn't change since last UUID generation.
   263  	// Should increase clock sequence.
   264  	if timeNow <= g.lastTime {
   265  		g.clockSequence++
   266  	}
   267  	g.lastTime = timeNow
   268  
   269  	return timeNow, g.clockSequence, nil
   270  }
   271  
   272  // NewV7 returns a k-sortable UUID based on the current millisecond precision
   273  // UNIX epoch and 74 bits of pseudorandom data.
   274  //
   275  // This is implemented based on revision 03 of the Peabody UUID draft, and may
   276  // be subject to change pending further revisions. Until the final specification
   277  // revision is finished, changes required to implement updates to the spec will
   278  // not be considered a breaking change. They will happen as a minor version
   279  // releases until the spec is final.
   280  func (g *Gen) NewV7() (UUID, error) {
   281  	var u UUID
   282  
   283  	if _, err := io.ReadFull(g.rand, u[6:]); err != nil {
   284  		return Nil, err
   285  	}
   286  
   287  	tn := g.epochFunc()
   288  	ms := uint64(tn.Unix())*1e3 + uint64(tn.Nanosecond())/1e6
   289  	u[0] = byte(ms >> 40)
   290  	u[1] = byte(ms >> 32)
   291  	u[2] = byte(ms >> 24)
   292  	u[3] = byte(ms >> 16)
   293  	u[4] = byte(ms >> 8)
   294  	u[5] = byte(ms)
   295  
   296  	u.SetVersion(V7)
   297  	u.SetVariant(VariantRFC4122)
   298  
   299  	return u, nil
   300  }
   301  
   302  // Returns the hardware address.
   303  func (g *Gen) getHardwareAddr() ([]byte, error) {
   304  	var err error
   305  	g.hardwareAddrOnce.Do(func() {
   306  		var hwAddr net.HardwareAddr
   307  		if hwAddr, err = g.hwAddrFunc(); err == nil {
   308  			copy(g.hardwareAddr[:], hwAddr)
   309  			return
   310  		}
   311  
   312  		// Initialize hardwareAddr randomly in case
   313  		// of real network interfaces absence.
   314  		if _, err = io.ReadFull(g.rand, g.hardwareAddr[:]); err != nil {
   315  			return
   316  		}
   317  		// Set multicast bit as recommended by RFC-4122
   318  		g.hardwareAddr[0] |= 0x01
   319  	})
   320  	if err != nil {
   321  		return []byte{}, err
   322  	}
   323  	return g.hardwareAddr[:], nil
   324  }
   325  
   326  // Returns the difference between UUID epoch (October 15, 1582)
   327  // and current time in 100-nanosecond intervals.
   328  func (g *Gen) getEpoch() uint64 {
   329  	return epochStart + uint64(g.epochFunc().UnixNano()/100)
   330  }
   331  
   332  // Returns the UUID based on the hashing of the namespace UUID and name.
   333  func newFromHash(h hash.Hash, ns UUID, name string) UUID {
   334  	u := UUID{}
   335  	h.Write(ns[:])
   336  	h.Write([]byte(name))
   337  	copy(u[:], h.Sum(nil))
   338  
   339  	return u
   340  }
   341  
   342  var netInterfaces = net.Interfaces
   343  
   344  // Returns the hardware address.
   345  func defaultHWAddrFunc() (net.HardwareAddr, error) {
   346  	ifaces, err := netInterfaces()
   347  	if err != nil {
   348  		return []byte{}, err
   349  	}
   350  	for _, iface := range ifaces {
   351  		if len(iface.HardwareAddr) >= 6 {
   352  			return iface.HardwareAddr, nil
   353  		}
   354  	}
   355  	return []byte{}, fmt.Errorf("uuid: no HW address found")
   356  }
   357  

View as plain text