Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Add helpers functions and add unsafe functions for uuid.string and optimize encodeHex #180

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions hash.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,15 +45,15 @@ 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)
}

// 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)
}
2 changes: 1 addition & 1 deletion marshal.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}

Expand Down
113 changes: 107 additions & 6 deletions uuid.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (
"io"
"strings"
"sync"
"unsafe"
)

// A UUID is a 128 bit (16 byte) Universal Unique IDentifier as defined in RFC
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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 {
Expand All @@ -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.
Expand Down Expand Up @@ -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
}
80 changes: 80 additions & 0 deletions uuid_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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")
}

}
}

Expand All @@ -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")
}

}
}

Expand Down Expand Up @@ -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 {
Expand Down
14 changes: 7 additions & 7 deletions version4.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,15 @@ 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())
}

// 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()
}
Expand All @@ -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)
Expand Down