174 lines
4.7 KiB
Go
174 lines
4.7 KiB
Go
|
// This package provides immutable UUID structs and the functions
|
||
|
// NewV3, NewV4, NewV5 and Parse() for generating versions 3, 4
|
||
|
// and 5 UUIDs as specified in RFC 4122.
|
||
|
//
|
||
|
// Copyright (C) 2011 by Krzysztof Kowalik <chris@nu7hat.ch>
|
||
|
package uuid
|
||
|
|
||
|
import (
|
||
|
"crypto/md5"
|
||
|
"crypto/rand"
|
||
|
"crypto/sha1"
|
||
|
"encoding/hex"
|
||
|
"errors"
|
||
|
"fmt"
|
||
|
"hash"
|
||
|
"regexp"
|
||
|
)
|
||
|
|
||
|
// The UUID reserved variants.
|
||
|
const (
|
||
|
ReservedNCS byte = 0x80
|
||
|
ReservedRFC4122 byte = 0x40
|
||
|
ReservedMicrosoft byte = 0x20
|
||
|
ReservedFuture byte = 0x00
|
||
|
)
|
||
|
|
||
|
// The following standard UUIDs are for use with NewV3() or NewV5().
|
||
|
var (
|
||
|
NamespaceDNS, _ = ParseHex("6ba7b810-9dad-11d1-80b4-00c04fd430c8")
|
||
|
NamespaceURL, _ = ParseHex("6ba7b811-9dad-11d1-80b4-00c04fd430c8")
|
||
|
NamespaceOID, _ = ParseHex("6ba7b812-9dad-11d1-80b4-00c04fd430c8")
|
||
|
NamespaceX500, _ = ParseHex("6ba7b814-9dad-11d1-80b4-00c04fd430c8")
|
||
|
)
|
||
|
|
||
|
// Pattern used to parse hex string representation of the UUID.
|
||
|
// FIXME: do something to consider both brackets at one time,
|
||
|
// current one allows to parse string with only one opening
|
||
|
// or closing bracket.
|
||
|
const hexPattern = "^(urn\\:uuid\\:)?\\{?([a-z0-9]{8})-([a-z0-9]{4})-" +
|
||
|
"([1-5][a-z0-9]{3})-([a-z0-9]{4})-([a-z0-9]{12})\\}?$"
|
||
|
|
||
|
var re = regexp.MustCompile(hexPattern)
|
||
|
|
||
|
// A UUID representation compliant with specification in
|
||
|
// RFC 4122 document.
|
||
|
type UUID [16]byte
|
||
|
|
||
|
// ParseHex creates a UUID object from given hex string
|
||
|
// representation. Function accepts UUID string in following
|
||
|
// formats:
|
||
|
//
|
||
|
// uuid.ParseHex("6ba7b814-9dad-11d1-80b4-00c04fd430c8")
|
||
|
// uuid.ParseHex("{6ba7b814-9dad-11d1-80b4-00c04fd430c8}")
|
||
|
// uuid.ParseHex("urn:uuid:6ba7b814-9dad-11d1-80b4-00c04fd430c8")
|
||
|
//
|
||
|
func ParseHex(s string) (u *UUID, err error) {
|
||
|
md := re.FindStringSubmatch(s)
|
||
|
if md == nil {
|
||
|
err = errors.New("Invalid UUID string")
|
||
|
return
|
||
|
}
|
||
|
hash := md[2] + md[3] + md[4] + md[5] + md[6]
|
||
|
b, err := hex.DecodeString(hash)
|
||
|
if err != nil {
|
||
|
return
|
||
|
}
|
||
|
u = new(UUID)
|
||
|
copy(u[:], b)
|
||
|
return
|
||
|
}
|
||
|
|
||
|
// Parse creates a UUID object from given bytes slice.
|
||
|
func Parse(b []byte) (u *UUID, err error) {
|
||
|
if len(b) != 16 {
|
||
|
err = errors.New("Given slice is not valid UUID sequence")
|
||
|
return
|
||
|
}
|
||
|
u = new(UUID)
|
||
|
copy(u[:], b)
|
||
|
return
|
||
|
}
|
||
|
|
||
|
// Generate a UUID based on the MD5 hash of a namespace identifier
|
||
|
// and a name.
|
||
|
func NewV3(ns *UUID, name []byte) (u *UUID, err error) {
|
||
|
if ns == nil {
|
||
|
err = errors.New("Invalid namespace UUID")
|
||
|
return
|
||
|
}
|
||
|
u = new(UUID)
|
||
|
// Set all bits to MD5 hash generated from namespace and name.
|
||
|
u.setBytesFromHash(md5.New(), ns[:], name)
|
||
|
u.setVariant(ReservedRFC4122)
|
||
|
u.setVersion(3)
|
||
|
return
|
||
|
}
|
||
|
|
||
|
// Generate a random UUID.
|
||
|
func NewV4() (u *UUID, err error) {
|
||
|
u = new(UUID)
|
||
|
// Set all bits to randomly (or pseudo-randomly) chosen values.
|
||
|
_, err = rand.Read(u[:])
|
||
|
if err != nil {
|
||
|
return
|
||
|
}
|
||
|
u.setVariant(ReservedRFC4122)
|
||
|
u.setVersion(4)
|
||
|
return
|
||
|
}
|
||
|
|
||
|
// Generate a UUID based on the SHA-1 hash of a namespace identifier
|
||
|
// and a name.
|
||
|
func NewV5(ns *UUID, name []byte) (u *UUID, err error) {
|
||
|
u = new(UUID)
|
||
|
// Set all bits to truncated SHA1 hash generated from namespace
|
||
|
// and name.
|
||
|
u.setBytesFromHash(sha1.New(), ns[:], name)
|
||
|
u.setVariant(ReservedRFC4122)
|
||
|
u.setVersion(5)
|
||
|
return
|
||
|
}
|
||
|
|
||
|
// Generate a MD5 hash of a namespace and a name, and copy it to the
|
||
|
// UUID slice.
|
||
|
func (u *UUID) setBytesFromHash(hash hash.Hash, ns, name []byte) {
|
||
|
hash.Write(ns[:])
|
||
|
hash.Write(name)
|
||
|
copy(u[:], hash.Sum([]byte{})[:16])
|
||
|
}
|
||
|
|
||
|
// Set the two most significant bits (bits 6 and 7) of the
|
||
|
// clock_seq_hi_and_reserved to zero and one, respectively.
|
||
|
func (u *UUID) setVariant(v byte) {
|
||
|
switch v {
|
||
|
case ReservedNCS:
|
||
|
u[8] = (u[8] | ReservedNCS) & 0xBF
|
||
|
case ReservedRFC4122:
|
||
|
u[8] = (u[8] | ReservedRFC4122) & 0x7F
|
||
|
case ReservedMicrosoft:
|
||
|
u[8] = (u[8] | ReservedMicrosoft) & 0x3F
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Variant returns the UUID Variant, which determines the internal
|
||
|
// layout of the UUID. This will be one of the constants: RESERVED_NCS,
|
||
|
// RFC_4122, RESERVED_MICROSOFT, RESERVED_FUTURE.
|
||
|
func (u *UUID) Variant() byte {
|
||
|
if u[8]&ReservedNCS == ReservedNCS {
|
||
|
return ReservedNCS
|
||
|
} else if u[8]&ReservedRFC4122 == ReservedRFC4122 {
|
||
|
return ReservedRFC4122
|
||
|
} else if u[8]&ReservedMicrosoft == ReservedMicrosoft {
|
||
|
return ReservedMicrosoft
|
||
|
}
|
||
|
return ReservedFuture
|
||
|
}
|
||
|
|
||
|
// Set the four most significant bits (bits 12 through 15) of the
|
||
|
// time_hi_and_version field to the 4-bit version number.
|
||
|
func (u *UUID) setVersion(v byte) {
|
||
|
u[6] = (u[6] & 0xF) | (v << 4)
|
||
|
}
|
||
|
|
||
|
// Version returns a version number of the algorithm used to
|
||
|
// generate the UUID sequence.
|
||
|
func (u *UUID) Version() uint {
|
||
|
return uint(u[6] >> 4)
|
||
|
}
|
||
|
|
||
|
// Returns unparsed version of the generated UUID sequence.
|
||
|
func (u *UUID) String() string {
|
||
|
return fmt.Sprintf("%x-%x-%x-%x-%x", u[0:4], u[4:6], u[6:8], u[8:10], u[10:])
|
||
|
}
|