mirror of
https://github.com/golang/go
synced 2024-11-20 05:34:40 -07:00
strings: Map: avoid allocation when string is unchanged
This speeds up strings.ToLower, etc. before/after: strings_test.BenchmarkMapNoChanges 1000000 1013 ns/op strings_test.BenchmarkMapNoChanges 5000000 442 ns/op R=r, rog, eh, rsc CC=golang-dev https://golang.org/cl/4306056
This commit is contained in:
parent
43512e6c70
commit
a7a854b82f
@ -312,9 +312,19 @@ func Map(mapping func(rune int) int, s string) string {
|
||||
// fine. It could also shrink but that falls out naturally.
|
||||
maxbytes := len(s) // length of b
|
||||
nbytes := 0 // number of bytes encoded in b
|
||||
b := make([]byte, maxbytes)
|
||||
for _, c := range s {
|
||||
// The output buffer b is initialized on demand, the first
|
||||
// time a character differs.
|
||||
var b []byte
|
||||
|
||||
for i, c := range s {
|
||||
rune := mapping(c)
|
||||
if b == nil {
|
||||
if rune == c {
|
||||
continue
|
||||
}
|
||||
b = make([]byte, maxbytes)
|
||||
nbytes = copy(b, s[:i])
|
||||
}
|
||||
if rune >= 0 {
|
||||
wid := 1
|
||||
if rune >= utf8.RuneSelf {
|
||||
@ -330,6 +340,9 @@ func Map(mapping func(rune int) int, s string) string {
|
||||
nbytes += utf8.EncodeRune(b[nbytes:maxbytes], rune)
|
||||
}
|
||||
}
|
||||
if b == nil {
|
||||
return s
|
||||
}
|
||||
return string(b[0:nbytes])
|
||||
}
|
||||
|
||||
|
@ -6,10 +6,12 @@ package strings_test
|
||||
|
||||
import (
|
||||
"os"
|
||||
"reflect"
|
||||
"strconv"
|
||||
. "strings"
|
||||
"testing"
|
||||
"unicode"
|
||||
"unsafe"
|
||||
"utf8"
|
||||
)
|
||||
|
||||
@ -429,12 +431,32 @@ func TestMap(t *testing.T) {
|
||||
if m != expect {
|
||||
t.Errorf("drop: expected %q got %q", expect, m)
|
||||
}
|
||||
|
||||
// 6. Identity
|
||||
identity := func(rune int) int {
|
||||
return rune
|
||||
}
|
||||
orig := "Input string that we expect not to be copied."
|
||||
m = Map(identity, orig)
|
||||
if (*reflect.StringHeader)(unsafe.Pointer(&orig)).Data !=
|
||||
(*reflect.StringHeader)(unsafe.Pointer(&m)).Data {
|
||||
t.Error("unexpected copy during identity map")
|
||||
}
|
||||
}
|
||||
|
||||
func TestToUpper(t *testing.T) { runStringTests(t, ToUpper, "ToUpper", upperTests) }
|
||||
|
||||
func TestToLower(t *testing.T) { runStringTests(t, ToLower, "ToLower", lowerTests) }
|
||||
|
||||
func BenchmarkMapNoChanges(b *testing.B) {
|
||||
identity := func(rune int) int {
|
||||
return rune
|
||||
}
|
||||
for i := 0; i < b.N; i++ {
|
||||
Map(identity, "Some string that won't be modified.")
|
||||
}
|
||||
}
|
||||
|
||||
func TestSpecialCase(t *testing.T) {
|
||||
lower := "abcçdefgğhıijklmnoöprsştuüvyz"
|
||||
upper := "ABCÇDEFGĞHIİJKLMNOÖPRSŞTUÜVYZ"
|
||||
|
Loading…
Reference in New Issue
Block a user