aboutsummaryrefslogtreecommitdiff
path: root/src/pkg/strings
diff options
context:
space:
mode:
authorRui Ueyama <ruiu@google.com>2014-06-21 22:08:43 -0700
committerRui Ueyama <ruiu@google.com>2014-06-21 22:08:43 -0700
commit3142861ff86a8b4064256f31a0f63dcd23c2f971 (patch)
treed26bc42254eeb316a7dd6b5c487595950e6e78bb /src/pkg/strings
parent9bfb66e962b631c2faf831ccc29ba5d4018ccf04 (diff)
downloadgo-3142861ff86a8b4064256f31a0f63dcd23c2f971.tar.xz
strings: use sync.Pool to cache buffer
benchmark old ns/op new ns/op delta BenchmarkByteReplacerWriteString 3596 3094 -13.96% benchmark old allocs new allocs delta BenchmarkByteReplacerWriteString 1 0 -100.00% LGTM=dvyukov R=bradfitz, dave, dvyukov CC=golang-codereviews https://golang.org/cl/101330053
Diffstat (limited to 'src/pkg/strings')
-rw-r--r--src/pkg/strings/replace.go33
-rw-r--r--src/pkg/strings/replace_test.go42
2 files changed, 50 insertions, 25 deletions
diff --git a/src/pkg/strings/replace.go b/src/pkg/strings/replace.go
index cb9d7b1fa4..89aca95bae 100644
--- a/src/pkg/strings/replace.go
+++ b/src/pkg/strings/replace.go
@@ -4,7 +4,10 @@
package strings
-import "io"
+import (
+ "io"
+ "sync"
+)
// A Replacer replaces a list of strings with replacements.
type Replacer struct {
@@ -451,27 +454,31 @@ func (r *byteReplacer) Replace(s string) string {
return string(buf)
}
-func (r *byteReplacer) WriteString(w io.Writer, s string) (n int, err error) {
- // TODO(bradfitz): use io.WriteString with slices of s, avoiding allocation.
- bufsize := 32 << 10
- if len(s) < bufsize {
- bufsize = len(s)
- }
- buf := make([]byte, bufsize)
+var bufferPool = sync.Pool{
+ New: func() interface{} {
+ b := make([]byte, 4096)
+ return &b
+ },
+}
+func (r *byteReplacer) WriteString(w io.Writer, s string) (n int, err error) {
+ bp := bufferPool.Get().(*[]byte)
+ buf := *bp
for len(s) > 0 {
- ncopy := copy(buf, s[:])
- s = s[ncopy:]
+ ncopy := copy(buf, s)
for i, b := range buf[:ncopy] {
buf[i] = r.new[b]
}
- wn, err := w.Write(buf[:ncopy])
+ s = s[ncopy:]
+ var wn int
+ wn, err = w.Write(buf[:ncopy])
n += wn
if err != nil {
- return n, err
+ break
}
}
- return n, nil
+ bufferPool.Put(bp)
+ return
}
// byteStringReplacer is the implementation that's used when all the
diff --git a/src/pkg/strings/replace_test.go b/src/pkg/strings/replace_test.go
index 2cb318b69d..77e48b988b 100644
--- a/src/pkg/strings/replace_test.go
+++ b/src/pkg/strings/replace_test.go
@@ -308,20 +308,21 @@ func TestReplacer(t *testing.T) {
}
}
+var algorithmTestCases = []struct {
+ r *Replacer
+ want string
+}{
+ {capitalLetters, "*strings.byteReplacer"},
+ {htmlEscaper, "*strings.byteStringReplacer"},
+ {NewReplacer("12", "123"), "*strings.singleStringReplacer"},
+ {NewReplacer("1", "12"), "*strings.byteStringReplacer"},
+ {NewReplacer("", "X"), "*strings.genericReplacer"},
+ {NewReplacer("a", "1", "b", "12", "cde", "123"), "*strings.genericReplacer"},
+}
+
// TestPickAlgorithm tests that NewReplacer picks the correct algorithm.
func TestPickAlgorithm(t *testing.T) {
- testCases := []struct {
- r *Replacer
- want string
- }{
- {capitalLetters, "*strings.byteReplacer"},
- {htmlEscaper, "*strings.byteStringReplacer"},
- {NewReplacer("12", "123"), "*strings.singleStringReplacer"},
- {NewReplacer("1", "12"), "*strings.byteStringReplacer"},
- {NewReplacer("", "X"), "*strings.genericReplacer"},
- {NewReplacer("a", "1", "b", "12", "cde", "123"), "*strings.genericReplacer"},
- }
- for i, tc := range testCases {
+ for i, tc := range algorithmTestCases {
got := fmt.Sprintf("%T", tc.r.Replacer())
if got != tc.want {
t.Errorf("%d. algorithm = %s, want %s", i, got, tc.want)
@@ -329,6 +330,23 @@ func TestPickAlgorithm(t *testing.T) {
}
}
+type errWriter struct{}
+
+func (errWriter) Write(p []byte) (n int, err error) {
+ return 0, fmt.Errorf("unwritable")
+}
+
+// TestWriteStringError tests that WriteString returns an error
+// received from the underlying io.Writer.
+func TestWriteStringError(t *testing.T) {
+ for i, tc := range algorithmTestCases {
+ n, err := tc.r.WriteString(errWriter{}, "abc")
+ if n != 0 || err == nil || err.Error() != "unwritable" {
+ t.Errorf("%d. WriteStringError = %d, %v, want 0, unwritable", i, n, err)
+ }
+ }
+}
+
// TestGenericTrieBuilding verifies the structure of the generated trie. There
// is one node per line, and the key ending with the current line is in the
// trie if it ends with a "+".