ptypes: optimize Is to avoid prefix scan (#601)

diff --git a/ptypes/any.go b/ptypes/any.go
index b2af97f..70276e8 100644
--- a/ptypes/any.go
+++ b/ptypes/any.go
@@ -130,10 +130,12 @@
 
 // Is returns true if any value contains a given message type.
 func Is(any *any.Any, pb proto.Message) bool {
-	aname, err := AnyMessageName(any)
-	if err != nil {
+	// The following is equivalent to AnyMessageName(any) == proto.MessageName(pb),
+	// but it avoids scanning TypeUrl for the slash.
+	if any == nil {
 		return false
 	}
-
-	return aname == proto.MessageName(pb)
+	name := proto.MessageName(pb)
+	prefix := len(any.TypeUrl) - len(name)
+	return prefix >= 1 && any.TypeUrl[prefix-1] == '/' && any.TypeUrl[prefix:] == name
 }
diff --git a/ptypes/any_test.go b/ptypes/any_test.go
index ed675b4..163ca31 100644
--- a/ptypes/any_test.go
+++ b/ptypes/any_test.go
@@ -60,8 +60,13 @@
 		t.Fatal(err)
 	}
 	if Is(a, &pb.DescriptorProto{}) {
+		// No spurious match for message names of different length.
 		t.Error("FileDescriptorProto is not a DescriptorProto, but Is says it is")
 	}
+	if Is(a, &pb.EnumDescriptorProto{}) {
+		// No spurious match for message names of equal length.
+		t.Error("FileDescriptorProto is not an EnumDescriptorProto, but Is says it is")
+	}
 	if !Is(a, &pb.FileDescriptorProto{}) {
 		t.Error("FileDescriptorProto is indeed a FileDescriptorProto, but Is says it is not")
 	}
@@ -75,6 +80,22 @@
 	}
 }
 
+
+func TestIsCornerCases(t *testing.T) {
+	m := &pb.FileDescriptorProto{}
+	if Is(nil, m) {
+		t.Errorf("message with nil type url incorrectly claimed to be %q", proto.MessageName(m))
+	}
+	noPrefix := &any.Any{TypeUrl: proto.MessageName(m)}
+	if Is(noPrefix, m) {
+		t.Errorf("message with type url %q incorrectly claimed to be %q", noPrefix.TypeUrl, proto.MessageName(m))
+	}
+	shortPrefix := &any.Any{TypeUrl: "/" + proto.MessageName(m)}
+	if !Is(shortPrefix, m) {
+		t.Errorf("message with type url %q didn't satisfy Is for type %q", shortPrefix.TypeUrl, proto.MessageName(m))
+	}
+}
+
 func TestUnmarshalDynamic(t *testing.T) {
 	want := &pb.FileDescriptorProto{Name: proto.String("foo")}
 	a, err := MarshalAny(want)
@@ -111,3 +132,24 @@
 		t.Errorf("got no error for an attempt to create a message of type %q, which shouldn't be linked in", a.TypeUrl)
 	}
 }
+
+func TestEmptyCornerCases(t *testing.T) {
+	_, err := Empty(nil)
+	if err == nil {
+		t.Error("expected Empty for nil to fail")
+	}
+	want := &pb.FileDescriptorProto{}
+	noPrefix := &any.Any{TypeUrl: proto.MessageName(want)}
+	_, err = Empty(noPrefix)
+	if err == nil {
+		t.Errorf("expected Empty for any type %q to fail", noPrefix.TypeUrl)
+	}
+	shortPrefix := &any.Any{TypeUrl: "/" + proto.MessageName(want)}
+	got, err := Empty(shortPrefix)
+	if err != nil {
+		t.Errorf("Empty for any type %q failed: %s", shortPrefix.TypeUrl, err)
+	}
+	if !proto.Equal(got, want) {
+		t.Errorf("Empty for any type %q differs, got %q, want %q", shortPrefix.TypeUrl, got, want)
+	}
+}