|
|
@@ -9,10 +9,104 @@ package fasttemplate
|
|
|
import (
|
|
|
"bytes"
|
|
|
"fmt"
|
|
|
+ "github.com/valyala/bytebufferpool"
|
|
|
"io"
|
|
|
- "sync"
|
|
|
)
|
|
|
|
|
|
+// 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 {
|
|
|
+ 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 {
|
|
|
@@ -20,9 +114,9 @@ type Template struct {
|
|
|
startTag string
|
|
|
endTag string
|
|
|
|
|
|
- texts [][]byte
|
|
|
- tags []string
|
|
|
- bytesBufferPool sync.Pool
|
|
|
+ texts [][]byte
|
|
|
+ tags []string
|
|
|
+ byteBufferPool bytebufferpool.Pool
|
|
|
}
|
|
|
|
|
|
// New parses the given template using the given startTag and endTag
|
|
|
@@ -55,10 +149,6 @@ func NewTemplate(template, startTag, endTag string) (*Template, error) {
|
|
|
return &t, nil
|
|
|
}
|
|
|
|
|
|
-func newBytesBuffer() interface{} {
|
|
|
- return &bytes.Buffer{}
|
|
|
-}
|
|
|
-
|
|
|
// TagFunc can be used as a substitution value in the map passed to Execute*.
|
|
|
// Execute* functions pass tag (placeholder) name in 'tag' argument.
|
|
|
//
|
|
|
@@ -76,7 +166,6 @@ type TagFunc func(w io.Writer, tag string) (int, error)
|
|
|
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.bytesBufferPool.New = newBytesBuffer
|
|
|
t.template = template
|
|
|
t.startTag = startTag
|
|
|
t.endTag = endTag
|
|
|
@@ -130,6 +219,9 @@ func (t *Template) Reset(template, startTag, endTag string) error {
|
|
|
// 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
|
|
|
|
|
|
@@ -141,22 +233,20 @@ func (t *Template) ExecuteFunc(w io.Writer, f TagFunc) (int64, error) {
|
|
|
|
|
|
for i := 0; i < n; i++ {
|
|
|
ni, err := w.Write(t.texts[i])
|
|
|
+ nn += int64(ni)
|
|
|
if err != nil {
|
|
|
return nn, err
|
|
|
}
|
|
|
- nn += int64(ni)
|
|
|
|
|
|
- if ni, err = f(w, t.tags[i]); err != nil {
|
|
|
+ ni, err = f(w, t.tags[i])
|
|
|
+ nn += int64(ni)
|
|
|
+ if err != nil {
|
|
|
return nn, err
|
|
|
}
|
|
|
- nn += int64(ni)
|
|
|
}
|
|
|
ni, err := w.Write(t.texts[n])
|
|
|
- if err != nil {
|
|
|
- return nn, err
|
|
|
- }
|
|
|
nn += int64(ni)
|
|
|
- return nn, nil
|
|
|
+ return nn, err
|
|
|
}
|
|
|
|
|
|
// Execute substitutes template tags (placeholders) with the corresponding
|
|
|
@@ -172,18 +262,21 @@ 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 call f on each template tag (placeholder) occurrence
|
|
|
+// 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 {
|
|
|
- w := t.bytesBufferPool.Get().(*bytes.Buffer)
|
|
|
- if _, err := t.ExecuteFunc(w, f); err != nil {
|
|
|
+ bb := t.byteBufferPool.Get()
|
|
|
+ if _, err := t.ExecuteFunc(bb, f); err != nil {
|
|
|
panic(fmt.Sprintf("unexpected error: %s", err))
|
|
|
}
|
|
|
- s := string(w.Bytes())
|
|
|
- w.Reset()
|
|
|
- t.bytesBufferPool.Put(w)
|
|
|
+ s := string(bb.Bytes())
|
|
|
+ bb.Reset()
|
|
|
+ t.byteBufferPool.Put(bb)
|
|
|
return s
|
|
|
}
|
|
|
|
|
|
@@ -195,6 +288,8 @@ func (t *Template) ExecuteFuncString(f TagFunc) string {
|
|
|
// * 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) })
|
|
|
}
|