uuid

package module
v1.0.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 4, 2018 License: MIT Imports: 15 Imported by: 0

README

go-uuid

A database-friendly implementation of Universally Unique Identifiers for Go.

License GoDoc Build Status Coverage Status Issues Pull Requests Latest Release

Quick Start

import "github.com/chronos-tachyon/go-uuid"

// Generate a new version 1 UUID
u := uuid.New()

// Print the UUID as a string like "77b99cea-8ab4-11e8-96a8-185e0fad6335".
fmt.Println(u.CanonicalString())

// Print the UUID as a string like "@EeiKtHe5nOqWqBheD61jNQ".
fmt.Println(u.DenseString())

// Serialize to SQL.  Defaults to a string like "@EeiKtHe5nOqWqBheD61jNQ",
// but you can also serialize as a canonical UUID string (plus several
// variants thereof), a lexical-order BLOB, or even a standard-order BLOB,
// depending on u.SetPreferences.
db.Exec(`INSERT INTO table (uuid) VALUES (?)`, u)

// Choose serialization preferences for this UUID object.
u.SetPreferences(uuid.Preferences{
  // Pick whether u.Value() returns text or a BLOB.
  Value:  uuid.Binary,

  // Pick whether BLOB serializations use lexical or standard byte ordering.
  // Also selects BLOBs deserialization behavior, as it can make guesses.
  Binary: uuid.DenseFirst,

  // Pick which format to use for text serializations.
  // Does not affect text deserialization; all formats are always recognized.
  Text:   uuid.Dense,
})

// Deserialize from SQL.
row := db.QueryRow(`SELECT uuid FROM table LIMIT 1`)
row.Scan(&u)

What's it about?

This is a fully-featured UUID library in Golang, comparable to the excellent github.com/satori/go.uuid. However, one thing that I found lacking about satori's package is that, like all other UUID implementations that I've found for Go, it isn't very friendly for use in SQL databases.

"But chronos, satori's package supports sql.Scanner and driver.Valuer!"

Ah yes, but the standard byte order for UUIDs, codified in RFC 4122, is a poor choice for use in indexed columns. In fact, if you try to use a standard UUID as a PRIMARY KEY, you're going to tank your database performance and spend years wondering what happened.

The fundamental issue is that the standard byte order for UUIDs puts the fields in the wrong order, particularly if you're using Version 1 (time-based) UUIDs.

Here's a quick anatomy lesson of a V1 UUID:

          timestamp = aa bb cc dd ee ff gg hh (60 bits plus 4-bit version)
    sequence number =                   ii jj (14 bits plus 2-bit variant)
        MAC address =       kk ll mm nn oo pp (48 bits)

    +-------------+-------+-------+-------+-------------------+
    | ee ff gg hh | cc dd | aa bb | ii jj | kk ll mm nn oo pp |
    +-------------+-------+-------+-------+-------------------+

Notice how the timestamp gets scrambled? That hurts SQL performance because SQL indexes are happy when keys are strictly increasing and very, very sad when they are not. The ideal would be to have the timestamp in pure big-endian order, followed by the sequence number in big-endian order, then finally the MAC address.

(You could argue that "MAC address then sequence number" makes slightly more sense, but it's kind of a wash either way, and it's fairly rare for two UUIDs generated on two different machines to share the same 100ns clock tick.)

After we reorder the bytes for SQL, the new UUID anatomy looks like this:

             out[0] = in[6]
             out[1] = in[7]
             out[2] = in[4]
             out[3] = in[5]
             out[4] = in[0]
             out[5] = in[1]
             out[6] = in[2]
             out[7] = in[3]
                    |
      ______________|____________
     /                           \
    +-------+-------+-------------+-------+-------------------+
    | aa bb | cc dd | ee ff gg hh | ii jj | kk ll mm nn oo pp |
    +-------+-------+-------------+-------+-------------------+
Base-64 representation

That's all well and good, but what if we don't want a BLOB? Suppose we require that our PRIMARY KEY be some string that can be copy-pasted from a terminal. What's a good textual representation for a UUID that uses this lexical byte order?

Well, encoding binary data in a textual format is kind of a solved problem. Why not use base-64? It already provides a pure-ASCII encoding with only 33% blowup, much better than the 100+% blowup of the canonical UUID text format. And if we prepend some distinctive ASCII character from outside the base-64 repertoire, such as "@", then we get a very quick way of determining which parser to use. So we move the bytes around to big-endian lexical order, then write a single "@", then write the base-64 representation of the lexical bytes. The length is implicit (a UUID is exactly 16 bytes), so we don't need any padding characters.

Examples:

