mirror of
https://github.com/google/uuid.git
synced 2025-02-16 12:23:51 +02:00
Merge branch 'master' of https://github.com/google/uuid
This commit is contained in:
commit
a34cdf67a6
@ -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
|
||||
}
|
||||
|
8
util.go
8
util.go
@ -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
36
uuid.go
@ -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.
|
||||
|
55
uuid_test.go
55
uuid_test.go
@ -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)
|
||||
|
Loading…
x
Reference in New Issue
Block a user