Commit 6910356e authored by Nigel Tao's avatar Nigel Tao

strings: fix NewReplacer(old0, new0, old1, new1, ...) to be consistent

when oldi == oldj.

Benchmark numbers show no substantial change.

R=eric.d.eisner, rogpeppe
CC=golang-dev
https://golang.org/cl/6496104
parent e39072d6
...@@ -33,47 +33,41 @@ func NewReplacer(oldnew ...string) *Replacer { ...@@ -33,47 +33,41 @@ func NewReplacer(oldnew ...string) *Replacer {
panic("strings.NewReplacer: odd argument count") panic("strings.NewReplacer: odd argument count")
} }
// Possible implementations. allNewBytes := true
var ( for i := 0; i < len(oldnew); i += 2 {
bb byteReplacer if len(oldnew[i]) != 1 {
bs byteStringReplacer return &Replacer{r: makeGenericReplacer(oldnew)}
gen genericReplacer }
) if len(oldnew[i+1]) != 1 {
allOldBytes, allNewBytes := true, true
for len(oldnew) > 0 {
old, new := oldnew[0], oldnew[1]
oldnew = oldnew[2:]
if len(old) != 1 {
allOldBytes = false
}
if len(new) != 1 {
allNewBytes = false allNewBytes = false
} }
// generic
gen.p = append(gen.p, pair{old, new})
// byte -> string
if allOldBytes {
bs.old.set(old[0])
bs.new[old[0]] = []byte(new)
} }
// byte -> byte if allNewBytes {
if allOldBytes && allNewBytes { bb := &byteReplacer{}
bb.old.set(old[0]) for i := 0; i < len(oldnew); i += 2 {
bb.new[old[0]] = new[0] o, n := oldnew[i][0], oldnew[i+1][0]
if bb.old[o>>5]&uint32(1<<(o&31)) != 0 {
// Later old->new maps do not override previous ones with the same old string.
continue
}
bb.old.set(o)
bb.new[o] = n
} }
return &Replacer{r: bb}
} }
if allOldBytes && allNewBytes { bs := &byteStringReplacer{}
return &Replacer{r: &bb} for i := 0; i < len(oldnew); i += 2 {
o, new := oldnew[i][0], oldnew[i+1]
if bs.old[o>>5]&uint32(1<<(o&31)) != 0 {
// Later old->new maps do not override previous ones with the same old string.
continue
} }
if allOldBytes { bs.old.set(o)
return &Replacer{r: &bs} bs.new[o] = []byte(new)
} }
return &Replacer{r: &gen} return &Replacer{r: bs}
} }
// Replace returns a copy of s with all replacements performed. // Replace returns a copy of s with all replacements performed.
...@@ -94,6 +88,16 @@ type genericReplacer struct { ...@@ -94,6 +88,16 @@ type genericReplacer struct {
type pair struct{ old, new string } type pair struct{ old, new string }
func makeGenericReplacer(oldnew []string) *genericReplacer {
gen := &genericReplacer{
p: make([]pair, len(oldnew)/2),
}
for i := 0; i < len(oldnew); i += 2 {
gen.p[i/2] = pair{oldnew[i], oldnew[i+1]}
}
return gen
}
type appendSliceWriter struct { type appendSliceWriter struct {
b []byte b []byte
} }
......
...@@ -70,7 +70,7 @@ func TestReplacer(t *testing.T) { ...@@ -70,7 +70,7 @@ func TestReplacer(t *testing.T) {
testCase{inc, "\x00\xff", "\x01\x00"}, testCase{inc, "\x00\xff", "\x01\x00"},
testCase{inc, "", ""}, testCase{inc, "", ""},
testCase{NewReplacer("a", "1", "a", "2"), "brad", "br2d"}, // TODO: should this be "br1d"? testCase{NewReplacer("a", "1", "a", "2"), "brad", "br1d"},
) )
// repeat maps "a"->"a", "b"->"bb", "c"->"ccc", ... // repeat maps "a"->"a", "b"->"bb", "c"->"ccc", ...
...@@ -95,7 +95,7 @@ func TestReplacer(t *testing.T) { ...@@ -95,7 +95,7 @@ func TestReplacer(t *testing.T) {
testCase{repeat, "abba", "abbbba"}, testCase{repeat, "abba", "abbbba"},
testCase{repeat, "", ""}, testCase{repeat, "", ""},
testCase{NewReplacer("a", "11", "a", "22"), "brad", "br22d"}, // TODO: should this be "br11d"? testCase{NewReplacer("a", "11", "a", "22"), "brad", "br11d"},
) )
// The remaining test cases have variable length old strings. // The remaining test cases have variable length old strings.
...@@ -246,6 +246,14 @@ func TestReplacer(t *testing.T) { ...@@ -246,6 +246,14 @@ func TestReplacer(t *testing.T) {
testCase{blankFoo, "", "X"}, testCase{blankFoo, "", "X"},
) )
// No-arg test cases.
nop := NewReplacer()
testCases = append(testCases,
testCase{nop, "abc", "abc"},
testCase{nop, "", ""},
)
// Run the test cases. // Run the test cases.
for i, tc := range testCases { for i, tc := range testCases {
...@@ -277,9 +285,10 @@ func TestPickAlgorithm(t *testing.T) { ...@@ -277,9 +285,10 @@ func TestPickAlgorithm(t *testing.T) {
want string want string
}{ }{
{capitalLetters, "*strings.byteReplacer"}, {capitalLetters, "*strings.byteReplacer"},
{htmlEscaper, "*strings.byteStringReplacer"},
{NewReplacer("12", "123"), "*strings.genericReplacer"}, {NewReplacer("12", "123"), "*strings.genericReplacer"},
{NewReplacer("1", "12"), "*strings.byteStringReplacer"}, {NewReplacer("1", "12"), "*strings.byteStringReplacer"},
{htmlEscaper, "*strings.byteStringReplacer"}, {NewReplacer("a", "1", "b", "12", "cde", "123"), "*strings.genericReplacer"},
} }
for i, tc := range testCases { for i, tc := range testCases {
got := fmt.Sprintf("%T", tc.r.Replacer()) got := fmt.Sprintf("%T", tc.r.Replacer())
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment