pax_global_header00006660000000000000000000000064132071175030014511gustar00rootroot0000000000000052 comment=5ce089897cb6bae7aac0276b46323f49a2673369 golang-github-valyala-fasttemplate-0.0~git20170224.0.dcecefd+dfsg/000077500000000000000000000000001320711750300243725ustar00rootroot00000000000000golang-github-valyala-fasttemplate-0.0~git20170224.0.dcecefd+dfsg/LICENSE000066400000000000000000000021001320711750300253700ustar00rootroot00000000000000The MIT License (MIT) Copyright (c) 2015 Aliaksandr Valialkin Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. golang-github-valyala-fasttemplate-0.0~git20170224.0.dcecefd+dfsg/README.md000066400000000000000000000055621320711750300256610ustar00rootroot00000000000000fasttemplate ============ Simple and fast template engine for Go. Fasttemplate peforms only a single task - it substitutes template placeholders with user-defined values. At high speed :) Take a look at [quicktemplate](https://github.com/valyala/quicktemplate) if you need fast yet powerful html template engine. *Please note that fasttemplate doesn't do any escaping on template values unlike [html/template](http://golang.org/pkg/html/template/) do. So values must be properly escaped before passing them to fasttemplate.* Fasttemplate is faster than [text/template](http://golang.org/pkg/text/template/), [strings.Replace](http://golang.org/pkg/strings/#Replace), [strings.Replacer](http://golang.org/pkg/strings/#Replacer) and [fmt.Fprintf](https://golang.org/pkg/fmt/#Fprintf) on placeholders' substitution. Below are benchmark results comparing fasttemplate performance to text/template, strings.Replace, strings.Replacer and fmt.Fprintf: ``` $ go test -bench=. -benchmem PASS BenchmarkFmtFprintf-4 2000000 790 ns/op 0 B/op 0 allocs/op BenchmarkStringsReplace-4 500000 3474 ns/op 2112 B/op 14 allocs/op BenchmarkStringsReplacer-4 500000 2657 ns/op 2256 B/op 23 allocs/op BenchmarkTextTemplate-4 500000 3333 ns/op 336 B/op 19 allocs/op BenchmarkFastTemplateExecuteFunc-4 5000000 349 ns/op 0 B/op 0 allocs/op BenchmarkFastTemplateExecute-4 3000000 383 ns/op 0 B/op 0 allocs/op BenchmarkFastTemplateExecuteFuncString-4 3000000 549 ns/op 144 B/op 1 allocs/op BenchmarkFastTemplateExecuteString-4 3000000 572 ns/op 144 B/op 1 allocs/op BenchmarkFastTemplateExecuteTagFunc-4 2000000 743 ns/op 144 B/op 3 allocs/op ``` Docs ==== See http://godoc.org/github.com/valyala/fasttemplate . Usage ===== ```go template := "http://{{host}}/?q={{query}}&foo={{bar}}{{bar}}" t := fasttemplate.New(template, "{{", "}}") s := t.ExecuteString(map[string]interface{}{ "host": "google.com", "query": url.QueryEscape("hello=world"), "bar": "foobar", }) fmt.Printf("%s", s) // Output: // http://google.com/?q=hello%3Dworld&foo=foobarfoobar ``` Advanced usage ============== ```go template := "Hello, [user]! You won [prize]!!! [foobar]" t, err := fasttemplate.NewTemplate(template, "[", "]") if err != nil { log.Fatalf("unexpected error when parsing template: %s", err) } s := t.ExecuteFuncString(func(w io.Writer, tag string) (int, error) { switch tag { case "user": return w.Write([]byte("John")) case "prize": return w.Write([]byte("$100500")) default: return w.Write([]byte(fmt.Sprintf("[unknown tag %q]", tag))) } }) fmt.Printf("%s", s) // Output: // Hello, John! You won $100500!!! [unknown tag "foobar"] ``` golang-github-valyala-fasttemplate-0.0~git20170224.0.dcecefd+dfsg/example_test.go000066400000000000000000000040461320711750300274170ustar00rootroot00000000000000package fasttemplate import ( "fmt" "io" "log" "net/url" ) func ExampleTemplate() { template := "http://{{host}}/?foo={{bar}}{{bar}}&q={{query}}&baz={{baz}}" t := New(template, "{{", "}}") // Substitution map. // Since "baz" tag is missing in the map, it will be substituted // by an empty string. m := map[string]interface{}{ "host": "google.com", // string - convenient "bar": []byte("foobar"), // byte slice - the fastest // TagFunc - flexible value. TagFunc is called only if the given // tag exists in the template. "query": TagFunc(func(w io.Writer, tag string) (int, error) { return w.Write([]byte(url.QueryEscape(tag + "=world"))) }), } s := t.ExecuteString(m) fmt.Printf("%s", s) // Output: // http://google.com/?foo=foobarfoobar&q=query%3Dworld&baz= } func ExampleTagFunc() { template := "foo[baz]bar" t, err := NewTemplate(template, "[", "]") if err != nil { log.Fatalf("unexpected error when parsing template: %s", err) } bazSlice := [][]byte{[]byte("123"), []byte("456"), []byte("789")} m := map[string]interface{}{ // Always wrap the function into TagFunc. // // "baz" tag function writes bazSlice contents into w. "baz": TagFunc(func(w io.Writer, tag string) (int, error) { var nn int for _, x := range bazSlice { n, err := w.Write(x) if err != nil { return nn, err } nn += n } return nn, nil }), } s := t.ExecuteString(m) fmt.Printf("%s", s) // Output: // foo123456789bar } func ExampleTemplate_ExecuteFuncString() { template := "Hello, [user]! You won [prize]!!! [foobar]" t, err := NewTemplate(template, "[", "]") if err != nil { log.Fatalf("unexpected error when parsing template: %s", err) } s := t.ExecuteFuncString(func(w io.Writer, tag string) (int, error) { switch tag { case "user": return w.Write([]byte("John")) case "prize": return w.Write([]byte("$100500")) default: return w.Write([]byte(fmt.Sprintf("[unknown tag %q]", tag))) } }) fmt.Printf("%s", s) // Output: // Hello, John! You won $100500!!! [unknown tag "foobar"] } golang-github-valyala-fasttemplate-0.0~git20170224.0.dcecefd+dfsg/template.go000066400000000000000000000215601320711750300265400ustar00rootroot00000000000000// 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 fasttemplate import ( "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)) } } golang-github-valyala-fasttemplate-0.0~git20170224.0.dcecefd+dfsg/template_test.go000066400000000000000000000210631320711750300275750ustar00rootroot00000000000000package fasttemplate import ( "bytes" "io" "testing" ) func TestEmptyTemplate(t *testing.T) { tpl := New("", "[", "]") s := tpl.ExecuteString(map[string]interface{}{"foo": "bar", "aaa": "bbb"}) if s != "" { t.Fatalf("unexpected string returned %q. Expected empty string", s) } } func TestEmptyTagStart(t *testing.T) { expectPanic(t, func() { NewTemplate("foobar", "", "]") }) } func TestEmptyTagEnd(t *testing.T) { expectPanic(t, func() { NewTemplate("foobar", "[", "") }) } func TestNoTags(t *testing.T) { template := "foobar" tpl := New(template, "[", "]") s := tpl.ExecuteString(map[string]interface{}{"foo": "bar", "aaa": "bbb"}) if s != template { t.Fatalf("unexpected template value %q. Expected %q", s, template) } } func TestEmptyTagName(t *testing.T) { template := "foo[]bar" tpl := New(template, "[", "]") s := tpl.ExecuteString(map[string]interface{}{"": "111", "aaa": "bbb"}) result := "foo111bar" if s != result { t.Fatalf("unexpected template value %q. Expected %q", s, result) } } func TestOnlyTag(t *testing.T) { template := "[foo]" tpl := New(template, "[", "]") s := tpl.ExecuteString(map[string]interface{}{"foo": "111", "aaa": "bbb"}) result := "111" if s != result { t.Fatalf("unexpected template value %q. Expected %q", s, result) } } func TestStartWithTag(t *testing.T) { template := "[foo]barbaz" tpl := New(template, "[", "]") s := tpl.ExecuteString(map[string]interface{}{"foo": "111", "aaa": "bbb"}) result := "111barbaz" if s != result { t.Fatalf("unexpected template value %q. Expected %q", s, result) } } func TestEndWithTag(t *testing.T) { template := "foobar[foo]" tpl := New(template, "[", "]") s := tpl.ExecuteString(map[string]interface{}{"foo": "111", "aaa": "bbb"}) result := "foobar111" if s != result { t.Fatalf("unexpected template value %q. Expected %q", s, result) } } func TestTemplateReset(t *testing.T) { template := "foo{bar}baz" tpl := New(template, "{", "}") s := tpl.ExecuteString(map[string]interface{}{"bar": "111"}) result := "foo111baz" if s != result { t.Fatalf("unexpected template value %q. Expected %q", s, result) } template = "[xxxyyyzz" if err := tpl.Reset(template, "[", "]"); err == nil { t.Fatalf("expecting error for unclosed tag on %q", template) } template = "[xxx]yyy[zz]" if err := tpl.Reset(template, "[", "]"); err != nil { t.Fatalf("unexpected error: %s", err) } s = tpl.ExecuteString(map[string]interface{}{"xxx": "11", "zz": "2222"}) result = "11yyy2222" if s != result { t.Fatalf("unexpected template value %q. Expected %q", s, result) } } func TestDuplicateTags(t *testing.T) { template := "[foo]bar[foo][foo]baz" tpl := New(template, "[", "]") s := tpl.ExecuteString(map[string]interface{}{"foo": "111", "aaa": "bbb"}) result := "111bar111111baz" if s != result { t.Fatalf("unexpected template value %q. Expected %q", s, result) } } func TestMultipleTags(t *testing.T) { template := "foo[foo]aa[aaa]ccc" tpl := New(template, "[", "]") s := tpl.ExecuteString(map[string]interface{}{"foo": "111", "aaa": "bbb"}) result := "foo111aabbbccc" if s != result { t.Fatalf("unexpected template value %q. Expected %q", s, result) } } func TestLongDelimiter(t *testing.T) { template := "foo{{{foo}}}bar" tpl := New(template, "{{{", "}}}") s := tpl.ExecuteString(map[string]interface{}{"foo": "111", "aaa": "bbb"}) result := "foo111bar" if s != result { t.Fatalf("unexpected template value %q. Expected %q", s, result) } } func TestIdenticalDelimiter(t *testing.T) { template := "foo@foo@foo@aaa@" tpl := New(template, "@", "@") s := tpl.ExecuteString(map[string]interface{}{"foo": "111", "aaa": "bbb"}) result := "foo111foobbb" if s != result { t.Fatalf("unexpected template value %q. Expected %q", s, result) } } func TestDlimitersWithDistinctSize(t *testing.T) { template := "foobar" tpl := New(template, "") s := tpl.ExecuteString(map[string]interface{}{"zzz": "111", "aaa": "bbb"}) result := "foobbbbar111" if s != result { t.Fatalf("unexpected template value %q. Expected %q", s, result) } } func TestEmptyValue(t *testing.T) { template := "foobar[foo]" tpl := New(template, "[", "]") s := tpl.ExecuteString(map[string]interface{}{"foo": "", "aaa": "bbb"}) result := "foobar" if s != result { t.Fatalf("unexpected template value %q. Expected %q", s, result) } } func TestNoValue(t *testing.T) { template := "foobar[foo]x[aaa]" tpl := New(template, "[", "]") s := tpl.ExecuteString(map[string]interface{}{"aaa": "bbb"}) result := "foobarxbbb" if s != result { t.Fatalf("unexpected template value %q. Expected %q", s, result) } } func TestNoEndDelimiter(t *testing.T) { template := "foobar[foo" _, err := NewTemplate(template, "[", "]") if err == nil { t.Fatalf("expected non-nil error. got nil") } expectPanic(t, func() { New(template, "[", "]") }) } func TestUnsupportedValue(t *testing.T) { template := "foobar[foo]" tpl := New(template, "[", "]") expectPanic(t, func() { tpl.ExecuteString(map[string]interface{}{"foo": 123, "aaa": "bbb"}) }) } func TestMixedValues(t *testing.T) { template := "foo[foo]bar[bar]baz[baz]" tpl := New(template, "[", "]") s := tpl.ExecuteString(map[string]interface{}{ "foo": "111", "bar": []byte("bbb"), "baz": TagFunc(func(w io.Writer, tag string) (int, error) { return w.Write([]byte(tag)) }), }) result := "foo111barbbbbazbaz" if s != result { t.Fatalf("unexpected template value %q. Expected %q", s, result) } } func TestExecuteFunc(t *testing.T) { testExecuteFunc(t, "", "") testExecuteFunc(t, "a", "a") testExecuteFunc(t, "abc", "abc") testExecuteFunc(t, "{foo}", "xxxx") testExecuteFunc(t, "a{foo}", "axxxx") testExecuteFunc(t, "{foo}a", "xxxxa") testExecuteFunc(t, "a{foo}bc", "axxxxbc") testExecuteFunc(t, "{foo}{foo}", "xxxxxxxx") testExecuteFunc(t, "{foo}bar{foo}", "xxxxbarxxxx") // unclosed tag testExecuteFunc(t, "{unclosed", "{unclosed") testExecuteFunc(t, "{{unclosed", "{{unclosed") testExecuteFunc(t, "{un{closed", "{un{closed") // test unknown tag testExecuteFunc(t, "{unknown}", "zz") testExecuteFunc(t, "{foo}q{unexpected}{missing}bar{foo}", "xxxxqzzzzbarxxxx") } func testExecuteFunc(t *testing.T, template, expectedOutput string) { var bb bytes.Buffer ExecuteFunc(template, "{", "}", &bb, func(w io.Writer, tag string) (int, error) { if tag == "foo" { return w.Write([]byte("xxxx")) } return w.Write([]byte("zz")) }) output := string(bb.Bytes()) if output != expectedOutput { t.Fatalf("unexpected output for template=%q: %q. Expected %q", template, output, expectedOutput) } } func TestExecute(t *testing.T) { testExecute(t, "", "") testExecute(t, "a", "a") testExecute(t, "abc", "abc") testExecute(t, "{foo}", "xxxx") testExecute(t, "a{foo}", "axxxx") testExecute(t, "{foo}a", "xxxxa") testExecute(t, "a{foo}bc", "axxxxbc") testExecute(t, "{foo}{foo}", "xxxxxxxx") testExecute(t, "{foo}bar{foo}", "xxxxbarxxxx") // unclosed tag testExecute(t, "{unclosed", "{unclosed") testExecute(t, "{{unclosed", "{{unclosed") testExecute(t, "{un{closed", "{un{closed") // test unknown tag testExecute(t, "{unknown}", "") testExecute(t, "{foo}q{unexpected}{missing}bar{foo}", "xxxxqbarxxxx") } func testExecute(t *testing.T, template, expectedOutput string) { var bb bytes.Buffer Execute(template, "{", "}", &bb, map[string]interface{}{"foo": "xxxx"}) output := string(bb.Bytes()) if output != expectedOutput { t.Fatalf("unexpected output for template=%q: %q. Expected %q", template, output, expectedOutput) } } func TestExecuteString(t *testing.T) { testExecuteString(t, "", "") testExecuteString(t, "a", "a") testExecuteString(t, "abc", "abc") testExecuteString(t, "{foo}", "xxxx") testExecuteString(t, "a{foo}", "axxxx") testExecuteString(t, "{foo}a", "xxxxa") testExecuteString(t, "a{foo}bc", "axxxxbc") testExecuteString(t, "{foo}{foo}", "xxxxxxxx") testExecuteString(t, "{foo}bar{foo}", "xxxxbarxxxx") // unclosed tag testExecuteString(t, "{unclosed", "{unclosed") testExecuteString(t, "{{unclosed", "{{unclosed") testExecuteString(t, "{un{closed", "{un{closed") // test unknown tag testExecuteString(t, "{unknown}", "") testExecuteString(t, "{foo}q{unexpected}{missing}bar{foo}", "xxxxqbarxxxx") } func testExecuteString(t *testing.T, template, expectedOutput string) { output := ExecuteString(template, "{", "}", map[string]interface{}{"foo": "xxxx"}) if output != expectedOutput { t.Fatalf("unexpected output for template=%q: %q. Expected %q", template, output, expectedOutput) } } func expectPanic(t *testing.T, f func()) { defer func() { if r := recover(); r == nil { t.Fatalf("missing panic") } }() f() } golang-github-valyala-fasttemplate-0.0~git20170224.0.dcecefd+dfsg/template_timing_test.go000066400000000000000000000141521320711750300311450ustar00rootroot00000000000000package fasttemplate import ( "bytes" "fmt" "io" "net/url" "strings" "testing" "text/template" ) var ( source = "http://{{uid}}.foo.bar.com/?cb={{cb}}{{width}}&width={{width}}&height={{height}}&timeout={{timeout}}&uid={{uid}}&subid={{subid}}&ref={{ref}}" result = "http://aaasdf.foo.bar.com/?cb=12341232&width=1232&height=123&timeout=123123&uid=aaasdf&subid=asdfds&ref=http://google.com/aaa/bbb/ccc" resultEscaped = "http://aaasdf.foo.bar.com/?cb=12341232&width=1232&height=123&timeout=123123&uid=aaasdf&subid=asdfds&ref=http%3A%2F%2Fgoogle.com%2Faaa%2Fbbb%2Fccc" resultBytes = []byte(result) resultEscapedBytes = []byte(resultEscaped) m = map[string]interface{}{ "cb": []byte("1234"), "width": []byte("1232"), "height": []byte("123"), "timeout": []byte("123123"), "uid": []byte("aaasdf"), "subid": []byte("asdfds"), "ref": []byte("http://google.com/aaa/bbb/ccc"), } ) func map2slice(m map[string]interface{}) []string { var a []string for k, v := range m { a = append(a, "{{"+k+"}}", string(v.([]byte))) } return a } func BenchmarkFmtFprintf(b *testing.B) { b.RunParallel(func(pb *testing.PB) { var w bytes.Buffer for pb.Next() { fmt.Fprintf(&w, "http://%[5]s.foo.bar.com/?cb=%[1]s%[2]s&width=%[2]s&height=%[3]s&timeout=%[4]s&uid=%[5]s&subid=%[6]s&ref=%[7]s", m["cb"], m["width"], m["height"], m["timeout"], m["uid"], m["subid"], m["ref"]) x := w.Bytes() if !bytes.Equal(x, resultBytes) { b.Fatalf("Unexpected result\n%q\nExpected\n%q\n", x, result) } w.Reset() } }) } func BenchmarkStringsReplace(b *testing.B) { mSlice := map2slice(m) b.ResetTimer() b.RunParallel(func(pb *testing.PB) { for pb.Next() { x := source for i := 0; i < len(mSlice); i += 2 { x = strings.Replace(x, mSlice[i], mSlice[i+1], -1) } if x != result { b.Fatalf("Unexpected result\n%q\nExpected\n%q\n", x, result) } } }) } func BenchmarkStringsReplacer(b *testing.B) { mSlice := map2slice(m) b.ResetTimer() b.RunParallel(func(pb *testing.PB) { for pb.Next() { r := strings.NewReplacer(mSlice...) x := r.Replace(source) if x != result { b.Fatalf("Unexpected result\n%q\nExpected\n%q\n", x, result) } } }) } func BenchmarkTextTemplate(b *testing.B) { s := strings.Replace(source, "{{", "{{.", -1) t, err := template.New("test").Parse(s) if err != nil { b.Fatalf("Error when parsing template: %s", err) } mm := make(map[string]string) for k, v := range m { mm[k] = string(v.([]byte)) } b.ResetTimer() b.RunParallel(func(pb *testing.PB) { var w bytes.Buffer for pb.Next() { if err := t.Execute(&w, mm); err != nil { b.Fatalf("error when executing template: %s", err) } x := w.Bytes() if !bytes.Equal(x, resultBytes) { b.Fatalf("unexpected result\n%q\nExpected\n%q\n", x, resultBytes) } w.Reset() } }) } func BenchmarkFastTemplateExecuteFunc(b *testing.B) { t, err := NewTemplate(source, "{{", "}}") if err != nil { b.Fatalf("error in template: %s", err) } b.ResetTimer() b.RunParallel(func(pb *testing.PB) { var w bytes.Buffer for pb.Next() { if _, err := t.ExecuteFunc(&w, testTagFunc); err != nil { b.Fatalf("unexpected error: %s", err) } x := w.Bytes() if !bytes.Equal(x, resultBytes) { b.Fatalf("unexpected result\n%q\nExpected\n%q\n", x, resultBytes) } w.Reset() } }) } func BenchmarkFastTemplateExecute(b *testing.B) { t, err := NewTemplate(source, "{{", "}}") if err != nil { b.Fatalf("error in template: %s", err) } b.ResetTimer() b.RunParallel(func(pb *testing.PB) { var w bytes.Buffer for pb.Next() { if _, err := t.Execute(&w, m); err != nil { b.Fatalf("unexpected error: %s", err) } x := w.Bytes() if !bytes.Equal(x, resultBytes) { b.Fatalf("unexpected result\n%q\nExpected\n%q\n", x, resultBytes) } w.Reset() } }) } func BenchmarkFastTemplateExecuteFuncString(b *testing.B) { t, err := NewTemplate(source, "{{", "}}") if err != nil { b.Fatalf("error in template: %s", err) } b.ResetTimer() b.RunParallel(func(pb *testing.PB) { for pb.Next() { x := t.ExecuteFuncString(testTagFunc) if x != result { b.Fatalf("unexpected result\n%q\nExpected\n%q\n", x, result) } } }) } func BenchmarkFastTemplateExecuteString(b *testing.B) { t, err := NewTemplate(source, "{{", "}}") if err != nil { b.Fatalf("error in template: %s", err) } b.ResetTimer() b.RunParallel(func(pb *testing.PB) { for pb.Next() { x := t.ExecuteString(m) if x != result { b.Fatalf("unexpected result\n%q\nExpected\n%q\n", x, result) } } }) } func BenchmarkFastTemplateExecuteTagFunc(b *testing.B) { t, err := NewTemplate(source, "{{", "}}") if err != nil { b.Fatalf("error in template: %s", err) } mm := make(map[string]interface{}) for k, v := range m { if k == "ref" { vv := v.([]byte) v = TagFunc(func(w io.Writer, tag string) (int, error) { return w.Write([]byte(url.QueryEscape(string(vv)))) }) } mm[k] = v } b.ResetTimer() b.RunParallel(func(pb *testing.PB) { var w bytes.Buffer for pb.Next() { if _, err := t.Execute(&w, mm); err != nil { b.Fatalf("unexpected error: %s", err) } x := w.Bytes() if !bytes.Equal(x, resultEscapedBytes) { b.Fatalf("unexpected result\n%q\nExpected\n%q\n", x, resultEscapedBytes) } w.Reset() } }) } func BenchmarkNewTemplate(b *testing.B) { b.RunParallel(func(pb *testing.PB) { for pb.Next() { _ = New(source, "{{", "}}") } }) } func BenchmarkTemplateReset(b *testing.B) { b.RunParallel(func(pb *testing.PB) { t := New(source, "{{", "}}") for pb.Next() { t.Reset(source, "{{", "}}") } }) } func BenchmarkTemplateResetExecuteFunc(b *testing.B) { b.RunParallel(func(pb *testing.PB) { t := New(source, "{{", "}}") var w bytes.Buffer for pb.Next() { t.Reset(source, "{{", "}}") t.ExecuteFunc(&w, testTagFunc) w.Reset() } }) } func BenchmarkExecuteFunc(b *testing.B) { b.RunParallel(func(pb *testing.PB) { var bb bytes.Buffer for pb.Next() { ExecuteFunc(source, "{{", "}}", &bb, testTagFunc) bb.Reset() } }) } func testTagFunc(w io.Writer, tag string) (int, error) { return w.Write(m[tag].([]byte)) } golang-github-valyala-fasttemplate-0.0~git20170224.0.dcecefd+dfsg/unsafe.go000066400000000000000000000005721320711750300262060ustar00rootroot00000000000000// +build !appengine package fasttemplate import ( "reflect" "unsafe" ) func unsafeBytes2String(b []byte) string { return *(*string)(unsafe.Pointer(&b)) } func unsafeString2Bytes(s string) []byte { sh := (*reflect.StringHeader)(unsafe.Pointer(&s)) bh := reflect.SliceHeader{ Data: sh.Data, Len: sh.Len, Cap: sh.Len, } return *(*[]byte)(unsafe.Pointer(&bh)) } golang-github-valyala-fasttemplate-0.0~git20170224.0.dcecefd+dfsg/unsafe_gae.go000066400000000000000000000002521320711750300270150ustar00rootroot00000000000000// +build appengine package fasttemplate func unsafeBytes2String(b []byte) string { return string(b) } func unsafeString2Bytes(s string) []byte { return []byte(s) }