pax_global_header00006660000000000000000000000064141524424560014521gustar00rootroot0000000000000052 comment=a4e4fe510c4ae4d60f8e7de2436b5e3a861cfb45 golang-github-valyala-fasttemplate-1.2.1+ds1/000077500000000000000000000000001415244245600210525ustar00rootroot00000000000000golang-github-valyala-fasttemplate-1.2.1+ds1/LICENSE000066400000000000000000000021001415244245600220500ustar00rootroot00000000000000The 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-1.2.1+ds1/README.md000066400000000000000000000055631415244245600223420ustar00rootroot00000000000000fasttemplate ============ Simple and fast template engine for Go. Fasttemplate performs 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-1.2.1+ds1/example_test.go000066400000000000000000000040461415244245600240770ustar00rootroot00000000000000package 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-1.2.1+ds1/go.mod000066400000000000000000000001421415244245600221550ustar00rootroot00000000000000module github.com/valyala/fasttemplate go 1.12 require github.com/valyala/bytebufferpool v1.0.0 golang-github-valyala-fasttemplate-1.2.1+ds1/go.sum000066400000000000000000000002711415244245600222050ustar00rootroot00000000000000github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= golang-github-valyala-fasttemplate-1.2.1+ds1/template.go000066400000000000000000000324341415244245600232220ustar00rootroot00000000000000// 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" "io" "github.com/valyala/bytebufferpool" ) // 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) if err != nil { return nn, err } 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) }) } // ExecuteStd works the same way as Execute, but keeps the unknown placeholders. // This can be used as a drop-in replacement for strings.Replacer // // 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.ExecuteStd for frozen templates. func ExecuteStd(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 keepUnknownTagFunc(w, startTag, endTag, 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 { s, err := ExecuteFuncStringWithErr(template, startTag, endTag, f) if err != nil { panic(fmt.Sprintf("unexpected error: %s", err)) } return s } // ExecuteFuncStringWithErr is nearly the same as ExecuteFuncString // but when f returns an error, ExecuteFuncStringWithErr won't panic like ExecuteFuncString // it just returns an empty string and the error f returned func ExecuteFuncStringWithErr(template, startTag, endTag string, f TagFunc) (string, error) { tagsCount := bytes.Count(unsafeString2Bytes(template), unsafeString2Bytes(startTag)) if tagsCount == 0 { return template, nil } bb := byteBufferPool.Get() if _, err := ExecuteFunc(template, startTag, endTag, bb, f); err != nil { bb.Reset() byteBufferPool.Put(bb) return "", err } s := string(bb.B) bb.Reset() byteBufferPool.Put(bb) return s, nil } 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) }) } // ExecuteStringStd works the same way as ExecuteString, but keeps the unknown placeholders. // This can be used as a drop-in replacement for strings.Replacer // // 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.ExecuteStringStd for frozen templates. func ExecuteStringStd(template, startTag, endTag string, m map[string]interface{}) string { return ExecuteFuncString(template, startTag, endTag, func(w io.Writer, tag string) (int, error) { return keepUnknownTagFunc(w, startTag, endTag, 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) }) } // ExecuteStd works the same way as Execute, but keeps the unknown placeholders. // This can be used as a drop-in replacement for strings.Replacer // // 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) ExecuteStd(w io.Writer, m map[string]interface{}) (int64, error) { return t.ExecuteFunc(w, func(w io.Writer, tag string) (int, error) { return keepUnknownTagFunc(w, t.startTag, t.endTag, 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 { s, err := t.ExecuteFuncStringWithErr(f) if err != nil { panic(fmt.Sprintf("unexpected error: %s", err)) } return s } // ExecuteFuncStringWithErr 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) ExecuteFuncStringWithErr(f TagFunc) (string, error) { bb := t.byteBufferPool.Get() if _, err := t.ExecuteFunc(bb, f); err != nil { bb.Reset() t.byteBufferPool.Put(bb) return "", err } s := string(bb.Bytes()) bb.Reset() t.byteBufferPool.Put(bb) return s, nil } // 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) }) } // ExecuteStringStd works the same way as ExecuteString, but keeps the unknown placeholders. // This can be used as a drop-in replacement for strings.Replacer // // 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 ExecuteStringStd for constantly changing templates. func (t *Template) ExecuteStringStd(m map[string]interface{}) string { return t.ExecuteFuncString(func(w io.Writer, tag string) (int, error) { return keepUnknownTagFunc(w, t.startTag, t.endTag, 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)) } } func keepUnknownTagFunc(w io.Writer, startTag, endTag, tag string, m map[string]interface{}) (int, error) { v, ok := m[tag] if !ok { if _, err := w.Write(unsafeString2Bytes(startTag)); err != nil { return 0, err } if _, err := w.Write(unsafeString2Bytes(tag)); err != nil { return 0, err } if _, err := w.Write(unsafeString2Bytes(endTag)); err != nil { return 0, err } return len(startTag) + len(tag) + len(endTag), nil } 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-1.2.1+ds1/template_test.go000066400000000000000000000310031415244245600242500ustar00rootroot00000000000000package fasttemplate import ( "bytes" "errors" "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 TestExecuteStd(t *testing.T) { testExecuteStd(t, "", "") testExecuteStd(t, "a", "a") testExecuteStd(t, "abc", "abc") testExecuteStd(t, "{foo}", "xxxx") testExecuteStd(t, "a{foo}", "axxxx") testExecuteStd(t, "{foo}a", "xxxxa") testExecuteStd(t, "a{foo}bc", "axxxxbc") testExecuteStd(t, "{foo}{foo}", "xxxxxxxx") testExecuteStd(t, "{foo}bar{foo}", "xxxxbarxxxx") // unclosed tag testExecuteStd(t, "{unclosed", "{unclosed") testExecuteStd(t, "{{unclosed", "{{unclosed") testExecuteStd(t, "{un{closed", "{un{closed") // test unknown tag testExecuteStd(t, "{unknown}", "{unknown}") testExecuteStd(t, "{foo}q{unexpected}{missing}bar{foo}", "xxxxq{unexpected}{missing}barxxxx") } func testExecuteStd(t *testing.T, template, expectedOutput string) { var bb bytes.Buffer ExecuteStd(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 TestExecuteStringStd(t *testing.T) { testExecuteStringStd(t, "", "") testExecuteStringStd(t, "a", "a") testExecuteStringStd(t, "abc", "abc") testExecuteStringStd(t, "{foo}", "xxxx") testExecuteStringStd(t, "a{foo}", "axxxx") testExecuteStringStd(t, "{foo}a", "xxxxa") testExecuteStringStd(t, "a{foo}bc", "axxxxbc") testExecuteStringStd(t, "{foo}{foo}", "xxxxxxxx") testExecuteStringStd(t, "{foo}bar{foo}", "xxxxbarxxxx") // unclosed tag testExecuteStringStd(t, "{unclosed", "{unclosed") testExecuteStringStd(t, "{{unclosed", "{{unclosed") testExecuteStringStd(t, "{un{closed", "{un{closed") // test unknown tag testExecuteStringStd(t, "{unknown}", "{unknown}") testExecuteStringStd(t, "{foo}q{unexpected}{missing}bar{foo}", "xxxxq{unexpected}{missing}barxxxx") } func testExecuteStringStd(t *testing.T, template, expectedOutput string) { output := ExecuteStringStd(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() } func TestExecuteFuncStringWithErr(t *testing.T) { var expectErr = errors.New("test111") result, err := ExecuteFuncStringWithErr(`{a} is {b}'s best friend`, "{", "}", func(w io.Writer, tag string) (int, error) { if tag == "a" { return w.Write([]byte("Alice")) } return 0, expectErr }) if err != expectErr { t.Fatalf("error must be the same as the error returned from f, expect: %s, actual: %s", expectErr, err) } if result != "" { t.Fatalf("result should be an empty string if error occurred") } result, err = ExecuteFuncStringWithErr(`{a} is {b}'s best friend`, "{", "}", func(w io.Writer, tag string) (int, error) { if tag == "a" { return w.Write([]byte("Alice")) } return w.Write([]byte("Bob")) }) if err != nil { t.Fatalf("should success but found err: %s", err) } if result != "Alice is Bob's best friend" { t.Fatalf("expect: %s, but: %s", "Alice is Bob's best friend", result) } } func TestTpl_ExecuteFuncStringWithErr(t *testing.T) { var expectErr = errors.New("test111") tpl, err := NewTemplate(`{a} is {b}'s best friend`, "{", "}") if err != nil { t.Fatalf("should success, but found err: %s", err) } result, err := tpl.ExecuteFuncStringWithErr(func(w io.Writer, tag string) (int, error) { if tag == "a" { return w.Write([]byte("Alice")) } return 0, expectErr }) if err != expectErr { t.Fatalf("error must be the same as the error returned from f, expect: %s, actual: %s", expectErr, err) } if result != "" { t.Fatalf("result should be an empty string if error occurred") } result, err = tpl.ExecuteFuncStringWithErr(func(w io.Writer, tag string) (int, error) { if tag == "a" { return w.Write([]byte("Alice")) } return w.Write([]byte("Bob")) }) if err != nil { t.Fatalf("should success but found err: %s", err) } if result != "Alice is Bob's best friend" { t.Fatalf("expect: %s, but: %s", "Alice is Bob's best friend", result) } } golang-github-valyala-fasttemplate-1.2.1+ds1/template_timing_test.go000066400000000000000000000170451415244245600256310ustar00rootroot00000000000000package 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}}&empty={{empty}}" 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&empty=" 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&empty=" resultStd = "http://aaasdf.foo.bar.com/?cb=12341232&width=1232&height=123&timeout=123123&uid=aaasdf&subid=asdfds&ref=http://google.com/aaa/bbb/ccc&empty={{empty}}" resultTextTemplate = "http://aaasdf.foo.bar.com/?cb=12341232&width=1232&height=123&timeout=123123&uid=aaasdf&subid=asdfds&ref=http://google.com/aaa/bbb/ccc&empty=" resultBytes = []byte(result) resultEscapedBytes = []byte(resultEscaped) resultStdBytes = []byte(resultStd) resultTextTemplateBytes = []byte(resultTextTemplate) 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&empty=", 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 != resultStd { b.Fatalf("Unexpected result\n%q\nExpected\n%q\n", x, resultStd) } } }) } 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 != resultStd { b.Fatalf("Unexpected result\n%q\nExpected\n%q\n", x, resultStd) } } }) } 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, resultTextTemplateBytes) { b.Fatalf("unexpected result\n%q\nExpected\n%q\n", x, resultTextTemplateBytes) } 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 BenchmarkFastTemplateExecuteStd(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.ExecuteStd(&w, m); err != nil { b.Fatalf("unexpected error: %s", err) } x := w.Bytes() if !bytes.Equal(x, resultStdBytes) { b.Fatalf("unexpected result\n%q\nExpected\n%q\n", x, resultStdBytes) } 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 BenchmarkFastTemplateExecuteStringStd(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.ExecuteStringStd(m) if x != resultStd { b.Fatalf("unexpected result\n%q\nExpected\n%q\n", x, resultStd) } } }) } 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) { if t, ok := m[tag]; ok { return w.Write(t.([]byte)) } return 0, nil } golang-github-valyala-fasttemplate-1.2.1+ds1/unsafe.go000066400000000000000000000005671415244245600226720ustar00rootroot00000000000000// +build !appengine package fasttemplate import ( "reflect" "unsafe" ) func unsafeBytes2String(b []byte) string { return *(*string)(unsafe.Pointer(&b)) } func unsafeString2Bytes(s string) (b []byte) { sh := (*reflect.StringHeader)(unsafe.Pointer(&s)) bh := (*reflect.SliceHeader)(unsafe.Pointer(&b)) bh.Data = sh.Data bh.Cap = sh.Len bh.Len = sh.Len return b } golang-github-valyala-fasttemplate-1.2.1+ds1/unsafe_gae.go000066400000000000000000000002521415244245600234750ustar00rootroot00000000000000// +build appengine package fasttemplate func unsafeBytes2String(b []byte) string { return string(b) } func unsafeString2Bytes(s string) []byte { return []byte(s) }