pax_global_header00006660000000000000000000000064135106557110014516gustar00rootroot0000000000000052 comment=10c3923834d38e951ae8f627bfec2dc632c5b6cb rogpeppe-fastuuid-10c3923/000077500000000000000000000000001351065571100154275ustar00rootroot00000000000000rogpeppe-fastuuid-10c3923/LICENSE000066400000000000000000000027451351065571100164440ustar00rootroot00000000000000Copyright © 2014, Roger Peppe All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of this project nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. rogpeppe-fastuuid-10c3923/README.md000066400000000000000000000053031351065571100167070ustar00rootroot00000000000000# fastuuid -- import "github.com/rogpeppe/fastuuid" Package fastuuid provides fast UUID generation of 192 bit universally unique identifiers. It also provides simple support for 128-bit RFC-4122 V4 UUID strings. Note that the generated UUIDs are not unguessable - each UUID generated from a Generator is adjacent to the previously generated UUID. By way of comparison with two other popular UUID-generation packages, github.com/satori/go.uuid and github.com/google/uuid, here are some benchmarks: BenchmarkNext-4 128272185 9.20 ns/op BenchmarkHex128-4 14323180 76.4 ns/op BenchmarkContended-4 45741997 26.4 ns/op BenchmarkSatoriNext-4 1231281 967 ns/op BenchmarkSatoriHex128-4 1000000 1041 ns/op BenchmarkSatoriContended-4 1765520 666 ns/op BenchmarkGoogleNext-4 1256250 958 ns/op BenchmarkGoogleHex128-4 1000000 1044 ns/op BenchmarkGoogleContended-4 1746570 690 ns/op ## Usage #### func Hex128 ```go func Hex128(uuid [24]byte) string ``` Hex128 returns an RFC4122 V4 representation of the first 128 bits of the given UUID. For example: f81d4fae-7dec-41d0-8765-00a0c91e6bf6. Note: before encoding, it swaps bytes 6 and 9 so that all the varying bits of the UUID as returned from Generator.Next are reflected in the Hex128 representation. If you want unpredictable UUIDs, you might want to consider hashing the uuid (using SHA256, for example) before passing it to Hex128. #### func ValidHex128 ```go func ValidHex128(id string) bool ``` ValidHex128 reports whether id is a valid UUID as returned by Hex128 and various other UUID packages, such as github.com/satori/go.uuid's NewV4 function. Note that it does not allow upper case hex. #### type Generator ```go type Generator struct { } ``` Generator represents a UUID generator that generates UUIDs in sequence from a random starting point. #### func MustNewGenerator ```go func MustNewGenerator() *Generator ``` MustNewGenerator is like NewGenerator but panics on failure. #### func NewGenerator ```go func NewGenerator() (*Generator, error) ``` NewGenerator returns a new Generator. It can fail if the crypto/rand read fails. #### func (*Generator) Hex128 ```go func (g *Generator) Hex128() string ``` Hex128 is a convenience method that returns Hex128(g.Next()). #### func (*Generator) Next ```go func (g *Generator) Next() [24]byte ``` Next returns the next UUID from the generator. Only the first 8 bytes can differ from the previous UUID, so taking a slice of the first 16 bytes is sufficient to provide a somewhat less secure 128 bit UUID. It is OK to call this method concurrently. rogpeppe-fastuuid-10c3923/go.mod000066400000000000000000000000551351065571100165350ustar00rootroot00000000000000module github.com/rogpeppe/fastuuid go 1.12 rogpeppe-fastuuid-10c3923/uuid.go000066400000000000000000000102071351065571100167240ustar00rootroot00000000000000// Package fastuuid provides fast UUID generation of 192 bit // universally unique identifiers. // // It also provides simple support for 128-bit RFC-4122 V4 UUID strings. // // Note that the generated UUIDs are not unguessable - each // UUID generated from a Generator is adjacent to the // previously generated UUID. // // By way of comparison with two other popular UUID-generation packages, github.com/satori/go.uuid // and github.com/google/uuid, here are some benchmarks: // // BenchmarkNext-4 128272185 9.20 ns/op // BenchmarkHex128-4 14323180 76.4 ns/op // BenchmarkContended-4 45741997 26.4 ns/op // BenchmarkSatoriNext-4 1231281 967 ns/op // BenchmarkSatoriHex128-4 1000000 1041 ns/op // BenchmarkSatoriContended-4 1765520 666 ns/op // BenchmarkGoogleNext-4 1256250 958 ns/op // BenchmarkGoogleHex128-4 1000000 1044 ns/op // BenchmarkGoogleContended-4 1746570 690 ns/op package fastuuid import ( "crypto/rand" "encoding/binary" "encoding/hex" "errors" "sync/atomic" ) // Generator represents a UUID generator that // generates UUIDs in sequence from a random starting // point. type Generator struct { // The constant seed. The first 8 bytes of this are // copied into counter and then ignored thereafter. seed [24]byte counter uint64 } // NewGenerator returns a new Generator. // It can fail if the crypto/rand read fails. func NewGenerator() (*Generator, error) { var g Generator _, err := rand.Read(g.seed[:]) if err != nil { return nil, errors.New("cannot generate random seed: " + err.Error()) } g.counter = binary.LittleEndian.Uint64(g.seed[:8]) return &g, nil } // MustNewGenerator is like NewGenerator // but panics on failure. func MustNewGenerator() *Generator { g, err := NewGenerator() if err != nil { panic(err) } return g } // Next returns the next UUID from the generator. // Only the first 8 bytes can differ from the previous // UUID, so taking a slice of the first 16 bytes // is sufficient to provide a somewhat less secure 128 bit UUID. // // It is OK to call this method concurrently. func (g *Generator) Next() [24]byte { x := atomic.AddUint64(&g.counter, 1) uuid := g.seed binary.LittleEndian.PutUint64(uuid[:8], x) return uuid } // Hex128 is a convenience method that returns Hex128(g.Next()). func (g *Generator) Hex128() string { return Hex128(g.Next()) } // Hex128 returns an RFC4122 V4 representation of the // first 128 bits of the given UUID. For example: // // f81d4fae-7dec-41d0-8765-00a0c91e6bf6. // // Note: before encoding, it swaps bytes 6 and 9 // so that all the varying bits of the UUID as // returned from Generator.Next are reflected // in the Hex128 representation. // // If you want unpredictable UUIDs, you might want to consider // hashing the uuid (using SHA256, for example) before passing it // to Hex128. func Hex128(uuid [24]byte) string { // As fastuuid only varies the first 8 bytes of the UUID and we // don't want to lose any of that variance, swap the UUID // version byte in that range for one outside it. uuid[6], uuid[9] = uuid[9], uuid[6] // Version 4. uuid[6] = (uuid[6] & 0x0f) | 0x40 // RFC4122 variant. uuid[8] = uuid[8]&0x3f | 0x80 b := make([]byte, 36) hex.Encode(b[0:8], uuid[0:4]) b[8] = '-' hex.Encode(b[9:13], uuid[4:6]) b[13] = '-' hex.Encode(b[14:18], uuid[6:8]) b[18] = '-' hex.Encode(b[19:23], uuid[8:10]) b[23] = '-' hex.Encode(b[24:], uuid[10:16]) return string(b) } // ValidHex128 reports whether id is a valid UUID as returned by Hex128 // and various other UUID packages, such as github.com/satori/go.uuid's // NewV4 function. // // Note that it does not allow upper case hex. func ValidHex128(id string) bool { if len(id) != 36 { return false } if id[8] != '-' || id[13] != '-' || id[18] != '-' || id[23] != '-' { return false } return isValidHex(id[0:8]) && isValidHex(id[9:13]) && isValidHex(id[14:18]) && isValidHex(id[19:23]) && isValidHex(id[24:]) } func isValidHex(s string) bool { for i := 0; i < len(s); i++ { c := s[i] if !('0' <= c && c <= '9' || 'a' <= c && c <= 'f') { return false } } return true } rogpeppe-fastuuid-10c3923/uuid_test.go000066400000000000000000000053571351065571100177750ustar00rootroot00000000000000package fastuuid import ( "bytes" "crypto/rand" "testing" ) func TestUUID(t *testing.T) { var buf [24]byte for i := range buf { buf[i] = byte(i) + 1 } oldReader := rand.Reader rand.Reader = bytes.NewReader(buf[:]) g, err := NewGenerator() rand.Reader = oldReader if err != nil { t.Fatalf("cannot make generator: %v", err) } uuid := g.Next() buf[0] = 1 + 1 if uuid != buf { t.Fatalf("unexpected UUID; got %x; want %x", uuid, buf) } uuid = g.Next() buf[0] = 1 + 2 if uuid != buf { t.Fatalf("unexpected next UUID; got %x; want %x", uuid, buf) } } const step = 32768 func TestUniqueness(t *testing.T) { g := MustNewGenerator() mc := make(chan map[[24]byte]int) const nproc = 4 for i := 0; i < nproc; i++ { go func() { m := make(map[[24]byte]int) for i := 0; i < step*10; i++ { uuid := g.Next() if old, ok := m[uuid]; ok { t.Errorf("non-unique uuid seq at %d, other %d", i, old) } m[uuid] = i } mc <- m }() } m := make(map[[24]byte]int) for i := 0; i < nproc; i++ { for uuid, iter := range <-mc { if old, ok := m[uuid]; ok { t.Errorf("non-unique uuid seq at %d, other %d", i, old) } m[uuid] = iter } } } func TestHex128(t *testing.T) { var b [24]byte for i := range b { b[i] = byte(i + 1) } // Note: byte 6 is swapped with byte 9. got, want := Hex128(b), "01020304-0506-4a08-8907-0b0c0d0e0f10" if got != want { t.Fatalf("unexpected Hex128 result; got %q want %q", got, want) } } var validHex128Tests = []struct { u string valid bool }{{ u: "01020304-0506-0708-090a-0b0c0d0e0f10", valid: true, }, { u: "01020304-0506-0708-090a-0b0c0d0e0f1", valid: false, }, { u: "0102030430506-0708-090a-0b0c0d0e0f1", valid: false, }, { u: "01020304-050630708-090a-0b0c0d0e0f1", valid: false, }, { u: "01020304-0506-07084090a-0b0c0d0e0f1", valid: false, }, { u: "01020304-0506-0708-090a50b0c0d0e0f1", valid: false, }, { u: "01020304-0506-0708-090a-0b0c0d0e0f1", valid: false, }, { u: "01020304-0506-0708-090a-0b0c0d0e0f102", valid: false, }, { u: "01020304-0506-0708-090a-0b0c0d0e0f1/", valid: false, }} func TestValidHex128(t *testing.T) { for _, test := range validHex128Tests { t.Run(test.u, func(t *testing.T) { if got := ValidHex128(test.u); got != test.valid { t.Fatalf("unexpected valid for %q; got %v want %v", test.u, got, test.valid) } }) } } var _s string func BenchmarkHex128(b *testing.B) { g := MustNewGenerator() for i := 0; i < b.N; i++ { _s = Hex128(g.Next()) } } func BenchmarkNext(b *testing.B) { g := MustNewGenerator() for i := 0; i < b.N; i++ { g.Next() } } func BenchmarkContended(b *testing.B) { g := MustNewGenerator() b.RunParallel(func(pb *testing.PB) { for pb.Next() { g.Next() } }) }