331a90da-9013-11e8-aad2-42010a800002 => @EeiQEzMakNqq0kIBCoAAAg
335984e8-9013-11e8-aad2-42010a800002 => @EeiQEzNZhOiq0kIBCoAAAg

Result: 36 characters becomes 23, for a 56% savings, AND the new text format has the property that two temporally-ordered V1 UUIDs will sort correctly after text conversion.

Automatic inference of byte order

As it turns out, there's just enough structure in a UUID that we can make an educated guess about which order a BLOB was serialized in:

  • The version field (top 4 bits of "aa") is always a value between 0001 and 0101, i.e. 5 values out of 15. That gives a 31.25% chance of a false positive.

  • The variant field (top 2 bits of "ii") is always 10xx xxxx. At 1 value out of 4, that gives a 25% chance of a false positive.

  • The probabilities are independent, so they combine as 7.8125%.

  • Timestamps before 1970-01-01 or after the current date plus 5 years are soft-rejected as implausible. The probability of landing in that timestamp range by chance will grow over time, but by 2100-01-01 it will only be 0.2309%. That is also an independent probability, so the odds of reaching this point by chance are 0.0180%.

  • In the extremely rare event that both interpretations have correct version bits, correct variant bits, AND plausible timestamps, the timestamp is reimagined as a probability by assuming a normal distribution with the current time as mean and one century as standard deviation. The most probable interpretation is then preferred.

Documentation

Overview

Package uuid provides an implementation of *Universally Unique Identifiers* that can intelligently reorder their bytes, such that V1 (timestamp-based) UUIDs are stored in a "dense" byte order.

This "dense" format has the property that UUIDs which were generated in increasing chronological order will also sort in increasing lexical order. This property makes them suitable for use in an indexed column of a SQL database, as modern SQL database implementations are much happier if their keys are inserted in lexically increasing order.

For more on the byte-reordering technique: https://www.percona.com/blog/2014/12/19/store-uuid-optimized-way/

Additionally, instead of using the binary format (hard to read) OR the traditional 8-4-4-4-12 text format (too verbose), we offer a new SQL-friendly text format of "@<base64>". This is about halfway in length between the two standard representations, but is human-readable and can be copy-pasted in a terminal.

A concrete example: the UUID `f8a2571a-8add-11e8-96a8-185e0fad6335` is reordered to "dense" format as `[11 e8 8a dd f8 a2 57 1a 96 a8 ...]`, and this "dense" binary representation is serialized to "@<base64>" format as `@EeiK3fiiVxqWqBheD61jNQ`. (Note that all bytes from `96 a8` onward have the same order in both binary representations.)

Index

Constants

View Source
const ByteLength = 16

ByteLength is the number of bytes in the binary representation of a UUID.

Variables

This section is empty.

Functions

func Equal

func Equal(u1, u2 UUID) bool

Equal returns true iff its two arguments hold the same UUID.

Types

type BinaryMode

type BinaryMode byte

BinaryMode selects the input/output format for parsing/producing binary represenatations.

const (

	// StandardOnly: produce RFC 4122 bytes, parse RFC 4122 bytes.
	StandardOnly BinaryMode

	// StandardFirst: produce RFC 4122 bytes, parse either byte ordering.
	// Ambiguous inputs will be interpreted as RFC 4122.
	StandardFirst

	// DenseOnly: produce "dense" bytes, parse "dense" bytes.
	DenseOnly

	// DenseFirst: produce "dense" bytes, parse either byte ordering.
	// Ambiguous inputs will be interpreted as "dense".
	DenseFirst
)

BinaryMode enum constants.

func (BinaryMode) String

func (bm BinaryMode) String() string

type ParseError

type ParseError struct {
	TypeName   string
	MethodName string
	Input      string
	Detail     string
	ExactInput []byte
}

ParseError represents an error in the contents of the input while parsing.

func (ParseError) Error

func (err ParseError) Error() string

type Preferences

type Preferences struct {
	Value  ValueMode
	Binary BinaryMode
	Text   TextMode
}

Preferences represents a combination of modes to apply to a UUID.

func (Preferences) FromBytes

func (pref Preferences) FromBytes(in []byte) (UUID, error)

FromBytes attempts to parse a binary UUID representation using these preferences.

func (Preferences) FromString

func (pref Preferences) FromString(in string) (UUID, error)

FromString attempts to parse a textual UUID representation with these preferences.

func (Preferences) MustFromBytes

func (pref Preferences) MustFromBytes(in []byte) UUID

MustFromBytes parses a binary UUID representation using these preferences, or panics if it cannot.

func (Preferences) MustFromString

func (pref Preferences) MustFromString(in string) UUID

MustFromString parses a textual UUID representation with these preferences, or panics if it cannot.

func (Preferences) New

func (pref Preferences) New() UUID

