mirror of
https://github.com/protocolbuffers/protobuf-go.git
synced 2025-04-17 02:42:35 +00:00
reflect/protoreflect: optimize Name.IsValid and FullName.IsValid
For simplicity, IsValid was implemented in terms of regular expressions, which are useful for verifying a string according to a strict grammar, but performs poorly. Implement the check in terms of hand-written code, which provides a 20x improvement to validate the name "google.protobuf.Any". name old time/op new time/op delta FullNameIsValid-8 683ns ± 2% 35ns ± 1% -94.86% (p=0.000 n=10+10) Change-Id: I980403befca0b72cea22acd274064a46cb02644b Reviewed-on: https://go-review.googlesource.com/c/protobuf/+/238002 Reviewed-by: Damien Neil <dneil@google.com>
This commit is contained in:
parent
0084168af8
commit
44e4150b30
@ -128,7 +128,6 @@ package protoreflect
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"google.golang.org/protobuf/encoding/protowire"
|
||||
@ -408,19 +407,14 @@ type EnumRanges interface {
|
||||
doNotImplement
|
||||
}
|
||||
|
||||
var (
|
||||
regexName = regexp.MustCompile(`^[_a-zA-Z][_a-zA-Z0-9]*$`)
|
||||
regexFullName = regexp.MustCompile(`^[_a-zA-Z][_a-zA-Z0-9]*(\.[_a-zA-Z][_a-zA-Z0-9]*)*$`)
|
||||
)
|
||||
|
||||
// Name is the short name for a proto declaration. This is not the name
|
||||
// as used in Go source code, which might not be identical to the proto name.
|
||||
type Name string // e.g., "Kind"
|
||||
|
||||
// IsValid reports whether n is a syntactically valid name.
|
||||
// IsValid reports whether s is a syntactically valid name.
|
||||
// An empty name is invalid.
|
||||
func (n Name) IsValid() bool {
|
||||
return regexName.MatchString(string(n))
|
||||
func (s Name) IsValid() bool {
|
||||
return consumeIdent(string(s)) == len(s)
|
||||
}
|
||||
|
||||
// Names represent a list of names.
|
||||
@ -443,10 +437,42 @@ type Names interface {
|
||||
// This should not have any leading or trailing dots.
|
||||
type FullName string // e.g., "google.protobuf.Field.Kind"
|
||||
|
||||
// IsValid reports whether n is a syntactically valid full name.
|
||||
// IsValid reports whether s is a syntactically valid full name.
|
||||
// An empty full name is invalid.
|
||||
func (n FullName) IsValid() bool {
|
||||
return regexFullName.MatchString(string(n))
|
||||
func (s FullName) IsValid() bool {
|
||||
i := consumeIdent(string(s))
|
||||
if i < 0 {
|
||||
return false
|
||||
}
|
||||
for len(s) > i {
|
||||
if s[i] != '.' {
|
||||
return false
|
||||
}
|
||||
i++
|
||||
n := consumeIdent(string(s[i:]))
|
||||
if n < 0 {
|
||||
return false
|
||||
}
|
||||
i += n
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func consumeIdent(s string) (i int) {
|
||||
if len(s) == 0 || !isLetter(s[i]) {
|
||||
return -1
|
||||
}
|
||||
i++
|
||||
for len(s) > i && isLetterDigit(s[i]) {
|
||||
i++
|
||||
}
|
||||
return i
|
||||
}
|
||||
func isLetter(c byte) bool {
|
||||
return c == '_' || ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z')
|
||||
}
|
||||
func isLetterDigit(c byte) bool {
|
||||
return isLetter(c) || ('0' <= c && c <= '9')
|
||||
}
|
||||
|
||||
// Name returns the short name, which is the last identifier segment.
|
||||
|
@ -72,3 +72,11 @@ func TestNameAppend(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var sink bool
|
||||
|
||||
func BenchmarkFullNameIsValid(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
sink = FullName("google.protobuf.Any").IsValid()
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user