// 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 stringsimport ()// Replacer replaces a list of strings with replacements.// It is safe for concurrent use by multiple goroutines.typeReplacerstruct { once sync.Once// guards buildOnce method r replacer oldnew []string}// 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)}// NewReplacer returns a new [Replacer] from a list of old, new string// pairs. Replacements are performed in the order they appear in the// target string, without overlapping matches. The old string// comparisons are done in argument order.//// NewReplacer panics if given an odd number of arguments.func ( ...string) *Replacer {iflen()%2 == 1 {panic("strings.NewReplacer: odd argument count") }return &Replacer{oldnew: append([]string(nil), ...)}}func ( *Replacer) () { .r = .build() .oldnew = nil}func ( *Replacer) () replacer { := .oldnewiflen() == 2 && len([0]) > 1 {returnmakeSingleStringReplacer([0], [1]) } := truefor := 0; < len(); += 2 {iflen([]) != 1 {returnmakeGenericReplacer() }iflen([+1]) != 1 { = false } }if { := byteReplacer{}for := range { [] = byte() }// The first occurrence of old->new map takes precedence // over the others with the same old string.for := len() - 2; >= 0; -= 2 { := [][0] := [+1][0] [] = }return & } := byteStringReplacer{toReplace: make([]string, 0, len()/2)}// The first occurrence of old->new map takes precedence // over the others with the same old string.for := len() - 2; >= 0; -= 2 { := [][0] := [+1]// To avoid counting repetitions multiple times.if .replacements[] == nil {// We need to use string([]byte{o}) instead of string(o), // to avoid utf8 encoding of o. // E. g. byte(150) produces string of length 2. .toReplace = append(.toReplace, string([]byte{})) } .replacements[] = []byte() }return &}// Replace returns a copy of s with all replacements performed.func ( *Replacer) ( string) string { .once.Do(.buildOnce)return .r.Replace()}// WriteString writes s to w with all replacements performed.func ( *Replacer) ( io.Writer, string) ( int, error) { .once.Do(.buildOnce)return .r.WriteString(, )}// trieNode is a node in a lookup trie for prioritized key/value pairs. Keys// and values may be empty. For example, the trie containing keys "ax", "ay",// "bcbc", "x" and "xy" could have eight nodes://// n0 -// n1 a-// n2 .x+// n3 .y+// n4 b-// n5 .cbc+// n6 x+// n7 .y+//// n0 is the root node, and its children are n1, n4 and n6; n1's children are// n2 and n3; n4's child is n5; n6's child is n7. Nodes n0, n1 and n4 (marked// with a trailing "-") are partial keys, and nodes n2, n3, n5, n6 and n7// (marked with a trailing "+") are complete keys.type trieNode struct {// value is the value of the trie node's key/value pair. It is empty if // this node is not a complete key. value string// priority is the priority (higher is more important) of the trie node's // key/value pair; keys are not necessarily matched shortest- or longest- // first. Priority is positive if this node is a complete key, and zero // otherwise. In the example above, positive/zero priorities are marked // with a trailing "+" or "-". priority int// A trie node may have zero, one or more child nodes: // * if the remaining fields are zero, there are no children. // * if prefix and next are non-zero, there is one child in next. // * if table is non-zero, it defines all the children. // // Prefixes are preferred over tables when there is one child, but the // root node always uses a table for lookup efficiency.// prefix is the difference in keys between this trie node and the next. // In the example above, node n4 has prefix "cbc" and n4's next node is n5. // Node n5 has no children and so has zero prefix, next and table fields. prefix string next *trieNode// table is a lookup table indexed by the next byte in the key, after // remapping that byte through genericReplacer.mapping to create a dense // index. In the example above, the keys only use 'a', 'b', 'c', 'x' and // 'y', which remap to 0, 1, 2, 3 and 4. All other bytes remap to 5, and // genericReplacer.tableSize will be 5. Node n0's table will be // []*trieNode{ 0:n1, 1:n4, 3:n6 }, where the 0, 1 and 3 are the remapped // 'a', 'b' and 'x'. table []*trieNode}func ( *trieNode) (, string, int, *genericReplacer) {if == "" {if .priority == 0 { .value = .priority = }return }if .prefix != "" {// Need to split the prefix among multiple nodes.varint// length of the longest common prefixfor ; < len(.prefix) && < len(); ++ {if .prefix[] != [] {break } }if == len(.prefix) { .next.([:], , , ) } elseif == 0 {// First byte differs, start a new lookup table here. Looking up // what is currently t.prefix[0] will lead to prefixNode, and // looking up key[0] will lead to keyNode.var *trieNodeiflen(.prefix) == 1 { = .next } else { = &trieNode{prefix: .prefix[1:],next: .next, } } := new(trieNode) .table = make([]*trieNode, .tableSize) .table[.mapping[.prefix[0]]] = .table[.mapping[[0]]] = .prefix = "" .next = nil .([1:], , , ) } else {// Insert new node after the common section of the prefix. := &trieNode{prefix: .prefix[:],next: .next, } .prefix = .prefix[:] .next = .([:], , , ) } } elseif .table != nil {// Insert into existing table. := .mapping[[0]]if .table[] == nil { .table[] = new(trieNode) } .table[].([1:], , , ) } else { .prefix = .next = new(trieNode) .next.("", , , ) }}func ( *genericReplacer) ( string, bool) ( string, int, bool) {// Iterate down the trie to the end, and grab the value and keylen with // the highest priority. := 0 := &.root := 0for != nil {if .priority > && !( && == &.root) { = .priority = .value = = true }if == "" {break }if .table != nil { := .mapping[[0]]ifint() == .tableSize {break } = .table[] = [1:] ++ } elseif .prefix != "" && HasPrefix(, .prefix) { += len(.prefix) = [len(.prefix):] = .next } else {break } }return}// genericReplacer is the fully generic algorithm.// It's used as a fallback when nothing faster can be used.type genericReplacer struct { root trieNode// tableSize is the size of a trie node's lookup table. It is the number // of unique key bytes. tableSize int// mapping maps from key bytes to a dense index for trieNode.table. mapping [256]byte}func makeGenericReplacer( []string) *genericReplacer { := new(genericReplacer)// Find each byte used, then assign them each an index.for := 0; < len(); += 2 { := []for := 0; < len(); ++ { .mapping[[]] = 1 } }for , := range .mapping { .tableSize += int() }varbytefor , := range .mapping {if == 0 { .mapping[] = byte(.tableSize) } else { .mapping[] = ++ } }// Ensure root node uses a lookup table (for performance). .root.table = make([]*trieNode, .tableSize)for := 0; < len(); += 2 { .root.add([], [+1], len()-, ) }return}type appendSliceWriter []byte// Write writes to the buffer to satisfy [io.Writer].func ( *appendSliceWriter) ( []byte) (int, error) { * = append(*, ...)returnlen(), nil}// WriteString writes to the buffer without string->[]byte->string allocations.func ( *appendSliceWriter) ( string) (int, error) { * = append(*, ...)returnlen(), nil}type stringWriter struct { w io.Writer}func ( stringWriter) ( string) (int, error) {return .w.Write([]byte())}func getStringWriter( io.Writer) io.StringWriter { , := .(io.StringWriter)if ! { = stringWriter{} }return}func ( *genericReplacer) ( string) string { := make(appendSliceWriter, 0, len()) .WriteString(&, )returnstring()}func ( *genericReplacer) ( io.Writer, string) ( int, error) { := getStringWriter()var , intvarboolfor := 0; <= len(); {// Fast path: s[i] is not a prefix of any pattern.if != len() && .root.priority == 0 { := int(.mapping[[]])if == .tableSize || .root.table[] == nil { ++continue } }// Ignore the empty match iff the previous loop found the empty match. , , := .lookup([:], ) = && == 0if { , = .WriteString([:]) += if != nil {return } , = .WriteString() += if != nil {return } += = continue } ++ }if != len() { , = .WriteString([:]) += }return}// singleStringReplacer is the implementation that's used when there is only// one string to replace (and that string has more than one byte).type singleStringReplacer struct { finder *stringFinder// value is the new string that replaces that pattern when it's found. value string}func makeSingleStringReplacer( string, string) *singleStringReplacer {return &singleStringReplacer{finder: makeStringFinder(), value: }}func ( *singleStringReplacer) ( string) string {varBuilder , := 0, falsefor { := .finder.next([:])if == -1 {break } = true .Grow( + len(.value)) .WriteString([ : +]) .WriteString(.value) += + len(.finder.pattern) }if ! {return } .WriteString([:])return .String()}func ( *singleStringReplacer) ( io.Writer, string) ( int, error) { := getStringWriter()var , intfor { := .finder.next([:])if == -1 {break } , = .WriteString([ : +]) += if != nil {return } , = .WriteString(.value) += if != nil {return } += + len(.finder.pattern) } , = .WriteString([:]) += return}// byteReplacer is the implementation that's used when all the "old"// and "new" values are single ASCII bytes.// The array contains replacement bytes indexed by old byte.type byteReplacer [256]bytefunc ( *byteReplacer) ( string) string {var []byte// lazily allocatedfor := 0; < len(); ++ { := []if [] != {if == nil { = []byte() } [] = [] } }if == nil {return }returnstring()}func ( *byteReplacer) ( io.Writer, string) ( int, error) { := getStringWriter() := 0for := 0; < len(); ++ { := []if [] == {continue }if != { , := .WriteString([:]) += if != nil {return , } } = + 1 , := .Write([ : int()+1]) += if != nil {return , } }if != len() { , := .WriteString([:]) += if != nil {return , } }return , 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 {// replacements contains replacement byte slices indexed by old byte. // A nil []byte means that the old byte should not be replaced. replacements [256][]byte// toReplace keeps a list of bytes to replace. Depending on length of toReplace // and length of target string it may be faster to use Count, or a plain loop. // We store single byte as a string, because Count takes a string. toReplace []string}// countCutOff controls the ratio of a string length to a number of replacements// at which (*byteStringReplacer).Replace switches algorithms.// For strings with higher ration of length to replacements than that value,// we call Count, for each replacement from toReplace.// For strings, with a lower ratio we use simple loop, because of Count overhead.// countCutOff is an empirically determined overhead multiplier.// TODO(tocarip) revisit once we have register-based abi/mid-stack inlining.const countCutOff = 8func ( *byteStringReplacer) ( string) string { := len() := false// Is it faster to use Count?iflen(.toReplace)*countCutOff <= len() {for , := range .toReplace {if := Count(, ); != 0 {// The -1 is because we are replacing 1 byte with len(replacements[b]) bytes. += * (len(.replacements[[0]]) - 1) = true } } } else {for := 0; < len(); ++ { := []if .replacements[] != nil {// See above for explanation of -1 += len(.replacements[]) - 1 = true } } }if ! {return } := make([]byte, ) := 0for := 0; < len(); ++ { := []if .replacements[] != nil { += copy([:], .replacements[]) } else { [] = ++ } }returnstring()}func ( *byteStringReplacer) ( io.Writer, string) ( int, error) { := getStringWriter() := 0for := 0; < len(); ++ { := []if .replacements[] == nil {continue }if != { , := .WriteString([:]) += if != nil {return , } } = + 1 , := .Write(.replacements[]) += if != nil {return , } }if != len() {varint , = .WriteString([:]) += }return}
The pages are generated with Goldsv0.7.3-preview. (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu.
PR and bug reports are welcome and can be submitted to the issue list.
Please follow @zigo_101 (reachable from the left QR code) to get the latest news of Golds.