mirror of git://gcc.gnu.org/git/gcc.git
				
				
				
			
		
			
				
	
	
		
			313 lines
		
	
	
		
			6.7 KiB
		
	
	
	
		
			Go
		
	
	
	
			
		
		
	
	
			313 lines
		
	
	
		
			6.7 KiB
		
	
	
	
		
			Go
		
	
	
	
| // 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
 | |
| }
 |