New returns a newly generated V1 UUID with these preferences.

func (Preferences) Nil

func (pref Preferences) Nil() UUID

Nil returns a Nil-valued UUID with these preferences.

func (Preferences) String

func (pref Preferences) String() string

type TextMode

type TextMode byte

TextMode selects the output format for producing textual representations.

const (

	// Dense: "@<base64>", e.g. "@EeiKtHe5nOqWqBheD61jNQ"
	Dense TextMode

	// Canonical: "8-4-4-4-12", e.g. "77b99cea-8ab4-11e8-96a8-185e0fad6335"
	Canonical

	// HashLike: raw hex digits, e.g. "77b99cea8ab411e896a8185e0fad6335"
	HashLike

	// Bracketed: "{8-4-4-4-12}", e.g. "{77b99cea-8ab4-11e8-96a8-185e0fad6335}"
	Bracketed

	// URN: URN format, e.g. "urn:uuid:77b99cea-8ab4-11e8-96a8-185e0fad6335"
	URN
)

TextMode enum constants.

func (TextMode) String

func (tm TextMode) String() string

type TypeError

type TypeError struct {
	TypeName      string
	MethodName    string
	Input         interface{}
	InputType     reflect.Type
	ExpectedTypes []reflect.Type
}

TypeError represents an error in the Go type of the input while scanning.

func (TypeError) Error

func (err TypeError) Error() string

type UUID

type UUID struct {
	// contains filtered or unexported fields
}

UUID holds a single Universally Unique Identifier.

func FromBytes

func FromBytes(in []byte) (UUID, error)

FromBytes attempts to parse a binary UUID representation.

func FromString

func FromString(in string) (UUID, error)

FromString attempts to parse a textual UUID representation.

func MustFromBytes

func MustFromBytes(in []byte) UUID

MustFromBytes parses a binary UUID representation, or panics if it cannot.

func MustFromString

func MustFromString(in string) UUID

MustFromString parses a textual UUID representation, or panics if it cannot.

func New

func New() UUID

New returns a newly generated V1 UUID.

func Nil

func Nil() UUID

Nil returns a Nil-valued UUID.

func (UUID) BracketedString

func (uuid UUID) BracketedString() string

BracketedString returns the textual representation of this UUID in "{8-4-4-4-12}" format.

func (UUID) Bytes

func (uuid UUID) Bytes() []byte

Bytes returns the binary representation of this UUID.

func (UUID) CanonicalString

func (uuid UUID) CanonicalString() string

CanonicalString returns the textual representation of this UUID in canonical "8-4-4-4-12" format.

func (UUID) DenseBytes

func (uuid UUID) DenseBytes() []byte

DenseBytes returns the binary representation of this UUID in "dense" byte order.

func (UUID) DenseString

func (uuid UUID) DenseString() string

DenseString returns the textual representation of this UUID in "@<base64>" format.

func (UUID) Equal

func (uuid UUID) Equal(other UUID) bool

Equal returns true iff this object and the argument hold the same UUID.

func (UUID) Format

func (uuid UUID) Format(s fmt.State, verb rune)

Format fulfills the "fmt".Formatter interface.

func (*UUID) FromBytes

func (uuid *UUID) FromBytes(in []byte) error

FromBytes attempts to parse a binary UUID representation.

func (*UUID) FromDenseBytes

func (uuid *UUID) FromDenseBytes(in []byte) error

FromDenseBytes attempts to parse a binary UUID representation in "dense" byte order.

func (*UUID) FromStandardBytes

func (uuid *UUID) FromStandardBytes(in []byte) error

FromStandardBytes attempts to parse a binary UUID representation in RFC 4122 byte order.

func (*UUID) FromString

func (uuid *UUID) FromString(in string) error

FromString attempts to parse a textual UUID representation.

func (UUID) GoString

func (uuid UUID) GoString() string

GoString fulfills the "fmt".GoStringer interface. It produces a textual representation of this UUID as a snippet of Go pseudocode.

func (UUID) HashLikeString

func (uuid UUID) HashLikeString() string

HashLikeString returns the textual representation of this UUID as a 32-character hex string.

func (UUID) IsNil

func (uuid UUID) IsNil() bool

IsNil returns true iff this object holds the Nil UUID.

func (UUID) IsV1

func (uuid UUID) IsV1() bool

IsV1 returns true iff this UUID has a Version of V1.

func (UUID) IsValid

func (uuid UUID) IsValid() bool

IsValid returns true iff this UUID has a valid Version and a valid Variant.

func (UUID) MarshalBinary

func (uuid UUID) MarshalBinary() ([]byte, error)

MarshalBinary fulfills the "encoding".BinaryMarshaler interface. It produces a binary representation of this UUID.

