pax_global_header00006660000000000000000000000064143715131350014515gustar00rootroot0000000000000052 comment=ba6b2241c1d1830634b1ba283ea68e57d1826f55 uuid-5.0.0/000077500000000000000000000000001437151313500124655ustar00rootroot00000000000000uuid-5.0.0/.github/000077500000000000000000000000001437151313500140255ustar00rootroot00000000000000uuid-5.0.0/.github/workflows/000077500000000000000000000000001437151313500160625ustar00rootroot00000000000000uuid-5.0.0/.github/workflows/go.yml000066400000000000000000000017431437151313500172170ustar00rootroot00000000000000name: Go on: push: branches: [ master ] pull_request: branches: [ master ] jobs: build: name: Build + Test Stable runs-on: ubuntu-latest env: GO111MODULE: auto steps: - name: Build uses: actions/setup-go@v2 with: go-version: '1.19.x' - name: Check out code into the Go module directory uses: actions/checkout@v2 - name: Build run: go build -v ./... - name: Test run: go test ./... -race -coverprofile=coverage.txt -covermode=atomic - name: Coverage uses: codecov/codecov-action@v2 build-legacy: name: Build + Test Previous Stable runs-on: ubuntu-latest env: GO111MODULE: auto steps: - name: Build uses: actions/setup-go@v2 with: go-version: '1.18.x' - name: Check out code into the Go module directory uses: actions/checkout@v2 - name: Build run: go build -v ./... - name: Test run: go test ./... uuid-5.0.0/.gitignore000066400000000000000000000003641437151313500144600ustar00rootroot00000000000000# Binaries for programs and plugins *.exe *.exe~ *.dll *.so *.dylib # Test binary, build with `go test -c` *.test # Output of the go coverage tool, specifically when used with LiteIDE *.out # binary bundle generated by go-fuzz uuid-fuzz.zip uuid-5.0.0/LICENSE000066400000000000000000000020721437151313500134730ustar00rootroot00000000000000Copyright (C) 2013-2018 by Maxim Bublis Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. uuid-5.0.0/README.md000066400000000000000000000073651437151313500137570ustar00rootroot00000000000000# UUID [![License](https://img.shields.io/github/license/gofrs/uuid.svg)](https://github.com/gofrs/uuid/blob/master/LICENSE) [![Build Status](https://travis-ci.org/gofrs/uuid.svg?branch=master)](https://travis-ci.org/gofrs/uuid) [![GoDoc](http://godoc.org/github.com/gofrs/uuid?status.svg)](http://godoc.org/github.com/gofrs/uuid) [![Coverage Status](https://codecov.io/gh/gofrs/uuid/branch/master/graphs/badge.svg?branch=master)](https://codecov.io/gh/gofrs/uuid/) [![Go Report Card](https://goreportcard.com/badge/github.com/gofrs/uuid)](https://goreportcard.com/report/github.com/gofrs/uuid) Package uuid provides a pure Go implementation of Universally Unique Identifiers (UUID) variant as defined in RFC-4122. This package supports both the creation and parsing of UUIDs in different formats. This package supports the following UUID versions: * Version 1, based on timestamp and MAC address (RFC-4122) * Version 3, based on MD5 hashing of a named value (RFC-4122) * Version 4, based on random numbers (RFC-4122) * Version 5, based on SHA-1 hashing of a named value (RFC-4122) This package also supports experimental Universally Unique Identifier implementations based on a [draft RFC](https://www.ietf.org/archive/id/draft-peabody-dispatch-new-uuid-format-04.html) that updates RFC-4122 * Version 6, a k-sortable id based on timestamp, and field-compatible with v1 (draft-peabody-dispatch-new-uuid-format, RFC-4122) * Version 7, a k-sortable id based on timestamp (draft-peabody-dispatch-new-uuid-format, RFC-4122) The v6 and v7 IDs are **not** considered a part of the stable API, and may be subject to behavior or API changes as part of minor releases to this package. They will be updated as the draft RFC changes, and will become stable if and when the draft RFC is accepted. ## Project History This project was originally forked from the [github.com/satori/go.uuid](https://github.com/satori/go.uuid) repository after it appeared to be no longer maintained, while exhibiting [critical flaws](https://github.com/satori/go.uuid/issues/73). We have decided to take over this project to ensure it receives regular maintenance for the benefit of the larger Go community. We'd like to thank Maxim Bublis for his hard work on the original iteration of the package. ## License This source code of this package is released under the MIT License. Please see the [LICENSE](https://github.com/gofrs/uuid/blob/master/LICENSE) for the full content of the license. ## Recommended Package Version We recommend using v2.0.0+ of this package, as versions prior to 2.0.0 were created before our fork of the original package and have some known deficiencies. ## Requirements This package requres Go 1.17 or later ## Usage Here is a quick overview of how to use this package. For more detailed documentation, please see the [GoDoc Page](http://godoc.org/github.com/gofrs/uuid). ```go package main import ( "log" "github.com/gofrs/uuid" ) // Create a Version 4 UUID, panicking on error. // Use this form to initialize package-level variables. var u1 = uuid.Must(uuid.NewV4()) func main() { // Create a Version 4 UUID. u2, err := uuid.NewV4() if err != nil { log.Fatalf("failed to generate UUID: %v", err) } log.Printf("generated Version 4 UUID %v", u2) // Parse a UUID from a string. s := "6ba7b810-9dad-11d1-80b4-00c04fd430c8" u3, err := uuid.FromString(s) if err != nil { log.Fatalf("failed to parse UUID %q: %v", s, err) } log.Printf("successfully parsed UUID %v", u3) } ``` ## References * [RFC-4122](https://tools.ietf.org/html/rfc4122) * [DCE 1.1: Authentication and Security Services](http://pubs.opengroup.org/onlinepubs/9696989899/chap5.htm#tagcjh_08_02_01_01) * [New UUID Formats RFC Draft (Peabody) Rev 04](https://www.ietf.org/archive/id/draft-peabody-dispatch-new-uuid-format-04.html#) uuid-5.0.0/codec.go000066400000000000000000000145511437151313500140770ustar00rootroot00000000000000// Copyright (C) 2013-2018 by Maxim Bublis // // Permission is hereby granted, free of charge, to any person obtaining // a copy of this software and associated documentation files (the // "Software"), to deal in the Software without restriction, including // without limitation the rights to use, copy, modify, merge, publish, // distribute, sublicense, and/or sell copies of the Software, and to // permit persons to whom the Software is furnished to do so, subject to // the following conditions: // // The above copyright notice and this permission notice shall be // included in all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. package uuid import ( "errors" "fmt" ) // FromBytes returns a UUID generated from the raw byte slice input. // It will return an error if the slice isn't 16 bytes long. func FromBytes(input []byte) (UUID, error) { u := UUID{} err := u.UnmarshalBinary(input) return u, err } // FromBytesOrNil returns a UUID generated from the raw byte slice input. // Same behavior as FromBytes(), but returns uuid.Nil instead of an error. func FromBytesOrNil(input []byte) UUID { uuid, err := FromBytes(input) if err != nil { return Nil } return uuid } var errInvalidFormat = errors.New("uuid: invalid UUID format") func fromHexChar(c byte) byte { switch { case '0' <= c && c <= '9': return c - '0' case 'a' <= c && c <= 'f': return c - 'a' + 10 case 'A' <= c && c <= 'F': return c - 'A' + 10 } return 255 } // Parse parses the UUID stored in the string text. Parsing and supported // formats are the same as UnmarshalText. func (u *UUID) Parse(s string) error { switch len(s) { case 32: // hash case 36: // canonical case 34, 38: if s[0] != '{' || s[len(s)-1] != '}' { return fmt.Errorf("uuid: incorrect UUID format in string %q", s) } s = s[1 : len(s)-1] case 41, 45: if s[:9] != "urn:uuid:" { return fmt.Errorf("uuid: incorrect UUID format in string %q", s[:9]) } s = s[9:] default: return fmt.Errorf("uuid: incorrect UUID length %d in string %q", len(s), s) } // canonical if len(s) == 36 { if s[8] != '-' || s[13] != '-' || s[18] != '-' || s[23] != '-' { return fmt.Errorf("uuid: incorrect UUID format in string %q", s) } for i, x := range [16]byte{ 0, 2, 4, 6, 9, 11, 14, 16, 19, 21, 24, 26, 28, 30, 32, 34, } { v1 := fromHexChar(s[x]) v2 := fromHexChar(s[x+1]) if v1|v2 == 255 { return errInvalidFormat } u[i] = (v1 << 4) | v2 } return nil } // hash like for i := 0; i < 32; i += 2 { v1 := fromHexChar(s[i]) v2 := fromHexChar(s[i+1]) if v1|v2 == 255 { return errInvalidFormat } u[i/2] = (v1 << 4) | v2 } return nil } // FromString returns a UUID parsed from the input string. // Input is expected in a form accepted by UnmarshalText. func FromString(text string) (UUID, error) { var u UUID err := u.Parse(text) return u, err } // FromStringOrNil returns a UUID parsed from the input string. // Same behavior as FromString(), but returns uuid.Nil instead of an error. func FromStringOrNil(input string) UUID { uuid, err := FromString(input) if err != nil { return Nil } return uuid } // MarshalText implements the encoding.TextMarshaler interface. // The encoding is the same as returned by the String() method. func (u UUID) MarshalText() ([]byte, error) { var buf [36]byte encodeCanonical(buf[:], u) return buf[:], nil } // UnmarshalText implements the encoding.TextUnmarshaler interface. // Following formats are supported: // // "6ba7b810-9dad-11d1-80b4-00c04fd430c8", // "{6ba7b810-9dad-11d1-80b4-00c04fd430c8}", // "urn:uuid:6ba7b810-9dad-11d1-80b4-00c04fd430c8" // "6ba7b8109dad11d180b400c04fd430c8" // "{6ba7b8109dad11d180b400c04fd430c8}", // "urn:uuid:6ba7b8109dad11d180b400c04fd430c8" // // ABNF for supported UUID text representation follows: // // URN := 'urn' // UUID-NID := 'uuid' // // hexdig := '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' | // 'a' | 'b' | 'c' | 'd' | 'e' | 'f' | // 'A' | 'B' | 'C' | 'D' | 'E' | 'F' // // hexoct := hexdig hexdig // 2hexoct := hexoct hexoct // 4hexoct := 2hexoct 2hexoct // 6hexoct := 4hexoct 2hexoct // 12hexoct := 6hexoct 6hexoct // // hashlike := 12hexoct // canonical := 4hexoct '-' 2hexoct '-' 2hexoct '-' 6hexoct // // plain := canonical | hashlike // uuid := canonical | hashlike | braced | urn // // braced := '{' plain '}' | '{' hashlike '}' // urn := URN ':' UUID-NID ':' plain func (u *UUID) UnmarshalText(b []byte) error { switch len(b) { case 32: // hash case 36: // canonical case 34, 38: if b[0] != '{' || b[len(b)-1] != '}' { return fmt.Errorf("uuid: incorrect UUID format in string %q", b) } b = b[1 : len(b)-1] case 41, 45: if string(b[:9]) != "urn:uuid:" { return fmt.Errorf("uuid: incorrect UUID format in string %q", b[:9]) } b = b[9:] default: return fmt.Errorf("uuid: incorrect UUID length %d in string %q", len(b), b) } if len(b) == 36 { if b[8] != '-' || b[13] != '-' || b[18] != '-' || b[23] != '-' { return fmt.Errorf("uuid: incorrect UUID format in string %q", b) } for i, x := range [16]byte{ 0, 2, 4, 6, 9, 11, 14, 16, 19, 21, 24, 26, 28, 30, 32, 34, } { v1 := fromHexChar(b[x]) v2 := fromHexChar(b[x+1]) if v1|v2 == 255 { return errInvalidFormat } u[i] = (v1 << 4) | v2 } return nil } for i := 0; i < 32; i += 2 { v1 := fromHexChar(b[i]) v2 := fromHexChar(b[i+1]) if v1|v2 == 255 { return errInvalidFormat } u[i/2] = (v1 << 4) | v2 } return nil } // MarshalBinary implements the encoding.BinaryMarshaler interface. func (u UUID) MarshalBinary() ([]byte, error) { return u.Bytes(), nil } // UnmarshalBinary implements the encoding.BinaryUnmarshaler interface. // It will return an error if the slice isn't 16 bytes long. func (u *UUID) UnmarshalBinary(data []byte) error { if len(data) != Size { return fmt.Errorf("uuid: UUID must be exactly 16 bytes long, got %d bytes", len(data)) } copy(u[:], data) return nil } uuid-5.0.0/codec_test.go000066400000000000000000000265271437151313500151440ustar00rootroot00000000000000// Copyright (C) 2013-2018 by Maxim Bublis // // Permission is hereby granted, free of charge, to any person obtaining // a copy of this software and associated documentation files (the // "Software"), to deal in the Software without restriction, including // without limitation the rights to use, copy, modify, merge, publish, // distribute, sublicense, and/or sell copies of the Software, and to // permit persons to whom the Software is furnished to do so, subject to // the following conditions: // // The above copyright notice and this permission notice shall be // included in all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. package uuid import ( "bytes" "flag" "fmt" "io/ioutil" "os" "path/filepath" "strings" "testing" ) // codecTestData holds []byte data for a UUID we commonly use for testing. var codecTestData = []byte{0x6b, 0xa7, 0xb8, 0x10, 0x9d, 0xad, 0x11, 0xd1, 0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8} // codecTestUUID is the UUID value corresponding to codecTestData. var codecTestUUID = UUID{0x6b, 0xa7, 0xb8, 0x10, 0x9d, 0xad, 0x11, 0xd1, 0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8} func TestFromBytes(t *testing.T) { t.Run("Valid", func(t *testing.T) { got, err := FromBytes(codecTestData) if err != nil { t.Fatal(err) } if got != codecTestUUID { t.Fatalf("FromBytes(%x) = %v, want %v", codecTestData, got, codecTestUUID) } }) t.Run("Invalid", func(t *testing.T) { var short [][]byte for i := 0; i < len(codecTestData); i++ { short = append(short, codecTestData[:i]) } var long [][]byte for i := 1; i < 17; i++ { tmp := append(codecTestData, make([]byte, i)...) long = append(long, tmp) } invalid := append(short, long...) for _, b := range invalid { got, err := FromBytes(b) if err == nil { t.Fatalf("FromBytes(%x): want err != nil, got %v", b, got) } } }) } func TestFromBytesOrNil(t *testing.T) { t.Run("Invalid", func(t *testing.T) { b := []byte{4, 8, 15, 16, 23, 42} got := FromBytesOrNil(b) if got != Nil { t.Errorf("FromBytesOrNil(%x): got %v, want %v", b, got, Nil) } }) t.Run("Valid", func(t *testing.T) { got := FromBytesOrNil(codecTestData) if got != codecTestUUID { t.Errorf("FromBytesOrNil(%x): got %v, want %v", codecTestData, got, codecTestUUID) } }) } type fromStringTest struct { input string variant string } // Run runs the FromString test in a subtest of t, named by fst.variant. func (fst fromStringTest) TestFromString(t *testing.T) { t.Run(fst.variant, func(t *testing.T) { got, err := FromString(fst.input) if err != nil { t.Fatalf("FromString(%q): %v", fst.input, err) } if want := codecTestUUID; got != want { t.Fatalf("FromString(%q) = %v, want %v", fst.input, got, want) } }) } func (fst fromStringTest) TestUnmarshalText(t *testing.T) { t.Run(fst.variant, func(t *testing.T) { var u UUID err := u.UnmarshalText([]byte(fst.input)) if err != nil { t.Fatalf("UnmarshalText(%q) (%s): %v", fst.input, fst.variant, err) } if want := codecTestData; !bytes.Equal(u[:], want[:]) { t.Fatalf("UnmarshalText(%q) (%s) = %v, want %v", fst.input, fst.variant, u, want) } }) } // fromStringTests contains UUID variants that are expected to be parsed // successfully by UnmarshalText / FromString. // // variants must be unique across elements of this slice. Please see the // comment in fuzz.go if you change this slice or add new tests to it. var fromStringTests = []fromStringTest{ { input: "6ba7b810-9dad-11d1-80b4-00c04fd430c8", variant: "Canonical", }, { input: "{6ba7b810-9dad-11d1-80b4-00c04fd430c8}", variant: "BracedCanonical", }, { input: "{6ba7b8109dad11d180b400c04fd430c8}", variant: "BracedHashlike", }, { input: "6ba7b8109dad11d180b400c04fd430c8", variant: "Hashlike", }, { input: "urn:uuid:6ba7b810-9dad-11d1-80b4-00c04fd430c8", variant: "URNCanonical", }, { input: "urn:uuid:6ba7b8109dad11d180b400c04fd430c8", variant: "URNHashlike", }, } var invalidFromStringInputs = []string{ // short "6ba7b810-9dad-11d1-80b4-00c04fd430c", "6ba7b8109dad11d180b400c04fd430c", // invalid hex "6ba7b8109dad11d180b400c04fd430q8", // long "6ba7b810-9dad-11d1-80b4-00c04fd430c8=", "6ba7b810-9dad-11d1-80b4-00c04fd430c8}", "{6ba7b810-9dad-11d1-80b4-00c04fd430c8}f", "6ba7b810-9dad-11d1-80b4-00c04fd430c800c04fd430c8", // malformed in other ways "ba7b8109dad11d180b400c04fd430c8}", "6ba7b8109dad11d180b400c04fd430c86ba7b8109dad11d180b400c04fd430c8", "urn:uuid:{6ba7b810-9dad-11d1-80b4-00c04fd430c8}", "uuid:urn:6ba7b810-9dad-11d1-80b4-00c04fd430c8", "uuid:urn:6ba7b8109dad11d180b400c04fd430c8", "6ba7b8109-dad-11d1-80b4-00c04fd430c8", "6ba7b810-9dad1-1d1-80b4-00c04fd430c8", "6ba7b810-9dad-11d18-0b4-00c04fd430c8", "6ba7b810-9dad-11d1-80b40-0c04fd430c8", "6ba7b810+9dad+11d1+80b4+00c04fd430c8", "(6ba7b810-9dad-11d1-80b4-00c04fd430c8}", "{6ba7b810-9dad-11d1-80b4-00c04fd430c8>", "zba7b810-9dad-11d1-80b4-00c04fd430c8", "6ba7b810-9dad11d180b400c04fd430c8", "6ba7b8109dad-11d180b400c04fd430c8", "6ba7b8109dad11d1-80b400c04fd430c8", "6ba7b8109dad11d180b4-00c04fd430c8", } func TestFromString(t *testing.T) { t.Run("Valid", func(t *testing.T) { for _, fst := range fromStringTests { fst.TestFromString(t) } }) t.Run("Invalid", func(t *testing.T) { for _, s := range invalidFromStringInputs { got, err := FromString(s) if err == nil { t.Errorf("FromString(%q): want err != nil, got %v", s, got) } } }) } func TestFromStringOrNil(t *testing.T) { t.Run("Invalid", func(t *testing.T) { s := "bad" got := FromStringOrNil(s) if got != Nil { t.Errorf("FromStringOrNil(%q): got %v, want Nil", s, got) } }) t.Run("Valid", func(t *testing.T) { s := "6ba7b810-9dad-11d1-80b4-00c04fd430c8" got := FromStringOrNil(s) if got != codecTestUUID { t.Errorf("FromStringOrNil(%q): got %v, want %v", s, got, codecTestUUID) } }) } func TestUnmarshalText(t *testing.T) { t.Run("Valid", func(t *testing.T) { for _, fst := range fromStringTests { fst.TestUnmarshalText(t) } }) t.Run("Invalid", func(t *testing.T) { for _, s := range invalidFromStringInputs { var u UUID err := u.UnmarshalText([]byte(s)) if err == nil { t.Errorf("FromBytes(%q): want err != nil, got %v", s, u) } } }) } // Test that UnmarshalText() and Parse() return identical errors func TestUnmarshalTextParseErrors(t *testing.T) { for _, s := range invalidFromStringInputs { var u UUID e1 := u.UnmarshalText([]byte(s)) e2 := u.Parse(s) if e1 == nil || e1.Error() != e2.Error() { t.Errorf("%q: errors don't match: UnmarshalText: %v Parse: %v", s, e1, e2) } } } func TestMarshalBinary(t *testing.T) { got, err := codecTestUUID.MarshalBinary() if err != nil { t.Fatal(err) } if !bytes.Equal(got, codecTestData) { t.Fatalf("%v.MarshalBinary() = %x, want %x", codecTestUUID, got, codecTestData) } } func TestMarshalText(t *testing.T) { want := []byte("6ba7b810-9dad-11d1-80b4-00c04fd430c8") got, err := codecTestUUID.MarshalText() if err != nil { t.Fatal(err) } if !bytes.Equal(got, want) { t.Errorf("%v.MarshalText(): got %s, want %s", codecTestUUID, got, want) } } func TestDecodePlainWithWrongLength(t *testing.T) { arg := []byte{'4', '2'} u := UUID{} if u.UnmarshalText(arg) == nil { t.Errorf("%v.UnmarshalText(%q): should return error, but it did not", u, arg) } } func TestFromHexChar(t *testing.T) { const hextable = "0123456789abcdef" t.Run("Valid", func(t *testing.T) { t.Run("Lower", func(t *testing.T) { for i, c := range []byte(hextable) { x := fromHexChar(c) if int(x) != i { t.Errorf("fromHexChar(%c): got %d want %d", c, x, i) } } }) t.Run("Upper", func(t *testing.T) { for i, c := range []byte(strings.ToUpper(hextable)) { x := fromHexChar(c) if int(x) != i { t.Errorf("fromHexChar(%c): got %d want %d", c, x, i) } } }) }) t.Run("Invalid", func(t *testing.T) { skip := make(map[byte]bool) for _, c := range []byte(hextable + strings.ToUpper(hextable)) { skip[c] = true } for i := 0; i < 256; i++ { c := byte(i) if !skip[c] { v := fromHexChar(c) if v != 255 { t.Errorf("fromHexChar(%c): got %d want: %d", c, v, 255) } } } }) } var stringBenchmarkSink string func BenchmarkString(b *testing.B) { for i := 0; i < b.N; i++ { stringBenchmarkSink = codecTestUUID.String() } } func BenchmarkFromBytes(b *testing.B) { for i := 0; i < b.N; i++ { FromBytes(codecTestData) } } func BenchmarkFromString(b *testing.B) { b.Run("canonical", func(b *testing.B) { for i := 0; i < b.N; i++ { FromString("6ba7b810-9dad-11d1-80b4-00c04fd430c8") } }) b.Run("urn", func(b *testing.B) { for i := 0; i < b.N; i++ { FromString("urn:uuid:6ba7b810-9dad-11d1-80b4-00c04fd430c8") } }) b.Run("braced", func(b *testing.B) { for i := 0; i < b.N; i++ { FromString("{6ba7b810-9dad-11d1-80b4-00c04fd430c8}") } }) } func BenchmarkUnmarshalText(b *testing.B) { b.Run("canonical", func(b *testing.B) { text := []byte(Must(FromString("6ba7b810-9dad-11d1-80b4-00c04fd430c8")).String()) u := new(UUID) if err := u.UnmarshalText(text); err != nil { b.Fatal(err) } b.ResetTimer() for i := 0; i < b.N; i++ { _ = u.UnmarshalText(text) } }) b.Run("urn", func(b *testing.B) { text := []byte(Must(FromString("urn:uuid:6ba7b810-9dad-11d1-80b4-00c04fd430c8")).String()) u := new(UUID) if err := u.UnmarshalText(text); err != nil { b.Fatal(err) } b.ResetTimer() for i := 0; i < b.N; i++ { _ = u.UnmarshalText(text) } }) b.Run("braced", func(b *testing.B) { text := []byte(Must(FromString("{6ba7b810-9dad-11d1-80b4-00c04fd430c8}")).String()) u := new(UUID) if err := u.UnmarshalText(text); err != nil { b.Fatal(err) } b.ResetTimer() for i := 0; i < b.N; i++ { _ = u.UnmarshalText(text) } }) } func BenchmarkMarshalBinary(b *testing.B) { for i := 0; i < b.N; i++ { codecTestUUID.MarshalBinary() } } func BenchmarkMarshalText(b *testing.B) { for i := 0; i < b.N; i++ { codecTestUUID.MarshalText() } } func BenchmarkParseV4(b *testing.B) { const text = "f52a747a-983f-45f7-90b5-e84d70f470dd" for i := 0; i < b.N; i++ { var u UUID if err := u.Parse(text); err != nil { b.Fatal(err) } } } var seedFuzzCorpus = flag.Bool("seed_fuzz_corpus", false, "seed fuzz test corpus") func TestSeedFuzzCorpus(t *testing.T) { // flag.Parse() is called for us by the test binary. if !*seedFuzzCorpus { t.Skip("seeding fuzz test corpus only on demand") } corpusDir := filepath.Join(".", "testdata", "corpus") writeSeedFile := func(name, data string) error { path := filepath.Join(corpusDir, name) return ioutil.WriteFile(path, []byte(data), os.ModePerm) } for _, fst := range fromStringTests { name := "seed_valid_" + fst.variant if err := writeSeedFile(name, fst.input); err != nil { t.Fatal(err) } } for i, s := range invalidFromStringInputs { name := fmt.Sprintf("seed_invalid_%d", i) if err := writeSeedFile(name, s); err != nil { t.Fatal(err) } } } uuid-5.0.0/fuzz.go000066400000000000000000000034231437151313500140140ustar00rootroot00000000000000// Copyright (c) 2018 Andrei Tudor Călin // // Permission is hereby granted, free of charge, to any person obtaining // a copy of this software and associated documentation files (the // "Software"), to deal in the Software without restriction, including // without limitation the rights to use, copy, modify, merge, publish, // distribute, sublicense, and/or sell copies of the Software, and to // permit persons to whom the Software is furnished to do so, subject to // the following conditions: // // The above copyright notice and this permission notice shall be // included in all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. //go:build gofuzz // +build gofuzz package uuid // Fuzz implements a simple fuzz test for FromString / UnmarshalText. // // To run: // // $ go get github.com/dvyukov/go-fuzz/... // $ cd $GOPATH/src/github.com/gofrs/uuid // $ go-fuzz-build github.com/gofrs/uuid // $ go-fuzz -bin=uuid-fuzz.zip -workdir=./testdata // // If you make significant changes to FromString / UnmarshalText and add // new cases to fromStringTests (in codec_test.go), please run // // $ go test -seed_fuzz_corpus // // to seed the corpus with the new interesting inputs, then run the fuzzer. func Fuzz(data []byte) int { _, err := FromString(string(data)) if err != nil { return 0 } return 1 } uuid-5.0.0/generator.go000066400000000000000000000350631437151313500150110ustar00rootroot00000000000000// Copyright (C) 2013-2018 by Maxim Bublis // // Permission is hereby granted, free of charge, to any person obtaining // a copy of this software and associated documentation files (the // "Software"), to deal in the Software without restriction, including // without limitation the rights to use, copy, modify, merge, publish, // distribute, sublicense, and/or sell copies of the Software, and to // permit persons to whom the Software is furnished to do so, subject to // the following conditions: // // The above copyright notice and this permission notice shall be // included in all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. package uuid import ( "crypto/md5" "crypto/rand" "crypto/sha1" "encoding/binary" "fmt" "hash" "io" "net" "sync" "time" ) // Difference in 100-nanosecond intervals between // UUID epoch (October 15, 1582) and Unix epoch (January 1, 1970). const epochStart = 122192928000000000 // EpochFunc is the function type used to provide the current time. type EpochFunc func() time.Time // HWAddrFunc is the function type used to provide hardware (MAC) addresses. type HWAddrFunc func() (net.HardwareAddr, error) // DefaultGenerator is the default UUID Generator used by this package. var DefaultGenerator Generator = NewGen() // NewV1 returns a UUID based on the current timestamp and MAC address. func NewV1() (UUID, error) { return DefaultGenerator.NewV1() } // NewV3 returns a UUID based on the MD5 hash of the namespace UUID and name. func NewV3(ns UUID, name string) UUID { return DefaultGenerator.NewV3(ns, name) } // NewV4 returns a randomly generated UUID. func NewV4() (UUID, error) { return DefaultGenerator.NewV4() } // NewV5 returns a UUID based on SHA-1 hash of the namespace UUID and name. func NewV5(ns UUID, name string) UUID { return DefaultGenerator.NewV5(ns, name) } // NewV6 returns a k-sortable UUID based on a timestamp and 48 bits of // pseudorandom data. The timestamp in a V6 UUID is the same as V1, with the bit // order being adjusted to allow the UUID to be k-sortable. // // This is implemented based on revision 03 of the Peabody UUID draft, and may // be subject to change pending further revisions. Until the final specification // revision is finished, changes required to implement updates to the spec will // not be considered a breaking change. They will happen as a minor version // releases until the spec is final. func NewV6() (UUID, error) { return DefaultGenerator.NewV6() } // NewV7 returns a k-sortable UUID based on the current millisecond precision // UNIX epoch and 74 bits of pseudorandom data. It supports single-node batch generation (multiple UUIDs in the same timestamp) with a Monotonic Random counter. // // This is implemented based on revision 04 of the Peabody UUID draft, and may // be subject to change pending further revisions. Until the final specification // revision is finished, changes required to implement updates to the spec will // not be considered a breaking change. They will happen as a minor version // releases until the spec is final. func NewV7() (UUID, error) { return DefaultGenerator.NewV7() } // Generator provides an interface for generating UUIDs. type Generator interface { NewV1() (UUID, error) NewV3(ns UUID, name string) UUID NewV4() (UUID, error) NewV5(ns UUID, name string) UUID NewV6() (UUID, error) NewV7() (UUID, error) } // Gen is a reference UUID generator based on the specifications laid out in // RFC-4122 and DCE 1.1: Authentication and Security Services. This type // satisfies the Generator interface as defined in this package. // // For consumers who are generating V1 UUIDs, but don't want to expose the MAC // address of the node generating the UUIDs, the NewGenWithHWAF() function has been // provided as a convenience. See the function's documentation for more info. // // The authors of this package do not feel that the majority of users will need // to obfuscate their MAC address, and so we recommend using NewGen() to create // a new generator. type Gen struct { clockSequenceOnce sync.Once hardwareAddrOnce sync.Once storageMutex sync.Mutex rand io.Reader epochFunc EpochFunc hwAddrFunc HWAddrFunc lastTime uint64 clockSequence uint16 hardwareAddr [6]byte } // GenOption is a function type that can be used to configure a Gen generator. type GenOption func(*Gen) // interface check -- build will fail if *Gen doesn't satisfy Generator var _ Generator = (*Gen)(nil) // NewGen returns a new instance of Gen with some default values set. Most // people should use this. func NewGen() *Gen { return NewGenWithHWAF(defaultHWAddrFunc) } // NewGenWithHWAF builds a new UUID generator with the HWAddrFunc provided. Most // consumers should use NewGen() instead. // // This is used so that consumers can generate their own MAC addresses, for use // in the generated UUIDs, if there is some concern about exposing the physical // address of the machine generating the UUID. // // The Gen generator will only invoke the HWAddrFunc once, and cache that MAC // address for all the future UUIDs generated by it. If you'd like to switch the // MAC address being used, you'll need to create a new generator using this // function. func NewGenWithHWAF(hwaf HWAddrFunc) *Gen { return NewGenWithOptions(WithHWAddrFunc(hwaf)) } // NewGenWithOptions returns a new instance of Gen with the options provided. // Most people should use NewGen() or NewGenWithHWAF() instead. // // To customize the generator, you can pass in one or more GenOption functions. // For example: // // gen := NewGenWithOptions( // WithHWAddrFunc(myHWAddrFunc), // WithEpochFunc(myEpochFunc), // WithRandomReader(myRandomReader), // ) // // NewGenWithOptions(WithHWAddrFunc(myHWAddrFunc)) is equivalent to calling // NewGenWithHWAF(myHWAddrFunc) // NewGenWithOptions() is equivalent to calling NewGen() func NewGenWithOptions(opts ...GenOption) *Gen { gen := &Gen{ epochFunc: time.Now, hwAddrFunc: defaultHWAddrFunc, rand: rand.Reader, } for _, opt := range opts { opt(gen) } return gen } // WithHWAddrFunc is a GenOption that allows you to provide your own HWAddrFunc // function. // When this option is nil, the defaultHWAddrFunc is used. func WithHWAddrFunc(hwaf HWAddrFunc) GenOption { return func(gen *Gen) { if hwaf == nil { hwaf = defaultHWAddrFunc } gen.hwAddrFunc = hwaf } } // WithEpochFunc is a GenOption that allows you to provide your own EpochFunc // function. // When this option is nil, time.Now is used. func WithEpochFunc(epochf EpochFunc) GenOption { return func(gen *Gen) { if epochf == nil { epochf = time.Now } gen.epochFunc = epochf } } // WithRandomReader is a GenOption that allows you to provide your own random // reader. // When this option is nil, the default rand.Reader is used. func WithRandomReader(reader io.Reader) GenOption { return func(gen *Gen) { if reader == nil { reader = rand.Reader } gen.rand = reader } } // NewV1 returns a UUID based on the current timestamp and MAC address. func (g *Gen) NewV1() (UUID, error) { u := UUID{} timeNow, clockSeq, err := g.getClockSequence(false) if err != nil { return Nil, err } binary.BigEndian.PutUint32(u[0:], uint32(timeNow)) binary.BigEndian.PutUint16(u[4:], uint16(timeNow>>32)) binary.BigEndian.PutUint16(u[6:], uint16(timeNow>>48)) binary.BigEndian.PutUint16(u[8:], clockSeq) hardwareAddr, err := g.getHardwareAddr() if err != nil { return Nil, err } copy(u[10:], hardwareAddr) u.SetVersion(V1) u.SetVariant(VariantRFC4122) return u, nil } // NewV3 returns a UUID based on the MD5 hash of the namespace UUID and name. func (g *Gen) NewV3(ns UUID, name string) UUID { u := newFromHash(md5.New(), ns, name) u.SetVersion(V3) u.SetVariant(VariantRFC4122) return u } // NewV4 returns a randomly generated UUID. func (g *Gen) NewV4() (UUID, error) { u := UUID{} if _, err := io.ReadFull(g.rand, u[:]); err != nil { return Nil, err } u.SetVersion(V4) u.SetVariant(VariantRFC4122) return u, nil } // NewV5 returns a UUID based on SHA-1 hash of the namespace UUID and name. func (g *Gen) NewV5(ns UUID, name string) UUID { u := newFromHash(sha1.New(), ns, name) u.SetVersion(V5) u.SetVariant(VariantRFC4122) return u } // NewV6 returns a k-sortable UUID based on a timestamp and 48 bits of // pseudorandom data. The timestamp in a V6 UUID is the same as V1, with the bit // order being adjusted to allow the UUID to be k-sortable. // // This is implemented based on revision 03 of the Peabody UUID draft, and may // be subject to change pending further revisions. Until the final specification // revision is finished, changes required to implement updates to the spec will // not be considered a breaking change. They will happen as a minor version // releases until the spec is final. func (g *Gen) NewV6() (UUID, error) { var u UUID if _, err := io.ReadFull(g.rand, u[10:]); err != nil { return Nil, err } timeNow, clockSeq, err := g.getClockSequence(false) if err != nil { return Nil, err } binary.BigEndian.PutUint32(u[0:], uint32(timeNow>>28)) // set time_high binary.BigEndian.PutUint16(u[4:], uint16(timeNow>>12)) // set time_mid binary.BigEndian.PutUint16(u[6:], uint16(timeNow&0xfff)) // set time_low (minus four version bits) binary.BigEndian.PutUint16(u[8:], clockSeq&0x3fff) // set clk_seq_hi_res (minus two variant bits) u.SetVersion(V6) u.SetVariant(VariantRFC4122) return u, nil } // getClockSequence returns the epoch and clock sequence for V1,V6 and V7 UUIDs. // // When useUnixTSMs is false, it uses the Coordinated Universal Time (UTC) as a count of 100- // // nanosecond intervals since 00:00:00.00, 15 October 1582 (the date of Gregorian reform to the Christian calendar). func (g *Gen) getClockSequence(useUnixTSMs bool) (uint64, uint16, error) { var err error g.clockSequenceOnce.Do(func() { buf := make([]byte, 2) if _, err = io.ReadFull(g.rand, buf); err != nil { return } g.clockSequence = binary.BigEndian.Uint16(buf) }) if err != nil { return 0, 0, err } g.storageMutex.Lock() defer g.storageMutex.Unlock() var timeNow uint64 if useUnixTSMs { timeNow = uint64(g.epochFunc().UnixMilli()) } else { timeNow = g.getEpoch() } // Clock didn't change since last UUID generation. // Should increase clock sequence. if timeNow <= g.lastTime { g.clockSequence++ } g.lastTime = timeNow return timeNow, g.clockSequence, nil } // NewV7 returns a k-sortable UUID based on the current millisecond precision // UNIX epoch and 74 bits of pseudorandom data. // // This is implemented based on revision 04 of the Peabody UUID draft, and may // be subject to change pending further revisions. Until the final specification // revision is finished, changes required to implement updates to the spec will // not be considered a breaking change. They will happen as a minor version // releases until the spec is final. func (g *Gen) NewV7() (UUID, error) { var u UUID /* https://www.ietf.org/archive/id/draft-peabody-dispatch-new-uuid-format-04.html#name-uuid-version-7 0 1 2 3 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | unix_ts_ms | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | unix_ts_ms | ver | rand_a | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |var| rand_b | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | rand_b | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ */ ms, clockSeq, err := g.getClockSequence(true) if err != nil { return Nil, err } //UUIDv7 features a 48 bit timestamp. First 32bit (4bytes) represents seconds since 1970, followed by 2 bytes for the ms granularity. u[0] = byte(ms >> 40) //1-6 bytes: big-endian unsigned number of Unix epoch timestamp u[1] = byte(ms >> 32) u[2] = byte(ms >> 24) u[3] = byte(ms >> 16) u[4] = byte(ms >> 8) u[5] = byte(ms) //support batching by using a monotonic pseudo-random sequence //The 6th byte contains the version and partially rand_a data. //We will lose the most significant bites from the clockSeq (with SetVersion), but it is ok, we need the least significant that contains the counter to ensure the monotonic property binary.BigEndian.PutUint16(u[6:8], clockSeq) // set rand_a with clock seq which is random and monotonic //override first 4bits of u[6]. u.SetVersion(V7) //set rand_b 64bits of pseudo-random bits (first 2 will be overridden) if _, err = io.ReadFull(g.rand, u[8:16]); err != nil { return Nil, err } //override first 2 bits of byte[8] for the variant u.SetVariant(VariantRFC4122) return u, nil } // Returns the hardware address. func (g *Gen) getHardwareAddr() ([]byte, error) { var err error g.hardwareAddrOnce.Do(func() { var hwAddr net.HardwareAddr if hwAddr, err = g.hwAddrFunc(); err == nil { copy(g.hardwareAddr[:], hwAddr) return } // Initialize hardwareAddr randomly in case // of real network interfaces absence. if _, err = io.ReadFull(g.rand, g.hardwareAddr[:]); err != nil { return } // Set multicast bit as recommended by RFC-4122 g.hardwareAddr[0] |= 0x01 }) if err != nil { return []byte{}, err } return g.hardwareAddr[:], nil } // Returns the difference between UUID epoch (October 15, 1582) // and current time in 100-nanosecond intervals. func (g *Gen) getEpoch() uint64 { return epochStart + uint64(g.epochFunc().UnixNano()/100) } // Returns the UUID based on the hashing of the namespace UUID and name. func newFromHash(h hash.Hash, ns UUID, name string) UUID { u := UUID{} h.Write(ns[:]) h.Write([]byte(name)) copy(u[:], h.Sum(nil)) return u } var netInterfaces = net.Interfaces // Returns the hardware address. func defaultHWAddrFunc() (net.HardwareAddr, error) { ifaces, err := netInterfaces() if err != nil { return []byte{}, err } for _, iface := range ifaces { if len(iface.HardwareAddr) >= 6 { return iface.HardwareAddr, nil } } return []byte{}, fmt.Errorf("uuid: no HW address found") } uuid-5.0.0/generator_test.go000066400000000000000000000570141437151313500160500ustar00rootroot00000000000000// Copyright (C) 2013-2018 by Maxim Bublis // // Permission is hereby granted, free of charge, to any person obtaining // a copy of this software and associated documentation files (the // "Software"), to deal in the Software without restriction, including // without limitation the rights to use, copy, modify, merge, publish, // distribute, sublicense, and/or sell copies of the Software, and to // permit persons to whom the Software is furnished to do so, subject to // the following conditions: // // The above copyright notice and this permission notice shall be // included in all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. package uuid import ( "bytes" "crypto/rand" "encoding/binary" "errors" "fmt" "net" "strings" "testing" "time" ) func TestGenerator(t *testing.T) { t.Run("NewV1", testNewV1) t.Run("NewV3", testNewV3) t.Run("NewV4", testNewV4) t.Run("NewV5", testNewV5) t.Run("NewV6", testNewV6) t.Run("NewV7", testNewV7) } func testNewV1(t *testing.T) { t.Run("Basic", testNewV1Basic) t.Run("BasicWithOptions", testNewV1BasicWithOptions) t.Run("DifferentAcrossCalls", testNewV1DifferentAcrossCalls) t.Run("StaleEpoch", testNewV1StaleEpoch) t.Run("FaultyRand", testNewV1FaultyRand) t.Run("FaultyRandWithOptions", testNewV1FaultyRandWithOptions) t.Run("MissingNetwork", testNewV1MissingNetwork) t.Run("MissingNetworkWithOptions", testNewV1MissingNetworkWithOptions) t.Run("MissingNetworkFaultyRand", testNewV1MissingNetworkFaultyRand) t.Run("MissingNetworkFaultyRandWithOptions", testNewV1MissingNetworkFaultyRandWithOptions) } func TestNewGenWithHWAF(t *testing.T) { addr := []byte{0, 1, 2, 3, 4, 42} fn := func() (net.HardwareAddr, error) { return addr, nil } var g *Gen var err error var uuid UUID g = NewGenWithHWAF(fn) if g == nil { t.Fatal("g is unexpectedly nil") } uuid, err = g.NewV1() if err != nil { t.Fatalf("g.NewV1() err = %v, want ", err) } node := uuid[10:] if !bytes.Equal(addr, node) { t.Fatalf("node = %v, want %v", node, addr) } } func testNewV1Basic(t *testing.T) { u, err := NewV1() if err != nil { t.Fatal(err) } if got, want := u.Version(), V1; got != want { t.Errorf("generated UUID with version %d, want %d", got, want) } if got, want := u.Variant(), VariantRFC4122; got != want { t.Errorf("generated UUID with variant %d, want %d", got, want) } } func testNewV1BasicWithOptions(t *testing.T) { g := NewGenWithOptions( WithHWAddrFunc(nil), WithEpochFunc(nil), WithRandomReader(nil), ) u, err := g.NewV1() if err != nil { t.Fatal(err) } if got, want := u.Version(), V1; got != want { t.Errorf("generated UUID with version %d, want %d", got, want) } if got, want := u.Variant(), VariantRFC4122; got != want { t.Errorf("generated UUID with variant %d, want %d", got, want) } } func testNewV1DifferentAcrossCalls(t *testing.T) { u1, err := NewV1() if err != nil { t.Fatal(err) } u2, err := NewV1() if err != nil { t.Fatal(err) } if u1 == u2 { t.Errorf("generated identical UUIDs across calls: %v", u1) } } func testNewV1StaleEpoch(t *testing.T) { g := &Gen{ epochFunc: func() time.Time { return time.Unix(0, 0) }, hwAddrFunc: defaultHWAddrFunc, rand: rand.Reader, } u1, err := g.NewV1() if err != nil { t.Fatal(err) } u2, err := g.NewV1() if err != nil { t.Fatal(err) } if u1 == u2 { t.Errorf("generated identical UUIDs across calls: %v", u1) } } func testNewV1FaultyRand(t *testing.T) { g := &Gen{ epochFunc: time.Now, hwAddrFunc: defaultHWAddrFunc, rand: &faultyReader{ readToFail: 0, // fail immediately }, } u, err := g.NewV1() if err == nil { t.Fatalf("got %v, want error", u) } if u != Nil { t.Fatalf("got %v on error, want Nil", u) } } func testNewV1MissingNetwork(t *testing.T) { g := &Gen{ epochFunc: time.Now, hwAddrFunc: func() (net.HardwareAddr, error) { return []byte{}, fmt.Errorf("uuid: no hw address found") }, rand: rand.Reader, } _, err := g.NewV1() if err != nil { t.Errorf("did not handle missing network interfaces: %v", err) } } func testNewV1MissingNetworkWithOptions(t *testing.T) { g := NewGenWithOptions( WithHWAddrFunc(func() (net.HardwareAddr, error) { return []byte{}, fmt.Errorf("uuid: no hw address found") }), ) _, err := g.NewV1() if err != nil { t.Errorf("did not handle missing network interfaces: %v", err) } } func testNewV1MissingNetworkFaultyRand(t *testing.T) { g := &Gen{ epochFunc: time.Now, hwAddrFunc: func() (net.HardwareAddr, error) { return []byte{}, fmt.Errorf("uuid: no hw address found") }, rand: &faultyReader{ readToFail: 1, }, } u, err := g.NewV1() if err == nil { t.Errorf("did not error on faulty reader and missing network, got %v", u) } } func testNewV1MissingNetworkFaultyRandWithOptions(t *testing.T) { g := NewGenWithOptions( WithHWAddrFunc(func() (net.HardwareAddr, error) { return []byte{}, fmt.Errorf("uuid: no hw address found") }), WithRandomReader(&faultyReader{ readToFail: 1, }), ) u, err := g.NewV1() if err == nil { t.Errorf("did not error on faulty reader and missing network, got %v", u) } } func testNewV1FaultyRandWithOptions(t *testing.T) { g := NewGenWithOptions(WithRandomReader(&faultyReader{ readToFail: 0, // fail immediately }), ) u, err := g.NewV1() if err == nil { t.Errorf("did not error on faulty reader and missing network, got %v", u) } } func testNewV3(t *testing.T) { t.Run("Basic", testNewV3Basic) t.Run("EqualNames", testNewV3EqualNames) t.Run("DifferentNamespaces", testNewV3DifferentNamespaces) } func testNewV3Basic(t *testing.T) { ns := NamespaceDNS name := "www.example.com" u := NewV3(ns, name) if got, want := u.Version(), V3; got != want { t.Errorf("NewV3(%v, %q): got version %d, want %d", ns, name, got, want) } if got, want := u.Variant(), VariantRFC4122; got != want { t.Errorf("NewV3(%v, %q): got variant %d, want %d", ns, name, got, want) } want := "5df41881-3aed-3515-88a7-2f4a814cf09e" if got := u.String(); got != want { t.Errorf("NewV3(%v, %q) = %q, want %q", ns, name, got, want) } } func testNewV3EqualNames(t *testing.T) { ns := NamespaceDNS name := "example.com" u1 := NewV3(ns, name) u2 := NewV3(ns, name) if u1 != u2 { t.Errorf("NewV3(%v, %q) generated %v and %v across two calls", ns, name, u1, u2) } } func testNewV3DifferentNamespaces(t *testing.T) { name := "example.com" ns1 := NamespaceDNS ns2 := NamespaceURL u1 := NewV3(ns1, name) u2 := NewV3(ns2, name) if u1 == u2 { t.Errorf("NewV3(%v, %q) == NewV3(%d, %q) (%v)", ns1, name, ns2, name, u1) } } func testNewV4(t *testing.T) { t.Run("Basic", testNewV4Basic) t.Run("DifferentAcrossCalls", testNewV4DifferentAcrossCalls) t.Run("FaultyRand", testNewV4FaultyRand) t.Run("FaultyRandWithOptions", testNewV4FaultyRandWithOptions) t.Run("ShortRandomRead", testNewV4ShortRandomRead) t.Run("ShortRandomReadWithOptions", testNewV4ShortRandomReadWithOptions) } func testNewV4Basic(t *testing.T) { u, err := NewV4() if err != nil { t.Fatal(err) } if got, want := u.Version(), V4; got != want { t.Errorf("got version %d, want %d", got, want) } if got, want := u.Variant(), VariantRFC4122; got != want { t.Errorf("got variant %d, want %d", got, want) } } func testNewV4DifferentAcrossCalls(t *testing.T) { u1, err := NewV4() if err != nil { t.Fatal(err) } u2, err := NewV4() if err != nil { t.Fatal(err) } if u1 == u2 { t.Errorf("generated identical UUIDs across calls: %v", u1) } } func testNewV4FaultyRand(t *testing.T) { g := &Gen{ epochFunc: time.Now, hwAddrFunc: defaultHWAddrFunc, rand: &faultyReader{ readToFail: 0, // fail immediately }, } u, err := g.NewV4() if err == nil { t.Errorf("got %v, nil error", u) } } func testNewV4FaultyRandWithOptions(t *testing.T) { g := NewGenWithOptions( WithRandomReader(&faultyReader{ readToFail: 0, // fail immediately }), ) u, err := g.NewV4() if err == nil { t.Errorf("got %v, nil error", u) } } func testNewV4ShortRandomRead(t *testing.T) { g := &Gen{ epochFunc: time.Now, hwAddrFunc: func() (net.HardwareAddr, error) { return []byte{}, fmt.Errorf("uuid: no hw address found") }, rand: bytes.NewReader([]byte{42}), } u, err := g.NewV4() if err == nil { t.Errorf("got %v, nil error", u) } } func testNewV4ShortRandomReadWithOptions(t *testing.T) { g := NewGenWithOptions( WithHWAddrFunc(func() (net.HardwareAddr, error) { return []byte{}, fmt.Errorf("uuid: no hw address found") }), WithRandomReader(&faultyReader{ readToFail: 0, // fail immediately }), ) u, err := g.NewV4() if err == nil { t.Errorf("got %v, nil error", u) } } func testNewV5(t *testing.T) { t.Run("Basic", testNewV5Basic) t.Run("EqualNames", testNewV5EqualNames) t.Run("DifferentNamespaces", testNewV5DifferentNamespaces) } func testNewV5Basic(t *testing.T) { ns := NamespaceDNS name := "www.example.com" u := NewV5(ns, name) if got, want := u.Version(), V5; got != want { t.Errorf("NewV5(%v, %q): got version %d, want %d", ns, name, got, want) } if got, want := u.Variant(), VariantRFC4122; got != want { t.Errorf("NewV5(%v, %q): got variant %d, want %d", ns, name, got, want) } want := "2ed6657d-e927-568b-95e1-2665a8aea6a2" if got := u.String(); got != want { t.Errorf("NewV5(%v, %q) = %q, want %q", ns, name, got, want) } } func testNewV5EqualNames(t *testing.T) { ns := NamespaceDNS name := "example.com" u1 := NewV5(ns, name) u2 := NewV5(ns, name) if u1 != u2 { t.Errorf("NewV5(%v, %q) generated %v and %v across two calls", ns, name, u1, u2) } } func testNewV5DifferentNamespaces(t *testing.T) { name := "example.com" ns1 := NamespaceDNS ns2 := NamespaceURL u1 := NewV5(ns1, name) u2 := NewV5(ns2, name) if u1 == u2 { t.Errorf("NewV5(%v, %q) == NewV5(%v, %q) (%v)", ns1, name, ns2, name, u1) } } func testNewV6(t *testing.T) { t.Run("Basic", testNewV6Basic) t.Run("DifferentAcrossCalls", testNewV6DifferentAcrossCalls) t.Run("StaleEpoch", testNewV6StaleEpoch) t.Run("StaleEpochWithOptions", testNewV6StaleEpochWithOptions) t.Run("FaultyRand", testNewV6FaultyRand) t.Run("FaultyRandWithOptions", testNewV6FaultyRandWithOptions) t.Run("ShortRandomRead", testNewV6ShortRandomRead) t.Run("ShortRandomReadWithOptions", testNewV6ShortRandomReadWithOptions) t.Run("KSortable", testNewV6KSortable) } func testNewV6Basic(t *testing.T) { u, err := NewV6() if err != nil { t.Fatal(err) } if got, want := u.Version(), V6; got != want { t.Errorf("generated UUID with version %d, want %d", got, want) } if got, want := u.Variant(), VariantRFC4122; got != want { t.Errorf("generated UUID with variant %d, want %d", got, want) } } func testNewV6DifferentAcrossCalls(t *testing.T) { u1, err := NewV6() if err != nil { t.Fatal(err) } u2, err := NewV6() if err != nil { t.Fatal(err) } if u1 == u2 { t.Errorf("generated identical UUIDs across calls: %v", u1) } } func testNewV6StaleEpoch(t *testing.T) { g := &Gen{ epochFunc: func() time.Time { return time.Unix(0, 0) }, hwAddrFunc: defaultHWAddrFunc, rand: rand.Reader, } u1, err := g.NewV6() if err != nil { t.Fatal(err) } u2, err := g.NewV6() if err != nil { t.Fatal(err) } if u1 == u2 { t.Errorf("generated identical UUIDs across calls: %v", u1) } } func testNewV6StaleEpochWithOptions(t *testing.T) { g := NewGenWithOptions( WithEpochFunc(func() time.Time { return time.Unix(0, 0) }), ) u1, err := g.NewV6() if err != nil { t.Fatal(err) } u2, err := g.NewV6() if err != nil { t.Fatal(err) } if u1 == u2 { t.Errorf("generated identical UUIDs across calls: %v", u1) } } func testNewV6FaultyRand(t *testing.T) { t.Run("randomData", func(t *testing.T) { g := &Gen{ epochFunc: time.Now, hwAddrFunc: defaultHWAddrFunc, rand: &faultyReader{ readToFail: 0, // fail immediately }, } u, err := g.NewV6() if err == nil { t.Fatalf("got %v, want error", u) } if u != Nil { t.Fatalf("got %v on error, want Nil", u) } }) t.Run("clockSequence", func(t *testing.T) { g := &Gen{ epochFunc: time.Now, hwAddrFunc: defaultHWAddrFunc, rand: &faultyReader{ readToFail: 1, // fail immediately }, } u, err := g.NewV6() if err == nil { t.Fatalf("got %v, want error", u) } if u != Nil { t.Fatalf("got %v on error, want Nil", u) } }) } func testNewV6FaultyRandWithOptions(t *testing.T) { t.Run("randomData", func(t *testing.T) { g := NewGenWithOptions( WithRandomReader(&faultyReader{ readToFail: 0, // fail immediately }), ) u, err := g.NewV6() if err == nil { t.Fatalf("got %v, want error", u) } if u != Nil { t.Fatalf("got %v on error, want Nil", u) } }) t.Run("clockSequence", func(t *testing.T) { g := NewGenWithOptions( WithRandomReader(&faultyReader{ readToFail: 1, // fail immediately }), ) u, err := g.NewV6() if err == nil { t.Fatalf("got %v, want error", u) } if u != Nil { t.Fatalf("got %v on error, want Nil", u) } }) } func testNewV6ShortRandomRead(t *testing.T) { g := &Gen{ epochFunc: time.Now, rand: bytes.NewReader([]byte{42}), } u, err := g.NewV6() if err == nil { t.Errorf("got %v, nil error", u) } } func testNewV6ShortRandomReadWithOptions(t *testing.T) { g := NewGenWithOptions( WithRandomReader(bytes.NewReader([]byte{42})), ) u, err := g.NewV6() if err == nil { t.Errorf("got %v, nil error", u) } } func testNewV6KSortable(t *testing.T) { uuids := make([]UUID, 10) for i := range uuids { u, err := NewV6() testErrCheck(t, "NewV6()", "", err) uuids[i] = u time.Sleep(time.Microsecond) } for i := 1; i < len(uuids); i++ { p, n := uuids[i-1], uuids[i] isLess := p.String() < n.String() if !isLess { t.Errorf("uuids[%d] (%s) not less than uuids[%d] (%s)", i-1, p, i, n) } } } func testNewV7(t *testing.T) { t.Run("Basic", makeTestNewV7Basic()) t.Run("TestVector", makeTestNewV7TestVector()) t.Run("Basic10000000", makeTestNewV7Basic10000000()) t.Run("DifferentAcrossCalls", makeTestNewV7DifferentAcrossCalls()) t.Run("StaleEpoch", makeTestNewV7StaleEpoch()) t.Run("StaleEpochWithOptions", makeTestNewV7StaleEpochWithOptions()) t.Run("FaultyRand", makeTestNewV7FaultyRand()) t.Run("FaultyRandWithOptions", makeTestNewV7FaultyRandWithOptions()) t.Run("ShortRandomRead", makeTestNewV7ShortRandomRead()) t.Run("KSortable", makeTestNewV7KSortable()) t.Run("ClockSequence", makeTestNewV7ClockSequence()) } func makeTestNewV7Basic() func(t *testing.T) { return func(t *testing.T) { u, err := NewV7() if err != nil { t.Fatal(err) } if got, want := u.Version(), V7; got != want { t.Errorf("got version %d, want %d", got, want) } if got, want := u.Variant(), VariantRFC4122; got != want { t.Errorf("got variant %d, want %d", got, want) } } } // makeTestNewV7TestVector as defined in Draft04 func makeTestNewV7TestVector() func(t *testing.T) { return func(t *testing.T) { pRand := make([]byte, 10) //first 2 bytes will be read by clockSeq. First 4 bits will be overridden by Version. The next bits should be 0xCC3(3267) binary.LittleEndian.PutUint16(pRand[:2], uint16(0xCC3)) //8bytes will be read for rand_b. First 2 bits will be overridden by Variant binary.LittleEndian.PutUint64(pRand[2:], uint64(0x18C4DC0C0C07398F)) g := &Gen{ epochFunc: func() time.Time { return time.UnixMilli(1645557742000) }, rand: bytes.NewReader(pRand), } u, err := g.NewV7() if err != nil { t.Fatal(err) } if got, want := u.Version(), V7; got != want { t.Errorf("got version %d, want %d", got, want) } if got, want := u.Variant(), VariantRFC4122; got != want { t.Errorf("got variant %d, want %d", got, want) } if got, want := u.String()[:15], "017f22e2-79b0-7"; got != want { t.Errorf("got version %q, want %q", got, want) } } } func makeTestNewV7Basic10000000() func(t *testing.T) { return func(t *testing.T) { if testing.Short() { t.Skip("skipping test in short mode.") } g := NewGen() for i := 0; i < 10000000; i++ { u, err := g.NewV7() if err != nil { t.Fatal(err) } if got, want := u.Version(), V7; got != want { t.Errorf("got version %d, want %d", got, want) } if got, want := u.Variant(), VariantRFC4122; got != want { t.Errorf("got variant %d, want %d", got, want) } } } } func makeTestNewV7DifferentAcrossCalls() func(t *testing.T) { return func(t *testing.T) { g := NewGen() u1, err := g.NewV7() if err != nil { t.Fatal(err) } u2, err := g.NewV7() if err != nil { t.Fatal(err) } if u1 == u2 { t.Errorf("generated identical UUIDs across calls: %v", u1) } } } func makeTestNewV7StaleEpoch() func(t *testing.T) { return func(t *testing.T) { g := &Gen{ epochFunc: func() time.Time { return time.Unix(0, 0) }, rand: rand.Reader, } u1, err := g.NewV7() if err != nil { t.Fatal(err) } u2, err := g.NewV7() if err != nil { t.Fatal(err) } if u1 == u2 { t.Errorf("generated identical UUIDs across calls: %v", u1) } } } func makeTestNewV7StaleEpochWithOptions() func(t *testing.T) { return func(t *testing.T) { g := NewGenWithOptions( WithEpochFunc(func() time.Time { return time.Unix(0, 0) }), ) u1, err := g.NewV7() if err != nil { t.Fatal(err) } u2, err := g.NewV7() if err != nil { t.Fatal(err) } if u1 == u2 { t.Errorf("generated identical UUIDs across calls: %v", u1) } } } func makeTestNewV7FaultyRand() func(t *testing.T) { return func(t *testing.T) { g := &Gen{ epochFunc: time.Now, rand: &faultyReader{ readToFail: 0, }, } u, err := g.NewV7() if err == nil { t.Errorf("got %v, nil error for clockSequence", u) } g = &Gen{ epochFunc: time.Now, rand: &faultyReader{ readToFail: 1, }, } u, err = g.NewV7() if err == nil { t.Errorf("got %v, nil error rand_b", u) } } } func makeTestNewV7FaultyRandWithOptions() func(t *testing.T) { return func(t *testing.T) { g := NewGenWithOptions( WithRandomReader(&faultyReader{ readToFail: 0, // fail immediately }), ) u, err := g.NewV7() if err == nil { t.Errorf("got %v, nil error", u) } } } func makeTestNewV7ShortRandomRead() func(t *testing.T) { return func(t *testing.T) { g := &Gen{ epochFunc: time.Now, rand: bytes.NewReader([]byte{42}), } u, err := g.NewV7() if err == nil { t.Errorf("got %v, nil error", u) } } } func makeTestNewV7ShortRandomReadWithOptions() func(t *testing.T) { return func(t *testing.T) { g := NewGenWithOptions( WithRandomReader(bytes.NewReader([]byte{42})), ) u, err := g.NewV7() if err == nil { t.Errorf("got %v, nil error", u) } } } func makeTestNewV7KSortable() func(t *testing.T) { return func(t *testing.T) { uuids := make([]UUID, 10) for i := range uuids { u, err := NewV7() testErrCheck(t, "NewV7()", "", err) uuids[i] = u time.Sleep(time.Millisecond) } for i := 1; i < len(uuids); i++ { p, n := uuids[i-1], uuids[i] isLess := p.String() < n.String() if !isLess { t.Errorf("uuids[%d] (%s) not less than uuids[%d] (%s)", i-1, p, i, n) } } } } func makeTestNewV7ClockSequence() func(t *testing.T) { return func(t *testing.T) { if testing.Short() { t.Skip("skipping test in short mode.") } g := NewGen() //always return the same TS g.epochFunc = func() time.Time { return time.UnixMilli(1645557742000) } //by being KSortable with the same timestamp, it means the sequence is Not empty, and it is monotonic uuids := make([]UUID, 10) for i := range uuids { u, err := g.NewV7() testErrCheck(t, "NewV7()", "", err) uuids[i] = u } for i := 1; i < len(uuids); i++ { p, n := uuids[i-1], uuids[i] isLess := p.String() < n.String() if !isLess { t.Errorf("uuids[%d] (%s) not less than uuids[%d] (%s)", i-1, p, i, n) } } } } func TestDefaultHWAddrFunc(t *testing.T) { tests := []struct { n string fn func() ([]net.Interface, error) hw net.HardwareAddr e string }{ { n: "Error", fn: func() ([]net.Interface, error) { return nil, errors.New("controlled failure") }, e: "controlled failure", }, { n: "NoValidHWAddrReturned", fn: func() ([]net.Interface, error) { s := []net.Interface{ { Index: 1, MTU: 1500, Name: "test0", HardwareAddr: net.HardwareAddr{1, 2, 3, 4}, }, { Index: 2, MTU: 1500, Name: "lo0", HardwareAddr: net.HardwareAddr{5, 6, 7, 8}, }, } return s, nil }, e: "uuid: no HW address found", }, { n: "ValidHWAddrReturned", fn: func() ([]net.Interface, error) { s := []net.Interface{ { Index: 1, MTU: 1500, Name: "test0", HardwareAddr: net.HardwareAddr{1, 2, 3, 4}, }, { Index: 2, MTU: 1500, Name: "lo0", HardwareAddr: net.HardwareAddr{5, 6, 7, 8, 9, 0}, }, } return s, nil }, hw: net.HardwareAddr{5, 6, 7, 8, 9, 0}, }, } for _, tt := range tests { t.Run(tt.n, func(t *testing.T) { // set the netInterfaces variable (function) for the test // and then set it back to default in the deferred function netInterfaces = tt.fn defer func() { netInterfaces = net.Interfaces }() var hw net.HardwareAddr var err error hw, err = defaultHWAddrFunc() if len(tt.e) > 0 { if err == nil { t.Fatalf("defaultHWAddrFunc() error = , should contain %q", tt.e) } if !strings.Contains(err.Error(), tt.e) { t.Fatalf("defaultHWAddrFunc() error = %q, should contain %q", err.Error(), tt.e) } return } if err != nil && tt.e == "" { t.Fatalf("defaultHWAddrFunc() error = %q, want ", err.Error()) } if !bytes.Equal(hw, tt.hw) { t.Fatalf("hw = %#v, want %#v", hw, tt.hw) } }) } } func BenchmarkGenerator(b *testing.B) { b.Run("NewV1", func(b *testing.B) { for i := 0; i < b.N; i++ { NewV1() } }) b.Run("NewV3", func(b *testing.B) { for i := 0; i < b.N; i++ { NewV3(NamespaceDNS, "www.example.com") } }) b.Run("NewV4", func(b *testing.B) { for i := 0; i < b.N; i++ { NewV4() } }) b.Run("NewV5", func(b *testing.B) { for i := 0; i < b.N; i++ { NewV5(NamespaceDNS, "www.example.com") } }) } type faultyReader struct { callsNum int readToFail int // Read call number to fail } func (r *faultyReader) Read(dest []byte) (int, error) { r.callsNum++ if (r.callsNum - 1) == r.readToFail { return 0, fmt.Errorf("io: reader is faulty") } return rand.Read(dest) } // testErrCheck looks to see if errContains is a substring of err.Error(). If // not, this calls t.Fatal(). It also calls t.Fatal() if there was an error, but // errContains is empty. Returns true if you should continue running the test, // or false if you should stop the test. func testErrCheck(t *testing.T, name string, errContains string, err error) bool { t.Helper() if len(errContains) > 0 { if err == nil { t.Fatalf("%s error = , should contain %q", name, errContains) return false } if errStr := err.Error(); !strings.Contains(errStr, errContains) { t.Fatalf("%s error = %q, should contain %q", name, errStr, errContains) return false } return false } if err != nil && len(errContains) == 0 { t.Fatalf("%s unexpected error: %v", name, err) return false } return true } uuid-5.0.0/go.mod000066400000000000000000000000511437151313500135670ustar00rootroot00000000000000module github.com/gofrs/uuid/v5 go 1.19 uuid-5.0.0/sql.go000066400000000000000000000061241437151313500136160ustar00rootroot00000000000000// Copyright (C) 2013-2018 by Maxim Bublis // // Permission is hereby granted, free of charge, to any person obtaining // a copy of this software and associated documentation files (the // "Software"), to deal in the Software without restriction, including // without limitation the rights to use, copy, modify, merge, publish, // distribute, sublicense, and/or sell copies of the Software, and to // permit persons to whom the Software is furnished to do so, subject to // the following conditions: // // The above copyright notice and this permission notice shall be // included in all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. package uuid import ( "database/sql" "database/sql/driver" "fmt" ) var _ driver.Valuer = UUID{} var _ sql.Scanner = (*UUID)(nil) // Value implements the driver.Valuer interface. func (u UUID) Value() (driver.Value, error) { return u.String(), nil } // Scan implements the sql.Scanner interface. // A 16-byte slice will be handled by UnmarshalBinary, while // a longer byte slice or a string will be handled by UnmarshalText. func (u *UUID) Scan(src interface{}) error { switch src := src.(type) { case UUID: // support gorm convert from UUID to NullUUID *u = src return nil case []byte: if len(src) == Size { return u.UnmarshalBinary(src) } return u.UnmarshalText(src) case string: uu, err := FromString(src) *u = uu return err } return fmt.Errorf("uuid: cannot convert %T to UUID", src) } // NullUUID can be used with the standard sql package to represent a // UUID value that can be NULL in the database. type NullUUID struct { UUID UUID Valid bool } // Value implements the driver.Valuer interface. func (u NullUUID) Value() (driver.Value, error) { if !u.Valid { return nil, nil } // Delegate to UUID Value function return u.UUID.Value() } // Scan implements the sql.Scanner interface. func (u *NullUUID) Scan(src interface{}) error { if src == nil { u.UUID, u.Valid = Nil, false return nil } // Delegate to UUID Scan function u.Valid = true return u.UUID.Scan(src) } var nullJSON = []byte("null") // MarshalJSON marshals the NullUUID as null or the nested UUID func (u NullUUID) MarshalJSON() ([]byte, error) { if !u.Valid { return nullJSON, nil } var buf [38]byte buf[0] = '"' encodeCanonical(buf[1:37], u.UUID) buf[37] = '"' return buf[:], nil } // UnmarshalJSON unmarshals a NullUUID func (u *NullUUID) UnmarshalJSON(b []byte) error { if string(b) == "null" { u.UUID, u.Valid = Nil, false return nil } if n := len(b); n >= 2 && b[0] == '"' { b = b[1 : n-1] } err := u.UUID.UnmarshalText(b) u.Valid = (err == nil) return err } uuid-5.0.0/sql_test.go000066400000000000000000000203561437151313500146600ustar00rootroot00000000000000// Copyright (C) 2013-2018 by Maxim Bublis // // Permission is hereby granted, free of charge, to any person obtaining // a copy of this software and associated documentation files (the // "Software"), to deal in the Software without restriction, including // without limitation the rights to use, copy, modify, merge, publish, // distribute, sublicense, and/or sell copies of the Software, and to // permit persons to whom the Software is furnished to do so, subject to // the following conditions: // // The above copyright notice and this permission notice shall be // included in all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. package uuid import ( "encoding/json" "fmt" "testing" ) func TestSQL(t *testing.T) { t.Run("Value", testSQLValue) t.Run("Scan", func(t *testing.T) { t.Run("Binary", testSQLScanBinary) t.Run("String", testSQLScanString) t.Run("Text", testSQLScanText) t.Run("Unsupported", testSQLScanUnsupported) t.Run("Nil", testSQLScanNil) }) } func testSQLValue(t *testing.T) { v, err := codecTestUUID.Value() if err != nil { t.Fatal(err) } got, ok := v.(string) if !ok { t.Fatalf("Value() returned %T, want string", v) } if want := codecTestUUID.String(); got != want { t.Errorf("Value() == %q, want %q", got, want) } } func testSQLScanBinary(t *testing.T) { got := UUID{} err := got.Scan(codecTestData) if err != nil { t.Fatal(err) } if got != codecTestUUID { t.Errorf("Scan(%x): got %v, want %v", codecTestData, got, codecTestUUID) } } func testSQLScanString(t *testing.T) { s := "6ba7b810-9dad-11d1-80b4-00c04fd430c8" got := UUID{} err := got.Scan(s) if err != nil { t.Fatal(err) } if got != codecTestUUID { t.Errorf("Scan(%q): got %v, want %v", s, got, codecTestUUID) } } func testSQLScanText(t *testing.T) { text := []byte("6ba7b810-9dad-11d1-80b4-00c04fd430c8") got := UUID{} err := got.Scan(text) if err != nil { t.Fatal(err) } if got != codecTestUUID { t.Errorf("Scan(%q): got %v, want %v", text, got, codecTestUUID) } } func testSQLScanUnsupported(t *testing.T) { unsupported := []interface{}{ true, 42, } for _, v := range unsupported { got := UUID{} err := got.Scan(v) if err == nil { t.Errorf("Scan(%T) succeeded, got %v", v, got) } } } func testSQLScanNil(t *testing.T) { got := UUID{} err := got.Scan(nil) if err == nil { t.Errorf("Scan(nil) succeeded, got %v", got) } } func TestNullUUID(t *testing.T) { t.Run("Value", func(t *testing.T) { t.Run("Nil", testNullUUIDValueNil) t.Run("Valid", testNullUUIDValueValid) }) t.Run("Scan", func(t *testing.T) { t.Run("Nil", testNullUUIDScanNil) t.Run("Valid", testNullUUIDScanValid) t.Run("UUID", testNullUUIDScanUUID) }) t.Run("MarshalJSON", func(t *testing.T) { t.Run("Nil", testNullUUIDMarshalJSONNil) t.Run("Null", testNullUUIDMarshalJSONNull) t.Run("Valid", testNullUUIDMarshalJSONValid) }) t.Run("UnmarshalJSON", func(t *testing.T) { t.Run("Nil", testNullUUIDUnmarshalJSONNil) t.Run("Null", testNullUUIDUnmarshalJSONNull) t.Run("Valid", testNullUUIDUnmarshalJSONValid) t.Run("Malformed", testNullUUIDUnmarshalJSONMalformed) }) } func testNullUUIDValueNil(t *testing.T) { nu := NullUUID{} got, err := nu.Value() if got != nil { t.Errorf("null NullUUID.Value returned non-nil driver.Value") } if err != nil { t.Errorf("null NullUUID.Value returned non-nil error") } } func testNullUUIDValueValid(t *testing.T) { nu := NullUUID{ Valid: true, UUID: codecTestUUID, } got, err := nu.Value() if err != nil { t.Fatal(err) } s, ok := got.(string) if !ok { t.Errorf("Value() returned %T, want string", got) } want := "6ba7b810-9dad-11d1-80b4-00c04fd430c8" if s != want { t.Errorf("%v.Value() == %s, want %s", nu, s, want) } } func testNullUUIDScanNil(t *testing.T) { u := NullUUID{} err := u.Scan(nil) if err != nil { t.Fatal(err) } if u.Valid { t.Error("NullUUID is valid after Scan(nil)") } if u.UUID != Nil { t.Errorf("NullUUID.UUID is %v after Scan(nil) want Nil", u.UUID) } } func testNullUUIDScanValid(t *testing.T) { s := "6ba7b810-9dad-11d1-80b4-00c04fd430c8" u := NullUUID{} err := u.Scan(s) if err != nil { t.Fatal(err) } if !u.Valid { t.Errorf("Valid == false after Scan(%q)", s) } if u.UUID != codecTestUUID { t.Errorf("UUID == %v after Scan(%q), want %v", u.UUID, s, codecTestUUID) } } func testNullUUIDScanUUID(t *testing.T) { u := NullUUID{} err := u.Scan(codecTestUUID) if err != nil { t.Fatal(err) } if !u.Valid { t.Errorf("Valid == false after scan(%v)", codecTestUUID) } if u.UUID != codecTestUUID { t.Errorf("UUID == %v after Scan(%v), want %v", u.UUID, codecTestUUID, codecTestUUID) } } func testNullUUIDMarshalJSONNil(t *testing.T) { u := NullUUID{Valid: true} data, err := u.MarshalJSON() if err != nil { t.Fatalf("(%#v).MarshalJSON err want: , got: %v", u, err) } dataStr := string(data) if dataStr != fmt.Sprintf("%q", Nil) { t.Fatalf("(%#v).MarshalJSON value want: %s, got: %s", u, Nil, dataStr) } } func testNullUUIDMarshalJSONValid(t *testing.T) { u := NullUUID{ Valid: true, UUID: codecTestUUID, } data, err := u.MarshalJSON() if err != nil { t.Fatalf("(%#v).MarshalJSON err want: , got: %v", u, err) } dataStr := string(data) if dataStr != fmt.Sprintf("%q", codecTestUUID) { t.Fatalf("(%#v).MarshalJSON value want: %s, got: %s", u, codecTestUUID, dataStr) } } func testNullUUIDMarshalJSONNull(t *testing.T) { u := NullUUID{} data, err := u.MarshalJSON() if err != nil { t.Fatalf("(%#v).MarshalJSON err want: , got: %v", u, err) } dataStr := string(data) if dataStr != "null" { t.Fatalf("(%#v).MarshalJSON value want: %s, got: %s", u, "null", dataStr) } } func testNullUUIDUnmarshalJSONNil(t *testing.T) { var u NullUUID data := []byte(`"00000000-0000-0000-0000-000000000000"`) if err := json.Unmarshal(data, &u); err != nil { t.Fatalf("json.Unmarshal err = %v, want ", err) } if !u.Valid { t.Fatalf("u.Valid = false, want true") } if u.UUID != Nil { t.Fatalf("u.UUID = %v, want %v", u.UUID, Nil) } } func testNullUUIDUnmarshalJSONNull(t *testing.T) { var u NullUUID data := []byte(`null`) if err := json.Unmarshal(data, &u); err != nil { t.Fatalf("json.Unmarshal err = %v, want ", err) } if u.Valid { t.Fatalf("u.Valid = true, want false") } if u.UUID != Nil { t.Fatalf("u.UUID = %v, want %v", u.UUID, Nil) } } func testNullUUIDUnmarshalJSONValid(t *testing.T) { var u NullUUID data := []byte(`"6ba7b810-9dad-11d1-80b4-00c04fd430c8"`) if err := json.Unmarshal(data, &u); err != nil { t.Fatalf("json.Unmarshal err = %v, want ", err) } if !u.Valid { t.Fatalf("u.Valid = false, want true") } if u.UUID != codecTestUUID { t.Fatalf("u.UUID = %v, want %v", u.UUID, Nil) } } func testNullUUIDUnmarshalJSONMalformed(t *testing.T) { var u NullUUID data := []byte(`257`) if err := json.Unmarshal(data, &u); err == nil { t.Fatal("json.Unmarshal err = , want error") } } func BenchmarkNullMarshalJSON(b *testing.B) { b.Run("Valid", func(b *testing.B) { u, err := FromString("6ba7b810-9dad-11d1-80b4-00c04fd430c8") if err != nil { b.Fatal(err) } n := NullUUID{UUID: u, Valid: true} for i := 0; i < b.N; i++ { n.MarshalJSON() } }) b.Run("Invalid", func(b *testing.B) { n := NullUUID{Valid: false} for i := 0; i < b.N; i++ { n.MarshalJSON() } }) } func BenchmarkNullUnmarshalJSON(b *testing.B) { baseUUID, err := FromString("6ba7b810-9dad-11d1-80b4-00c04fd430c8") if err != nil { b.Fatal(err) } data, err := json.Marshal(&baseUUID) if err != nil { b.Fatal(err) } b.Run("Valid", func(b *testing.B) { var u NullUUID for i := 0; i < b.N; i++ { u.UnmarshalJSON(data) } }) b.Run("Invalid", func(b *testing.B) { invalid := []byte("null") var n NullUUID for i := 0; i < b.N; i++ { n.UnmarshalJSON(invalid) } }) } uuid-5.0.0/testdata/000077500000000000000000000000001437151313500142765ustar00rootroot00000000000000uuid-5.0.0/testdata/corpus/000077500000000000000000000000001437151313500156115ustar00rootroot00000000000000uuid-5.0.0/testdata/corpus/1416586f4a34d02bcb506f6107b40df512b9f2f9000066400000000000000000000000401437151313500230140ustar00rootroot00000000000000zba7b810-9dad-11d1-80b4-00c04fd4uuid-5.0.0/testdata/corpus/3b46a7e7b02ec193581e6c9fa2c8a72f50a64e08-1000066400000000000000000000000441437151313500233310ustar00rootroot000000000000006ba7b810-9dad-11d1-80F4-00c"4fd430c8uuid-5.0.0/testdata/corpus/50c54bb75fcfdc488f162bf2f0c6dec6103bfa18-5000066400000000000000000000000401437151313500236220ustar00rootroot000000000000006ad1DdE8dda91DdE80F400c0Bool30t:uuid-5.0.0/testdata/corpus/69c581ab749cbd56be8684d3a58ac2cfab9af0f4-5000066400000000000000000000000401437151313500236440ustar00rootroot000000000000006ba7b810Edad1DdE80F400c0Bool30c8uuid-5.0.0/testdata/corpus/752bf000e0bff06777dd0d6f0be6353844de678a-3000066400000000000000000000000401437151313500233300ustar00rootroot000000000000006ba7b8109dad1Dd180F400c0Bool30c8uuid-5.0.0/testdata/corpus/a4483762d4ece8466d82cca5cacd35a0829c4e60-2000066400000000000000000000000441437151313500234130ustar00rootroot000000000000006ba7b810-9dad-11d1-80F4-F0c"4fd430c8uuid-5.0.0/testdata/corpus/d0952c45e0c823fc5cc12bcf7d9b877d150ab523-1000066400000000000000000000000401437151313500233750ustar00rootroot000000000000006ba7b8109dad11d180b400c0Bool30c8uuid-5.0.0/testdata/corpus/da39a3ee5e6b4b0d3255bfef95601890afd80709000066400000000000000000000000001437151313500232470ustar00rootroot00000000000000uuid-5.0.0/testdata/corpus/e2b84d2065846891f18ae109b12e01d224e1c7c3-4000066400000000000000000000000401437151313500230740ustar00rootroot000000000000006ba7b8109dad1DdE80F400c0Bool30c8uuid-5.0.0/testdata/corpus/e320d749435115e874f77420e17d0937e07f69f3-2000066400000000000000000000000401437151313500227110ustar00rootroot000000000000006ba7b8109dad1Dd180b400c0Bool30c8uuid-5.0.0/testdata/corpus/ed132d47d757f6468443a22df8a2a965efb34098-7000066400000000000000000000000401437151313500232130ustar00rootroot000000000000006ba1DdE8dDAE8DdE80F400c0BoUl30touuid-5.0.0/testdata/corpus/eeefb01f7bb3c627aedb292c994b20f739ffd613-6000066400000000000000000000000401437151313500236370ustar00rootroot000000000000006ad1DdE8dDdE8DdE80F400c0Bool30t:uuid-5.0.0/testdata/corpus/seed_invalid_0000077500000000000000000000000431437151313500204010ustar00rootroot000000000000006ba7b810-9dad-11d1-80b4-00c04fd430cuuid-5.0.0/testdata/corpus/seed_invalid_1000077500000000000000000000000371437151313500204050ustar00rootroot000000000000006ba7b8109dad11d180b400c04fd430cuuid-5.0.0/testdata/corpus/seed_invalid_10000077500000000000000000000000551437151313500204650ustar00rootroot00000000000000uuid:urn:6ba7b810-9dad-11d1-80b4-00c04fd430c8uuid-5.0.0/testdata/corpus/seed_invalid_11000077500000000000000000000000511437151313500204620ustar00rootroot00000000000000uuid:urn:6ba7b8109dad11d180b400c04fd430c8uuid-5.0.0/testdata/corpus/seed_invalid_12000077500000000000000000000000441437151313500204650ustar00rootroot000000000000006ba7b8109-dad-11d1-80b4-00c04fd430c8uuid-5.0.0/testdata/corpus/seed_invalid_13000077500000000000000000000000441437151313500204660ustar00rootroot000000000000006ba7b810-9dad1-1d1-80b4-00c04fd430c8uuid-5.0.0/testdata/corpus/seed_invalid_14000077500000000000000000000000441437151313500204670ustar00rootroot000000000000006ba7b810-9dad-11d18-0b4-00c04fd430c8uuid-5.0.0/testdata/corpus/seed_invalid_15000077500000000000000000000000441437151313500204700ustar00rootroot000000000000006ba7b810-9dad-11d1-80b40-0c04fd430c8uuid-5.0.0/testdata/corpus/seed_invalid_16000077500000000000000000000000441437151313500204710ustar00rootroot000000000000006ba7b810+9dad+11d1+80b4+00c04fd430c8uuid-5.0.0/testdata/corpus/seed_invalid_17000077500000000000000000000000461437151313500204740ustar00rootroot00000000000000(6ba7b810-9dad-11d1-80b4-00c04fd430c8}uuid-5.0.0/testdata/corpus/seed_invalid_18000077500000000000000000000000461437151313500204750ustar00rootroot00000000000000{6ba7b810-9dad-11d1-80b4-00c04fd430c8>uuid-5.0.0/testdata/corpus/seed_invalid_19000077500000000000000000000000441437151313500204740ustar00rootroot00000000000000zba7b810-9dad-11d1-80b4-00c04fd430c8uuid-5.0.0/testdata/corpus/seed_invalid_2000077500000000000000000000000401437151313500204000ustar00rootroot000000000000006ba7b8109dad11d180b400c04fd430q8uuid-5.0.0/testdata/corpus/seed_invalid_20000077500000000000000000000000411437151313500204610ustar00rootroot000000000000006ba7b810-9dad11d180b400c04fd430c8uuid-5.0.0/testdata/corpus/seed_invalid_21000077500000000000000000000000411437151313500204620ustar00rootroot000000000000006ba7b8109dad-11d180b400c04fd430c8uuid-5.0.0/testdata/corpus/seed_invalid_22000077500000000000000000000000411437151313500204630ustar00rootroot000000000000006ba7b8109dad11d1-80b400c04fd430c8uuid-5.0.0/testdata/corpus/seed_invalid_23000077500000000000000000000000411437151313500204640ustar00rootroot000000000000006ba7b8109dad11d180b4-00c04fd430c8uuid-5.0.0/testdata/corpus/seed_invalid_3000077500000000000000000000000451437151313500204060ustar00rootroot000000000000006ba7b810-9dad-11d1-80b4-00c04fd430c8=uuid-5.0.0/testdata/corpus/seed_invalid_4000077500000000000000000000000451437151313500204070ustar00rootroot000000000000006ba7b810-9dad-11d1-80b4-00c04fd430c8}uuid-5.0.0/testdata/corpus/seed_invalid_5000077500000000000000000000000471437151313500204120ustar00rootroot00000000000000{6ba7b810-9dad-11d1-80b4-00c04fd430c8}fuuid-5.0.0/testdata/corpus/seed_invalid_6000077500000000000000000000000601437151313500204060ustar00rootroot000000000000006ba7b810-9dad-11d1-80b4-00c04fd430c800c04fd430c8uuid-5.0.0/testdata/corpus/seed_invalid_7000077500000000000000000000000401437151313500204050ustar00rootroot00000000000000ba7b8109dad11d180b400c04fd430c8}uuid-5.0.0/testdata/corpus/seed_invalid_8000077500000000000000000000001001437151313500204030ustar00rootroot000000000000006ba7b8109dad11d180b400c04fd430c86ba7b8109dad11d180b400c04fd430c8uuid-5.0.0/testdata/corpus/seed_invalid_9000077500000000000000000000000571437151313500204170ustar00rootroot00000000000000urn:uuid:{6ba7b810-9dad-11d1-80b4-00c04fd430c8}uuid-5.0.0/testdata/corpus/seed_valid_BracedCanonical000077500000000000000000000000461437151313500227060ustar00rootroot00000000000000{6ba7b810-9dad-11d1-80b4-00c04fd430c8}uuid-5.0.0/testdata/corpus/seed_valid_BracedHashlike000077500000000000000000000000421437151313500225430ustar00rootroot00000000000000{6ba7b8109dad11d180b400c04fd430c8}uuid-5.0.0/testdata/corpus/seed_valid_Canonical000077500000000000000000000000441437151313500216030ustar00rootroot000000000000006ba7b810-9dad-11d1-80b4-00c04fd430c8uuid-5.0.0/testdata/corpus/seed_valid_Hashlike000077500000000000000000000000401437151313500214400ustar00rootroot000000000000006ba7b8109dad11d180b400c04fd430c8uuid-5.0.0/testdata/corpus/seed_valid_URNCanonical000077500000000000000000000000551437151313500221720ustar00rootroot00000000000000urn:uuid:6ba7b810-9dad-11d1-80b4-00c04fd430c8uuid-5.0.0/testdata/corpus/seed_valid_URNHashlike000077500000000000000000000000511437151313500220270ustar00rootroot00000000000000urn:uuid:6ba7b8109dad11d180b400c04fd430c8uuid-5.0.0/uuid.go000066400000000000000000000215401437151313500137640ustar00rootroot00000000000000// Copyright (C) 2013-2018 by Maxim Bublis // // Permission is hereby granted, free of charge, to any person obtaining // a copy of this software and associated documentation files (the // "Software"), to deal in the Software without restriction, including // without limitation the rights to use, copy, modify, merge, publish, // distribute, sublicense, and/or sell copies of the Software, and to // permit persons to whom the Software is furnished to do so, subject to // the following conditions: // // The above copyright notice and this permission notice shall be // included in all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // Package uuid provides implementations of the Universally Unique Identifier // (UUID), as specified in RFC-4122 and the Peabody RFC Draft (revision 03). // // RFC-4122[1] provides the specification for versions 1, 3, 4, and 5. The // Peabody UUID RFC Draft[2] provides the specification for the new k-sortable // UUIDs, versions 6 and 7. // // DCE 1.1[3] provides the specification for version 2, but version 2 support // was removed from this package in v4 due to some concerns with the // specification itself. Reading the spec, it seems that it would result in // generating UUIDs that aren't very unique. In having read the spec it seemed // that our implementation did not meet the spec. It also seems to be at-odds // with RFC 4122, meaning we would need quite a bit of special code to support // it. Lastly, there were no Version 2 implementations that we could find to // ensure we were understanding the specification correctly. // // [1] https://tools.ietf.org/html/rfc4122 // [2] https://datatracker.ietf.org/doc/html/draft-peabody-dispatch-new-uuid-format-03 // [3] http://pubs.opengroup.org/onlinepubs/9696989899/chap5.htm#tagcjh_08_02_01_01 package uuid import ( "encoding/binary" "encoding/hex" "fmt" "time" ) // Size of a UUID in bytes. const Size = 16 // UUID is an array type to represent the value of a UUID, as defined in RFC-4122. type UUID [Size]byte // UUID versions. const ( _ byte = iota V1 // Version 1 (date-time and MAC address) _ // Version 2 (date-time and MAC address, DCE security version) [removed] V3 // Version 3 (namespace name-based) V4 // Version 4 (random) V5 // Version 5 (namespace name-based) V6 // Version 6 (k-sortable timestamp and random data, field-compatible with v1) [peabody draft] V7 // Version 7 (k-sortable timestamp and random data) [peabody draft] _ // Version 8 (k-sortable timestamp, meant for custom implementations) [peabody draft] [not implemented] ) // UUID layout variants. const ( VariantNCS byte = iota VariantRFC4122 VariantMicrosoft VariantFuture ) // UUID DCE domains. const ( DomainPerson = iota DomainGroup DomainOrg ) // Timestamp is the count of 100-nanosecond intervals since 00:00:00.00, // 15 October 1582 within a V1 UUID. This type has no meaning for other // UUID versions since they don't have an embedded timestamp. type Timestamp uint64 const _100nsPerSecond = 10000000 // Time returns the UTC time.Time representation of a Timestamp func (t Timestamp) Time() (time.Time, error) { secs := uint64(t) / _100nsPerSecond nsecs := 100 * (uint64(t) % _100nsPerSecond) return time.Unix(int64(secs)-(epochStart/_100nsPerSecond), int64(nsecs)), nil } // TimestampFromV1 returns the Timestamp embedded within a V1 UUID. // Returns an error if the UUID is any version other than 1. func TimestampFromV1(u UUID) (Timestamp, error) { if u.Version() != 1 { err := fmt.Errorf("uuid: %s is version %d, not version 1", u, u.Version()) return 0, err } low := binary.BigEndian.Uint32(u[0:4]) mid := binary.BigEndian.Uint16(u[4:6]) hi := binary.BigEndian.Uint16(u[6:8]) & 0xfff return Timestamp(uint64(low) + (uint64(mid) << 32) + (uint64(hi) << 48)), nil } // TimestampFromV6 returns the Timestamp embedded within a V6 UUID. This // function returns an error if the UUID is any version other than 6. // // This is implemented based on revision 03 of the Peabody UUID draft, and may // be subject to change pending further revisions. Until the final specification // revision is finished, changes required to implement updates to the spec will // not be considered a breaking change. They will happen as a minor version // releases until the spec is final. func TimestampFromV6(u UUID) (Timestamp, error) { if u.Version() != 6 { return 0, fmt.Errorf("uuid: %s is version %d, not version 6", u, u.Version()) } hi := binary.BigEndian.Uint32(u[0:4]) mid := binary.BigEndian.Uint16(u[4:6]) low := binary.BigEndian.Uint16(u[6:8]) & 0xfff return Timestamp(uint64(low) + (uint64(mid) << 12) + (uint64(hi) << 28)), nil } // Nil is the nil UUID, as specified in RFC-4122, that has all 128 bits set to // zero. var Nil = UUID{} // Predefined namespace UUIDs. var ( NamespaceDNS = Must(FromString("6ba7b810-9dad-11d1-80b4-00c04fd430c8")) NamespaceURL = Must(FromString("6ba7b811-9dad-11d1-80b4-00c04fd430c8")) NamespaceOID = Must(FromString("6ba7b812-9dad-11d1-80b4-00c04fd430c8")) NamespaceX500 = Must(FromString("6ba7b814-9dad-11d1-80b4-00c04fd430c8")) ) // IsNil returns if the UUID is equal to the nil UUID func (u UUID) IsNil() bool { return u == Nil } // Version returns the algorithm version used to generate the UUID. func (u UUID) Version() byte { return u[6] >> 4 } // Variant returns the UUID layout variant. func (u UUID) Variant() byte { switch { case (u[8] >> 7) == 0x00: return VariantNCS case (u[8] >> 6) == 0x02: return VariantRFC4122 case (u[8] >> 5) == 0x06: return VariantMicrosoft case (u[8] >> 5) == 0x07: fallthrough default: return VariantFuture } } // Bytes returns a byte slice representation of the UUID. func (u UUID) Bytes() []byte { return u[:] } // encodeCanonical encodes the canonical RFC-4122 form of UUID u into the // first 36 bytes dst. func encodeCanonical(dst []byte, u UUID) { const hextable = "0123456789abcdef" dst[8] = '-' dst[13] = '-' dst[18] = '-' dst[23] = '-' for i, x := range [16]byte{ 0, 2, 4, 6, 9, 11, 14, 16, 19, 21, 24, 26, 28, 30, 32, 34, } { c := u[i] dst[x] = hextable[c>>4] dst[x+1] = hextable[c&0x0f] } } // String returns a canonical RFC-4122 string representation of the UUID: // xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx. func (u UUID) String() string { var buf [36]byte encodeCanonical(buf[:], u) return string(buf[:]) } // Format implements fmt.Formatter for UUID values. // // The behavior is as follows: // The 'x' and 'X' verbs output only the hex digits of the UUID, using a-f for 'x' and A-F for 'X'. // The 'v', '+v', 's' and 'q' verbs return the canonical RFC-4122 string representation. // The 'S' verb returns the RFC-4122 format, but with capital hex digits. // The '#v' verb returns the "Go syntax" representation, which is a 16 byte array initializer. // All other verbs not handled directly by the fmt package (like '%p') are unsupported and will return // "%!verb(uuid.UUID=value)" as recommended by the fmt package. func (u UUID) Format(f fmt.State, c rune) { if c == 'v' && f.Flag('#') { fmt.Fprintf(f, "%#v", [Size]byte(u)) return } switch c { case 'x', 'X': b := make([]byte, 32) hex.Encode(b, u[:]) if c == 'X' { toUpperHex(b) } _, _ = f.Write(b) case 'v', 's', 'S': b, _ := u.MarshalText() if c == 'S' { toUpperHex(b) } _, _ = f.Write(b) case 'q': b := make([]byte, 38) b[0] = '"' encodeCanonical(b[1:], u) b[37] = '"' _, _ = f.Write(b) default: // invalid/unsupported format verb fmt.Fprintf(f, "%%!%c(uuid.UUID=%s)", c, u.String()) } } func toUpperHex(b []byte) { for i, c := range b { if 'a' <= c && c <= 'f' { b[i] = c - ('a' - 'A') } } } // SetVersion sets the version bits. func (u *UUID) SetVersion(v byte) { u[6] = (u[6] & 0x0f) | (v << 4) } // SetVariant sets the variant bits. func (u *UUID) SetVariant(v byte) { switch v { case VariantNCS: u[8] = (u[8]&(0xff>>1) | (0x00 << 7)) case VariantRFC4122: u[8] = (u[8]&(0xff>>2) | (0x02 << 6)) case VariantMicrosoft: u[8] = (u[8]&(0xff>>3) | (0x06 << 5)) case VariantFuture: fallthrough default: u[8] = (u[8]&(0xff>>3) | (0x07 << 5)) } } // Must is a helper that wraps a call to a function returning (UUID, error) // and panics if the error is non-nil. It is intended for use in variable // initializations such as // // var packageUUID = uuid.Must(uuid.FromString("123e4567-e89b-12d3-a456-426655440000")) func Must(u UUID, err error) UUID { if err != nil { panic(err) } return u } uuid-5.0.0/uuid_test.go000066400000000000000000000212531437151313500150240ustar00rootroot00000000000000// Copyright (C) 2013-2018 by Maxim Bublis // // Permission is hereby granted, free of charge, to any person obtaining // a copy of this software and associated documentation files (the // "Software"), to deal in the Software without restriction, including // without limitation the rights to use, copy, modify, merge, publish, // distribute, sublicense, and/or sell copies of the Software, and to // permit persons to whom the Software is furnished to do so, subject to // the following conditions: // // The above copyright notice and this permission notice shall be // included in all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. package uuid import ( "bytes" "fmt" "io" "testing" "time" ) func TestUUID(t *testing.T) { t.Run("IsNil", testUUIDIsNil) t.Run("Bytes", testUUIDBytes) t.Run("String", testUUIDString) t.Run("Version", testUUIDVersion) t.Run("Variant", testUUIDVariant) t.Run("SetVersion", testUUIDSetVersion) t.Run("SetVariant", testUUIDSetVariant) t.Run("Format", testUUIDFormat) } func testUUIDIsNil(t *testing.T) { u := UUID{} got := u.IsNil() want := true if got != want { t.Errorf("%v.IsNil() = %t, want %t", u, got, want) } } func testUUIDBytes(t *testing.T) { got := codecTestUUID.Bytes() want := codecTestData if !bytes.Equal(got, want) { t.Errorf("%v.Bytes() = %x, want %x", codecTestUUID, got, want) } } func testUUIDString(t *testing.T) { got := NamespaceDNS.String() want := "6ba7b810-9dad-11d1-80b4-00c04fd430c8" if got != want { t.Errorf("%v.String() = %q, want %q", NamespaceDNS, got, want) } } func testUUIDVersion(t *testing.T) { u := UUID{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} if got, want := u.Version(), V1; got != want { t.Errorf("%v.Version() == %d, want %d", u, got, want) } } func testUUIDVariant(t *testing.T) { tests := []struct { u UUID want byte }{ { u: UUID{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, want: VariantNCS, }, { u: UUID{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, want: VariantRFC4122, }, { u: UUID{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, want: VariantMicrosoft, }, { u: UUID{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, want: VariantFuture, }, } for _, tt := range tests { if got := tt.u.Variant(); got != tt.want { t.Errorf("%v.Variant() == %d, want %d", tt.u, got, tt.want) } } } func testUUIDSetVersion(t *testing.T) { u := UUID{} want := V4 u.SetVersion(want) if got := u.Version(); got != want { t.Errorf("%v.Version() == %d after SetVersion(%d)", u, got, want) } } func testUUIDSetVariant(t *testing.T) { variants := []byte{ VariantNCS, VariantRFC4122, VariantMicrosoft, VariantFuture, } for _, want := range variants { u := UUID{} u.SetVariant(want) if got := u.Variant(); got != want { t.Errorf("%v.Variant() == %d after SetVariant(%d)", u, got, want) } } } func testUUIDFormat(t *testing.T) { val := Must(FromString("12345678-90ab-cdef-1234-567890abcdef")) tests := []struct { u UUID f string want string }{ {u: val, f: "%s", want: "12345678-90ab-cdef-1234-567890abcdef"}, {u: val, f: "%S", want: "12345678-90AB-CDEF-1234-567890ABCDEF"}, {u: val, f: "%q", want: `"12345678-90ab-cdef-1234-567890abcdef"`}, {u: val, f: "%x", want: "1234567890abcdef1234567890abcdef"}, {u: val, f: "%X", want: "1234567890ABCDEF1234567890ABCDEF"}, {u: val, f: "%v", want: "12345678-90ab-cdef-1234-567890abcdef"}, {u: val, f: "%+v", want: "12345678-90ab-cdef-1234-567890abcdef"}, {u: val, f: "%#v", want: "[16]uint8{0x12, 0x34, 0x56, 0x78, 0x90, 0xab, 0xcd, 0xef, 0x12, 0x34, 0x56, 0x78, 0x90, 0xab, 0xcd, 0xef}"}, {u: val, f: "%T", want: "uuid.UUID"}, {u: val, f: "%t", want: "%!t(uuid.UUID=12345678-90ab-cdef-1234-567890abcdef)"}, {u: val, f: "%b", want: "%!b(uuid.UUID=12345678-90ab-cdef-1234-567890abcdef)"}, {u: val, f: "%c", want: "%!c(uuid.UUID=12345678-90ab-cdef-1234-567890abcdef)"}, {u: val, f: "%d", want: "%!d(uuid.UUID=12345678-90ab-cdef-1234-567890abcdef)"}, {u: val, f: "%e", want: "%!e(uuid.UUID=12345678-90ab-cdef-1234-567890abcdef)"}, {u: val, f: "%E", want: "%!E(uuid.UUID=12345678-90ab-cdef-1234-567890abcdef)"}, {u: val, f: "%f", want: "%!f(uuid.UUID=12345678-90ab-cdef-1234-567890abcdef)"}, {u: val, f: "%F", want: "%!F(uuid.UUID=12345678-90ab-cdef-1234-567890abcdef)"}, {u: val, f: "%g", want: "%!g(uuid.UUID=12345678-90ab-cdef-1234-567890abcdef)"}, {u: val, f: "%G", want: "%!G(uuid.UUID=12345678-90ab-cdef-1234-567890abcdef)"}, {u: val, f: "%o", want: "%!o(uuid.UUID=12345678-90ab-cdef-1234-567890abcdef)"}, {u: val, f: "%U", want: "%!U(uuid.UUID=12345678-90ab-cdef-1234-567890abcdef)"}, } for _, tt := range tests { got := fmt.Sprintf(tt.f, tt.u) if tt.want != got { t.Errorf(`Format("%s") got %s, want %s`, tt.f, got, tt.want) } } } func TestMust(t *testing.T) { sentinel := fmt.Errorf("uuid: sentinel error") defer func() { r := recover() if r == nil { t.Fatalf("did not panic, want %v", sentinel) } err, ok := r.(error) if !ok { t.Fatalf("panicked with %T, want error (%v)", r, sentinel) } if err != sentinel { t.Fatalf("panicked with %v, want %v", err, sentinel) } }() fn := func() (UUID, error) { return Nil, sentinel } Must(fn()) } func TestTimeFromTimestamp(t *testing.T) { tests := []struct { t Timestamp want time.Time }{ // a zero timestamp represents October 15, 1582 at midnight UTC {t: Timestamp(0), want: time.Date(1582, 10, 15, 0, 0, 0, 0, time.UTC)}, // a one value is 100ns later {t: Timestamp(1), want: time.Date(1582, 10, 15, 0, 0, 0, 100, time.UTC)}, // 10 million 100ns intervals later is one second {t: Timestamp(10000000), want: time.Date(1582, 10, 15, 0, 0, 1, 0, time.UTC)}, {t: Timestamp(60 * 10000000), want: time.Date(1582, 10, 15, 0, 1, 0, 0, time.UTC)}, {t: Timestamp(60 * 60 * 10000000), want: time.Date(1582, 10, 15, 1, 0, 0, 0, time.UTC)}, {t: Timestamp(24 * 60 * 60 * 10000000), want: time.Date(1582, 10, 16, 0, 0, 0, 0, time.UTC)}, {t: Timestamp(365 * 24 * 60 * 60 * 10000000), want: time.Date(1583, 10, 15, 0, 0, 0, 0, time.UTC)}, // maximum timestamp value in a UUID is the year 5236 {t: Timestamp(uint64(1<<60 - 1)), want: time.Date(5236, 03, 31, 21, 21, 0, 684697500, time.UTC)}, } for _, tt := range tests { got, _ := tt.t.Time() if !got.Equal(tt.want) { t.Errorf("%v.Time() == %v, want %v", tt.t, got, tt.want) } } } func TestTimestampFromV1(t *testing.T) { tests := []struct { u UUID want Timestamp wanterr bool }{ {u: Must(NewV4()), wanterr: true}, {u: Must(FromString("00000000-0000-1000-0000-000000000000")), want: 0}, {u: Must(FromString("424f137e-a2aa-11e8-98d0-529269fb1459")), want: 137538640775418750}, {u: Must(FromString("ffffffff-ffff-1fff-ffff-ffffffffffff")), want: Timestamp(1<<60 - 1)}, } for _, tt := range tests { got, goterr := TimestampFromV1(tt.u) if tt.wanterr && goterr == nil { t.Errorf("TimestampFromV1(%v) want error, got %v", tt.u, got) } else if tt.want != got { t.Errorf("TimestampFromV1(%v) got %v, want %v", tt.u, got, tt.want) } } } func TestTimestampFromV6(t *testing.T) { tests := []struct { u UUID want Timestamp wanterr bool }{ {u: Must(NewV1()), wanterr: true}, {u: Must(FromString("00000000-0000-6000-0000-000000000000")), want: 0}, {u: Must(FromString("1ec06cff-e9b1-621c-8627-ba3fd7e551c9")), want: 138493178941215260}, {u: Must(FromString("ffffffff-ffff-6fff-ffff-ffffffffffff")), want: Timestamp(1<<60 - 1)}, } for _, tt := range tests { got, err := TimestampFromV6(tt.u) switch { case tt.wanterr && err == nil: t.Errorf("TimestampFromV6(%v) want error, got %v", tt.u, got) case tt.want != got: t.Errorf("TimestampFromV6(%v) got %v, want %v", tt.u, got, tt.want) } } } func BenchmarkFormat(b *testing.B) { var tests = []string{ "%s", "%S", "%q", "%x", "%X", "%v", "%+v", "%#v", } for _, x := range tests { b.Run(x[1:], func(b *testing.B) { for i := 0; i < b.N; i++ { fmt.Fprintf(io.Discard, x, &codecTestUUID) } }) } }