|
|
- // This package provides RFC4122 UUIDs.
- //
- // NewV1, NewV3, NewV4, NewV5, for generating versions 1, 3, 4
- // and 5 UUIDs as specified in RFC-4122.
- //
- // New([]byte), unsafe; NewHex(string); and Parse(string) for
- // creating UUIDs from existing data.
- //
- // The original version was from Krzysztof Kowalik <chris@nu7hat.ch>
- // Unfortunately, that version was non compliant with RFC4122.
- // I forked it but have since heavily redesigned it.
- //
- // The example code in the specification was also used as reference
- // for design.
- //
- // Copyright (C) 2014 twinj@github.com 2014 MIT style licence
- package uuid
-
- /****************
- * Date: 31/01/14
- * Time: 3:35 PM
- ***************/
-
- import (
- "encoding"
- "encoding/hex"
- "errors"
- "fmt"
- "hash"
- "regexp"
- "strings"
- "bytes"
- )
-
- const (
- ReservedNCS byte = 0x00
- ReservedRFC4122 byte = 0x80 // or and A0 if masked with 1F
- ReservedMicrosoft byte = 0xC0
- ReservedFuture byte = 0xE0
- TakeBack byte = 0xF0
- )
-
- const (
-
- // Pattern used to parse string representation of the UUID.
- // Current one allows to parse string where only one opening
- // or closing bracket or any of the hyphens are optional.
- // It is only used to extract the main bytes to create a UUID,
- // so these imperfections are of no consequence.
- hexPattern = `^(urn\:uuid\:)?[\{(\[]?([A-Fa-f0-9]{8})-?([A-Fa-f0-9]{4})-?([1-5][A-Fa-f0-9]{3})-?([A-Fa-f0-9]{4})-?([A-Fa-f0-9]{12})[\]\})]?$`
- )
-
- var (
- parseUUIDRegex = regexp.MustCompile(hexPattern)
- format string
- )
-
- func init() {
- SwitchFormat(CleanHyphen)
- }
-
- // ****************************************************** UUID
-
- // The main interface for UUIDs
- // Each implementation must also implement the UniqueName interface
- type UUID interface {
- encoding.BinaryMarshaler
- encoding.BinaryUnmarshaler
-
- // Marshals the UUID bytes or data
- Bytes() (data []byte)
-
- // Organises data into a new UUID
- Unmarshal(pData []byte)
-
- // Size is used where different implementations require
- // different sizes. Should return the number of bytes in
- // the implementation.
- // Enables unmarshal and Bytes to screen for size
- Size() int
-
- // Version returns a version number of the algorithm used
- // to generate the UUID.
- // This may may behave independently across non RFC4122 UUIDs
- Version() int
-
- // Variant returns the UUID Variant
- // This will be one of the constants:
- // ReservedRFC4122,
- // ReservedMicrosoft,
- // ReservedFuture,
- // ReservedNCS.
- // This may behave differently across non RFC4122 UUIDs
- Variant() byte
-
- // UUID can be used as a Name within a namespace
- // Is simply just a String() string method
- // Returns a formatted version of the UUID.
- String() string
- }
-
- // New creates a UUID from a slice of bytes.
- // Truncates any bytes past the default length of 16
- // Will panic if data slice is too small.
- func New(pData []byte) UUID {
- o := new(Array)
- o.Unmarshal(pData[:length])
- return o
- }
-
-
- // Creates a UUID from a hex string
- // Will panic if hex string is invalid - will panic even with hyphens and brackets
- // Expects a clean string use Parse otherwise.
- func NewHex(pUuid string) UUID {
- bytes, err := hex.DecodeString(pUuid)
- if err != nil {
- panic(err)
- }
- return New(bytes)
- }
-
- // Parse creates a UUID from a valid string representation.
- // Accepts UUID string in following formats:
- // 6ba7b8149dad11d180b400c04fd430c8
- // 6ba7b814-9dad-11d1-80b4-00c04fd430c8
- // {6ba7b814-9dad-11d1-80b4-00c04fd430c8}
- // urn:uuid:6ba7b814-9dad-11d1-80b4-00c04fd430c8
- // [6ba7b814-9dad-11d1-80b4-00c04fd430c8]
- //
- func Parse(pUUID string) (UUID, error) {
- md := parseUUIDRegex.FindStringSubmatch(pUUID)
- if md == nil {
- return nil, errors.New("uuid.Parse: invalid string")
- }
- return NewHex(md[2] + md[3] + md[4] + md[5] + md[6]), nil
- }
-
- // Digest a namespace UUID and a UniqueName, which then marshals to
- // a new UUID
- func Digest(o, pNs UUID, pName UniqueName, pHash hash.Hash) {
- // Hash writer never returns an error
- pHash.Write(pNs.Bytes())
- pHash.Write([]byte(pName.String()))
- o.Unmarshal(pHash.Sum(nil)[:o.Size()])
- }
-
- // Function provides a safe way to unmarshal bytes into an
- // existing UUID.
- // Checks for length.
- func UnmarshalBinary(o UUID, pData []byte) error {
- if len(pData) != o.Size() {
- return errors.New("uuid.UnmarshalBinary: invalid length")
- }
- o.Unmarshal(pData)
- return nil
- }
-
- // ********************************************** UUID Names
-
- // A UUID Name is a simple string which implements UniqueName
- // which satisfies the Stringer interface.
- type Name string
-
- // Returns the name as a string. Satisfies the Stringer interface.
- func (o Name) String() string {
- return string(o)
- }
-
- // NewName will create a unique name from several sources
- func NewName(salt string, pNames ...UniqueName) UniqueName {
- var s string
- for _, s2 := range pNames {
- s += s2.String()
- }
- return Name(s + salt)
- }
-
- // UniqueName is a Stinger interface
- // Made for easy passing of IPs, URLs, the several Address types,
- // Buffers and any other type which implements Stringer
- // string, []byte types and Hash sums will need to be cast to
- // the Name type or some other type which implements
- // Stringer or UniqueName
- type UniqueName interface {
-
- // Many go types implement this method for use with printing
- // Will convert the current type to its native string format
- String() string
- }
-
- // ********************************************** UUID Printing
-
- // A Format is a pattern used by the stringer interface with which to print
- // the UUID.
- type Format string
-
- const (
- Clean Format = "%x%x%x%x%x%x"
- Curly Format = "{%x%x%x%x%x%x}"
- Bracket Format = "(%x%x%x%x%x%x)"
-
- // This is the default format.
- CleanHyphen Format = "%x-%x-%x-%x%x-%x"
-
- CurlyHyphen Format = "{%x-%x-%x-%x%x-%x}"
- BracketHyphen Format = "(%x-%x-%x-%x%x-%x)"
- GoIdFormat Format = "[%X-%X-%x-%X%X-%x]"
- )
-
- // Gets the current default format pattern
- func GetFormat() string {
- return format
- }
-
- // Switches the default printing format for ALL UUID strings
- // A valid format will have 6 groups if the supplied Format does not
- func SwitchFormat(pFormat Format) {
- form := string(pFormat)
- if strings.Count(form, "%") != 6 {
- panic(errors.New("uuid.switchFormat: invalid formatting"))
- }
- format = form
- }
-
- // Same as SwitchFormat but will make it uppercase
- func SwitchFormatUpperCase(pFormat Format) {
- form := strings.ToUpper(string(pFormat))
- SwitchFormat(Format(form))
- }
-
- // Compares whether each UUID is the same
- func Equal(p1 UUID, p2 UUID) bool {
- return bytes.Equal(p1.Bytes(), p2.Bytes())
- }
-
- // Format a UUID into a human readable string which matches the given Format
- // Use this for one time formatting when setting the default using SwitchFormat
- // is overkill.
- func Formatter(pUUID UUID, pFormat Format) string {
- form := string(pFormat)
- if strings.Count(form, "%") != 6 {
- panic(errors.New("uuid.Formatter: invalid formatting"))
- }
- return formatter(pUUID, form)
- }
-
- // ********************************************** UUID Versions
-
- type UUIDVersion int
-
- const (
- NONE UUIDVersion = iota
- RFC4122v1
- DunnoYetv2
- RFC4122v3
- RFC4122v4
- RFC4122v5
- )
-
- // *************************************************** Helpers
-
- // Retrieves the variant from the given byte
- func variant(pVariant byte) byte {
- switch pVariant & variantGet {
- case ReservedRFC4122, 0xA0:
- return ReservedRFC4122
- case ReservedMicrosoft:
- return ReservedMicrosoft
- case ReservedFuture:
- return ReservedFuture
- }
- return ReservedNCS
- }
-
- // not strictly required
- func setVariant(pByte *byte, pVariant byte) {
- switch pVariant {
- case ReservedRFC4122:
- *pByte &= variantSet
- case ReservedFuture, ReservedMicrosoft:
- *pByte &= 0x1F
- case ReservedNCS:
- *pByte &= 0x7F
- default:
- panic(errors.New("uuid.setVariant: invalid variant mask"))
- }
- *pByte |= pVariant
- }
-
- // format a UUID into a human readable string
- func formatter(pUUID UUID, pFormat string) string {
- b := pUUID.Bytes()
- return fmt.Sprintf(pFormat, b[0:4], b[4:6], b[6:8], b[8:9], b[9:10], b[10:pUUID.Size()])
- }
-
|