From 4477e273698b8d55f7a634b66bb20bc6774fe04f Mon Sep 17 00:00:00 2001 From: Eduardo Castillo Perera Date: Wed, 21 Jan 2026 14:22:30 -0800 Subject: [PATCH 1/4] Use chacha8 faster generator --- uuid.go | 46 +++++++++++++++++++++++++++++++++++++--------- 1 file changed, 37 insertions(+), 9 deletions(-) diff --git a/uuid.go b/uuid.go index dc75cee..1a7c70b 100644 --- a/uuid.go +++ b/uuid.go @@ -6,11 +6,15 @@ package uuid import ( "bytes" - "crypto/rand" "encoding/hex" "errors" "fmt" "io" + + // Uses math/rand/v2 for random number generation + // because it uses chacha8 as the default source, + // which is cryptographically secure and much faster + chacha8RandV2 "math/rand/v2" "strings" "sync" ) @@ -42,8 +46,8 @@ const Standard = RFC4122 const randPoolSize = 16 * 16 var ( - rander = rand.Reader // random function - poolEnabled = false + rander io.Reader = defaultRandReader{} + poolEnabled = false poolMu sync.Mutex poolPos = randPoolSize // protected with poolMu pool [randPoolSize]byte // protected with poolMu @@ -52,7 +56,7 @@ var ( ErrInvalidBracketedFormat = errors.New("invalid bracketed UUID format") ) -type URNPrefixError struct { prefix string } +type URNPrefixError struct{ prefix string } func (e URNPrefixError) Error() string { return fmt.Sprintf("invalid urn prefix: %q", e.prefix) @@ -215,10 +219,12 @@ func Must(uuid UUID, err error) UUID { } // Validate returns an error if s is not a properly formatted UUID in one of the following formats: -// xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx -// urn:uuid:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx -// xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx -// {xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx} +// +// xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx +// urn:uuid:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx +// xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +// {xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx} +// // It returns an error if the format is invalid, otherwise nil. func Validate(s string) error { switch len(s) { @@ -346,7 +352,7 @@ func (v Variant) String() string { // generator. func SetRand(r io.Reader) { if r == nil { - rander = rand.Reader + rander = defaultRandReader{} return } rander = r @@ -391,3 +397,25 @@ func (uuids UUIDs) Strings() []string { } return uuidStrs } + +// Singleton with no state that implements +// io.Reader and uses the default rand/v2 +// package to read bytes. +type defaultRandReader struct{} + +// Read fills the provided byte slice `b` with random data using a +// cryptographically secure random number generator. +func (d defaultRandReader) Read(b []byte) (n int, err error) { + var num uint64 + numByteIndex := 0 + + for i := range b { + if numByteIndex == 0 { + num = chacha8RandV2.Uint64() + } + b[i] = byte(num >> (numByteIndex * 8)) + numByteIndex = (numByteIndex + 1) % 8 + } + + return len(b), nil +} From 38a0a149ceed1a86a667648506392f1e4e01d3df Mon Sep 17 00:00:00 2001 From: Eduardo Castillo Perera Date: Wed, 21 Jan 2026 14:49:10 -0800 Subject: [PATCH 2/4] Tabs --- version4.go | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/version4.go b/version4.go index 7697802..62ac273 100644 --- a/version4.go +++ b/version4.go @@ -9,7 +9,7 @@ import "io" // New creates a new random UUID or panics. New is equivalent to // the expression // -// uuid.Must(uuid.NewRandom()) +// uuid.Must(uuid.NewRandom()) func New() UUID { return Must(NewRandom()) } @@ -17,7 +17,7 @@ func New() UUID { // NewString creates a new random UUID and returns it as a string or panics. // NewString is equivalent to the expression // -// uuid.New().String() +// uuid.New().String() func NewString() string { return Must(NewRandom()).String() } @@ -31,11 +31,11 @@ func NewString() string { // // A note about uniqueness derived from the UUID Wikipedia entry: // -// Randomly generated UUIDs have 122 random bits. One's annual risk of being -// hit by a meteorite is estimated to be one chance in 17 billion, that -// means the probability is about 0.00000000006 (6 × 10−11), -// equivalent to the odds of creating a few tens of trillions of UUIDs in a -// year and having one duplicate. +// Randomly generated UUIDs have 122 random bits. One's annual risk of being +// hit by a meteorite is estimated to be one chance in 17 billion, that +// means the probability is about 0.00000000006 (6 × 10−11), +// equivalent to the odds of creating a few tens of trillions of UUIDs in a +// year and having one duplicate. func NewRandom() (UUID, error) { if !poolEnabled { return NewRandomFromReader(rander) From 4f39721990caf8bbfcf9a5cde6454b3733704d6d Mon Sep 17 00:00:00 2001 From: Eduardo Castillo Perera Date: Wed, 21 Jan 2026 16:58:12 -0800 Subject: [PATCH 3/4] Move to V4 --- uuid.go | 24 +----------------------- version4.go | 43 ++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 43 insertions(+), 24 deletions(-) diff --git a/uuid.go b/uuid.go index 1a7c70b..4bcb226 100644 --- a/uuid.go +++ b/uuid.go @@ -14,7 +14,7 @@ import ( // Uses math/rand/v2 for random number generation // because it uses chacha8 as the default source, // which is cryptographically secure and much faster - chacha8RandV2 "math/rand/v2" + "strings" "sync" ) @@ -397,25 +397,3 @@ func (uuids UUIDs) Strings() []string { } return uuidStrs } - -// Singleton with no state that implements -// io.Reader and uses the default rand/v2 -// package to read bytes. -type defaultRandReader struct{} - -// Read fills the provided byte slice `b` with random data using a -// cryptographically secure random number generator. -func (d defaultRandReader) Read(b []byte) (n int, err error) { - var num uint64 - numByteIndex := 0 - - for i := range b { - if numByteIndex == 0 { - num = chacha8RandV2.Uint64() - } - b[i] = byte(num >> (numByteIndex * 8)) - numByteIndex = (numByteIndex + 1) % 8 - } - - return len(b), nil -} diff --git a/version4.go b/version4.go index 62ac273..727a2f4 100644 --- a/version4.go +++ b/version4.go @@ -4,7 +4,11 @@ package uuid -import "io" +import ( + "encoding/binary" + "io" + chacha8RandV2 "math/rand/v2" +) // New creates a new random UUID or panics. New is equivalent to // the expression @@ -37,6 +41,10 @@ func NewString() string { // equivalent to the odds of creating a few tens of trillions of UUIDs in a // year and having one duplicate. func NewRandom() (UUID, error) { + if _, ok := rander.(defaultRandReader); ok { + return fastRandV4DefaultZeroAlloc(), nil + } + if !poolEnabled { return NewRandomFromReader(rander) } @@ -74,3 +82,36 @@ func newRandomFromPool() (UUID, error) { uuid[8] = (uuid[8] & 0x3f) | 0x80 // Variant is 10 return uuid, nil } + +// Singleton with no state that implements +// io.Reader and uses the default rand/v2 +// package to read bytes. +type defaultRandReader struct{} + +// Read fills the provided byte slice `b` with random data using a +// cryptographically secure random number generator. +func (d defaultRandReader) Read(b []byte) (n int, err error) { + var num uint64 + numByteIndex := 0 + + for i := range b { + if numByteIndex == 0 { + num = chacha8RandV2.Uint64() + } + b[i] = byte(num >> (numByteIndex * 8)) + numByteIndex = (numByteIndex + 1) % 8 + } + + return len(b), nil +} + +func fastRandV4DefaultZeroAlloc() UUID { + var uuid UUID + hi, low := chacha8RandV2.Uint64(), chacha8RandV2.Uint64() + binary.LittleEndian.PutUint64(uuid[0:8], hi) + binary.LittleEndian.PutUint64(uuid[8:16], low) + + uuid[6] = (uuid[6] & 0x0f) | 0x40 // Version 4 + uuid[8] = (uuid[8] & 0x3f) | 0x80 // Variant is 10 + return uuid +} From cca30c36b3b2426447db43d48948b4d8bd51a1cd Mon Sep 17 00:00:00 2001 From: Eduardo Castillo Perera Date: Wed, 21 Jan 2026 17:25:28 -0800 Subject: [PATCH 4/4] Use big endian for uniformity --- version4.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/version4.go b/version4.go index 727a2f4..fa25d1c 100644 --- a/version4.go +++ b/version4.go @@ -108,8 +108,8 @@ func (d defaultRandReader) Read(b []byte) (n int, err error) { func fastRandV4DefaultZeroAlloc() UUID { var uuid UUID hi, low := chacha8RandV2.Uint64(), chacha8RandV2.Uint64() - binary.LittleEndian.PutUint64(uuid[0:8], hi) - binary.LittleEndian.PutUint64(uuid[8:16], low) + binary.BigEndian.PutUint64(uuid[0:8], hi) + binary.BigEndian.PutUint64(uuid[8:16], low) uuid[6] = (uuid[6] & 0x0f) | 0x40 // Version 4 uuid[8] = (uuid[8] & 0x3f) | 0x80 // Variant is 10