From c544e0fbdb7344b2025650aaf70bab3b09d72003 Mon Sep 17 00:00:00 2001 From: Brad Fitzpatrick Date: Thu, 2 Aug 2018 20:02:24 +0000 Subject: [PATCH] strings: select Replacer algorithm and build machine lazily Saves 22KB of memory in stdlib packages. Updates #26775 Change-Id: Ia19fe7aff61f6e2ddd83cd35969d7ff94526591f Reviewed-on: https://go-review.googlesource.com/127661 Run-TryBot: Brad Fitzpatrick TryBot-Result: Gobot Gobot Reviewed-by: Ian Lance Taylor --- src/strings/export_test.go | 2 ++ src/strings/replace.go | 28 ++++++++++++++++++++++------ 2 files changed, 24 insertions(+), 6 deletions(-) diff --git a/src/strings/export_test.go b/src/strings/export_test.go index 17c806aa56..b39cee6b1d 100644 --- a/src/strings/export_test.go +++ b/src/strings/export_test.go @@ -5,10 +5,12 @@ package strings func (r *Replacer) Replacer() interface{} { + r.once.Do(r.buildOnce) return r.r } func (r *Replacer) PrintTrie() string { + r.once.Do(r.buildOnce) gen := r.r.(*genericReplacer) return gen.printNode(&gen.root, 0) } diff --git a/src/strings/replace.go b/src/strings/replace.go index 58a11a63db..dbda950194 100644 --- a/src/strings/replace.go +++ b/src/strings/replace.go @@ -4,12 +4,17 @@ package strings -import "io" +import ( + "io" + "sync" +) // Replacer replaces a list of strings with replacements. // It is safe for concurrent use by multiple goroutines. type Replacer struct { - r replacer + once sync.Once // guards buildOnce method + r replacer + oldnew []string } // replacer is the interface that a replacement algorithm needs to implement. @@ -25,15 +30,24 @@ func NewReplacer(oldnew ...string) *Replacer { if len(oldnew)%2 == 1 { panic("strings.NewReplacer: odd argument count") } + return &Replacer{oldnew: append([]string(nil), oldnew...)} +} + +func (r *Replacer) buildOnce() { + r.r = r.build() + r.oldnew = nil +} +func (b *Replacer) build() replacer { + oldnew := b.oldnew if len(oldnew) == 2 && len(oldnew[0]) > 1 { - return &Replacer{r: makeSingleStringReplacer(oldnew[0], oldnew[1])} + return makeSingleStringReplacer(oldnew[0], oldnew[1]) } allNewBytes := true for i := 0; i < len(oldnew); i += 2 { if len(oldnew[i]) != 1 { - return &Replacer{r: makeGenericReplacer(oldnew)} + return makeGenericReplacer(oldnew) } if len(oldnew[i+1]) != 1 { allNewBytes = false @@ -52,7 +66,7 @@ func NewReplacer(oldnew ...string) *Replacer { n := oldnew[i+1][0] r[o] = n } - return &Replacer{r: &r} + return &r } r := byteStringReplacer{toReplace: make([]string, 0, len(oldnew)/2)} @@ -71,16 +85,18 @@ func NewReplacer(oldnew ...string) *Replacer { r.replacements[o] = []byte(n) } - return &Replacer{r: &r} + return &r } // Replace returns a copy of s with all replacements performed. func (r *Replacer) Replace(s string) string { + r.once.Do(r.buildOnce) return r.r.Replace(s) } // WriteString writes s to w with all replacements performed. func (r *Replacer) WriteString(w io.Writer, s string) (n int, err error) { + r.once.Do(r.buildOnce) return r.r.WriteString(w, s) } -- 2.50.0