Source File
generator.go
Belonging Package
github.com/gofrs/uuid
// Copyright (C) 2013-2018 by Maxim Bublis <b@codemonkey.ru>//// Permission is hereby granted, free of charge, to any person obtaining// a copy of this software and associated documentation files (the// "Software"), to deal in the Software without restriction, including// without limitation the rights to use, copy, modify, merge, publish,// distribute, sublicense, and/or sell copies of the Software, and to// permit persons to whom the Software is furnished to do so, subject to// the following conditions://// The above copyright notice and this permission notice shall be// included in all copies or substantial portions of the Software.//// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.package uuidimport ()// Difference in 100-nanosecond intervals between// UUID epoch (October 15, 1582) and Unix epoch (January 1, 1970).const epochStart = 122192928000000000// EpochFunc is the function type used to provide the current time.type EpochFunc func() time.Time// HWAddrFunc is the function type used to provide hardware (MAC) addresses.type HWAddrFunc func() (net.HardwareAddr, error)// DefaultGenerator is the default UUID Generator used by this package.var DefaultGenerator Generator = NewGen()// NewV1 returns a UUID based on the current timestamp and MAC address.func () (UUID, error) {return DefaultGenerator.NewV1()}// NewV3 returns a UUID based on the MD5 hash of the namespace UUID and name.func ( UUID, string) UUID {return DefaultGenerator.NewV3(, )}// NewV4 returns a randomly generated UUID.func () (UUID, error) {return DefaultGenerator.NewV4()}// NewV5 returns a UUID based on SHA-1 hash of the namespace UUID and name.func ( UUID, string) UUID {return DefaultGenerator.NewV5(, )}// NewV6 returns a k-sortable UUID based on a timestamp and 48 bits of// pseudorandom data. The timestamp in a V6 UUID is the same as V1, with the bit// order being adjusted to allow the UUID to be k-sortable.//// This is implemented based on revision 03 of the Peabody UUID draft, and may// be subject to change pending further revisions. Until the final specification// revision is finished, changes required to implement updates to the spec will// not be considered a breaking change. They will happen as a minor version// releases until the spec is final.func () (UUID, error) {return DefaultGenerator.NewV6()}// NewV7 returns a k-sortable UUID based on the current millisecond precision// UNIX epoch and 74 bits of pseudorandom data. It supports single-node batch generation (multiple UUIDs in the same timestamp) with a Monotonic Random counter.//// This is implemented based on revision 04 of the Peabody UUID draft, and may// be subject to change pending further revisions. Until the final specification// revision is finished, changes required to implement updates to the spec will// not be considered a breaking change. They will happen as a minor version// releases until the spec is final.func () (UUID, error) {return DefaultGenerator.NewV7()}// Generator provides an interface for generating UUIDs.type Generator interface {NewV1() (UUID, error)NewV3(ns UUID, name string) UUIDNewV4() (UUID, error)NewV5(ns UUID, name string) UUIDNewV6() (UUID, error)NewV7() (UUID, error)}// Gen is a reference UUID generator based on the specifications laid out in// RFC-4122 and DCE 1.1: Authentication and Security Services. This type// satisfies the Generator interface as defined in this package.//// For consumers who are generating V1 UUIDs, but don't want to expose the MAC// address of the node generating the UUIDs, the NewGenWithHWAF() function has been// provided as a convenience. See the function's documentation for more info.//// The authors of this package do not feel that the majority of users will need// to obfuscate their MAC address, and so we recommend using NewGen() to create// a new generator.type Gen struct {clockSequenceOnce sync.OncehardwareAddrOnce sync.OncestorageMutex sync.Mutexrand io.ReaderepochFunc EpochFunchwAddrFunc HWAddrFunclastTime uint64clockSequence uint16hardwareAddr [6]byte}// GenOption is a function type that can be used to configure a Gen generator.type GenOption func(*Gen)// interface check -- build will fail if *Gen doesn't satisfy Generatorvar _ Generator = (*Gen)(nil)// NewGen returns a new instance of Gen with some default values set. Most// people should use this.func () *Gen {return NewGenWithHWAF(defaultHWAddrFunc)}// NewGenWithHWAF builds a new UUID generator with the HWAddrFunc provided. Most// consumers should use NewGen() instead.//// This is used so that consumers can generate their own MAC addresses, for use// in the generated UUIDs, if there is some concern about exposing the physical// address of the machine generating the UUID.//// The Gen generator will only invoke the HWAddrFunc once, and cache that MAC// address for all the future UUIDs generated by it. If you'd like to switch the// MAC address being used, you'll need to create a new generator using this// function.func ( HWAddrFunc) *Gen {return NewGenWithOptions(WithHWAddrFunc())}// NewGenWithOptions returns a new instance of Gen with the options provided.// Most people should use NewGen() or NewGenWithHWAF() instead.//// To customize the generator, you can pass in one or more GenOption functions.// For example://// gen := NewGenWithOptions(// WithHWAddrFunc(myHWAddrFunc),// WithEpochFunc(myEpochFunc),// WithRandomReader(myRandomReader),// )//// NewGenWithOptions(WithHWAddrFunc(myHWAddrFunc)) is equivalent to calling// NewGenWithHWAF(myHWAddrFunc)// NewGenWithOptions() is equivalent to calling NewGen()func ( ...GenOption) *Gen {:= &Gen{epochFunc: time.Now,hwAddrFunc: defaultHWAddrFunc,rand: rand.Reader,}for , := range {()}return}// WithHWAddrFunc is a GenOption that allows you to provide your own HWAddrFunc// function.// When this option is nil, the defaultHWAddrFunc is used.func ( HWAddrFunc) GenOption {return func( *Gen) {if == nil {= defaultHWAddrFunc}.hwAddrFunc =}}// WithEpochFunc is a GenOption that allows you to provide your own EpochFunc// function.// When this option is nil, time.Now is used.func ( EpochFunc) GenOption {return func( *Gen) {if == nil {= time.Now}.epochFunc =}}// WithRandomReader is a GenOption that allows you to provide your own random// reader.// When this option is nil, the default rand.Reader is used.func ( io.Reader) GenOption {return func( *Gen) {if == nil {= rand.Reader}.rand =}}// NewV1 returns a UUID based on the current timestamp and MAC address.func ( *Gen) () (UUID, error) {:= UUID{}, , := .getClockSequence(false)if != nil {return Nil,}binary.BigEndian.PutUint32([0:], uint32())binary.BigEndian.PutUint16([4:], uint16(>>32))binary.BigEndian.PutUint16([6:], uint16(>>48))binary.BigEndian.PutUint16([8:], ), := .getHardwareAddr()if != nil {return Nil,}copy([10:], ).SetVersion(V1).SetVariant(VariantRFC4122)return , nil}// NewV3 returns a UUID based on the MD5 hash of the namespace UUID and name.func ( *Gen) ( UUID, string) UUID {:= newFromHash(md5.New(), , ).SetVersion(V3).SetVariant(VariantRFC4122)return}// NewV4 returns a randomly generated UUID.func ( *Gen) () (UUID, error) {:= UUID{}if , := io.ReadFull(.rand, [:]); != nil {return Nil,}.SetVersion(V4).SetVariant(VariantRFC4122)return , nil}// NewV5 returns a UUID based on SHA-1 hash of the namespace UUID and name.func ( *Gen) ( UUID, string) UUID {:= newFromHash(sha1.New(), , ).SetVersion(V5).SetVariant(VariantRFC4122)return}// NewV6 returns a k-sortable UUID based on a timestamp and 48 bits of// pseudorandom data. The timestamp in a V6 UUID is the same as V1, with the bit// order being adjusted to allow the UUID to be k-sortable.//// This is implemented based on revision 03 of the Peabody UUID draft, and may// be subject to change pending further revisions. Until the final specification// revision is finished, changes required to implement updates to the spec will// not be considered a breaking change. They will happen as a minor version// releases until the spec is final.func ( *Gen) () (UUID, error) {var UUIDif , := io.ReadFull(.rand, [10:]); != nil {return Nil,}, , := .getClockSequence(false)if != nil {return Nil,}binary.BigEndian.PutUint32([0:], uint32(>>28)) // set time_highbinary.BigEndian.PutUint16([4:], uint16(>>12)) // set time_midbinary.BigEndian.PutUint16([6:], uint16(&0xfff)) // set time_low (minus four version bits)binary.BigEndian.PutUint16([8:], &0x3fff) // set clk_seq_hi_res (minus two variant bits).SetVersion(V6).SetVariant(VariantRFC4122)return , nil}// getClockSequence returns the epoch and clock sequence for V1,V6 and V7 UUIDs.//// When useUnixTSMs is false, it uses the Coordinated Universal Time (UTC) as a count of 100-//// nanosecond intervals since 00:00:00.00, 15 October 1582 (the date of Gregorian reform to the Christian calendar).func ( *Gen) ( bool) (uint64, uint16, error) {var error.clockSequenceOnce.Do(func() {:= make([]byte, 2)if _, = io.ReadFull(.rand, ); != nil {return}.clockSequence = binary.BigEndian.Uint16()})if != nil {return 0, 0,}.storageMutex.Lock()defer .storageMutex.Unlock()var uint64if {= uint64(.epochFunc().UnixMilli())} else {= .getEpoch()}// Clock didn't change since last UUID generation.// Should increase clock sequence.if <= .lastTime {.clockSequence++}.lastTime =return , .clockSequence, nil}// NewV7 returns a k-sortable UUID based on the current millisecond precision// UNIX epoch and 74 bits of pseudorandom data.//// This is implemented based on revision 04 of the Peabody UUID draft, and may// be subject to change pending further revisions. Until the final specification// revision is finished, changes required to implement updates to the spec will// not be considered a breaking change. They will happen as a minor version// releases until the spec is final.func ( *Gen) () (UUID, error) {var UUID/* https://www.ietf.org/archive/id/draft-peabody-dispatch-new-uuid-format-04.html#name-uuid-version-70 1 2 30 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+| unix_ts_ms |+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+| unix_ts_ms | ver | rand_a |+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+|var| rand_b |+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+| rand_b |+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ */, , := .getClockSequence(true)if != nil {return Nil,}//UUIDv7 features a 48 bit timestamp. First 32bit (4bytes) represents seconds since 1970, followed by 2 bytes for the ms granularity.[0] = byte( >> 40) //1-6 bytes: big-endian unsigned number of Unix epoch timestamp[1] = byte( >> 32)[2] = byte( >> 24)[3] = byte( >> 16)[4] = byte( >> 8)[5] = byte()//support batching by using a monotonic pseudo-random sequence//The 6th byte contains the version and partially rand_a data.//We will lose the most significant bites from the clockSeq (with SetVersion), but it is ok, we need the least significant that contains the counter to ensure the monotonic propertybinary.BigEndian.PutUint16([6:8], ) // set rand_a with clock seq which is random and monotonic//override first 4bits of u[6]..SetVersion(V7)//set rand_b 64bits of pseudo-random bits (first 2 will be overridden)if _, = io.ReadFull(.rand, [8:16]); != nil {return Nil,}//override first 2 bits of byte[8] for the variant.SetVariant(VariantRFC4122)return , nil}// Returns the hardware address.func ( *Gen) () ([]byte, error) {var error.hardwareAddrOnce.Do(func() {var net.HardwareAddrif , = .hwAddrFunc(); == nil {copy(.hardwareAddr[:], )return}// Initialize hardwareAddr randomly in case// of real network interfaces absence.if _, = io.ReadFull(.rand, .hardwareAddr[:]); != nil {return}// Set multicast bit as recommended by RFC-4122.hardwareAddr[0] |= 0x01})if != nil {return []byte{},}return .hardwareAddr[:], nil}// Returns the difference between UUID epoch (October 15, 1582)// and current time in 100-nanosecond intervals.func ( *Gen) () uint64 {return epochStart + uint64(.epochFunc().UnixNano()/100)}// Returns the UUID based on the hashing of the namespace UUID and name.func newFromHash( hash.Hash, UUID, string) UUID {:= UUID{}.Write([:]).Write([]byte())copy([:], .Sum(nil))return}var netInterfaces = net.Interfaces// Returns the hardware address.func defaultHWAddrFunc() (net.HardwareAddr, error) {, := netInterfaces()if != nil {return []byte{},}for , := range {if len(.HardwareAddr) >= 6 {return .HardwareAddr, nil}}return []byte{}, fmt.Errorf("uuid: no HW address found")}
![]() |
The pages are generated with Golds v0.6.7. (GOOS=linux GOARCH=amd64) Golds is a Go 101 project developed by Tapir Liu. PR and bug reports are welcome and can be submitted to the issue list. Please follow @Go100and1 (reachable from the left QR code) to get the latest news of Golds. |