Team Ai
Datasetpublic

codekingpro/portable-devtools

sourceHugging Faceupdated 5mo agoView on Hugging Face
1likes14kdownloads
replace.go579 linesDownload Raw Back to strings
1// Copyright 2011 The Go Authors. All rights reserved.2// Use of this source code is governed by a BSD-style3// license that can be found in the LICENSE file.4 5package strings6 7import (8	"io"9	"sync"10)11 12// Replacer replaces a list of strings with replacements.13// It is safe for concurrent use by multiple goroutines.14type Replacer struct {15	once   sync.Once // guards buildOnce method16	r      replacer17	oldnew []string18}19 20// replacer is the interface that a replacement algorithm needs to implement.21type replacer interface {22	Replace(s string) string23	WriteString(w io.Writer, s string) (n int, err error)24}25 26// NewReplacer returns a new [Replacer] from a list of old, new string27// pairs. Replacements are performed in the order they appear in the28// target string, without overlapping matches. The old string29// comparisons are done in argument order.30//31// NewReplacer panics if given an odd number of arguments.32func NewReplacer(oldnew ...string) *Replacer {33	if len(oldnew)%2 == 1 {34		panic("strings.NewReplacer: odd argument count")35	}36	return &Replacer{oldnew: append([]string(nil), oldnew...)}37}38 39func (r *Replacer) buildOnce() {40	r.r = r.build()41	r.oldnew = nil42}43 44func (b *Replacer) build() replacer {45	oldnew := b.oldnew46	if len(oldnew) == 2 && len(oldnew[0]) > 1 {47		return makeSingleStringReplacer(oldnew[0], oldnew[1])48	}49 50	allNewBytes := true51	for i := 0; i < len(oldnew); i += 2 {52		if len(oldnew[i]) != 1 {53			return makeGenericReplacer(oldnew)54		}55		if len(oldnew[i+1]) != 1 {56			allNewBytes = false57		}58	}59 60	if allNewBytes {61		r := byteReplacer{}62		for i := range r {63			r[i] = byte(i)64		}65		// The first occurrence of old->new map takes precedence66		// over the others with the same old string.67		for i := len(oldnew) - 2; i >= 0; i -= 2 {68			o := oldnew[i][0]69			n := oldnew[i+1][0]70			r[o] = n71		}72		return &r73	}74 75	r := byteStringReplacer{toReplace: make([]string, 0, len(oldnew)/2)}76	// The first occurrence of old->new map takes precedence77	// over the others with the same old string.78	for i := len(oldnew) - 2; i >= 0; i -= 2 {79		o := oldnew[i][0]80		n := oldnew[i+1]81		// To avoid counting repetitions multiple times.82		if r.replacements[o] == nil {83			// We need to use string([]byte{o}) instead of string(o),84			// to avoid utf8 encoding of o.85			// E. g. byte(150) produces string of length 2.86			r.toReplace = append(r.toReplace, string([]byte{o}))87		}88		r.replacements[o] = []byte(n)89 90	}91	return &r92}93 94// Replace returns a copy of s with all replacements performed.95func (r *Replacer) Replace(s string) string {96	r.once.Do(r.buildOnce)97	return r.r.Replace(s)98}99 100// WriteString writes s to w with all replacements performed.101func (r *Replacer) WriteString(w io.Writer, s string) (n int, err error) {102	r.once.Do(r.buildOnce)103	return r.r.WriteString(w, s)104}105 106// trieNode is a node in a lookup trie for prioritized key/value pairs. Keys107// and values may be empty. For example, the trie containing keys "ax", "ay",108// "bcbc", "x" and "xy" could have eight nodes:109//110//	n0  -111//	n1  a-112//	n2  .x+113//	n3  .y+114//	n4  b-115//	n5  .cbc+116//	n6  x+117//	n7  .y+118//119// n0 is the root node, and its children are n1, n4 and n6; n1's children are120// n2 and n3; n4's child is n5; n6's child is n7. Nodes n0, n1 and n4 (marked121// with a trailing "-") are partial keys, and nodes n2, n3, n5, n6 and n7122// (marked with a trailing "+") are complete keys.123type trieNode struct {124	// value is the value of the trie node's key/value pair. It is empty if125	// this node is not a complete key.126	value string127	// priority is the priority (higher is more important) of the trie node's128	// key/value pair; keys are not necessarily matched shortest- or longest-129	// first. Priority is positive if this node is a complete key, and zero130	// otherwise. In the example above, positive/zero priorities are marked131	// with a trailing "+" or "-".132	priority int133 134	// A trie node may have zero, one or more child nodes:135	//  * if the remaining fields are zero, there are no children.136	//  * if prefix and next are non-zero, there is one child in next.137	//  * if table is non-zero, it defines all the children.138	//139	// Prefixes are preferred over tables when there is one child, but the140	// root node always uses a table for lookup efficiency.141 142	// prefix is the difference in keys between this trie node and the next.143	// In the example above, node n4 has prefix "cbc" and n4's next node is n5.144	// Node n5 has no children and so has zero prefix, next and table fields.145	prefix string146	next   *trieNode147 148	// table is a lookup table indexed by the next byte in the key, after149	// remapping that byte through genericReplacer.mapping to create a dense150	// index. In the example above, the keys only use 'a', 'b', 'c', 'x' and151	// 'y', which remap to 0, 1, 2, 3 and 4. All other bytes remap to 5, and152	// genericReplacer.tableSize will be 5. Node n0's table will be153	// []*trieNode{ 0:n1, 1:n4, 3:n6 }, where the 0, 1 and 3 are the remapped154	// 'a', 'b' and 'x'.155	table []*trieNode156}157 158func (t *trieNode) add(key, val string, priority int, r *genericReplacer) {159	if key == "" {160		if t.priority == 0 {161			t.value = val162			t.priority = priority163		}164		return165	}166 167	if t.prefix != "" {168		// Need to split the prefix among multiple nodes.169		var n int // length of the longest common prefix170		for ; n < len(t.prefix) && n < len(key); n++ {171			if t.prefix[n] != key[n] {172				break173			}174		}175		if n == len(t.prefix) {176			t.next.add(key[n:], val, priority, r)177		} else if n == 0 {178			// First byte differs, start a new lookup table here. Looking up179			// what is currently t.prefix[0] will lead to prefixNode, and180			// looking up key[0] will lead to keyNode.181			var prefixNode *trieNode182			if len(t.prefix) == 1 {183				prefixNode = t.next184			} else {185				prefixNode = &trieNode{186					prefix: t.prefix[1:],187					next:   t.next,188				}189			}190			keyNode := new(trieNode)191			t.table = make([]*trieNode, r.tableSize)192			t.table[r.mapping[t.prefix[0]]] = prefixNode193			t.table[r.mapping[key[0]]] = keyNode194			t.prefix = ""195			t.next = nil196			keyNode.add(key[1:], val, priority, r)197		} else {198			// Insert new node after the common section of the prefix.199			next := &trieNode{200				prefix: t.prefix[n:],201				next:   t.next,202			}203			t.prefix = t.prefix[:n]204			t.next = next205			next.add(key[n:], val, priority, r)206		}207	} else if t.table != nil {208		// Insert into existing table.209		m := r.mapping[key[0]]210		if t.table[m] == nil {211			t.table[m] = new(trieNode)212		}213		t.table[m].add(key[1:], val, priority, r)214	} else {215		t.prefix = key216		t.next = new(trieNode)217		t.next.add("", val, priority, r)218	}219}220 221func (r *genericReplacer) lookup(s string, ignoreRoot bool) (val string, keylen int, found bool) {222	// Iterate down the trie to the end, and grab the value and keylen with223	// the highest priority.224	bestPriority := 0225	node := &r.root226	n := 0227	for node != nil {228		if node.priority > bestPriority && !(ignoreRoot && node == &r.root) {229			bestPriority = node.priority230			val = node.value231			keylen = n232			found = true233		}234 235		if s == "" {236			break237		}238		if node.table != nil {239			index := r.mapping[s[0]]240			if int(index) == r.tableSize {241				break242			}243			node = node.table[index]244			s = s[1:]245			n++246		} else if node.prefix != "" && HasPrefix(s, node.prefix) {247			n += len(node.prefix)248			s = s[len(node.prefix):]249			node = node.next250		} else {251			break252		}253	}254	return255}256 257// genericReplacer is the fully generic algorithm.258// It's used as a fallback when nothing faster can be used.259type genericReplacer struct {260	root trieNode261	// tableSize is the size of a trie node's lookup table. It is the number262	// of unique key bytes.263	tableSize int264	// mapping maps from key bytes to a dense index for trieNode.table.265	mapping [256]byte266}267 268func makeGenericReplacer(oldnew []string) *genericReplacer {269	r := new(genericReplacer)270	// Find each byte used, then assign them each an index.271	for i := 0; i < len(oldnew); i += 2 {272		key := oldnew[i]273		for j := 0; j < len(key); j++ {274			r.mapping[key[j]] = 1275		}276	}277 278	for _, b := range r.mapping {279		r.tableSize += int(b)280	}281 282	var index byte283	for i, b := range r.mapping {284		if b == 0 {285			r.mapping[i] = byte(r.tableSize)286		} else {287			r.mapping[i] = index288			index++289		}290	}291	// Ensure root node uses a lookup table (for performance).292	r.root.table = make([]*trieNode, r.tableSize)293 294	for i := 0; i < len(oldnew); i += 2 {295		r.root.add(oldnew[i], oldnew[i+1], len(oldnew)-i, r)296	}297	return r298}299 300type appendSliceWriter []byte301 302// Write writes to the buffer to satisfy [io.Writer].303func (w *appendSliceWriter) Write(p []byte) (int, error) {304	*w = append(*w, p...)305	return len(p), nil306}307 308// WriteString writes to the buffer without string->[]byte->string allocations.309func (w *appendSliceWriter) WriteString(s string) (int, error) {310	*w = append(*w, s...)311	return len(s), nil312}313 314type stringWriter struct {315	w io.Writer316}317 318func (w stringWriter) WriteString(s string) (int, error) {319	return w.w.Write([]byte(s))320}321 322func getStringWriter(w io.Writer) io.StringWriter {323	sw, ok := w.(io.StringWriter)324	if !ok {325		sw = stringWriter{w}326	}327	return sw328}329 330func (r *genericReplacer) Replace(s string) string {331	buf := make(appendSliceWriter, 0, len(s))332	r.WriteString(&buf, s)333	return string(buf)334}335 336func (r *genericReplacer) WriteString(w io.Writer, s string) (n int, err error) {337	sw := getStringWriter(w)338	var last, wn int339	var prevMatchEmpty bool340	for i := 0; i <= len(s); {341		// Fast path: s[i] is not a prefix of any pattern.342		if i != len(s) && r.root.priority == 0 {343			index := int(r.mapping[s[i]])344			if index == r.tableSize || r.root.table[index] == nil {345				i++346				continue347			}348		}349 350		// Ignore the empty match iff the previous loop found the empty match.351		val, keylen, match := r.lookup(s[i:], prevMatchEmpty)352		prevMatchEmpty = match && keylen == 0353		if match {354			wn, err = sw.WriteString(s[last:i])355			n += wn356			if err != nil {357				return358			}359			wn, err = sw.WriteString(val)360			n += wn361			if err != nil {362				return363			}364			i += keylen365			last = i366			continue367		}368		i++369	}370	if last != len(s) {371		wn, err = sw.WriteString(s[last:])372		n += wn373	}374	return375}376 377// singleStringReplacer is the implementation that's used when there is only378// one string to replace (and that string has more than one byte).379type singleStringReplacer struct {380	finder *stringFinder381	// value is the new string that replaces that pattern when it's found.382	value string383}384 385func makeSingleStringReplacer(pattern string, value string) *singleStringReplacer {386	return &singleStringReplacer{finder: makeStringFinder(pattern), value: value}387}388 389func (r *singleStringReplacer) Replace(s string) string {390	var buf Builder391	i, matched := 0, false392	for {393		match := r.finder.next(s[i:])394		if match == -1 {395			break396		}397		matched = true398		buf.Grow(match + len(r.value))399		buf.WriteString(s[i : i+match])400		buf.WriteString(r.value)401		i += match + len(r.finder.pattern)402	}403	if !matched {404		return s405	}406	buf.WriteString(s[i:])407	return buf.String()408}409 410func (r *singleStringReplacer) WriteString(w io.Writer, s string) (n int, err error) {411	sw := getStringWriter(w)412	var i, wn int413	for {414		match := r.finder.next(s[i:])415		if match == -1 {416			break417		}418		wn, err = sw.WriteString(s[i : i+match])419		n += wn420		if err != nil {421			return422		}423		wn, err = sw.WriteString(r.value)424		n += wn425		if err != nil {426			return427		}428		i += match + len(r.finder.pattern)429	}430	wn, err = sw.WriteString(s[i:])431	n += wn432	return433}434 435// byteReplacer is the implementation that's used when all the "old"436// and "new" values are single ASCII bytes.437// The array contains replacement bytes indexed by old byte.438type byteReplacer [256]byte439 440func (r *byteReplacer) Replace(s string) string {441	var buf []byte // lazily allocated442	for i := 0; i < len(s); i++ {443		b := s[i]444		if r[b] != b {445			if buf == nil {446				buf = []byte(s)447			}448			buf[i] = r[b]449		}450	}451	if buf == nil {452		return s453	}454	return string(buf)455}456 457func (r *byteReplacer) WriteString(w io.Writer, s string) (n int, err error) {458	sw := getStringWriter(w)459	last := 0460	for i := 0; i < len(s); i++ {461		b := s[i]462		if r[b] == b {463			continue464		}465		if last != i {466			wn, err := sw.WriteString(s[last:i])467			n += wn468			if err != nil {469				return n, err470			}471		}472		last = i + 1473		nw, err := w.Write(r[b : int(b)+1])474		n += nw475		if err != nil {476			return n, err477		}478	}479	if last != len(s) {480		nw, err := sw.WriteString(s[last:])481		n += nw482		if err != nil {483			return n, err484		}485	}486	return n, nil487}488 489// byteStringReplacer is the implementation that's used when all the490// "old" values are single ASCII bytes but the "new" values vary in size.491type byteStringReplacer struct {492	// replacements contains replacement byte slices indexed by old byte.493	// A nil []byte means that the old byte should not be replaced.494	replacements [256][]byte495	// toReplace keeps a list of bytes to replace. Depending on length of toReplace496	// and length of target string it may be faster to use Count, or a plain loop.497	// We store single byte as a string, because Count takes a string.498	toReplace []string499}500 501// countCutOff controls the ratio of a string length to a number of replacements502// at which (*byteStringReplacer).Replace switches algorithms.503// For strings with higher ration of length to replacements than that value,504// we call Count, for each replacement from toReplace.505// For strings, with a lower ratio we use simple loop, because of Count overhead.506// countCutOff is an empirically determined overhead multiplier.507// TODO(tocarip) revisit once we have register-based abi/mid-stack inlining.508const countCutOff = 8509 510func (r *byteStringReplacer) Replace(s string) string {511	newSize := len(s)512	anyChanges := false513	// Is it faster to use Count?514	if len(r.toReplace)*countCutOff <= len(s) {515		for _, x := range r.toReplace {516			if c := Count(s, x); c != 0 {517				// The -1 is because we are replacing 1 byte with len(replacements[b]) bytes.518				newSize += c * (len(r.replacements[x[0]]) - 1)519				anyChanges = true520			}521 522		}523	} else {524		for i := 0; i < len(s); i++ {525			b := s[i]526			if r.replacements[b] != nil {527				// See above for explanation of -1528				newSize += len(r.replacements[b]) - 1529				anyChanges = true530			}531		}532	}533	if !anyChanges {534		return s535	}536	buf := make([]byte, newSize)537	j := 0538	for i := 0; i < len(s); i++ {539		b := s[i]540		if r.replacements[b] != nil {541			j += copy(buf[j:], r.replacements[b])542		} else {543			buf[j] = b544			j++545		}546	}547	return string(buf)548}549 550func (r *byteStringReplacer) WriteString(w io.Writer, s string) (n int, err error) {551	sw := getStringWriter(w)552	last := 0553	for i := 0; i < len(s); i++ {554		b := s[i]555		if r.replacements[b] == nil {556			continue557		}558		if last != i {559			nw, err := sw.WriteString(s[last:i])560			n += nw561			if err != nil {562				return n, err563			}564		}565		last = i + 1566		nw, err := w.Write(r.replacements[b])567		n += nw568		if err != nil {569			return n, err570		}571	}572	if last != len(s) {573		var nw int574		nw, err = sw.WriteString(s[last:])575		n += nw576	}577	return578}579 
codekingpro/portable-devtools · Team Ai