func (UUID) MarshalJSON

func (uuid UUID) MarshalJSON() ([]byte, error)

MarshalJSON fulfills the "encoding/json".Marshaler interface. It produces a textual representation of this UUID as a JSON string.

func (UUID) MarshalText

func (uuid UUID) MarshalText() ([]byte, error)

MarshalText fulfills the "encoding".TextMarshaler interface. It produces a textual representation of this UUID.

func (*UUID) MustFromBytes

func (uuid *UUID) MustFromBytes(in []byte)

MustFromBytes parses a binary UUID representation, or panics if it cannot.

func (*UUID) MustFromString

func (uuid *UUID) MustFromString(in string)

MustFromString parses a textual UUID representation, or panics if it cannot.

func (UUID) Preferences

func (uuid UUID) Preferences() Preferences

Preferences returns the preference knobs for this object.

func (*UUID) Scan

func (uuid *UUID) Scan(value interface{}) error

Scan fulfills the "database/sql".Scanner interface. It attempts to interpret a SQL value as a UUID representation of some kind.

func (*UUID) SetNew

func (uuid *UUID) SetNew()

SetNew updates this UUID to hold a newly generated V1 UUID.

func (*UUID) SetNil

func (uuid *UUID) SetNil()

SetNil updates this UUID to hold the Nil UUID.

func (*UUID) SetPreferences

func (uuid *UUID) SetPreferences(pref Preferences)

SetPreferences updates the preference knobs for this object.

func (UUID) StandardBytes

func (uuid UUID) StandardBytes() []byte

StandardBytes returns the binary representation of this UUID in RFC 4122 byte order.

func (UUID) String

func (uuid UUID) String() string

String fulfills the "fmt".Stringer interface. It produces a textual representation of this UUID.

func (UUID) URNString

func (uuid UUID) URNString() string

URNString returns the textual representation of this UUID in "urn:uuid:8-4-4-4-12" format.

func (*UUID) UnmarshalBinary

func (uuid *UUID) UnmarshalBinary(in []byte) error

UnmarshalBinary fulfills the "encoding".BinaryUnmarshaler interface. It attempts to parse a binary UUID representation.

func (*UUID) UnmarshalJSON

func (uuid *UUID) UnmarshalJSON(in []byte) error

UnmarshalJSON fulfills the "encoding/json".Unmarshaler interface. It attempts to parse a JSON value as a textual UUID representation.

func (*UUID) UnmarshalText

func (uuid *UUID) UnmarshalText(in []byte) error

UnmarshalText fulfills the "encoding".TextUnmarshaler interface. It attempts to parse a textual UUID representation.

func (UUID) Value

func (uuid UUID) Value() (driver.Value, error)

Value fulfills the "database/sql/driver".Valuer interface. It produces a SQL representation of the UUID.

func (UUID) Variant

func (uuid UUID) Variant() Variant

Variant returns this UUID's Variant.

func (UUID) Version

func (uuid UUID) Version() Version

Version returns this UUID's Version.

func (UUID) VersionAndVariant

func (uuid UUID) VersionAndVariant() (version Version, variant Variant)

VersionAndVariant returns this UUID's Version and Variant. This method is slightly more efficient than calling Version() and Variant() separately.

type ValueMode

type ValueMode byte

ValueMode selects the output behavior of the SQL-oriented Value method.

const (

	// Text: output is a string in the current TextMode.
	Text ValueMode

	// Binary: output is a byte slice in the current BinaryMode.
	Binary
)

ValueMode enum constants.

func (ValueMode) String

func (vm ValueMode) String() string

type Variant

type Variant byte

Variant indicates the RFC 4122-defined UUID variant.

const (
	VariantNCS       Variant = 1
	VariantRFC4122   Variant = 2
	VariantMicrosoft Variant = 3
	VariantFuture    Variant = 4
)

Variant enum constants.

func (*Variant) FromString

func (variant *Variant) FromString(in string) error

FromString attempts to parse the string representation of a Variant.

func (Variant) IsValid

func (variant Variant) IsValid() bool

IsValid returns true iff the Variant is VariantRFC4122.

func (Variant) String

func (variant Variant) String() string

type Version

type Version byte

Version indicates the RFC 4122-defined UUID version.

const (
	V1 Version = 1
	V2 Version = 2
	V3 Version = 3
	V4 Version = 4
	V5 Version = 5
)

Version enum constants.

func (*Version) FromString

func (version *Version) FromString(in string) error

FromString attempts to parse the string representation of a Version.

func (Version) IsValid

func (version Version) IsValid() bool

IsValid returns true iff the Version is one of the UUID versions known to RFC 4122.

func (Version) String

func (version Version) String() string

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL