/uuid.go
Go | 185 lines | 123 code | 19 blank | 43 comment | 19 complexity | f79b35e20f30055084e24619cd331fdc MD5 | raw file
- // 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"
- "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 {
- const s byte = '-'
- buf := make([]byte, 36)
- hex.Encode(buf[0:8], u[0:4])
- buf[8] = s
- hex.Encode(buf[9:13], u[4:6])
- buf[13] = s
- hex.Encode(buf[14:18], u[6:8])
- buf[18] = s
- hex.Encode(buf[19:23], u[8:10])
- buf[23] = s
- hex.Encode(buf[24:], u[10:])
- return string(buf)
- }