1
0
mirror of https://github.com/google/uuid.git synced 2025-02-16 12:23:51 +02:00
This commit is contained in:
Paul Borman 2016-02-29 06:19:06 -08:00
commit a34cdf67a6
4 changed files with 45 additions and 61 deletions

View File

@ -4,10 +4,7 @@
package uuid
import (
"fmt"
"unsafe"
)
import "fmt"
// MarshalText implements encoding.TextMarshaler.
func (uuid UUID) MarshalText() ([]byte, error) {
@ -20,7 +17,7 @@ func (uuid UUID) MarshalText() ([]byte, error) {
func (uuid *UUID) UnmarshalText(data []byte) error {
// See comment in ParseBytes why we do this.
// id, err := ParseBytes(data)
id, err := Parse(*(*string)(unsafe.Pointer(&data)))
id, err := ParseBytes(data)
if err == nil {
*uuid = id
}

View File

@ -35,9 +35,9 @@ var xvalues = [256]byte{
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
}
// xtob converts the the first two hex bytes of x into a byte.
func xtob(x string) (byte, bool) {
b1 := xvalues[x[0]]
b2 := xvalues[x[1]]
// xtob converts hex characters x1 and x2 into a byte.
func xtob(x1, x2 byte) (byte, bool) {
b1 := xvalues[x1]
b2 := xvalues[x2]
return (b1 << 4) | b2, b1 != 255 && b2 != 255
}

36
uuid.go
View File

@ -5,13 +5,13 @@
package uuid
import (
"bytes"
"crypto/rand"
"encoding/hex"
"errors"
"fmt"
"io"
"strings"
"unsafe"
)
// A UUID is a 128 bit (16 byte) Universal Unique IDentifier as defined in RFC
@ -58,7 +58,7 @@ func Parse(s string) (UUID, error) {
14, 16,
19, 21,
24, 26, 28, 30, 32, 34} {
if v, ok := xtob(s[x:]); !ok {
if v, ok := xtob(s[x], s[x+1]); !ok {
return uuid, errors.New("invalid UUID format")
} else {
uuid[i] = v
@ -69,12 +69,32 @@ func Parse(s string) (UUID, error) {
// ParseBytes is like Parse, except it parses a byte slice instead of a string.
func ParseBytes(b []byte) (UUID, error) {
// Parsing a string is actually faster than parsing a byte slice as it
// is cheaper to slice a string. Further, it is not safe to convert
// a string into a byte slice but the opposite direction is. These
// stem from the fact that a byte slice is 3 words while a string
// is only 2 words.
return Parse(*(*string)(unsafe.Pointer(&b)))
var uuid UUID
if len(b) != 36 {
if len(b) != 36+9 {
return uuid, fmt.Errorf("invalid UUID length: %d", len(b))
}
if !bytes.Equal(bytes.ToLower(b[:9]), []byte("urn:uuid:")) {
return uuid, fmt.Errorf("invalid urn prefix: %q", b[:9])
}
b = b[9:]
}
if b[8] != '-' || b[13] != '-' || b[18] != '-' || b[23] != '-' {
return uuid, errors.New("invalid UUID format")
}
for i, x := range [16]int{
0, 2, 4, 6,
9, 11,
14, 16,
19, 21,
24, 26, 28, 30, 32, 34} {
if v, ok := xtob(b[x], b[x+1]); !ok {
return uuid, errors.New("invalid UUID format")
} else {
uuid[i] = v
}
}
return uuid, nil
}
// Must returns uuid if err is nil and panics otherwise.

View File

@ -6,12 +6,12 @@ package uuid
import (
"bytes"
"errors"
"fmt"
"os"
"strings"
"testing"
"time"
"unsafe"
)
type test struct {
@ -466,59 +466,26 @@ func BenchmarkParseBytes(b *testing.B) {
}
}
// parseBytesCopy is to benchmark not using unsafe.
func parseBytesCopy(b []byte) (UUID, error) {
return Parse(string(b))
// parseBytesUnsafe is to benchmark using unsafe.
func parseBytesUnsafe(b []byte) (UUID, error) {
return Parse(*(*string)(unsafe.Pointer(&b)))
}
// xtobb converts the the first two hex bytes of x into a byte.
func xtobb(x []byte) (byte, bool) {
b1 := xvalues[x[0]]
b2 := xvalues[x[1]]
return (b1 << 4) | b2, b1 != 255 && b2 != 255
}
// parseBytes is the same as Parse, but with byte slices. It demonstrates
// that it is faster to convert the byte slice into a string and then parse
// than to parse the byte slice directly.
func parseBytes(s []byte) (UUID, error) {
var uuid UUID
if len(s) != 36 {
if len(s) != 36+9 {
return uuid, fmt.Errorf("invalid UUID length: %d", len(s))
}
if !bytes.Equal(bytes.ToLower(s[:9]), []byte("urn:uuid:")) {
return uuid, fmt.Errorf("invalid urn prefix: %q", s[:9])
}
s = s[9:]
}
if s[8] != '-' || s[13] != '-' || s[18] != '-' || s[23] != '-' {
return uuid, errors.New("invalid UUID format")
}
for i, x := range [16]int{
0, 2, 4, 6,
9, 11,
14, 16,
19, 21,
24, 26, 28, 30, 32, 34} {
if v, ok := xtobb(s[x:]); !ok {
return uuid, errors.New("invalid UUID format")
} else {
uuid[i] = v
}
}
return uuid, nil
}
func BenchmarkParseBytesNative(b *testing.B) {
func BenchmarkParseBytesUnsafe(b *testing.B) {
for i := 0; i < b.N; i++ {
_, err := parseBytes(asBytes)
_, err := parseBytesUnsafe(asBytes)
if err != nil {
b.Fatal(err)
}
}
}
// parseBytesCopy is to benchmark not using unsafe.
func parseBytesCopy(b []byte) (UUID, error) {
return Parse(string(b))
}
func BenchmarkParseBytesCopy(b *testing.B) {
for i := 0; i < b.N; i++ {
_, err := parseBytesCopy(asBytes)