| // Copyright 2011 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 strings | |
| import "io" | |
| // A Replacer replaces a list of strings with replacements. | |
| type Replacer struct { | |
| r replacer | |
| } | |
| // replacer is the interface that a replacement algorithm needs to implement. | |
| type replacer interface { | |
| Replace(s string) string | |
| WriteString(w io.Writer, s string) (n int, err error) | |
| } | |
| // byteBitmap represents bytes which are sought for replacement. | |
| // byteBitmap is 256 bits wide, with a bit set for each old byte to be | |
| // replaced. | |
| type byteBitmap [256 / 32]uint32 | |
| func (m *byteBitmap) set(b byte) { | |
| m[b>>5] |= uint32(1 << (b & 31)) | |
| } | |
| // NewReplacer returns a new Replacer from a list of old, new string pairs. | |
| // Replacements are performed in order, without overlapping matches. | |
| func NewReplacer(oldnew ...string) *Replacer { | |
| if len(oldnew)%2 == 1 { | |
| panic("strings.NewReplacer: odd argument count") | |
| } | |
| // Possible implementations. | |
| var ( | |
| bb byteReplacer | |
| bs byteStringReplacer | |
| gen genericReplacer | |
| ) | |
| 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 | |
| } | |
| // 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 allOldBytes && allNewBytes { | |
| bb.old.set(old[0]) | |
| bb.new[old[0]] = new[0] | |
| } | |
| } | |
| if allOldBytes && allNewBytes { | |
| return &Replacer{r: &bb} | |
| } | |
| if allOldBytes { | |
| return &Replacer{r: &bs} | |
| } | |
| return &Replacer{r: &gen} | |
| } | |
| // Replace returns a copy of s with all replacements performed. | |
| func (r *Replacer) Replace(s string) string { | |
| 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) { | |
| return r.r.WriteString(w, s) | |
| } | |
| // genericReplacer is the fully generic (and least optimized) algorithm. | |
| // It's used as a fallback when nothing faster can be used. | |
| type genericReplacer struct { | |
| p []pair | |
| } | |
| type pair struct{ old, new string } | |
| type appendSliceWriter struct { | |
| b []byte | |
| } | |
| func (w *appendSliceWriter) Write(p []byte) (int, error) { | |
| w.b = append(w.b, p...) | |
| return len(p), nil | |
| } | |
| func (r *genericReplacer) Replace(s string) string { | |
| // TODO(bradfitz): optimized version | |
| n, _ := r.WriteString(discard, s) | |
| w := appendSliceWriter{make([]byte, 0, n)} | |
| r.WriteString(&w, s) | |
| return string(w.b) | |
| } | |
| func (r *genericReplacer) WriteString(w io.Writer, s string) (n int, err error) { | |
| lastEmpty := false // the last replacement was of the empty string | |
| Input: | |
| // TODO(bradfitz): optimized version | |
| for i := 0; i < len(s); { | |
| for _, p := range r.p { | |
| if p.old == "" && lastEmpty { | |
| // Don't let old match twice in a row. | |
| // (it doesn't advance the input and | |
| // would otherwise loop forever) | |
| continue | |
| } | |
| if HasPrefix(s[i:], p.old) { | |
| if p.new != "" { | |
| wn, err := w.Write([]byte(p.new)) | |
| n += wn | |
| if err != nil { | |
| return n, err | |
| } | |
| } | |
| i += len(p.old) | |
| lastEmpty = p.old == "" | |
| continue Input | |
| } | |
| } | |
| wn, err := w.Write([]byte{s[i]}) | |
| n += wn | |
| if err != nil { | |
| return n, err | |
| } | |
| i++ | |
| } | |
| // Final empty match at end. | |
| for _, p := range r.p { | |
| if p.old == "" { | |
| if p.new != "" { | |
| wn, err := w.Write([]byte(p.new)) | |
| n += wn | |
| if err != nil { | |
| return n, err | |
| } | |
| } | |
| break | |
| } | |
| } | |
| return n, nil | |
| } | |
| // byteReplacer is the implementation that's used when all the "old" | |
| // and "new" values are single ASCII bytes. | |
| type byteReplacer struct { | |
| // old has a bit set for each old byte that should be replaced. | |
| old byteBitmap | |
| // replacement byte, indexed by old byte. only valid if | |
| // corresponding old bit is set. | |
| new [256]byte | |
| } | |
| func (r *byteReplacer) Replace(s string) string { | |
| var buf []byte // lazily allocated | |
| for i := 0; i < len(s); i++ { | |
| b := s[i] | |
| if r.old[b>>5]&uint32(1<<(b&31)) != 0 { | |
| if buf == nil { | |
| buf = []byte(s) | |
| } | |
| buf[i] = r.new[b] | |
| } | |
| } | |
| if buf == nil { | |
| return s | |
| } | |
| 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) | |
| for len(s) > 0 { | |
| ncopy := copy(buf, s[:]) | |
| s = s[ncopy:] | |
| for i, b := range buf[:ncopy] { | |
| if r.old[b>>5]&uint32(1<<(b&31)) != 0 { | |
| buf[i] = r.new[b] | |
| } | |
| } | |
| wn, err := w.Write(buf[:ncopy]) | |
| n += wn | |
| if err != nil { | |
| return n, err | |
| } | |
| } | |
| return n, nil | |
| } | |
| // byteStringReplacer is the implementation that's used when all the | |
| // "old" values are single ASCII bytes but the "new" values vary in | |
| // size. | |
| type byteStringReplacer struct { | |
| // old has a bit set for each old byte that should be replaced. | |
| old byteBitmap | |
| // replacement string, indexed by old byte. only valid if | |
| // corresponding old bit is set. | |
| new [256][]byte | |
| } | |
| func (r *byteStringReplacer) Replace(s string) string { | |
| newSize := 0 | |
| anyChanges := false | |
| for i := 0; i < len(s); i++ { | |
| b := s[i] | |
| if r.old[b>>5]&uint32(1<<(b&31)) != 0 { | |
| anyChanges = true | |
| newSize += len(r.new[b]) | |
| } else { | |
| newSize++ | |
| } | |
| } | |
| if !anyChanges { | |
| return s | |
| } | |
| buf := make([]byte, newSize) | |
| bi := buf | |
| for i := 0; i < len(s); i++ { | |
| b := s[i] | |
| if r.old[b>>5]&uint32(1<<(b&31)) != 0 { | |
| n := copy(bi[:], r.new[b]) | |
| bi = bi[n:] | |
| } else { | |
| bi[0] = b | |
| bi = bi[1:] | |
| } | |
| } | |
| return string(buf) | |
| } | |
| // WriteString maintains one buffer that's at most 32KB. The bytes in | |
| // s are enumerated and the buffer is filled. If it reaches its | |
| // capacity or a byte has a replacement, the buffer is flushed to w. | |
| func (r *byteStringReplacer) WriteString(w io.Writer, s string) (n int, err error) { | |
| // TODO(bradfitz): use io.WriteString with slices of s instead. | |
| bufsize := 32 << 10 | |
| if len(s) < bufsize { | |
| bufsize = len(s) | |
| } | |
| buf := make([]byte, bufsize) | |
| bi := buf[:0] | |
| for i := 0; i < len(s); i++ { | |
| b := s[i] | |
| var new []byte | |
| if r.old[b>>5]&uint32(1<<(b&31)) != 0 { | |
| new = r.new[b] | |
| } else { | |
| bi = append(bi, b) | |
| } | |
| if len(bi) == cap(bi) || (len(bi) > 0 && len(new) > 0) { | |
| nw, err := w.Write(bi) | |
| n += nw | |
| if err != nil { | |
| return n, err | |
| } | |
| bi = buf[:0] | |
| } | |
| if len(new) > 0 { | |
| nw, err := w.Write(new) | |
| n += nw | |
| if err != nil { | |
| return n, err | |
| } | |
| } | |
| } | |
| if len(bi) > 0 { | |
| nw, err := w.Write(bi) | |
| n += nw | |
| if err != nil { | |
| return n, err | |
| } | |
| } | |
| return n, nil | |
| } | |
| // strings is too low-level to import io/ioutil | |
| var discard io.Writer = devNull(0) | |
| type devNull int | |
| func (devNull) Write(p []byte) (int, error) { | |
| return len(p), nil | |
| } |