From d45856ae09c003e848b22de3fba73db59152144f Mon Sep 17 00:00:00 2001 From: Iskander Sharipov Date: Fri, 20 Mar 2020 14:18:07 +0300 Subject: [PATCH] meta: make NameEquals alloc-free MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit name old time/op new time/op delta NameEquals/1part-8 76.2ns ± 2% 15.3ns ± 2% -79.94% (p=0.008 n=5+5) NameEquals/3parts-8 174ns ± 1% 30ns ± 1% -82.74% (p=0.008 n=5+5) name old alloc/op new alloc/op delta NameEquals/1part-8 16.0B ± 0% 0.0B -100.00% (p=0.008 n=5+5) NameEquals/3parts-8 48.0B ± 0% 0.0B -100.00% (p=0.008 n=5+5) name old allocs/op new allocs/op delta NameEquals/1part-8 1.00 ± 0% 0.00 -100.00% (p=0.008 n=5+5) NameEquals/3parts-8 1.00 ± 0% 0.00 -100.00% (p=0.008 n=5+5) Refs #364 Signed-off-by: Iskander Sharipov --- src/meta/meta_test.go | 77 +++++++++++++++++++++++++++++++++++++++++++ src/meta/metainfo.go | 12 +++---- 2 files changed, 82 insertions(+), 7 deletions(-) create mode 100644 src/meta/meta_test.go diff --git a/src/meta/meta_test.go b/src/meta/meta_test.go new file mode 100644 index 000000000..f153aab59 --- /dev/null +++ b/src/meta/meta_test.go @@ -0,0 +1,77 @@ +package meta + +import ( + "strings" + "testing" + + "github.com/VKCOM/noverify/src/php/parser/node" + "github.com/VKCOM/noverify/src/php/parser/node/name" +) + +func TestNameEquals(t *testing.T) { + tests := []struct { + x string + y string + want bool + }{ + {`foo`, `foo`, true}, + {`foo\bar2`, `foo\bar2`, true}, + {`foo\bar2\BazBaz`, `foo\bar2\BazBaz`, true}, + {`foo \ bar`, `foo\bar`, true}, + {`a\b\c\d`, `a\b\c\d`, true}, + + {`first`, ``, false}, + {`first\second`, `first`, false}, + {`first`, `first\second`, false}, + {`first\first`, `first\second`, false}, + {`first\second`, `firstsecond`, false}, + {`firstsecond`, `first\second`, false}, + {`a\b\c`, `a\b\x`, false}, + } + + makeNameNode := func(s string) *name.Name { + parts := strings.Split(s, `\`) + nm := &name.Name{Parts: make([]node.Node, len(parts))} + for i := range parts { + nm.Parts[i] = &name.NamePart{Value: strings.TrimSpace(parts[i])} + } + return nm + } + + for _, test := range tests { + x := makeNameNode(test.x) + y := test.y + have := NameEquals(x, y) + if have != test.want { + t.Errorf("NameEquals(%q, %q): have %v, want %v", + test.x, test.y, have, test.want) + } + } +} + +var theName1 = &name.Name{ + Parts: []node.Node{ + &name.NamePart{Value: `method_exists`}, + }, +} + +var theName3 = &name.Name{ + Parts: []node.Node{ + &name.NamePart{Value: `a`}, + &name.NamePart{Value: `b`}, + &name.NamePart{Value: `c`}, + }, +} + +func BenchmarkNameEquals(b *testing.B) { + b.Run("1part", func(b *testing.B) { + for i := 0; i < b.N; i++ { + _ = NameEquals(theName1, `method_exists`) + } + }) + b.Run("3parts", func(b *testing.B) { + for i := 0; i < b.N; i++ { + _ = NameEquals(theName3, `a\b\c`) + } + }) +} diff --git a/src/meta/metainfo.go b/src/meta/metainfo.go index f83b85e01..fe79ac714 100644 --- a/src/meta/metainfo.go +++ b/src/meta/metainfo.go @@ -492,16 +492,14 @@ func NameEquals(n *name.Name, s string) bool { return false } - sParts := strings.Split(s, `\`) - + rest := s for i, part := range n.Parts { - p, ok := part.(*name.NamePart) - if !ok { + part := part.(*name.NamePart) + if !strings.HasPrefix(rest, part.Value) { return false } - - if p.Value != sParts[i] { - return false + if i != len(n.Parts)-1 { + rest = rest[len(part.Value)+len(`\`):] } }