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