template.go 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217
  1. // Package fasttemplate implements simple and fast template library.
  2. //
  3. // Fasttemplate is faster than text/template, strings.Replace
  4. // and strings.Replacer.
  5. //
  6. // Fasttemplate ideally fits for fast and simple placeholders' substitutions.
  7. package fasttemplate
  8. import (
  9. "bytes"
  10. "fmt"
  11. "io"
  12. "sync"
  13. )
  14. // Template implements simple template engine, which can be used for fast
  15. // tags' (aka placeholders) substitution.
  16. type Template struct {
  17. template string
  18. startTag string
  19. endTag string
  20. texts [][]byte
  21. tags []string
  22. bytesBufferPool sync.Pool
  23. }
  24. // New parses the given template using the given startTag and endTag
  25. // as tag start and tag end.
  26. //
  27. // The returned template can be executed by concurrently running goroutines
  28. // using Execute* methods.
  29. //
  30. // New panics if the given template cannot be parsed. Use NewTemplate instead
  31. // if template may contain errors.
  32. func New(template, startTag, endTag string) *Template {
  33. t, err := NewTemplate(template, startTag, endTag)
  34. if err != nil {
  35. panic(err)
  36. }
  37. return t
  38. }
  39. // NewTemplate parses the given template using the given startTag and endTag
  40. // as tag start and tag end.
  41. //
  42. // The returned template can be executed by concurrently running goroutines
  43. // using Execute* methods.
  44. func NewTemplate(template, startTag, endTag string) (*Template, error) {
  45. var t Template
  46. err := t.Reset(template, startTag, endTag)
  47. if err != nil {
  48. return nil, err
  49. }
  50. return &t, nil
  51. }
  52. func newBytesBuffer() interface{} {
  53. return &bytes.Buffer{}
  54. }
  55. // TagFunc can be used as a substitution value in the map passed to Execute*.
  56. // Execute* functions pass tag (placeholder) name in 'tag' argument.
  57. //
  58. // TagFunc must be safe to call from concurrently running goroutines.
  59. //
  60. // TagFunc must write contents to w and return the number of bytes written.
  61. type TagFunc func(w io.Writer, tag string) (int, error)
  62. // Reset resets the template t to new one defined by
  63. // template, startTag and endTag.
  64. //
  65. // Reset allows Template object re-use.
  66. //
  67. // Reset may be called only if no other goroutines call t methods at the moment.
  68. func (t *Template) Reset(template, startTag, endTag string) error {
  69. // Keep these vars in t, so GC won't collect them and won't break
  70. // vars derived via unsafe*
  71. t.bytesBufferPool.New = newBytesBuffer
  72. t.template = template
  73. t.startTag = startTag
  74. t.endTag = endTag
  75. t.texts = t.texts[:0]
  76. t.tags = t.tags[:0]
  77. if len(startTag) == 0 {
  78. panic("startTag cannot be empty")
  79. }
  80. if len(endTag) == 0 {
  81. panic("endTag cannot be empty")
  82. }
  83. s := unsafeString2Bytes(template)
  84. a := unsafeString2Bytes(startTag)
  85. b := unsafeString2Bytes(endTag)
  86. tagsCount := bytes.Count(s, a)
  87. if tagsCount == 0 {
  88. return nil
  89. }
  90. if tagsCount+1 > cap(t.texts) {
  91. t.texts = make([][]byte, 0, tagsCount+1)
  92. }
  93. if tagsCount > cap(t.tags) {
  94. t.tags = make([]string, 0, tagsCount)
  95. }
  96. for {
  97. n := bytes.Index(s, a)
  98. if n < 0 {
  99. t.texts = append(t.texts, s)
  100. break
  101. }
  102. t.texts = append(t.texts, s[:n])
  103. s = s[n+len(a):]
  104. n = bytes.Index(s, b)
  105. if n < 0 {
  106. return fmt.Errorf("Cannot find end tag=%q in the template=%q starting from %q", endTag, template, s)
  107. }
  108. t.tags = append(t.tags, unsafeBytes2String(s[:n]))
  109. s = s[n+len(b):]
  110. }
  111. return nil
  112. }
  113. // ExecuteFunc calls f on each template tag (placeholder) occurrence.
  114. //
  115. // Returns the number of bytes written to w.
  116. func (t *Template) ExecuteFunc(w io.Writer, f TagFunc) (int64, error) {
  117. var nn int64
  118. n := len(t.texts) - 1
  119. if n == -1 {
  120. ni, err := w.Write(unsafeString2Bytes(t.template))
  121. return int64(ni), err
  122. }
  123. for i := 0; i < n; i++ {
  124. ni, err := w.Write(t.texts[i])
  125. if err != nil {
  126. return nn, err
  127. }
  128. nn += int64(ni)
  129. if ni, err = f(w, t.tags[i]); err != nil {
  130. return nn, err
  131. }
  132. nn += int64(ni)
  133. }
  134. ni, err := w.Write(t.texts[n])
  135. if err != nil {
  136. return nn, err
  137. }
  138. nn += int64(ni)
  139. return nn, nil
  140. }
  141. // Execute substitutes template tags (placeholders) with the corresponding
  142. // values from the map m and writes the result to the given writer w.
  143. //
  144. // Substitution map m may contain values with the following types:
  145. // * []byte - the fastest value type
  146. // * string - convenient value type
  147. // * TagFunc - flexible value type
  148. //
  149. // Returns the number of bytes written to w.
  150. func (t *Template) Execute(w io.Writer, m map[string]interface{}) (int64, error) {
  151. return t.ExecuteFunc(w, func(w io.Writer, tag string) (int, error) { return stdTagFunc(w, tag, m) })
  152. }
  153. // ExecuteFuncString call f on each template tag (placeholder) occurrence
  154. // and substitutes it with the data written to TagFunc's w.
  155. //
  156. // Returns the resulting string.
  157. func (t *Template) ExecuteFuncString(f TagFunc) string {
  158. w := t.bytesBufferPool.Get().(*bytes.Buffer)
  159. if _, err := t.ExecuteFunc(w, f); err != nil {
  160. panic(fmt.Sprintf("unexpected error: %s", err))
  161. }
  162. s := string(w.Bytes())
  163. w.Reset()
  164. t.bytesBufferPool.Put(w)
  165. return s
  166. }
  167. // ExecuteString substitutes template tags (placeholders) with the corresponding
  168. // values from the map m and returns the result.
  169. //
  170. // Substitution map m may contain values with the following types:
  171. // * []byte - the fastest value type
  172. // * string - convenient value type
  173. // * TagFunc - flexible value type
  174. //
  175. func (t *Template) ExecuteString(m map[string]interface{}) string {
  176. return t.ExecuteFuncString(func(w io.Writer, tag string) (int, error) { return stdTagFunc(w, tag, m) })
  177. }
  178. func stdTagFunc(w io.Writer, tag string, m map[string]interface{}) (int, error) {
  179. v := m[tag]
  180. if v == nil {
  181. return 0, nil
  182. }
  183. switch value := v.(type) {
  184. case []byte:
  185. return w.Write(value)
  186. case string:
  187. return w.Write([]byte(value))
  188. case TagFunc:
  189. return value(w, tag)
  190. default:
  191. panic(fmt.Sprintf("tag=%q contains unexpected value type=%#v. Expected []byte, string or TagFunc", tag, v))
  192. }
  193. }