| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317 | // Package fasttemplate implements simple and fast template library.//// Fasttemplate is faster than text/template, strings.Replace// and strings.Replacer.//// Fasttemplate ideally fits for fast and simple placeholders' substitutions.package fasttemplateimport (	"bytes"	"fmt"	"github.com/valyala/bytebufferpool"	"io")// ExecuteFunc calls f on each template tag (placeholder) occurrence.//// Returns the number of bytes written to w.//// This function is optimized for constantly changing templates.// Use Template.ExecuteFunc for frozen templates.func ExecuteFunc(template, startTag, endTag string, w io.Writer, f TagFunc) (int64, error) {	s := unsafeString2Bytes(template)	a := unsafeString2Bytes(startTag)	b := unsafeString2Bytes(endTag)	var nn int64	var ni int	var err error	for {		n := bytes.Index(s, a)		if n < 0 {			break		}		ni, err = w.Write(s[:n])		nn += int64(ni)		if err != nil {			return nn, err		}		s = s[n+len(a):]		n = bytes.Index(s, b)		if n < 0 {			// cannot find end tag - just write it to the output.			ni, _ = w.Write(a)			nn += int64(ni)			break		}		ni, err = f(w, unsafeBytes2String(s[:n]))		nn += int64(ni)		s = s[n+len(b):]	}	ni, err = w.Write(s)	nn += int64(ni)	return nn, err}// Execute substitutes template tags (placeholders) with the corresponding// values from the map m and writes the result to the given writer w.//// Substitution map m may contain values with the following types://   * []byte - the fastest value type//   * string - convenient value type//   * TagFunc - flexible value type//// Returns the number of bytes written to w.//// This function is optimized for constantly changing templates.// Use Template.Execute for frozen templates.func Execute(template, startTag, endTag string, w io.Writer, m map[string]interface{}) (int64, error) {	return ExecuteFunc(template, startTag, endTag, w, func(w io.Writer, tag string) (int, error) { return stdTagFunc(w, tag, m) })}// ExecuteFuncString calls f on each template tag (placeholder) occurrence// and substitutes it with the data written to TagFunc's w.//// Returns the resulting string.//// This function is optimized for constantly changing templates.// Use Template.ExecuteFuncString for frozen templates.func ExecuteFuncString(template, startTag, endTag string, f TagFunc) string {	tagsCount := bytes.Count(unsafeString2Bytes(template), unsafeString2Bytes(startTag))	if tagsCount == 0 {		return template	}	bb := byteBufferPool.Get()	if _, err := ExecuteFunc(template, startTag, endTag, bb, f); err != nil {		panic(fmt.Sprintf("unexpected error: %s", err))	}	s := string(bb.B)	bb.Reset()	byteBufferPool.Put(bb)	return s}var byteBufferPool bytebufferpool.Pool// ExecuteString substitutes template tags (placeholders) with the corresponding// values from the map m and returns the result.//// Substitution map m may contain values with the following types://   * []byte - the fastest value type//   * string - convenient value type//   * TagFunc - flexible value type//// This function is optimized for constantly changing templates.// Use Template.ExecuteString for frozen templates.func ExecuteString(template, startTag, endTag string, m map[string]interface{}) string {	return ExecuteFuncString(template, startTag, endTag, func(w io.Writer, tag string) (int, error) { return stdTagFunc(w, tag, m) })}// Template implements simple template engine, which can be used for fast// tags' (aka placeholders) substitution.type Template struct {	template string	startTag string	endTag   string	texts          [][]byte	tags           []string	byteBufferPool bytebufferpool.Pool}// New parses the given template using the given startTag and endTag// as tag start and tag end.//// The returned template can be executed by concurrently running goroutines// using Execute* methods.//// New panics if the given template cannot be parsed. Use NewTemplate instead// if template may contain errors.func New(template, startTag, endTag string) *Template {	t, err := NewTemplate(template, startTag, endTag)	if err != nil {		panic(err)	}	return t}// NewTemplate parses the given template using the given startTag and endTag// as tag start and tag end.//// The returned template can be executed by concurrently running goroutines// using Execute* methods.func NewTemplate(template, startTag, endTag string) (*Template, error) {	var t Template	err := t.Reset(template, startTag, endTag)	if err != nil {		return nil, err	}	return &t, nil}// TagFunc can be used as a substitution value in the map passed to Execute*.// Execute* functions pass tag (placeholder) name in 'tag' argument.//// TagFunc must be safe to call from concurrently running goroutines.//// TagFunc must write contents to w and return the number of bytes written.type TagFunc func(w io.Writer, tag string) (int, error)// Reset resets the template t to new one defined by// template, startTag and endTag.//// Reset allows Template object re-use.//// Reset may be called only if no other goroutines call t methods at the moment.func (t *Template) Reset(template, startTag, endTag string) error {	// Keep these vars in t, so GC won't collect them and won't break	// vars derived via unsafe*	t.template = template	t.startTag = startTag	t.endTag = endTag	t.texts = t.texts[:0]	t.tags = t.tags[:0]	if len(startTag) == 0 {		panic("startTag cannot be empty")	}	if len(endTag) == 0 {		panic("endTag cannot be empty")	}	s := unsafeString2Bytes(template)	a := unsafeString2Bytes(startTag)	b := unsafeString2Bytes(endTag)	tagsCount := bytes.Count(s, a)	if tagsCount == 0 {		return nil	}	if tagsCount+1 > cap(t.texts) {		t.texts = make([][]byte, 0, tagsCount+1)	}	if tagsCount > cap(t.tags) {		t.tags = make([]string, 0, tagsCount)	}	for {		n := bytes.Index(s, a)		if n < 0 {			t.texts = append(t.texts, s)			break		}		t.texts = append(t.texts, s[:n])		s = s[n+len(a):]		n = bytes.Index(s, b)		if n < 0 {			return fmt.Errorf("Cannot find end tag=%q in the template=%q starting from %q", endTag, template, s)		}		t.tags = append(t.tags, unsafeBytes2String(s[:n]))		s = s[n+len(b):]	}	return nil}// ExecuteFunc calls f on each template tag (placeholder) occurrence.//// Returns the number of bytes written to w.//// This function is optimized for frozen templates.// Use ExecuteFunc for constantly changing templates.func (t *Template) ExecuteFunc(w io.Writer, f TagFunc) (int64, error) {	var nn int64	n := len(t.texts) - 1	if n == -1 {		ni, err := w.Write(unsafeString2Bytes(t.template))		return int64(ni), err	}	for i := 0; i < n; i++ {		ni, err := w.Write(t.texts[i])		nn += int64(ni)		if err != nil {			return nn, err		}		ni, err = f(w, t.tags[i])		nn += int64(ni)		if err != nil {			return nn, err		}	}	ni, err := w.Write(t.texts[n])	nn += int64(ni)	return nn, err}// Execute substitutes template tags (placeholders) with the corresponding// values from the map m and writes the result to the given writer w.//// Substitution map m may contain values with the following types://   * []byte - the fastest value type//   * string - convenient value type//   * TagFunc - flexible value type//// Returns the number of bytes written to w.func (t *Template) Execute(w io.Writer, m map[string]interface{}) (int64, error) {	return t.ExecuteFunc(w, func(w io.Writer, tag string) (int, error) { return stdTagFunc(w, tag, m) })}// ExecuteFuncString calls f on each template tag (placeholder) occurrence// and substitutes it with the data written to TagFunc's w.//// Returns the resulting string.//// This function is optimized for frozen templates.// Use ExecuteFuncString for constantly changing templates.func (t *Template) ExecuteFuncString(f TagFunc) string {	bb := t.byteBufferPool.Get()	if _, err := t.ExecuteFunc(bb, f); err != nil {		panic(fmt.Sprintf("unexpected error: %s", err))	}	s := string(bb.Bytes())	bb.Reset()	t.byteBufferPool.Put(bb)	return s}// ExecuteString substitutes template tags (placeholders) with the corresponding// values from the map m and returns the result.//// Substitution map m may contain values with the following types://   * []byte - the fastest value type//   * string - convenient value type//   * TagFunc - flexible value type//// This function is optimized for frozen templates.// Use ExecuteString for constantly changing templates.func (t *Template) ExecuteString(m map[string]interface{}) string {	return t.ExecuteFuncString(func(w io.Writer, tag string) (int, error) { return stdTagFunc(w, tag, m) })}func stdTagFunc(w io.Writer, tag string, m map[string]interface{}) (int, error) {	v := m[tag]	if v == nil {		return 0, nil	}	switch value := v.(type) {	case []byte:		return w.Write(value)	case string:		return w.Write([]byte(value))	case TagFunc:		return value(w, tag)	default:		panic(fmt.Sprintf("tag=%q contains unexpected value type=%#v. Expected []byte, string or TagFunc", tag, v))	}}
 |