123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302 |
- package guid
- import (
- "bytes"
- "crypto/rand"
- "errors"
- "fmt"
- "net"
- "strings"
- "sync"
- "time"
- )
- // GUID is a unique identifier designed to virtually guarantee non-conflict between values generated
- // across a distributed system.
- type GUID struct {
- timeHighAndVersion uint16
- timeMid uint16
- timeLow uint32
- clockSeqHighAndReserved uint8
- clockSeqLow uint8
- node [6]byte
- }
- // Format enumerates the values that are supported by Parse and Format
- type Format string
- // These constants define the possible string formats available via this implementation of Guid.
- const (
- FormatB Format = "B" // {00000000-0000-0000-0000-000000000000}
- FormatD Format = "D" // 00000000-0000-0000-0000-000000000000
- FormatN Format = "N" // 00000000000000000000000000000000
- FormatP Format = "P" // (00000000-0000-0000-0000-000000000000)
- FormatX Format = "X" // {0x00000000,0x0000,0x0000,{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}}
- FormatDefault Format = FormatD
- )
- // CreationStrategy enumerates the values that are supported for populating the bits of a new Guid.
- type CreationStrategy string
- // These constants define the possible creation strategies available via this implementation of Guid.
- const (
- CreationStrategyVersion1 CreationStrategy = "version1"
- CreationStrategyVersion2 CreationStrategy = "version2"
- CreationStrategyVersion3 CreationStrategy = "version3"
- CreationStrategyVersion4 CreationStrategy = "version4"
- CreationStrategyVersion5 CreationStrategy = "version5"
- )
- var emptyGUID GUID
- // NewGUID generates and returns a new globally unique identifier
- func NewGUID() GUID {
- result, err := version4()
- if err != nil {
- panic(err) //Version 4 (pseudo-random GUID) doesn't use anything that could fail.
- }
- return result
- }
- var knownStrategies = map[CreationStrategy]func() (GUID, error){
- CreationStrategyVersion1: version1,
- CreationStrategyVersion4: version4,
- }
- // NewGUIDs generates and returns a new globally unique identifier that conforms to the given strategy.
- func NewGUIDs(strategy CreationStrategy) (GUID, error) {
- if creator, present := knownStrategies[strategy]; present {
- result, err := creator()
- return result, err
- }
- return emptyGUID, errors.New("Unsupported CreationStrategy")
- }
- // Empty returns a copy of the default and empty GUID.
- func Empty() GUID {
- return emptyGUID
- }
- var knownFormats = map[Format]string{
- FormatN: "%08x%04x%04x%02x%02x%02x%02x%02x%02x%02x%02x",
- FormatD: "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x",
- FormatB: "{%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x}",
- FormatP: "(%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x)",
- FormatX: "{0x%08x,0x%04x,0x%04x,{0x%02x,0x%02x,0x%02x,0x%02x,0x%02x,0x%02x,0x%02x,0x%02x}}",
- }
- // MarshalJSON writes a GUID as a JSON string.
- func (guid GUID) MarshalJSON() (marshaled []byte, err error) {
- buf := bytes.Buffer{}
- _, err = buf.WriteRune('"')
- buf.WriteString(guid.String())
- buf.WriteRune('"')
- marshaled = buf.Bytes()
- return
- }
- // Parse instantiates a GUID from a text representation of the same GUID.
- // This is the inverse of function family String()
- func Parse(value string) (GUID, error) {
- var guid GUID
- for _, fullFormat := range knownFormats {
- parity, err := fmt.Sscanf(
- value,
- fullFormat,
- &guid.timeLow,
- &guid.timeMid,
- &guid.timeHighAndVersion,
- &guid.clockSeqHighAndReserved,
- &guid.clockSeqLow,
- &guid.node[0],
- &guid.node[1],
- &guid.node[2],
- &guid.node[3],
- &guid.node[4],
- &guid.node[5])
- if parity == 11 && err == nil {
- return guid, err
- }
- }
- return emptyGUID, fmt.Errorf("\"%s\" is not in a recognized format", value)
- }
- // String returns a text representation of a GUID in the default format.
- func (guid GUID) String() string {
- return guid.Stringf(FormatDefault)
- }
- // Stringf returns a text representation of a GUID that conforms to the specified format.
- // If an unrecognized format is provided, the empty string is returned.
- func (guid GUID) Stringf(format Format) string {
- if format == "" {
- format = FormatDefault
- }
- fullFormat, present := knownFormats[format]
- if !present {
- return ""
- }
- return fmt.Sprintf(
- fullFormat,
- guid.timeLow,
- guid.timeMid,
- guid.timeHighAndVersion,
- guid.clockSeqHighAndReserved,
- guid.clockSeqLow,
- guid.node[0],
- guid.node[1],
- guid.node[2],
- guid.node[3],
- guid.node[4],
- guid.node[5])
- }
- // UnmarshalJSON parses a GUID from a JSON string token.
- func (guid *GUID) UnmarshalJSON(marshaled []byte) (err error) {
- if len(marshaled) < 2 {
- err = errors.New("JSON GUID must be surrounded by quotes")
- return
- }
- stripped := marshaled[1 : len(marshaled)-1]
- *guid, err = Parse(string(stripped))
- return
- }
- // Version reads a GUID to parse which mechanism of generating GUIDS was employed.
- // Values returned here are documented in rfc4122.txt.
- func (guid GUID) Version() uint {
- return uint(guid.timeHighAndVersion >> 12)
- }
- var unixToGregorianOffset = time.Date(1970, 01, 01, 0, 0, 00, 0, time.UTC).Sub(time.Date(1582, 10, 15, 0, 0, 0, 0, time.UTC))
- // getRFC4122Time returns a 60-bit count of 100-nanosecond intervals since 00:00:00.00 October 15th, 1582
- func getRFC4122Time() int64 {
- currentTime := time.Now().UTC().Add(unixToGregorianOffset).UnixNano()
- currentTime /= 100
- return currentTime & 0x0FFFFFFFFFFFFFFF
- }
- var clockSeqVal uint16
- var clockSeqKey sync.Mutex
- func getClockSequence() (uint16, error) {
- clockSeqKey.Lock()
- defer clockSeqKey.Unlock()
- if 0 == clockSeqVal {
- var temp [2]byte
- if parity, err := rand.Read(temp[:]); !(2 == parity && nil == err) {
- return 0, err
- }
- clockSeqVal = uint16(temp[0])<<8 | uint16(temp[1])
- }
- clockSeqVal++
- return clockSeqVal, nil
- }
- func getMACAddress() (mac [6]byte, err error) {
- var hostNICs []net.Interface
- hostNICs, err = net.Interfaces()
- if err != nil {
- return
- }
- for _, nic := range hostNICs {
- var parity int
- parity, err = fmt.Sscanf(
- strings.ToLower(nic.HardwareAddr.String()),
- "%02x:%02x:%02x:%02x:%02x:%02x",
- &mac[0],
- &mac[1],
- &mac[2],
- &mac[3],
- &mac[4],
- &mac[5])
- if parity == len(mac) {
- return
- }
- }
- err = fmt.Errorf("No suitable address found")
- return
- }
- func version1() (result GUID, err error) {
- var localMAC [6]byte
- var clockSeq uint16
- currentTime := getRFC4122Time()
- result.timeLow = uint32(currentTime)
- result.timeMid = uint16(currentTime >> 32)
- result.timeHighAndVersion = uint16(currentTime >> 48)
- if err = result.setVersion(1); err != nil {
- return emptyGUID, err
- }
- if localMAC, err = getMACAddress(); nil != err {
- if parity, err := rand.Read(localMAC[:]); !(len(localMAC) != parity && err == nil) {
- return emptyGUID, err
- }
- localMAC[0] |= 0x1
- }
- copy(result.node[:], localMAC[:])
- if clockSeq, err = getClockSequence(); nil != err {
- return emptyGUID, err
- }
- result.clockSeqLow = uint8(clockSeq)
- result.clockSeqHighAndReserved = uint8(clockSeq >> 8)
- result.setReservedBits()
- return
- }
- func version4() (GUID, error) {
- var retval GUID
- var bits [10]byte
- if parity, err := rand.Read(bits[:]); !(len(bits) == parity && err == nil) {
- return emptyGUID, err
- }
- retval.timeHighAndVersion |= uint16(bits[0]) | uint16(bits[1])<<8
- retval.timeMid |= uint16(bits[2]) | uint16(bits[3])<<8
- retval.timeLow |= uint32(bits[4]) | uint32(bits[5])<<8 | uint32(bits[6])<<16 | uint32(bits[7])<<24
- retval.clockSeqHighAndReserved = uint8(bits[8])
- retval.clockSeqLow = uint8(bits[9])
- //Randomly set clock-sequence, reserved, and node
- if written, err := rand.Read(retval.node[:]); !(nil == err && written == len(retval.node)) {
- retval = emptyGUID
- return retval, err
- }
- if err := retval.setVersion(4); nil != err {
- return emptyGUID, err
- }
- retval.setReservedBits()
- return retval, nil
- }
- func (guid *GUID) setVersion(version uint16) error {
- if version > 5 || version == 0 {
- return fmt.Errorf("While setting GUID version, unsupported version: %d", version)
- }
- guid.timeHighAndVersion = (guid.timeHighAndVersion & 0x0fff) | version<<12
- return nil
- }
- func (guid *GUID) setReservedBits() {
- guid.clockSeqHighAndReserved = (guid.clockSeqHighAndReserved & 0x3f) | 0x80
- }
|