secure/precis: fix compare order of operations Reduce the number of heap allocations required by the compare operation, and factor out enforce and compare transformations into a single function. Also throw in a minor typo fix in a comment. Change-Id: I8e9fec1fc51d38a1950f95be72e07fb80d9949e3 Reviewed-on: https://go-review.googlesource.com/29966 Reviewed-by: Marcel van Lohuizen <mpvl@golang.org>
diff --git a/secure/precis/compare_test.go b/secure/precis/compare_test.go new file mode 100644 index 0000000..d709d74 --- /dev/null +++ b/secure/precis/compare_test.go
@@ -0,0 +1,53 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package precis + +import ( + "fmt" + "testing" + + "golang.org/x/text/internal/testtext" +) + +type compareTestCase struct { + a string + b string + result bool +} + +var compareTestCases = []struct { + name string + p *Profile + cases []compareTestCase +}{ + {"Nickname", Nickname, []compareTestCase{ + {"a", "b", false}, + {" Swan of Avon ", "swan of avon", true}, + {"Foo", "foo", true}, + {"foo", "foo", true}, + {"Foo Bar", "foo bar", true}, + {"foo bar", "foo bar", true}, + {"\u03A3", "\u03C3", true}, + {"\u03A3", "\u03C2", false}, + {"\u03C3", "\u03C2", false}, + {"Richard \u2163", "richard iv", true}, + {"Å", "å", true}, + {"ff", "ff", true}, // because of NFKC + {"ß", "sS", false}, + }}, +} + +func TestCompare(t *testing.T) { + for _, g := range compareTestCases { + for i, tc := range g.cases { + name := fmt.Sprintf("%s:%d:%+q", g.name, i, tc.a) + testtext.Run(t, name, func(t *testing.T) { + if result := g.p.Compare(tc.a, tc.b); result != tc.result { + t.Errorf("got %v; want %v", result, tc.result) + } + }) + } + } +}
diff --git a/secure/precis/profile.go b/secure/precis/profile.go index 5fb74b8..e21f8c6 100644 --- a/secure/precis/profile.go +++ b/secure/precis/profile.go
@@ -106,14 +106,14 @@ return err } -func (b *buffers) enforce(p *Profile, src []byte) (str []byte, err error) { +func (b *buffers) enforce(p *Profile, src []byte, comparing bool) (str []byte, err error) { b.src = src // These transforms are applied in the order defined in // https://tools.ietf.org/html/rfc7564#section-7 // TODO: allow different width transforms options. - if p.options.foldWidth { + if p.options.foldWidth || (p.options.ignorecase && comparing) { // TODO: use Span, once available. if err = b.apply(width.Fold); err != nil { return nil, err @@ -129,6 +129,11 @@ return nil, err } } + if comparing && p.options.ignorecase { + if err = b.apply(cases.Lower(language.Und, cases.HandleFinalSigma(false))); err != nil { + return nil, err + } + } if n := p.norm.QuickSpan(b.src); n < len(b.src) { x := b.next & 1 n = copy(b.buf[x], b.src[:n]) @@ -171,7 +176,7 @@ func (p *Profile) Append(dst, src []byte) ([]byte, error) { var buf buffers buf.init(8 + len(src) + len(src)>>2) - b, err := buf.enforce(p, src) + b, err := buf.enforce(p, src, false) if err != nil { return nil, err } @@ -182,7 +187,7 @@ func (p *Profile) Bytes(b []byte) ([]byte, error) { var buf buffers buf.init(8 + len(b) + len(b)>>2) - b, err := buf.enforce(p, b) + b, err := buf.enforce(p, b, false) if err != nil { return nil, err } @@ -198,7 +203,7 @@ func (p *Profile) String(s string) (string, error) { var buf buffers buf.init(8 + len(s) + len(s)>>2) - b, err := buf.enforce(p, []byte(s)) + b, err := buf.enforce(p, []byte(s), false) if err != nil { return "", err } @@ -209,26 +214,21 @@ // (byte-for-byte equality). If either string cannot be enforced, the comparison // is false. func (p *Profile) Compare(a, b string) bool { - a, err := p.String(a) + var buf buffers + + buf.init(8 + len(a) + len(a)>>2) + str, err := buf.enforce(p, []byte(a), true) if err != nil { return false } - b, err = p.String(b) + a = string(str) + + buf.init(8 + len(b) + len(b)>>2) + str, err = buf.enforce(p, []byte(b), true) if err != nil { return false } - - // TODO: This is out of order. Need to extract the transformation logic and - // put this in where the normal case folding would go (but only for - // comparison). - if p.options.ignorecase { - a = width.Fold.String(a) - b = width.Fold.String(a) - - caser := cases.Lower(language.Und, cases.HandleFinalSigma(false)) - a = caser.String(a) - b = caser.String(b) - } + b = string(str) return a == b }
diff --git a/secure/precis/tables_test.go b/secure/precis/tables_test.go index b4f152c..67f5b40 100644 --- a/secure/precis/tables_test.go +++ b/secure/precis/tables_test.go
@@ -31,7 +31,7 @@ } }) -// Ensure that ceratain properties were generated correctly. +// Ensure that certain properties were generated correctly. func TestTable(t *testing.T) { tests := []tableTest{ tableTest{