mirror of
https://github.com/google/uuid.git
synced 2025-11-06 08:59:16 +02:00
Use a custom error type for invalid lengths, replacing fmt.Errorf (#69)
* Add benchmarks for different kinds of invalid UUIDs
Also add a test case for too-short UUIDs to ensure behavior doesn’t
change.
* Use a custom error type for invalid lengths, replacing `fmt.Errorf`
This significantly improves the speed of failed parses due to wrong
lengths. Previously the `fmt.Errorf` call dominated, making this the
most expensive error and more expensive than successfully parsing:
BenchmarkParse-4 29226529 36.1 ns/op
BenchmarkParseBadLength-4 6923106 174 ns/op
BenchmarkParseLen32Truncated-4 26641954 38.1 ns/op
BenchmarkParseLen36Corrupted-4 19405598 59.5 ns/op
When the formatting is not required and done on-demand, the failure per
se is much faster:
BenchmarkParse-4 29641700 36.3 ns/op
BenchmarkParseBadLength-4 58602537 20.0 ns/op
BenchmarkParseLen32Truncated-4 30664791 43.6 ns/op
BenchmarkParseLen36Corrupted-4 18882410 61.9 ns/op
This commit is contained in:
10
uuid.go
10
uuid.go
@@ -35,6 +35,12 @@ const (
|
||||
|
||||
var rander = rand.Reader // random function
|
||||
|
||||
type invalidLengthError struct{ len int }
|
||||
|
||||
func (err *invalidLengthError) Error() string {
|
||||
return fmt.Sprintf("invalid UUID length: %d", err.len)
|
||||
}
|
||||
|
||||
// Parse decodes s into a UUID or returns an error. Both the standard UUID
|
||||
// forms of xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx and
|
||||
// urn:uuid:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx are decoded as well as the
|
||||
@@ -68,7 +74,7 @@ func Parse(s string) (UUID, error) {
|
||||
}
|
||||
return uuid, nil
|
||||
default:
|
||||
return uuid, fmt.Errorf("invalid UUID length: %d", len(s))
|
||||
return uuid, &invalidLengthError{len(s)}
|
||||
}
|
||||
// s is now at least 36 bytes long
|
||||
// it must be of the form xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
|
||||
@@ -112,7 +118,7 @@ func ParseBytes(b []byte) (UUID, error) {
|
||||
}
|
||||
return uuid, nil
|
||||
default:
|
||||
return uuid, fmt.Errorf("invalid UUID length: %d", len(b))
|
||||
return uuid, &invalidLengthError{len(b)}
|
||||
}
|
||||
// s is now at least 36 bytes long
|
||||
// it must be of the form xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
|
||||
|
||||
39
uuid_test.go
39
uuid_test.go
@@ -517,6 +517,15 @@ func TestRandomFromReader(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestWrongLength(t *testing.T) {
|
||||
_, err := Parse("12345")
|
||||
if err == nil {
|
||||
t.Errorf("expected ‘12345’ was invalid")
|
||||
} else if err.Error() != "invalid UUID length: 5" {
|
||||
t.Errorf("expected a different error message for an invalid length")
|
||||
}
|
||||
}
|
||||
|
||||
var asString = "f47ac10b-58cc-0372-8567-0e02b2c3d479"
|
||||
var asBytes = []byte(asString)
|
||||
|
||||
@@ -595,3 +604,33 @@ func BenchmarkUUID_URN(b *testing.B) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkParseBadLength(b *testing.B) {
|
||||
short := asString[:10]
|
||||
for i := 0; i < b.N; i++ {
|
||||
_, err := Parse(short)
|
||||
if err == nil {
|
||||
b.Fatalf("expected ‘%s’ was invalid", short)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkParseLen32Truncated(b *testing.B) {
|
||||
partial := asString[:len(asString)-4]
|
||||
for i := 0; i < b.N; i++ {
|
||||
_, err := Parse(partial)
|
||||
if err == nil {
|
||||
b.Fatalf("expected ‘%s’ was invalid", partial)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkParseLen36Corrupted(b *testing.B) {
|
||||
wrong := asString[:len(asString)-1] + "x"
|
||||
for i := 0; i < b.N; i++ {
|
||||
_, err := Parse(wrong)
|
||||
if err == nil {
|
||||
b.Fatalf("expected ‘%s’ was invalid", wrong)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user