| | |
| | |
| | |
| |
|
| | package strings |
| |
|
| | import ( |
| | "io" |
| | "sync" |
| | ) |
| |
|
| | |
| | |
| | type Replacer struct { |
| | once sync.Once |
| | r replacer |
| | oldnew []string |
| | } |
| |
|
| | |
| | type replacer interface { |
| | Replace(s string) string |
| | WriteString(w io.Writer, s string) (n int, err error) |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | 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 makeSingleStringReplacer(oldnew[0], oldnew[1]) |
| | } |
| |
|
| | allNewBytes := true |
| | for i := 0; i < len(oldnew); i += 2 { |
| | if len(oldnew[i]) != 1 { |
| | return makeGenericReplacer(oldnew) |
| | } |
| | if len(oldnew[i+1]) != 1 { |
| | allNewBytes = false |
| | } |
| | } |
| |
|
| | if allNewBytes { |
| | r := byteReplacer{} |
| | for i := range r { |
| | r[i] = byte(i) |
| | } |
| | |
| | |
| | for i := len(oldnew) - 2; i >= 0; i -= 2 { |
| | o := oldnew[i][0] |
| | n := oldnew[i+1][0] |
| | r[o] = n |
| | } |
| | return &r |
| | } |
| |
|
| | r := byteStringReplacer{toReplace: make([]string, 0, len(oldnew)/2)} |
| | |
| | |
| | for i := len(oldnew) - 2; i >= 0; i -= 2 { |
| | o := oldnew[i][0] |
| | n := oldnew[i+1] |
| | |
| | if r.replacements[o] == nil { |
| | |
| | |
| | |
| | r.toReplace = append(r.toReplace, string([]byte{o})) |
| | } |
| | r.replacements[o] = []byte(n) |
| |
|
| | } |
| | return &r |
| | } |
| |
|
| | |
| | func (r *Replacer) Replace(s string) string { |
| | r.once.Do(r.buildOnce) |
| | return r.r.Replace(s) |
| | } |
| |
|
| | |
| | func (r *Replacer) WriteString(w io.Writer, s string) (n int, err error) { |
| | r.once.Do(r.buildOnce) |
| | return r.r.WriteString(w, s) |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | type trieNode struct { |
| | |
| | |
| | value string |
| | |
| | |
| | |
| | |
| | |
| | priority int |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| |
|
| | |
| | |
| | |
| | prefix string |
| | next *trieNode |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | table []*trieNode |
| | } |
| |
|
| | func (t *trieNode) add(key, val string, priority int, r *genericReplacer) { |
| | if key == "" { |
| | if t.priority == 0 { |
| | t.value = val |
| | t.priority = priority |
| | } |
| | return |
| | } |
| |
|
| | if t.prefix != "" { |
| | |
| | var n int |
| | for ; n < len(t.prefix) && n < len(key); n++ { |
| | if t.prefix[n] != key[n] { |
| | break |
| | } |
| | } |
| | if n == len(t.prefix) { |
| | t.next.add(key[n:], val, priority, r) |
| | } else if n == 0 { |
| | |
| | |
| | |
| | var prefixNode *trieNode |
| | if len(t.prefix) == 1 { |
| | prefixNode = t.next |
| | } else { |
| | prefixNode = &trieNode{ |
| | prefix: t.prefix[1:], |
| | next: t.next, |
| | } |
| | } |
| | keyNode := new(trieNode) |
| | t.table = make([]*trieNode, r.tableSize) |
| | t.table[r.mapping[t.prefix[0]]] = prefixNode |
| | t.table[r.mapping[key[0]]] = keyNode |
| | t.prefix = "" |
| | t.next = nil |
| | keyNode.add(key[1:], val, priority, r) |
| | } else { |
| | |
| | next := &trieNode{ |
| | prefix: t.prefix[n:], |
| | next: t.next, |
| | } |
| | t.prefix = t.prefix[:n] |
| | t.next = next |
| | next.add(key[n:], val, priority, r) |
| | } |
| | } else if t.table != nil { |
| | |
| | m := r.mapping[key[0]] |
| | if t.table[m] == nil { |
| | t.table[m] = new(trieNode) |
| | } |
| | t.table[m].add(key[1:], val, priority, r) |
| | } else { |
| | t.prefix = key |
| | t.next = new(trieNode) |
| | t.next.add("", val, priority, r) |
| | } |
| | } |
| |
|
| | func (r *genericReplacer) lookup(s string, ignoreRoot bool) (val string, keylen int, found bool) { |
| | |
| | |
| | bestPriority := 0 |
| | node := &r.root |
| | n := 0 |
| | for node != nil { |
| | if node.priority > bestPriority && !(ignoreRoot && node == &r.root) { |
| | bestPriority = node.priority |
| | val = node.value |
| | keylen = n |
| | found = true |
| | } |
| |
|
| | if s == "" { |
| | break |
| | } |
| | if node.table != nil { |
| | index := r.mapping[s[0]] |
| | if int(index) == r.tableSize { |
| | break |
| | } |
| | node = node.table[index] |
| | s = s[1:] |
| | n++ |
| | } else if node.prefix != "" && HasPrefix(s, node.prefix) { |
| | n += len(node.prefix) |
| | s = s[len(node.prefix):] |
| | node = node.next |
| | } else { |
| | break |
| | } |
| | } |
| | return |
| | } |
| |
|
| | |
| | |
| | type genericReplacer struct { |
| | root trieNode |
| | |
| | |
| | tableSize int |
| | |
| | mapping [256]byte |
| | } |
| |
|
| | func makeGenericReplacer(oldnew []string) *genericReplacer { |
| | r := new(genericReplacer) |
| | |
| | for i := 0; i < len(oldnew); i += 2 { |
| | key := oldnew[i] |
| | for j := 0; j < len(key); j++ { |
| | r.mapping[key[j]] = 1 |
| | } |
| | } |
| |
|
| | for _, b := range r.mapping { |
| | r.tableSize += int(b) |
| | } |
| |
|
| | var index byte |
| | for i, b := range r.mapping { |
| | if b == 0 { |
| | r.mapping[i] = byte(r.tableSize) |
| | } else { |
| | r.mapping[i] = index |
| | index++ |
| | } |
| | } |
| | |
| | r.root.table = make([]*trieNode, r.tableSize) |
| |
|
| | for i := 0; i < len(oldnew); i += 2 { |
| | r.root.add(oldnew[i], oldnew[i+1], len(oldnew)-i, r) |
| | } |
| | return r |
| | } |
| |
|
| | type appendSliceWriter []byte |
| |
|
| | |
| | func (w *appendSliceWriter) Write(p []byte) (int, error) { |
| | *w = append(*w, p...) |
| | return len(p), nil |
| | } |
| |
|
| | |
| | func (w *appendSliceWriter) WriteString(s string) (int, error) { |
| | *w = append(*w, s...) |
| | return len(s), nil |
| | } |
| |
|
| | type stringWriter struct { |
| | w io.Writer |
| | } |
| |
|
| | func (w stringWriter) WriteString(s string) (int, error) { |
| | return w.w.Write([]byte(s)) |
| | } |
| |
|
| | func getStringWriter(w io.Writer) io.StringWriter { |
| | sw, ok := w.(io.StringWriter) |
| | if !ok { |
| | sw = stringWriter{w} |
| | } |
| | return sw |
| | } |
| |
|
| | func (r *genericReplacer) Replace(s string) string { |
| | buf := make(appendSliceWriter, 0, len(s)) |
| | r.WriteString(&buf, s) |
| | return string(buf) |
| | } |
| |
|
| | func (r *genericReplacer) WriteString(w io.Writer, s string) (n int, err error) { |
| | sw := getStringWriter(w) |
| | var last, wn int |
| | var prevMatchEmpty bool |
| | for i := 0; i <= len(s); { |
| | |
| | if i != len(s) && r.root.priority == 0 { |
| | index := int(r.mapping[s[i]]) |
| | if index == r.tableSize || r.root.table[index] == nil { |
| | i++ |
| | continue |
| | } |
| | } |
| |
|
| | |
| | val, keylen, match := r.lookup(s[i:], prevMatchEmpty) |
| | prevMatchEmpty = match && keylen == 0 |
| | if match { |
| | wn, err = sw.WriteString(s[last:i]) |
| | n += wn |
| | if err != nil { |
| | return |
| | } |
| | wn, err = sw.WriteString(val) |
| | n += wn |
| | if err != nil { |
| | return |
| | } |
| | i += keylen |
| | last = i |
| | continue |
| | } |
| | i++ |
| | } |
| | if last != len(s) { |
| | wn, err = sw.WriteString(s[last:]) |
| | n += wn |
| | } |
| | return |
| | } |
| |
|
| | |
| | |
| | type singleStringReplacer struct { |
| | finder *stringFinder |
| | |
| | value string |
| | } |
| |
|
| | func makeSingleStringReplacer(pattern string, value string) *singleStringReplacer { |
| | return &singleStringReplacer{finder: makeStringFinder(pattern), value: value} |
| | } |
| |
|
| | func (r *singleStringReplacer) Replace(s string) string { |
| | var buf Builder |
| | i, matched := 0, false |
| | for { |
| | match := r.finder.next(s[i:]) |
| | if match == -1 { |
| | break |
| | } |
| | matched = true |
| | buf.Grow(match + len(r.value)) |
| | buf.WriteString(s[i : i+match]) |
| | buf.WriteString(r.value) |
| | i += match + len(r.finder.pattern) |
| | } |
| | if !matched { |
| | return s |
| | } |
| | buf.WriteString(s[i:]) |
| | return buf.String() |
| | } |
| |
|
| | func (r *singleStringReplacer) WriteString(w io.Writer, s string) (n int, err error) { |
| | sw := getStringWriter(w) |
| | var i, wn int |
| | for { |
| | match := r.finder.next(s[i:]) |
| | if match == -1 { |
| | break |
| | } |
| | wn, err = sw.WriteString(s[i : i+match]) |
| | n += wn |
| | if err != nil { |
| | return |
| | } |
| | wn, err = sw.WriteString(r.value) |
| | n += wn |
| | if err != nil { |
| | return |
| | } |
| | i += match + len(r.finder.pattern) |
| | } |
| | wn, err = sw.WriteString(s[i:]) |
| | n += wn |
| | return |
| | } |
| |
|
| | |
| | |
| | |
| | type byteReplacer [256]byte |
| |
|
| | func (r *byteReplacer) Replace(s string) string { |
| | var buf []byte |
| | for i := 0; i < len(s); i++ { |
| | b := s[i] |
| | if r[b] != b { |
| | if buf == nil { |
| | buf = []byte(s) |
| | } |
| | buf[i] = r[b] |
| | } |
| | } |
| | if buf == nil { |
| | return s |
| | } |
| | return string(buf) |
| | } |
| |
|
| | func (r *byteReplacer) WriteString(w io.Writer, s string) (n int, err error) { |
| | sw := getStringWriter(w) |
| | last := 0 |
| | for i := 0; i < len(s); i++ { |
| | b := s[i] |
| | if r[b] == b { |
| | continue |
| | } |
| | if last != i { |
| | wn, err := sw.WriteString(s[last:i]) |
| | n += wn |
| | if err != nil { |
| | return n, err |
| | } |
| | } |
| | last = i + 1 |
| | nw, err := w.Write(r[b : int(b)+1]) |
| | n += nw |
| | if err != nil { |
| | return n, err |
| | } |
| | } |
| | if last != len(s) { |
| | nw, err := sw.WriteString(s[last:]) |
| | n += nw |
| | if err != nil { |
| | return n, err |
| | } |
| | } |
| | return n, nil |
| | } |
| |
|
| | |
| | |
| | type byteStringReplacer struct { |
| | |
| | |
| | replacements [256][]byte |
| | |
| | |
| | |
| | toReplace []string |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | const countCutOff = 8 |
| |
|
| | func (r *byteStringReplacer) Replace(s string) string { |
| | newSize := len(s) |
| | anyChanges := false |
| | |
| | if len(r.toReplace)*countCutOff <= len(s) { |
| | for _, x := range r.toReplace { |
| | if c := Count(s, x); c != 0 { |
| | |
| | newSize += c * (len(r.replacements[x[0]]) - 1) |
| | anyChanges = true |
| | } |
| |
|
| | } |
| | } else { |
| | for i := 0; i < len(s); i++ { |
| | b := s[i] |
| | if r.replacements[b] != nil { |
| | |
| | newSize += len(r.replacements[b]) - 1 |
| | anyChanges = true |
| | } |
| | } |
| | } |
| | if !anyChanges { |
| | return s |
| | } |
| | buf := make([]byte, newSize) |
| | j := 0 |
| | for i := 0; i < len(s); i++ { |
| | b := s[i] |
| | if r.replacements[b] != nil { |
| | j += copy(buf[j:], r.replacements[b]) |
| | } else { |
| | buf[j] = b |
| | j++ |
| | } |
| | } |
| | return string(buf) |
| | } |
| |
|
| | func (r *byteStringReplacer) WriteString(w io.Writer, s string) (n int, err error) { |
| | sw := getStringWriter(w) |
| | last := 0 |
| | for i := 0; i < len(s); i++ { |
| | b := s[i] |
| | if r.replacements[b] == nil { |
| | continue |
| | } |
| | if last != i { |
| | nw, err := sw.WriteString(s[last:i]) |
| | n += nw |
| | if err != nil { |
| | return n, err |
| | } |
| | } |
| | last = i + 1 |
| | nw, err := w.Write(r.replacements[b]) |
| | n += nw |
| | if err != nil { |
| | return n, err |
| | } |
| | } |
| | if last != len(s) { |
| | var nw int |
| | nw, err = sw.WriteString(s[last:]) |
| | n += nw |
| | } |
| | return |
| | } |
| |
|