template.go 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437
  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. "github.com/valyala/bytebufferpool"
  13. )
  14. // ExecuteFunc calls f on each template tag (placeholder) occurrence.
  15. //
  16. // Returns the number of bytes written to w.
  17. //
  18. // This function is optimized for constantly changing templates.
  19. // Use Template.ExecuteFunc for frozen templates.
  20. func ExecuteFunc(template, startTag, endTag string, w io.Writer, f TagFunc) (int64, error) {
  21. s := unsafeString2Bytes(template)
  22. a := unsafeString2Bytes(startTag)
  23. b := unsafeString2Bytes(endTag)
  24. var nn int64
  25. var ni int
  26. var err error
  27. for {
  28. n := bytes.Index(s, a)
  29. if n < 0 {
  30. break
  31. }
  32. ni, err = w.Write(s[:n])
  33. nn += int64(ni)
  34. if err != nil {
  35. return nn, err
  36. }
  37. s = s[n+len(a):]
  38. n = bytes.Index(s, b)
  39. if n < 0 {
  40. // cannot find end tag - just write it to the output.
  41. ni, _ = w.Write(a)
  42. nn += int64(ni)
  43. break
  44. }
  45. ni, err = f(w, unsafeBytes2String(s[:n]))
  46. nn += int64(ni)
  47. if err != nil {
  48. return nn, err
  49. }
  50. s = s[n+len(b):]
  51. }
  52. ni, err = w.Write(s)
  53. nn += int64(ni)
  54. return nn, err
  55. }
  56. // Execute substitutes template tags (placeholders) with the corresponding
  57. // values from the map m and writes the result to the given writer w.
  58. //
  59. // Substitution map m may contain values with the following types:
  60. // * []byte - the fastest value type
  61. // * string - convenient value type
  62. // * TagFunc - flexible value type
  63. //
  64. // Returns the number of bytes written to w.
  65. //
  66. // This function is optimized for constantly changing templates.
  67. // Use Template.Execute for frozen templates.
  68. func Execute(template, startTag, endTag string, w io.Writer, m map[string]interface{}) (int64, error) {
  69. return ExecuteFunc(template, startTag, endTag, w, func(w io.Writer, tag string) (int, error) { return stdTagFunc(w, tag, m) })
  70. }
  71. // ExecuteStd works the same way as Execute, but keeps the unknown placeholders.
  72. // This can be used as a drop-in replacement for strings.Replacer
  73. //
  74. // Substitution map m may contain values with the following types:
  75. // * []byte - the fastest value type
  76. // * string - convenient value type
  77. // * TagFunc - flexible value type
  78. //
  79. // Returns the number of bytes written to w.
  80. //
  81. // This function is optimized for constantly changing templates.
  82. // Use Template.ExecuteStd for frozen templates.
  83. func ExecuteStd(template, startTag, endTag string, w io.Writer, m map[string]interface{}) (int64, error) {
  84. return ExecuteFunc(template, startTag, endTag, w, func(w io.Writer, tag string) (int, error) { return keepUnknownTagFunc(w, startTag, endTag, tag, m) })
  85. }
  86. // ExecuteFuncString calls f on each template tag (placeholder) occurrence
  87. // and substitutes it with the data written to TagFunc's w.
  88. //
  89. // Returns the resulting string.
  90. //
  91. // This function is optimized for constantly changing templates.
  92. // Use Template.ExecuteFuncString for frozen templates.
  93. func ExecuteFuncString(template, startTag, endTag string, f TagFunc) string {
  94. s, err := ExecuteFuncStringWithErr(template, startTag, endTag, f)
  95. if err != nil {
  96. panic(fmt.Sprintf("unexpected error: %s", err))
  97. }
  98. return s
  99. }
  100. // ExecuteFuncStringWithErr is nearly the same as ExecuteFuncString
  101. // but when f returns an error, ExecuteFuncStringWithErr won't panic like ExecuteFuncString
  102. // it just returns an empty string and the error f returned
  103. func ExecuteFuncStringWithErr(template, startTag, endTag string, f TagFunc) (string, error) {
  104. tagsCount := bytes.Count(unsafeString2Bytes(template), unsafeString2Bytes(startTag))
  105. if tagsCount == 0 {
  106. return template, nil
  107. }
  108. bb := byteBufferPool.Get()
  109. if _, err := ExecuteFunc(template, startTag, endTag, bb, f); err != nil {
  110. bb.Reset()
  111. byteBufferPool.Put(bb)
  112. return "", err
  113. }
  114. s := string(bb.B)
  115. bb.Reset()
  116. byteBufferPool.Put(bb)
  117. return s, nil
  118. }
  119. var byteBufferPool bytebufferpool.Pool
  120. // ExecuteString substitutes template tags (placeholders) with the corresponding
  121. // values from the map m and returns the result.
  122. //
  123. // Substitution map m may contain values with the following types:
  124. // * []byte - the fastest value type
  125. // * string - convenient value type
  126. // * TagFunc - flexible value type
  127. //
  128. // This function is optimized for constantly changing templates.
  129. // Use Template.ExecuteString for frozen templates.
  130. func ExecuteString(template, startTag, endTag string, m map[string]interface{}) string {
  131. return ExecuteFuncString(template, startTag, endTag, func(w io.Writer, tag string) (int, error) { return stdTagFunc(w, tag, m) })
  132. }
  133. // ExecuteStringStd works the same way as ExecuteString, but keeps the unknown placeholders.
  134. // This can be used as a drop-in replacement for strings.Replacer
  135. //
  136. // Substitution map m may contain values with the following types:
  137. // * []byte - the fastest value type
  138. // * string - convenient value type
  139. // * TagFunc - flexible value type
  140. //
  141. // This function is optimized for constantly changing templates.
  142. // Use Template.ExecuteStringStd for frozen templates.
  143. func ExecuteStringStd(template, startTag, endTag string, m map[string]interface{}) string {
  144. return ExecuteFuncString(template, startTag, endTag, func(w io.Writer, tag string) (int, error) { return keepUnknownTagFunc(w, startTag, endTag, tag, m) })
  145. }
  146. // Template implements simple template engine, which can be used for fast
  147. // tags' (aka placeholders) substitution.
  148. type Template struct {
  149. template string
  150. startTag string
  151. endTag string
  152. texts [][]byte
  153. tags []string
  154. byteBufferPool bytebufferpool.Pool
  155. }
  156. // New parses the given template using the given startTag and endTag
  157. // as tag start and tag end.
  158. //
  159. // The returned template can be executed by concurrently running goroutines
  160. // using Execute* methods.
  161. //
  162. // New panics if the given template cannot be parsed. Use NewTemplate instead
  163. // if template may contain errors.
  164. func New(template, startTag, endTag string) *Template {
  165. t, err := NewTemplate(template, startTag, endTag)
  166. if err != nil {
  167. panic(err)
  168. }
  169. return t
  170. }
  171. // NewTemplate parses the given template using the given startTag and endTag
  172. // as tag start and tag end.
  173. //
  174. // The returned template can be executed by concurrently running goroutines
  175. // using Execute* methods.
  176. func NewTemplate(template, startTag, endTag string) (*Template, error) {
  177. var t Template
  178. err := t.Reset(template, startTag, endTag)
  179. if err != nil {
  180. return nil, err
  181. }
  182. return &t, nil
  183. }
  184. // TagFunc can be used as a substitution value in the map passed to Execute*.
  185. // Execute* functions pass tag (placeholder) name in 'tag' argument.
  186. //
  187. // TagFunc must be safe to call from concurrently running goroutines.
  188. //
  189. // TagFunc must write contents to w and return the number of bytes written.
  190. type TagFunc func(w io.Writer, tag string) (int, error)
  191. // Reset resets the template t to new one defined by
  192. // template, startTag and endTag.
  193. //
  194. // Reset allows Template object re-use.
  195. //
  196. // Reset may be called only if no other goroutines call t methods at the moment.
  197. func (t *Template) Reset(template, startTag, endTag string) error {
  198. // Keep these vars in t, so GC won't collect them and won't break
  199. // vars derived via unsafe*
  200. t.template = template
  201. t.startTag = startTag
  202. t.endTag = endTag
  203. t.texts = t.texts[:0]
  204. t.tags = t.tags[:0]
  205. if len(startTag) == 0 {
  206. panic("startTag cannot be empty")
  207. }
  208. if len(endTag) == 0 {
  209. panic("endTag cannot be empty")
  210. }
  211. s := unsafeString2Bytes(template)
  212. a := unsafeString2Bytes(startTag)
  213. b := unsafeString2Bytes(endTag)
  214. tagsCount := bytes.Count(s, a)
  215. if tagsCount == 0 {
  216. return nil
  217. }
  218. if tagsCount+1 > cap(t.texts) {
  219. t.texts = make([][]byte, 0, tagsCount+1)
  220. }
  221. if tagsCount > cap(t.tags) {
  222. t.tags = make([]string, 0, tagsCount)
  223. }
  224. for {
  225. n := bytes.Index(s, a)
  226. if n < 0 {
  227. t.texts = append(t.texts, s)
  228. break
  229. }
  230. t.texts = append(t.texts, s[:n])
  231. s = s[n+len(a):]
  232. n = bytes.Index(s, b)
  233. if n < 0 {
  234. return fmt.Errorf("Cannot find end tag=%q in the template=%q starting from %q", endTag, template, s)
  235. }
  236. t.tags = append(t.tags, unsafeBytes2String(s[:n]))
  237. s = s[n+len(b):]
  238. }
  239. return nil
  240. }
  241. // ExecuteFunc calls f on each template tag (placeholder) occurrence.
  242. //
  243. // Returns the number of bytes written to w.
  244. //
  245. // This function is optimized for frozen templates.
  246. // Use ExecuteFunc for constantly changing templates.
  247. func (t *Template) ExecuteFunc(w io.Writer, f TagFunc) (int64, error) {
  248. var nn int64
  249. n := len(t.texts) - 1
  250. if n == -1 {
  251. ni, err := w.Write(unsafeString2Bytes(t.template))
  252. return int64(ni), err
  253. }
  254. for i := 0; i < n; i++ {
  255. ni, err := w.Write(t.texts[i])
  256. nn += int64(ni)
  257. if err != nil {
  258. return nn, err
  259. }
  260. ni, err = f(w, t.tags[i])
  261. nn += int64(ni)
  262. if err != nil {
  263. return nn, err
  264. }
  265. }
  266. ni, err := w.Write(t.texts[n])
  267. nn += int64(ni)
  268. return nn, err
  269. }
  270. // Execute substitutes template tags (placeholders) with the corresponding
  271. // values from the map m and writes the result to the given writer w.
  272. //
  273. // Substitution map m may contain values with the following types:
  274. // * []byte - the fastest value type
  275. // * string - convenient value type
  276. // * TagFunc - flexible value type
  277. //
  278. // Returns the number of bytes written to w.
  279. func (t *Template) Execute(w io.Writer, m map[string]interface{}) (int64, error) {
  280. return t.ExecuteFunc(w, func(w io.Writer, tag string) (int, error) { return stdTagFunc(w, tag, m) })
  281. }
  282. // ExecuteStd works the same way as Execute, but keeps the unknown placeholders.
  283. // This can be used as a drop-in replacement for strings.Replacer
  284. //
  285. // Substitution map m may contain values with the following types:
  286. // * []byte - the fastest value type
  287. // * string - convenient value type
  288. // * TagFunc - flexible value type
  289. //
  290. // Returns the number of bytes written to w.
  291. func (t *Template) ExecuteStd(w io.Writer, m map[string]interface{}) (int64, error) {
  292. return t.ExecuteFunc(w, func(w io.Writer, tag string) (int, error) { return keepUnknownTagFunc(w, t.startTag, t.endTag, tag, m) })
  293. }
  294. // ExecuteFuncString calls f on each template tag (placeholder) occurrence
  295. // and substitutes it with the data written to TagFunc's w.
  296. //
  297. // Returns the resulting string.
  298. //
  299. // This function is optimized for frozen templates.
  300. // Use ExecuteFuncString for constantly changing templates.
  301. func (t *Template) ExecuteFuncString(f TagFunc) string {
  302. s, err := t.ExecuteFuncStringWithErr(f)
  303. if err != nil {
  304. panic(fmt.Sprintf("unexpected error: %s", err))
  305. }
  306. return s
  307. }
  308. // ExecuteFuncStringWithErr calls f on each template tag (placeholder) occurrence
  309. // and substitutes it with the data written to TagFunc's w.
  310. //
  311. // Returns the resulting string.
  312. //
  313. // This function is optimized for frozen templates.
  314. // Use ExecuteFuncString for constantly changing templates.
  315. func (t *Template) ExecuteFuncStringWithErr(f TagFunc) (string, error) {
  316. bb := t.byteBufferPool.Get()
  317. if _, err := t.ExecuteFunc(bb, f); err != nil {
  318. bb.Reset()
  319. t.byteBufferPool.Put(bb)
  320. return "", err
  321. }
  322. s := string(bb.Bytes())
  323. bb.Reset()
  324. t.byteBufferPool.Put(bb)
  325. return s, nil
  326. }
  327. // ExecuteString substitutes template tags (placeholders) with the corresponding
  328. // values from the map m and returns the result.
  329. //
  330. // Substitution map m may contain values with the following types:
  331. // * []byte - the fastest value type
  332. // * string - convenient value type
  333. // * TagFunc - flexible value type
  334. //
  335. // This function is optimized for frozen templates.
  336. // Use ExecuteString for constantly changing templates.
  337. func (t *Template) ExecuteString(m map[string]interface{}) string {
  338. return t.ExecuteFuncString(func(w io.Writer, tag string) (int, error) { return stdTagFunc(w, tag, m) })
  339. }
  340. // ExecuteStringStd works the same way as ExecuteString, but keeps the unknown placeholders.
  341. // This can be used as a drop-in replacement for strings.Replacer
  342. //
  343. // Substitution map m may contain values with the following types:
  344. // * []byte - the fastest value type
  345. // * string - convenient value type
  346. // * TagFunc - flexible value type
  347. //
  348. // This function is optimized for frozen templates.
  349. // Use ExecuteStringStd for constantly changing templates.
  350. func (t *Template) ExecuteStringStd(m map[string]interface{}) string {
  351. return t.ExecuteFuncString(func(w io.Writer, tag string) (int, error) { return keepUnknownTagFunc(w, t.startTag, t.endTag, tag, m) })
  352. }
  353. func stdTagFunc(w io.Writer, tag string, m map[string]interface{}) (int, error) {
  354. v := m[tag]
  355. if v == nil {
  356. return 0, nil
  357. }
  358. switch value := v.(type) {
  359. case []byte:
  360. return w.Write(value)
  361. case string:
  362. return w.Write([]byte(value))
  363. case TagFunc:
  364. return value(w, tag)
  365. default:
  366. panic(fmt.Sprintf("tag=%q contains unexpected value type=%#v. Expected []byte, string or TagFunc", tag, v))
  367. }
  368. }
  369. func keepUnknownTagFunc(w io.Writer, startTag, endTag, tag string, m map[string]interface{}) (int, error) {
  370. v, ok := m[tag]
  371. if !ok {
  372. if _, err := w.Write(unsafeString2Bytes(startTag)); err != nil {
  373. return 0, err
  374. }
  375. if _, err := w.Write(unsafeString2Bytes(tag)); err != nil {
  376. return 0, err
  377. }
  378. if _, err := w.Write(unsafeString2Bytes(endTag)); err != nil {
  379. return 0, err
  380. }
  381. return len(startTag) + len(tag) + len(endTag), nil
  382. }
  383. if v == nil {
  384. return 0, nil
  385. }
  386. switch value := v.(type) {
  387. case []byte:
  388. return w.Write(value)
  389. case string:
  390. return w.Write([]byte(value))
  391. case TagFunc:
  392. return value(w, tag)
  393. default:
  394. panic(fmt.Sprintf("tag=%q contains unexpected value type=%#v. Expected []byte, string or TagFunc", tag, v))
  395. }
  396. }