pax_global_header00006660000000000000000000000064141500254230014506gustar00rootroot0000000000000052 comment=0c84a43ce25087b7041833685982950a65630d28 uuid-4.2.0/000077500000000000000000000000001415002542300124575ustar00rootroot00000000000000uuid-4.2.0/.github/000077500000000000000000000000001415002542300140175ustar00rootroot00000000000000uuid-4.2.0/.github/workflows/000077500000000000000000000000001415002542300160545ustar00rootroot00000000000000uuid-4.2.0/.github/workflows/go.yml000066400000000000000000000020551415002542300172060ustar00rootroot00000000000000name: 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.17.x' - name: Check out code into the Go module directory uses: actions/checkout@v2 - name: Get Coverage run: go get golang.org/x/tools/cmd/cover - 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.16.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-4.2.0/.gitignore000066400000000000000000000003641415002542300144520ustar00rootroot00000000000000# 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-4.2.0/LICENSE000066400000000000000000000020721415002542300134650ustar00rootroot00000000000000Copyright (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-4.2.0/README.md000066400000000000000000000106661415002542300137470ustar00rootroot00000000000000# 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) ## 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. ## Installation It is recommended to use a package manager like `dep` that understands tagged releases of a package, as well as semantic versioning. If you are unable to make use of a dependency manager with your project, you can use the `go get` command to download it directly: ```Shell $ go get github.com/gofrs/uuid ``` ## Requirements Due to subtests not being supported in older versions of Go, this package is only regularly tested against Go 1.7+. This package may work perfectly fine with Go 1.2+, but support for these older versions is not actively maintained. ## Go 1.11 Modules As of v3.2.0, this repository no longer adopts Go modules, and v3.2.0 no longer has a `go.mod` file. As a result, v3.2.0 also drops support for the `github.com/gofrs/uuid/v3` import path. Only module-based consumers are impacted. With the v3.2.0 release, _all_ gofrs/uuid consumers should use the `github.com/gofrs/uuid` import path. An existing module-based consumer will continue to be able to build using the `github.com/gofrs/uuid/v3` import path using any valid consumer `go.mod` that worked prior to the publishing of v3.2.0, but any module-based consumer should start using the `github.com/gofrs/uuid` import path when possible and _must_ use the `github.com/gofrs/uuid` import path prior to upgrading to v3.2.0. Please refer to [Issue #61](https://github.com/gofrs/uuid/issues/61) and [Issue #66](https://github.com/gofrs/uuid/issues/66) for more details. ## 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 02](https://datatracker.ietf.org/doc/html/draft-peabody-dispatch-new-uuid-format-02) uuid-4.2.0/codec.go000066400000000000000000000143221415002542300140650ustar00rootroot00000000000000// 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" "encoding/hex" "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 } // FromString returns a UUID parsed from the input string. // Input is expected in a form accepted by UnmarshalText. func FromString(input string) (UUID, error) { u := UUID{} err := u.UnmarshalText([]byte(input)) 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) { return []byte(u.String()), 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(text []byte) error { switch len(text) { case 32: return u.decodeHashLike(text) case 34, 38: return u.decodeBraced(text) case 36: return u.decodeCanonical(text) case 41, 45: return u.decodeURN(text) default: return fmt.Errorf("uuid: incorrect UUID length %d in string %q", len(text), text) } } // decodeCanonical decodes UUID strings that are formatted as defined in RFC-4122 (section 3): // "6ba7b810-9dad-11d1-80b4-00c04fd430c8". func (u *UUID) decodeCanonical(t []byte) error { if t[8] != '-' || t[13] != '-' || t[18] != '-' || t[23] != '-' { return fmt.Errorf("uuid: incorrect UUID format in string %q", t) } src := t dst := u[:] for i, byteGroup := range byteGroups { if i > 0 { src = src[1:] // skip dash } _, err := hex.Decode(dst[:byteGroup/2], src[:byteGroup]) if err != nil { return err } src = src[byteGroup:] dst = dst[byteGroup/2:] } return nil } // decodeHashLike decodes UUID strings that are using the following format: // "6ba7b8109dad11d180b400c04fd430c8". func (u *UUID) decodeHashLike(t []byte) error { src := t[:] dst := u[:] _, err := hex.Decode(dst, src) return err } // decodeBraced decodes UUID strings that are using the following formats: // "{6ba7b810-9dad-11d1-80b4-00c04fd430c8}" // "{6ba7b8109dad11d180b400c04fd430c8}". func (u *UUID) decodeBraced(t []byte) error { l := len(t) if t[0] != '{' || t[l-1] != '}' { return fmt.Errorf("uuid: incorrect UUID format in string %q", t) } return u.decodePlain(t[1 : l-1]) } // decodeURN decodes UUID strings that are using the following formats: // "urn:uuid:6ba7b810-9dad-11d1-80b4-00c04fd430c8" // "urn:uuid:6ba7b8109dad11d180b400c04fd430c8". func (u *UUID) decodeURN(t []byte) error { total := len(t) urnUUIDPrefix := t[:9] if !bytes.Equal(urnUUIDPrefix, urnPrefix) { return fmt.Errorf("uuid: incorrect UUID format in string %q", t) } return u.decodePlain(t[9:total]) } // decodePlain decodes UUID strings that are using the following formats: // "6ba7b810-9dad-11d1-80b4-00c04fd430c8" or in hash-like format // "6ba7b8109dad11d180b400c04fd430c8". func (u *UUID) decodePlain(t []byte) error { switch len(t) { case 32: return u.decodeHashLike(t) case 36: return u.decodeCanonical(t) default: return fmt.Errorf("uuid: incorrect UUID length %d in string %q", len(t), t) } } // 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-4.2.0/codec_test.go000066400000000000000000000203461415002542300151270ustar00rootroot00000000000000// 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" "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) Run(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) } }) } // 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.Run(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 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.decodePlain(arg) == nil { t.Errorf("%v.decodePlain(%q): should return error, but it did not", u, arg) } } 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 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() } } 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-4.2.0/fuzz.go000066400000000000000000000034241415002542300140070ustar00rootroot00000000000000// 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. // +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-4.2.0/generator.go000066400000000000000000000377161415002542300150120ustar00rootroot00000000000000// 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" "errors" "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 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 02 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 UNIX epoch, with the // ability to configure the timestamp's precision from millisecond all the way // to nanosecond. The additional precision is supported by reducing the amount // of pseudorandom data that makes up the rest of the UUID. // // If an unknown Precision argument is passed to this method it will panic. As // such it's strongly encouraged to use the package-provided constants for this // value. // // This is implemented based on revision 02 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(p Precision) (UUID, error) { return DefaultGenerator.NewV7(p) } // 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(Precision) (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 v7LastTime uint64 v7LastSubsec uint64 v7ClockSequence uint16 } // 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 &Gen{ epochFunc: time.Now, hwAddrFunc: hwaf, rand: 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() 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 02 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() 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 and V6 UUIDs. func (g *Gen) getClockSequence() (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() 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 } // Precision is used to configure the V7 generator, to specify how precise the // timestamp within the UUID should be. type Precision byte const ( NanosecondPrecision Precision = iota MicrosecondPrecision MillisecondPrecision ) func (p Precision) String() string { switch p { case NanosecondPrecision: return "nanosecond" case MicrosecondPrecision: return "microsecond" case MillisecondPrecision: return "millisecond" default: return "unknown" } } // Duration returns the time.Duration for a specific precision. If the Precision // value is not known, this returns 0. func (p Precision) Duration() time.Duration { switch p { case NanosecondPrecision: return time.Nanosecond case MicrosecondPrecision: return time.Microsecond case MillisecondPrecision: return time.Millisecond default: return 0 } } // NewV7 returns a k-sortable UUID based on the current UNIX epoch, with the // ability to configure the timestamp's precision from millisecond all the way // to nanosecond. The additional precision is supported by reducing the amount // of pseudorandom data that makes up the rest of the UUID. // // If an unknown Precision argument is passed to this method it will panic. As // such it's strongly encouraged to use the package-provided constants for this // value. // // This is implemented based on revision 02 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(p Precision) (UUID, error) { var u UUID var err error switch p { case NanosecondPrecision: u, err = g.newV7Nano() case MicrosecondPrecision: u, err = g.newV7Micro() case MillisecondPrecision: u, err = g.newV7Milli() default: panic(fmt.Sprintf("unknown precision value %d", p)) } if err != nil { return Nil, err } u.SetVersion(V7) u.SetVariant(VariantRFC4122) return u, nil } func (g *Gen) newV7Milli() (UUID, error) { var u UUID if _, err := io.ReadFull(g.rand, u[8:]); err != nil { return Nil, err } sec, nano, seq, err := g.getV7ClockSequence(MillisecondPrecision) if err != nil { return Nil, err } msec := (nano / 1000000) & 0xfff d := (sec << 28) // set unixts field d |= (msec << 16) // set msec field d |= (uint64(seq) & 0xfff) // set seq field binary.BigEndian.PutUint64(u[:], d) return u, nil } func (g *Gen) newV7Micro() (UUID, error) { var u UUID if _, err := io.ReadFull(g.rand, u[10:]); err != nil { return Nil, err } sec, nano, seq, err := g.getV7ClockSequence(MicrosecondPrecision) if err != nil { return Nil, err } usec := nano / 1000 usech := (usec << 4) & 0xfff0000 usecl := usec & 0xfff d := (sec << 28) // set unixts field d |= usech | usecl // set usec fields binary.BigEndian.PutUint64(u[:], d) binary.BigEndian.PutUint16(u[8:], seq) return u, nil } func (g *Gen) newV7Nano() (UUID, error) { var u UUID if _, err := io.ReadFull(g.rand, u[11:]); err != nil { return Nil, err } sec, nano, seq, err := g.getV7ClockSequence(NanosecondPrecision) if err != nil { return Nil, err } nano &= 0x3fffffffff nanoh := nano >> 26 nanom := (nano >> 14) & 0xfff nanol := uint16(nano & 0x3fff) d := (sec << 28) // set unixts field d |= (nanoh << 16) | nanom // set nsec high and med fields binary.BigEndian.PutUint64(u[:], d) binary.BigEndian.PutUint16(u[8:], nanol) // set nsec low field u[10] = byte(seq) // set seq field return u, nil } const ( maxSeq14 = (1 << 14) - 1 maxSeq12 = (1 << 12) - 1 maxSeq8 = (1 << 8) - 1 ) // getV7ClockSequence returns the unix epoch, nanoseconds of current second, and // the sequence for V7 UUIDs. func (g *Gen) getV7ClockSequence(p Precision) (epoch uint64, nano uint64, seq uint16, err error) { g.storageMutex.Lock() defer g.storageMutex.Unlock() tn := g.epochFunc() unix := uint64(tn.Unix()) nsec := uint64(tn.Nanosecond()) // V7 UUIDs have more precise requirements around how the clock sequence // value is generated and used. Specifically they require that the sequence // be zero, unless we've already generated a UUID within this unit of time // (millisecond, microsecond, or nanosecond) at which point you should // increment the sequence. Likewise if time has warped backwards for some reason (NTP // adjustment?), we also increment the clock sequence to reduce the risk of a // collision. switch { case unix < g.v7LastTime: g.v7ClockSequence++ case unix > g.v7LastTime: g.v7ClockSequence = 0 case unix == g.v7LastTime: switch p { case NanosecondPrecision: if nsec <= g.v7LastSubsec { if g.v7ClockSequence >= maxSeq8 { return 0, 0, 0, errors.New("generating nanosecond precision UUIDv7s too fast: internal clock sequence would roll over") } g.v7ClockSequence++ } else { g.v7ClockSequence = 0 } case MicrosecondPrecision: if nsec/1000 <= g.v7LastSubsec/1000 { if g.v7ClockSequence >= maxSeq14 { return 0, 0, 0, errors.New("generating microsecond precision UUIDv7s too fast: internal clock sequence would roll over") } g.v7ClockSequence++ } else { g.v7ClockSequence = 0 } case MillisecondPrecision: if nsec/1000000 <= g.v7LastSubsec/1000000 { if g.v7ClockSequence >= maxSeq12 { return 0, 0, 0, errors.New("generating millisecond precision UUIDv7s too fast: internal clock sequence would roll over") } g.v7ClockSequence++ } else { g.v7ClockSequence = 0 } default: panic(fmt.Sprintf("unknown precision value %d", p)) } } g.v7LastTime = unix g.v7LastSubsec = nsec return unix, nsec, g.v7ClockSequence, 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 } // Returns the hardware address. func defaultHWAddrFunc() (net.HardwareAddr, error) { ifaces, err := net.Interfaces() 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-4.2.0/generator_test.go000066400000000000000000000467161415002542300160510ustar00rootroot00000000000000// 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" "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("DifferentAcrossCalls", testNewV1DifferentAcrossCalls) t.Run("StaleEpoch", testNewV1StaleEpoch) t.Run("FaultyRand", testNewV1FaultyRand) t.Run("MissingNetwork", testNewV1MissingNetwork) t.Run("MissingNetworkFaultyRand", testNewV1MissingNetworkFaultyRand) } 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 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 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 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("ShortRandomRead", testNewV4ShortRandomRead) } 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 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 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("FaultyRand", testNewV6FaultyRand) t.Run("ShortRandomRead", testNewV6ShortRandomRead) 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 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 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 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("InvalidPrecision", testNewV7InvalidPrecision) for _, p := range []Precision{NanosecondPrecision, MicrosecondPrecision, MillisecondPrecision} { t.Run(p.String(), func(t *testing.T) { t.Run("Basic", makeTestNewV7Basic(p)) t.Run("Basic10000000", makeTestNewV7Basic10000000(p)) t.Run("DifferentAcrossCalls", makeTestNewV7DifferentAcrossCalls(p)) t.Run("StaleEpoch", makeTestNewV7StaleEpoch(p)) t.Run("FaultyRand", makeTestNewV7FaultyRand(p)) t.Run("ShortRandomRead", makeTestNewV7ShortRandomRead(p)) t.Run("ClockSequenceBehaviors", makeTestNewV7ClockSequenceBehaviors(p)) t.Run("KSortable", makeTestNewV7KSortable(p)) }) } t.Run("ClockSequence", testNewV7ClockSequence) } func testNewV7InvalidPrecision(t *testing.T) { t.Run("NewV7", func(t *testing.T) { defer func() { if r := recover(); r == nil { t.Fatal("call did not panic") } }() NewV7(255) }) t.Run("getV7ClockSequence", func(t *testing.T) { defer func() { if r := recover(); r == nil { t.Fatal("expected panic did not occur") } }() g := NewGen() g.epochFunc = func() time.Time { return time.Unix(0, 0) } g.getV7ClockSequence(255) }) } func makeTestNewV7Basic(p Precision) func(t *testing.T) { return func(t *testing.T) { u, err := NewV7(p) 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 makeTestNewV7Basic10000000(p Precision) func(t *testing.T) { return func(t *testing.T) { if testing.Short() { t.Skip("skipping test in short mode.") } if p == MillisecondPrecision { t.Skip("skipping test, see: https://github.com/uuid6/uuid6-ietf-draft/issues/40") } g := NewGen() for i := 0; i < 10000000; i++ { u, err := g.NewV7(p) 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(p Precision) func(t *testing.T) { return func(t *testing.T) { g := NewGen() u1, err := g.NewV7(p) if err != nil { t.Fatal(err) } u2, err := g.NewV7(p) if err != nil { t.Fatal(err) } if u1 == u2 { t.Errorf("generated identical UUIDs across calls: %v", u1) } } } func makeTestNewV7StaleEpoch(p Precision) 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(p) if err != nil { t.Fatal(err) } u2, err := g.NewV7(p) if err != nil { t.Fatal(err) } if u1 == u2 { t.Errorf("generated identical UUIDs across calls: %v", u1) } } } func makeTestNewV7FaultyRand(p Precision) func(t *testing.T) { return func(t *testing.T) { g := &Gen{ epochFunc: time.Now, rand: &faultyReader{ readToFail: 0, // fail immediately }, } u, err := g.NewV7(p) if err == nil { t.Errorf("got %v, nil error", u) } } } func makeTestNewV7ShortRandomRead(p Precision) func(t *testing.T) { return func(t *testing.T) { g := &Gen{ epochFunc: time.Now, rand: bytes.NewReader([]byte{42}), } u, err := g.NewV7(p) if err == nil { t.Errorf("got %v, nil error", u) } } } func makeTestNewV7KSortable(p Precision) func(t *testing.T) { return func(t *testing.T) { uuids := make([]UUID, 10) for i := range uuids { u, err := NewV7(p) testErrCheck(t, "NewV6()", "", err) uuids[i] = u time.Sleep(p.Duration()) } 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) } } } } // to get 100% code coverage we need to do some glass box testing func makeTestNewV7ClockSequenceBehaviors(p Precision) func(t *testing.T) { return func(t *testing.T) { t.Run("TimeWarp", func(t *testing.T) { g := NewGen() tn := time.Now() unix := uint64(tn.Unix()) + 100 nsec := uint64(tn.Nanosecond()) g.v7LastTime = unix g.v7LastSubsec = nsec _, err := g.NewV7(p) testErrCheck(t, "g.NewV7()", "", err) if g.v7ClockSequence != 1 { t.Fatalf("g.v7ClockSequence = %d, want 1", g.v7ClockSequence) } }) t.Run("NominalTime", func(t *testing.T) { g := NewGen() g.v7ClockSequence = 100 tn := time.Now() unix := uint64(tn.Unix()) - 100 nsec := uint64(tn.Nanosecond()) g.v7LastTime = unix g.v7LastSubsec = nsec _, err := g.NewV7(p) testErrCheck(t, "g.NewV7()", "", err) if g.v7ClockSequence != 0 { t.Fatalf("g.v7ClockSequence = %d, want 0", g.v7ClockSequence) } }) t.Run("Overflow", func(t *testing.T) { if testing.Short() { t.Skip("skipping test in short mode.") } wantErrStr := fmt.Sprintf("generating %s precision UUIDv7s too fast: internal clock sequence would roll over", p.String()) g := NewGen() g.epochFunc = func() time.Time { return time.Unix(0, 0) } g.v7ClockSequence = maxSeq14 + 1 g.v7LastTime = uint64(g.epochFunc().Unix()) g.v7LastSubsec = uint64(g.epochFunc().Nanosecond()) _, err := g.NewV7(p) testErrCheck(t, "g.NewV7()", wantErrStr, err) }) } } func testNewV7ClockSequence(t *testing.T) { if testing.Short() { t.Skip("skipping test in short mode.") } g := NewGen() // hack to try and reduce race conditions based on when the test starts nsec := time.Now().Nanosecond() sleepDur := int(time.Second) - nsec time.Sleep(time.Duration(sleepDur)) u1, err := g.NewV7(MillisecondPrecision) if err != nil { t.Fatalf("failed to generate V7 UUID #1: %v", err) } u2, err := g.NewV7(MillisecondPrecision) if err != nil { t.Fatalf("failed to generate V7 UUID #2: %v", err) } time.Sleep(time.Millisecond) u3, err := g.NewV7(MillisecondPrecision) if err != nil { t.Fatalf("failed to generate V7 UUID #3: %v", err) } time.Sleep(time.Second) u4, err := g.NewV7(MillisecondPrecision) if err != nil { t.Fatalf("failed to generate V7 UUID #3: %v", err) } s1 := binary.BigEndian.Uint16(u1[6:8]) & 0xfff s2 := binary.BigEndian.Uint16(u2[6:8]) & 0xfff s3 := binary.BigEndian.Uint16(u3[6:8]) & 0xfff s4 := binary.BigEndian.Uint16(u4[6:8]) & 0xfff if s1 != 0 { t.Errorf("sequence 1 should be zero, was %d", s1) } if s2 != s1+1 { t.Errorf("sequence 2 expected to be one above sequence 1; seq 1: %d, seq 2: %d", s1, s2) } if s3 != 0 { t.Errorf("sequence 3 should be zero, was %d", s3) } if s4 != 0 { t.Errorf("sequence 4 should be zero, was %d", s4) } } func TestPrecision_String(t *testing.T) { tests := []struct { p Precision want string }{ { p: NanosecondPrecision, want: "nanosecond", }, { p: MillisecondPrecision, want: "millisecond", }, { p: MicrosecondPrecision, want: "microsecond", }, { p: 0xff, want: "unknown", }, } for _, tt := range tests { t.Run(tt.want, func(t *testing.T) { if got := tt.p.String(); got != tt.want { t.Errorf("got = %s, want %s", got, tt.want) } }) } } func TestPrecision_Duration(t *testing.T) { tests := []struct { p Precision want time.Duration }{ { p: NanosecondPrecision, want: time.Nanosecond, }, { p: MillisecondPrecision, want: time.Millisecond, }, { p: MicrosecondPrecision, want: time.Microsecond, }, { p: 0xff, want: 0, }, } for _, tt := range tests { t.Run(tt.p.String(), func(t *testing.T) { if got := tt.p.Duration(); got != tt.want { t.Errorf("got = %s, want %s", got, tt.want) } }) } } 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-4.2.0/sql.go000066400000000000000000000056321415002542300136130ustar00rootroot00000000000000// 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" "database/sql/driver" "encoding/json" "fmt" ) // 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: return u.UnmarshalText([]byte(src)) } 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) } // MarshalJSON marshals the NullUUID as null or the nested UUID func (u NullUUID) MarshalJSON() ([]byte, error) { if !u.Valid { return json.Marshal(nil) } return json.Marshal(u.UUID) } // UnmarshalJSON unmarshals a NullUUID func (u *NullUUID) UnmarshalJSON(b []byte) error { if bytes.Equal(b, []byte("null")) { u.UUID, u.Valid = Nil, false return nil } if err := json.Unmarshal(b, &u.UUID); err != nil { return err } u.Valid = true return nil } uuid-4.2.0/sql_test.go000066400000000000000000000165511415002542300146540ustar00rootroot00000000000000// 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") } } uuid-4.2.0/testdata/000077500000000000000000000000001415002542300142705ustar00rootroot00000000000000uuid-4.2.0/testdata/corpus/000077500000000000000000000000001415002542300156035ustar00rootroot00000000000000uuid-4.2.0/testdata/corpus/1416586f4a34d02bcb506f6107b40df512b9f2f9000066400000000000000000000000401415002542300230060ustar00rootroot00000000000000zba7b810-9dad-11d1-80b4-00c04fd4uuid-4.2.0/testdata/corpus/3b46a7e7b02ec193581e6c9fa2c8a72f50a64e08-1000066400000000000000000000000441415002542300233230ustar00rootroot000000000000006ba7b810-9dad-11d1-80F4-00c"4fd430c8uuid-4.2.0/testdata/corpus/50c54bb75fcfdc488f162bf2f0c6dec6103bfa18-5000066400000000000000000000000401415002542300236140ustar00rootroot000000000000006ad1DdE8dda91DdE80F400c0Bool30t:uuid-4.2.0/testdata/corpus/69c581ab749cbd56be8684d3a58ac2cfab9af0f4-5000066400000000000000000000000401415002542300236360ustar00rootroot000000000000006ba7b810Edad1DdE80F400c0Bool30c8uuid-4.2.0/testdata/corpus/752bf000e0bff06777dd0d6f0be6353844de678a-3000066400000000000000000000000401415002542300233220ustar00rootroot000000000000006ba7b8109dad1Dd180F400c0Bool30c8uuid-4.2.0/testdata/corpus/a4483762d4ece8466d82cca5cacd35a0829c4e60-2000066400000000000000000000000441415002542300234050ustar00rootroot000000000000006ba7b810-9dad-11d1-80F4-F0c"4fd430c8uuid-4.2.0/testdata/corpus/d0952c45e0c823fc5cc12bcf7d9b877d150ab523-1000066400000000000000000000000401415002542300233670ustar00rootroot000000000000006ba7b8109dad11d180b400c0Bool30c8uuid-4.2.0/testdata/corpus/da39a3ee5e6b4b0d3255bfef95601890afd80709000066400000000000000000000000001415002542300232410ustar00rootroot00000000000000uuid-4.2.0/testdata/corpus/e2b84d2065846891f18ae109b12e01d224e1c7c3-4000066400000000000000000000000401415002542300230660ustar00rootroot000000000000006ba7b8109dad1DdE80F400c0Bool30c8uuid-4.2.0/testdata/corpus/e320d749435115e874f77420e17d0937e07f69f3-2000066400000000000000000000000401415002542300227030ustar00rootroot000000000000006ba7b8109dad1Dd180b400c0Bool30c8uuid-4.2.0/testdata/corpus/ed132d47d757f6468443a22df8a2a965efb34098-7000066400000000000000000000000401415002542300232050ustar00rootroot000000000000006ba1DdE8dDAE8DdE80F400c0BoUl30touuid-4.2.0/testdata/corpus/eeefb01f7bb3c627aedb292c994b20f739ffd613-6000066400000000000000000000000401415002542300236310ustar00rootroot000000000000006ad1DdE8dDdE8DdE80F400c0Bool30t:uuid-4.2.0/testdata/corpus/seed_invalid_0000077500000000000000000000000431415002542300203730ustar00rootroot000000000000006ba7b810-9dad-11d1-80b4-00c04fd430cuuid-4.2.0/testdata/corpus/seed_invalid_1000077500000000000000000000000371415002542300203770ustar00rootroot000000000000006ba7b8109dad11d180b400c04fd430cuuid-4.2.0/testdata/corpus/seed_invalid_10000077500000000000000000000000551415002542300204570ustar00rootroot00000000000000uuid:urn:6ba7b810-9dad-11d1-80b4-00c04fd430c8uuid-4.2.0/testdata/corpus/seed_invalid_11000077500000000000000000000000511415002542300204540ustar00rootroot00000000000000uuid:urn:6ba7b8109dad11d180b400c04fd430c8uuid-4.2.0/testdata/corpus/seed_invalid_12000077500000000000000000000000441415002542300204570ustar00rootroot000000000000006ba7b8109-dad-11d1-80b4-00c04fd430c8uuid-4.2.0/testdata/corpus/seed_invalid_13000077500000000000000000000000441415002542300204600ustar00rootroot000000000000006ba7b810-9dad1-1d1-80b4-00c04fd430c8uuid-4.2.0/testdata/corpus/seed_invalid_14000077500000000000000000000000441415002542300204610ustar00rootroot000000000000006ba7b810-9dad-11d18-0b4-00c04fd430c8uuid-4.2.0/testdata/corpus/seed_invalid_15000077500000000000000000000000441415002542300204620ustar00rootroot000000000000006ba7b810-9dad-11d1-80b40-0c04fd430c8uuid-4.2.0/testdata/corpus/seed_invalid_16000077500000000000000000000000441415002542300204630ustar00rootroot000000000000006ba7b810+9dad+11d1+80b4+00c04fd430c8uuid-4.2.0/testdata/corpus/seed_invalid_17000077500000000000000000000000461415002542300204660ustar00rootroot00000000000000(6ba7b810-9dad-11d1-80b4-00c04fd430c8}uuid-4.2.0/testdata/corpus/seed_invalid_18000077500000000000000000000000461415002542300204670ustar00rootroot00000000000000{6ba7b810-9dad-11d1-80b4-00c04fd430c8>uuid-4.2.0/testdata/corpus/seed_invalid_19000077500000000000000000000000441415002542300204660ustar00rootroot00000000000000zba7b810-9dad-11d1-80b4-00c04fd430c8uuid-4.2.0/testdata/corpus/seed_invalid_2000077500000000000000000000000401415002542300203720ustar00rootroot000000000000006ba7b8109dad11d180b400c04fd430q8uuid-4.2.0/testdata/corpus/seed_invalid_20000077500000000000000000000000411415002542300204530ustar00rootroot000000000000006ba7b810-9dad11d180b400c04fd430c8uuid-4.2.0/testdata/corpus/seed_invalid_21000077500000000000000000000000411415002542300204540ustar00rootroot000000000000006ba7b8109dad-11d180b400c04fd430c8uuid-4.2.0/testdata/corpus/seed_invalid_22000077500000000000000000000000411415002542300204550ustar00rootroot000000000000006ba7b8109dad11d1-80b400c04fd430c8uuid-4.2.0/testdata/corpus/seed_invalid_23000077500000000000000000000000411415002542300204560ustar00rootroot000000000000006ba7b8109dad11d180b4-00c04fd430c8uuid-4.2.0/testdata/corpus/seed_invalid_3000077500000000000000000000000451415002542300204000ustar00rootroot000000000000006ba7b810-9dad-11d1-80b4-00c04fd430c8=uuid-4.2.0/testdata/corpus/seed_invalid_4000077500000000000000000000000451415002542300204010ustar00rootroot000000000000006ba7b810-9dad-11d1-80b4-00c04fd430c8}uuid-4.2.0/testdata/corpus/seed_invalid_5000077500000000000000000000000471415002542300204040ustar00rootroot00000000000000{6ba7b810-9dad-11d1-80b4-00c04fd430c8}fuuid-4.2.0/testdata/corpus/seed_invalid_6000077500000000000000000000000601415002542300204000ustar00rootroot000000000000006ba7b810-9dad-11d1-80b4-00c04fd430c800c04fd430c8uuid-4.2.0/testdata/corpus/seed_invalid_7000077500000000000000000000000401415002542300203770ustar00rootroot00000000000000ba7b8109dad11d180b400c04fd430c8}uuid-4.2.0/testdata/corpus/seed_invalid_8000077500000000000000000000001001415002542300203750ustar00rootroot000000000000006ba7b8109dad11d180b400c04fd430c86ba7b8109dad11d180b400c04fd430c8uuid-4.2.0/testdata/corpus/seed_invalid_9000077500000000000000000000000571415002542300204110ustar00rootroot00000000000000urn:uuid:{6ba7b810-9dad-11d1-80b4-00c04fd430c8}uuid-4.2.0/testdata/corpus/seed_valid_BracedCanonical000077500000000000000000000000461415002542300227000ustar00rootroot00000000000000{6ba7b810-9dad-11d1-80b4-00c04fd430c8}uuid-4.2.0/testdata/corpus/seed_valid_BracedHashlike000077500000000000000000000000421415002542300225350ustar00rootroot00000000000000{6ba7b8109dad11d180b400c04fd430c8}uuid-4.2.0/testdata/corpus/seed_valid_Canonical000077500000000000000000000000441415002542300215750ustar00rootroot000000000000006ba7b810-9dad-11d1-80b4-00c04fd430c8uuid-4.2.0/testdata/corpus/seed_valid_Hashlike000077500000000000000000000000401415002542300214320ustar00rootroot000000000000006ba7b8109dad11d180b400c04fd430c8uuid-4.2.0/testdata/corpus/seed_valid_URNCanonical000077500000000000000000000000551415002542300221640ustar00rootroot00000000000000urn:uuid:6ba7b810-9dad-11d1-80b4-00c04fd430c8uuid-4.2.0/testdata/corpus/seed_valid_URNHashlike000077500000000000000000000000511415002542300220210ustar00rootroot00000000000000urn:uuid:6ba7b8109dad11d180b400c04fd430c8uuid-4.2.0/uuid.go000066400000000000000000000217171415002542300137640ustar00rootroot00000000000000// 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 02). // // 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-02 // [3] http://pubs.opengroup.org/onlinepubs/9696989899/chap5.htm#tagcjh_08_02_01_01 package uuid import ( "encoding/binary" "encoding/hex" "fmt" "io" "strings" "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) [peabody draft] V7 // Version 7 (k-sortable timestamp, with configurable precision, 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 01 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 } // String parse helpers. var ( urnPrefix = []byte("urn:uuid:") byteGroups = []int{8, 4, 4, 4, 12} ) // 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[:] } // String returns a canonical RFC-4122 string representation of the UUID: // xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx. func (u UUID) String() string { buf := make([]byte, 36) hex.Encode(buf[0:8], u[0:4]) buf[8] = '-' hex.Encode(buf[9:13], u[4:6]) buf[13] = '-' hex.Encode(buf[14:18], u[6:8]) buf[18] = '-' hex.Encode(buf[19:23], u[8:10]) buf[23] = '-' hex.Encode(buf[24:], u[10:]) 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) { switch c { case 'x', 'X': s := hex.EncodeToString(u.Bytes()) if c == 'X' { s = strings.Map(toCapitalHexDigits, s) } _, _ = io.WriteString(f, s) case 'v': var s string if f.Flag('#') { s = fmt.Sprintf("%#v", [Size]byte(u)) } else { s = u.String() } _, _ = io.WriteString(f, s) case 's', 'S': s := u.String() if c == 'S' { s = strings.Map(toCapitalHexDigits, s) } _, _ = io.WriteString(f, s) case 'q': _, _ = io.WriteString(f, `"`+u.String()+`"`) default: // invalid/unsupported format verb fmt.Fprintf(f, "%%!%c(uuid.UUID=%s)", c, u.String()) } } func toCapitalHexDigits(ch rune) rune { // convert a-f hex digits to A-F switch ch { case 'a': return 'A' case 'b': return 'B' case 'c': return 'C' case 'd': return 'D' case 'e': return 'E' case 'f': return 'F' default: return ch } } // 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-4.2.0/uuid_test.go000066400000000000000000000206101415002542300150120ustar00rootroot00000000000000// 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" "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) } } }