diff --git a/hash.go b/hash.go index d3bc00a..1c947fd 100644 --- a/hash.go +++ b/hash.go @@ -45,7 +45,7 @@ func NewHash(h hash.Hash, space UUID, data []byte, version int) UUID { // NewMD5 returns a new MD5 (Version 3) UUID based on the // supplied name space and data. It is the same as calling: // -// NewHash(md5.New(), space, data, 3) +// NewHash(md5.New(), space, data, 3) func NewMD5(space UUID, data []byte) UUID { return NewHash(md5.New(), space, data, 3) } @@ -53,7 +53,7 @@ func NewMD5(space UUID, data []byte) UUID { // NewSHA1 returns a new SHA1 (Version 5) UUID based on the // supplied name space and data. It is the same as calling: // -// NewHash(sha1.New(), space, data, 5) +// NewHash(sha1.New(), space, data, 5) func NewSHA1(space UUID, data []byte) UUID { return NewHash(sha1.New(), space, data, 5) } diff --git a/marshal.go b/marshal.go index 14bd340..9a0cac3 100644 --- a/marshal.go +++ b/marshal.go @@ -9,7 +9,7 @@ import "fmt" // MarshalText implements encoding.TextMarshaler. func (uuid UUID) MarshalText() ([]byte, error) { var js [36]byte - encodeHex(js[:], uuid) + encodeHexNew(js[:], uuid) return js[:], nil } diff --git a/uuid.go b/uuid.go index dc75cee..acc0765 100644 --- a/uuid.go +++ b/uuid.go @@ -13,6 +13,7 @@ import ( "io" "strings" "sync" + "unsafe" ) // A UUID is a 128 bit (16 byte) Universal Unique IDentifier as defined in RFC @@ -52,7 +53,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 +216,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) { @@ -275,6 +278,19 @@ func (uuid UUID) String() string { return string(buf[:]) } +func (u UUID) Equal(other UUID) bool { + return u == other +} + +// You are sure that the string created will not be modified +func (uuid UUID) StringUnsafe() string { + var buf [36]byte + b := buf[:] + encodeHexNew(b, uuid) + + return *(*string)(unsafe.Pointer(&b)) +} + // URN returns the RFC 2141 URN form of uuid, // urn:uuid:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx, or "" if uuid is invalid. func (uuid UUID) URN() string { @@ -284,16 +300,87 @@ func (uuid UUID) URN() string { return string(buf[:]) } +func (uuid UUID) URNUnsafe() string { + var buf [36 + 9]byte + b := buf[:] + copy(b, "urn:uuid:") + encodeHexNew(b[9:], uuid) + + return *(*string)(unsafe.Pointer(&b)) +} + +// SetZero sets the UUID to zero. +func (u *UUID) SetZero() { + *u = Nil +} + func encodeHex(dst []byte, uuid UUID) { - hex.Encode(dst, uuid[:4]) + hex.Encode(dst[:8], uuid[:4]) dst[8] = '-' + + // Encode bytes 4-6 hex.Encode(dst[9:13], uuid[4:6]) dst[13] = '-' + + // Encode bytes 6-8 hex.Encode(dst[14:18], uuid[6:8]) dst[18] = '-' + + // Encode bytes 8-10 hex.Encode(dst[19:23], uuid[8:10]) dst[23] = '-' + + // Encode the remaining 6 bytes hex.Encode(dst[24:], uuid[10:]) + +} + +func encodeHexNew(dst []byte, uuid UUID) { + + hexDigits := "0123456789abcdef" + + dst[8] = '-' + dst[13] = '-' + dst[18] = '-' + dst[23] = '-' + + // every byte to format + dst[0] = hexDigits[uuid[0]>>4] + dst[1] = hexDigits[uuid[0]&0x0F] + dst[2] = hexDigits[uuid[1]>>4] + dst[3] = hexDigits[uuid[1]&0x0F] + dst[4] = hexDigits[uuid[2]>>4] + dst[5] = hexDigits[uuid[2]&0x0F] + dst[6] = hexDigits[uuid[3]>>4] + dst[7] = hexDigits[uuid[3]&0x0F] + + dst[9] = hexDigits[uuid[4]>>4] + dst[10] = hexDigits[uuid[4]&0x0F] + dst[11] = hexDigits[uuid[5]>>4] + dst[12] = hexDigits[uuid[5]&0x0F] + + dst[14] = hexDigits[uuid[6]>>4] + dst[15] = hexDigits[uuid[6]&0x0F] + dst[16] = hexDigits[uuid[7]>>4] + dst[17] = hexDigits[uuid[7]&0x0F] + + dst[19] = hexDigits[uuid[8]>>4] + dst[20] = hexDigits[uuid[8]&0x0F] + dst[21] = hexDigits[uuid[9]>>4] + dst[22] = hexDigits[uuid[9]&0x0F] + + dst[24] = hexDigits[uuid[10]>>4] + dst[25] = hexDigits[uuid[10]&0x0F] + dst[26] = hexDigits[uuid[11]>>4] + dst[27] = hexDigits[uuid[11]&0x0F] + dst[28] = hexDigits[uuid[12]>>4] + dst[29] = hexDigits[uuid[12]&0x0F] + dst[30] = hexDigits[uuid[13]>>4] + dst[31] = hexDigits[uuid[13]&0x0F] + dst[32] = hexDigits[uuid[14]>>4] + dst[33] = hexDigits[uuid[14]&0x0F] + dst[34] = hexDigits[uuid[15]>>4] + dst[35] = hexDigits[uuid[15]&0x0F] } // Variant returns the variant encoded in uuid. @@ -391,3 +478,17 @@ func (uuids UUIDs) Strings() []string { } return uuidStrs } + +func (uuids UUIDs) StringsUnsafe() []string { + l := len(uuids) + if l == 0 { + return nil + } + + uuidStrs := make([]string, l) + for i := 0; i < l; i++ { + uuidStrs[i] = uuids[i].StringUnsafe() + } + + return uuidStrs +} diff --git a/uuid_test.go b/uuid_test.go index 906ecbe..b2ef60c 100644 --- a/uuid_test.go +++ b/uuid_test.go @@ -134,6 +134,19 @@ func TestUUID(t *testing.T) { } } +func TestEqual(t *testing.T) { + uuid := New() + + another, err := Parse(uuid.StringUnsafe()) + if err != nil { + t.Fatalf("%s", err) + } + if !uuid.Equal(another) { + t.Fatalf("Equal() got %v expected true", uuid) + } + +} + func TestFromBytes(t *testing.T) { b := []byte{ 0x7d, 0x44, 0x48, 0x40, @@ -637,6 +650,26 @@ func TestValidate(t *testing.T) { var asString = "f47ac10b-58cc-0372-8567-0e02b2c3d479" var asBytes = []byte(asString) +func BenchmarkHexNew(b *testing.B) { + var dst [36]byte + var uuid UUID + for i := 0; i < b.N; i++ { + encodeHexNew(dst[:], uuid) + } + + //00000000-0000-0000-0000-000000000000 +} + +func BenchmarkHex(b *testing.B) { + var dst [36]byte + var uuid UUID + for i := 0; i < b.N; i++ { + encodeHex(dst[:], uuid) + } + + //00000000-0000-0000-0000-000000000000 +} + func BenchmarkParse(b *testing.B) { for i := 0; i < b.N; i++ { _, err := Parse(asString) @@ -698,6 +731,22 @@ func BenchmarkUUID_String(b *testing.B) { if uuid.String() == "" { b.Fatal("invalid uuid") } + + } + +} + +func BenchmarkUUID_String_unsafe(b *testing.B) { + uuid, err := Parse("f47ac10b-58cc-0372-8567-0e02b2c3d479") + if err != nil { + b.Fatal(err) + } + + for i := 0; i < b.N; i++ { + if uuid.StringUnsafe() == "" { + b.Fatal("invalid uuid") + } + } } @@ -710,6 +759,22 @@ func BenchmarkUUID_URN(b *testing.B) { if uuid.URN() == "" { b.Fatal("invalid uuid") } + + } + +} + +func BenchmarkUUID_URN_unsafe(b *testing.B) { + uuid, err := Parse("f47ac10b-58cc-0372-8567-0e02b2c3d479") + if err != nil { + b.Fatal(err) + } + + for i := 0; i < b.N; i++ { + if uuid.URNUnsafe() == "" { + b.Fatal("invalid uuid") + } + } } @@ -781,6 +846,21 @@ func BenchmarkUUIDs_Strings(b *testing.B) { } } +func BenchmarkUUIDs_Strings_unsafe(b *testing.B) { + uuid1, err := Parse("f47ac10b-58cc-0372-8567-0e02b2c3d479") + if err != nil { + b.Fatal(err) + } + uuid2, err := Parse("7d444840-9dc0-11d1-b245-5ffdce74fad2") + if err != nil { + b.Fatal(err) + } + uuids := UUIDs{uuid1, uuid2} + for i := 0; i < b.N; i++ { + uuids.StringsUnsafe() + } +} + func TestVersion6(t *testing.T) { uuid1, err := NewV6() if err != nil { 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)