diff --git a/general_test.go b/general_test.go new file mode 100644 index 00000000..eb6f4701 --- /dev/null +++ b/general_test.go @@ -0,0 +1,59 @@ +package main +import "testing" +import "io/ioutil" +import "html/template" + +func BenchmarkTemplates(b *testing.B) { + user := User{0,"Bob",0,false,false,false,false,false,false,"",false,"","","","",""} + admin := User{1,"Admin",0,true,true,true,true,true,false,"",false,"","","","",""} + var noticeList map[int]string = make(map[int]string) + noticeList[0] = "test" + + topic := TopicUser{0,"Lol",template.HTML("Hey everyone!"),0,false,false,"",0,"","","",no_css_tmpl,0,"","","",""} + var replyList map[int]interface{} = make(map[int]interface{}) + replyList[0] = Reply{0,0,"Hey everyone!",template.HTML("Hey everyone!"),0,"","",0,0,"",no_css_tmpl,0,"","","",""} + pi := Page{"Topic Blah","topic",user,noticeList,replyList,topic} + pi2 := Page{"Topic Blah","topic",admin,noticeList,replyList,topic} + w := ioutil.Discard + + b.Run("compiled_writer_collated_useradmin", func(b *testing.B) { + for i := 0; i < b.N; i++ { + template_topic(pi2,w) + } + }) + b.Run("compiled_writer_useradmin", func(b *testing.B) { + for i := 0; i < b.N; i++ { + template_topic2(pi2,w) + } + }) + b.Run("compiled_useradmin", func(b *testing.B) { + for i := 0; i < b.N; i++ { + w.Write([]byte(template_topic3(pi2))) + } + }) + b.Run("interpreted_useradmin", func(b *testing.B) { + for i := 0; i < b.N; i++ { + templates.ExecuteTemplate(w,"topic.html", pi2) + } + }) + b.Run("compiled_writer_collated_userguest", func(b *testing.B) { + for i := 0; i < b.N; i++ { + template_topic(pi,w) + } + }) + b.Run("compiled_writer_userguest", func(b *testing.B) { + for i := 0; i < b.N; i++ { + template_topic2(pi,w) + } + }) + b.Run("compiled_userguest", func(b *testing.B) { + for i := 0; i < b.N; i++ { + w.Write([]byte(template_topic3(pi))) + } + }) + b.Run("interpreted_userguest", func(b *testing.B) { + for i := 0; i < b.N; i++ { + templates.ExecuteTemplate(w,"topic.html", pi) + } + }) +} diff --git a/gosora.exe b/gosora.exe index 3ee01cd4..e07ce32f 100644 Binary files a/gosora.exe and b/gosora.exe differ diff --git a/images/gosora-test.PNG b/images/gosora-test.PNG new file mode 100644 index 00000000..dfc527c6 Binary files /dev/null and b/images/gosora-test.PNG differ diff --git a/main.go b/main.go index 97715121..ec5cabb5 100644 --- a/main.go +++ b/main.go @@ -8,6 +8,7 @@ import ( "mime" "strings" "path/filepath" + "io" "io/ioutil" "os" "html/template" @@ -31,7 +32,7 @@ var external_sites map[string]string = make(map[string]string) var groups map[int]Group = make(map[int]Group) var forums map[int]Forum = make(map[int]Forum) var static_files map[string]SFile = make(map[string]SFile) -var ctemplates map[string]func(Page)string = make(map[string]func(Page)string) +var ctemplates map[string]func(Page,io.Writer) = make(map[string]func(Page,io.Writer)) func compile_templates() { var c CTemplateSet diff --git a/routes.go b/routes.go index 35a6c906..3181c301 100644 --- a/routes.go +++ b/routes.go @@ -413,7 +413,7 @@ func route_topic_id(w http.ResponseWriter, r *http.Request){ pi := Page{topic.Title,"topic",user,noticeList,replyList,topic} if ctemplates["topic"] != nil { - w.Write([]byte(ctemplates["topic"](pi))) + ctemplates["topic"](pi,w) } else { err = templates.ExecuteTemplate(w,"topic.html", pi) if err != nil { diff --git a/template_profile.go b/template_profile.go index 5e69cfad..5c4ca506 100644 --- a/template_profile.go +++ b/template_profile.go @@ -1,176 +1,130 @@ package main +import "io" import "strconv" func init() { ctemplates["profile"] = template_profile } -func template_profile(tmpl_profile_vars Page) (tmpl string) { +func template_profile(tmpl_profile_vars Page, w io.Writer) { var extra_data User = tmpl_profile_vars.Something.(User) -tmpl += ` +w.Write([]byte(` - ` -tmpl += tmpl_profile_vars.Title -tmpl += ` + ` + tmpl_profile_vars.Title + `
-` -tmpl += ` +`)) if len(tmpl_profile_vars.NoticeList) != 0 { for _, item := range tmpl_profile_vars.NoticeList { -tmpl += `
` -tmpl += item -tmpl += `
` +w.Write([]byte(`
` + item + `
`)) } } -tmpl += ` +w.Write([]byte(`
-
+
- ` -tmpl += extra_data.Name -tmpl += `` + ` + extra_data.Name + ``)) if extra_data.Tag != "" { -tmpl += `` -tmpl += extra_data.Tag -tmpl += `` +w.Write([]byte(`` + extra_data.Tag + ``)) } -tmpl += ` +w.Write([]byte(`
Add Friend - ` + `)) if tmpl_profile_vars.CurrentUser.Is_Super_Mod && !extra_data.Is_Super_Mod { -tmpl += ` - ` +w.Write([]byte(` + `)) if extra_data.Is_Banned { -tmpl += `Unban` +w.Write([]byte(`Unban`)) } else { -tmpl += `Ban` +w.Write([]byte(`Ban`)) } -tmpl += ` - ` +w.Write([]byte(` + `)) } -tmpl += ` - Report +w.Write([]byte(` + Report
- ` + `)) if len(tmpl_profile_vars.ItemList) != 0 { for _, item := range tmpl_profile_vars.ItemList { -tmpl += ` -
- ` -tmpl += string(item.(Reply).ContentHtml) -tmpl += ` +w.Write([]byte(`"> + ` + string(item.(Reply).ContentHtml) + `

- ` -tmpl += item.(Reply).CreatedByName -tmpl += ` - ` + ` + item.(Reply).CreatedByName + ` + `)) if tmpl_profile_vars.CurrentUser.Is_Mod { -tmpl += ` - ` +w.Write([]byte(` + `)) } -tmpl += ` - - ` +w.Write([]byte(` + + `)) if item.(Reply).Tag != "" { -tmpl += `` -tmpl += item.(Reply).Tag -tmpl += `` +w.Write([]byte(`` + item.(Reply).Tag + ``)) } -tmpl += ` -
` +w.Write([]byte(` +
`)) } } -tmpl += ` +w.Write([]byte(`
-` +`)) if !tmpl_profile_vars.CurrentUser.Is_Banned { -tmpl += ` +w.Write([]byte(`
- +
@@ -179,13 +133,11 @@ tmpl += `' type="hidden" />
-` +`)) } -tmpl += ` -` -tmpl += ` +w.Write([]byte(` + -` -return tmpl +`)) } diff --git a/template_topic.go b/template_topic.go index 8836abfc..cc9078d0 100644 --- a/template_topic.go +++ b/template_topic.go @@ -1,246 +1,168 @@ package main +import "io" import "strconv" +import "html/template" func init() { ctemplates["topic"] = template_topic } -func template_topic(tmpl_topic_vars Page) (tmpl string) { +func template_topic(tmpl_topic_vars Page, w io.Writer) { var extra_data TopicUser = tmpl_topic_vars.Something.(TopicUser) -tmpl += ` +w.Write([]byte(` - ` -tmpl += tmpl_topic_vars.Title -tmpl += ` + ` + tmpl_topic_vars.Title + `
-` -tmpl += ` +`)) if len(tmpl_topic_vars.NoticeList) != 0 { for _, item := range tmpl_topic_vars.NoticeList { -tmpl += `
` -tmpl += item -tmpl += `
` +w.Write([]byte(`
` + item + `
`)) } } -tmpl += ` +w.Write([]byte(`
-
-
+
- ` -tmpl += extra_data.Title -tmpl += ` - ` -tmpl += extra_data.Status -tmpl += ` +w.Write([]byte(`> + ` + extra_data.Title + ` + ` + extra_data.Status + ` Status - ` + `)) if tmpl_topic_vars.CurrentUser.Is_Mod { -tmpl += ` - Edit - Delete - ` +w.Write([]byte(` + Edit + Delete + `)) if extra_data.Sticky { -tmpl += `Unpin` +w.Write([]byte(`Unpin`)) } else { -tmpl += `Pin` +w.Write([]byte(`Pin`)) } -tmpl += ` +w.Write([]byte(` - + - ` + `)) } -tmpl += ` - Report +w.Write([]byte(` + Report
-
- ` -tmpl += string(tmpl_topic_varsstring(.Something.(TopicUser).Content)) -tmpl += ` - +w.Write([]byte(`"> + ` + string(extra_data.Content.(template.HTML)) + ` +

- ` -tmpl += extra_data.CreatedByName -tmpl += ` - ` + ` + extra_data.CreatedByName + ` + `)) if extra_data.Tag != "" { -tmpl += `` -tmpl += extra_data.Tag -tmpl += `` +w.Write([]byte(`` + extra_data.Tag + ``)) } else { if extra_data.URLName != "" { -tmpl += `` -tmpl += extra_data.URLName -tmpl += ` - ` -tmpl += extra_data.URLPrefix -tmpl += `` +w.Write([]byte(`` + extra_data.URLName + ` + ` + extra_data.URLPrefix + ``)) } } -tmpl += ` +w.Write([]byte(`

- ` + `)) if len(tmpl_topic_vars.ItemList) != 0 { for _, item := range tmpl_topic_vars.ItemList { -tmpl += ` -
- ` -tmpl += string(item.(Reply).ContentHtml) -tmpl += ` +w.Write([]byte(`"> + ` + string(item.(Reply).ContentHtml) + `

- ` -tmpl += item.(Reply).CreatedByName -tmpl += ` - ` + ` + item.(Reply).CreatedByName + ` + `)) if tmpl_topic_vars.CurrentUser.Is_Mod { -tmpl += ` - ` +w.Write([]byte(` + `)) } -tmpl += ` - - ` +w.Write([]byte(` + + `)) if item.(Reply).Tag != "" { -tmpl += `` -tmpl += item.(Reply).Tag -tmpl += `` +w.Write([]byte(`` + item.(Reply).Tag + ``)) } else { if item.(Reply).URLName != "" { -tmpl += `` -tmpl += item.(Reply).URLName -tmpl += ` - ` -tmpl += item.(Reply).URLPrefix -tmpl += `` +w.Write([]byte(`` + item.(Reply).URLName + ` + ` + item.(Reply).URLPrefix + ``)) } } -tmpl += ` -
` +w.Write([]byte(` +
`)) } } -tmpl += ` +w.Write([]byte(`
-` +`)) if !tmpl_topic_vars.CurrentUser.Is_Banned { -tmpl += ` +w.Write([]byte(`
- +
@@ -249,13 +171,11 @@ tmpl += `' type="hidden" />
-` +`)) } -tmpl += ` -` -tmpl += ` +w.Write([]byte(` + -` -return tmpl +`)) } diff --git a/template_topic_old.go b/template_topic_old.go new file mode 100644 index 00000000..81d54454 --- /dev/null +++ b/template_topic_old.go @@ -0,0 +1,262 @@ +package main +import "strconv" +import "html/template" + +//func init() { +//ctemplates["topic2"] = template_topic2 +//} + +func template_topic3(tmpl_topic_vars Page) (tmpl string) { +var extra_data TopicUser = tmpl_topic_vars.Something.(TopicUser) +tmpl += ` + + + ` +tmpl += tmpl_topic_vars.Title +tmpl += ` + + + + + + +
+` +tmpl += `` +tmpl += ` +` +if len(tmpl_topic_vars.NoticeList) != 0 { +for _, item := range tmpl_topic_vars.NoticeList { +tmpl += `
` +tmpl += item +tmpl += `
` +} +} +tmpl += ` +
+
+
+ ` +tmpl += extra_data.Title +tmpl += ` + ` +tmpl += extra_data.Status +tmpl += ` + Status + ` +if tmpl_topic_vars.CurrentUser.Is_Mod { +tmpl += ` + Edit + Delete + ` +if extra_data.Sticky { +tmpl += `Unpin` +} else { +tmpl += `Pin` +} +tmpl += ` + + + + + ` +} +tmpl += ` + Report +
+
+
+
+
+ ` +tmpl += string(extra_data.Content.(template.HTML)) +tmpl += ` + +

+ ` +tmpl += extra_data.CreatedByName +tmpl += ` + ` +if extra_data.Tag != "" { +tmpl += `` +tmpl += extra_data.Tag +tmpl += `` +} else { +if extra_data.URLName != "" { +tmpl += `` +tmpl += extra_data.URLName +tmpl += ` + ` +tmpl += extra_data.URLPrefix +tmpl += `` +} +} +tmpl += ` +
+

+
+ ` +if len(tmpl_topic_vars.ItemList) != 0 { +for _, item := range tmpl_topic_vars.ItemList { +tmpl += ` +
+ ` +tmpl += string(item.(Reply).ContentHtml) +tmpl += ` +

+ ` +tmpl += item.(Reply).CreatedByName +tmpl += ` + ` +if tmpl_topic_vars.CurrentUser.Is_Mod { +tmpl += ` + ` +} +tmpl += ` + + ` +if item.(Reply).Tag != "" { +tmpl += `` +tmpl += item.(Reply).Tag +tmpl += `` +} else { +if item.(Reply).URLName != "" { +tmpl += `` +tmpl += item.(Reply).URLName +tmpl += ` + ` +tmpl += item.(Reply).URLPrefix +tmpl += `` +} +} +tmpl += ` +
` +} +} +tmpl += ` +
+` +if !tmpl_topic_vars.CurrentUser.Is_Banned { +tmpl += ` +
+
+ +
+
+
+
+
+
+
+
+` +} +tmpl += ` +` +tmpl += ` +
+ +` +return tmpl +} diff --git a/template_topic_writer.go b/template_topic_writer.go new file mode 100644 index 00000000..9e406069 --- /dev/null +++ b/template_topic_writer.go @@ -0,0 +1,262 @@ +package main +import "io" +import "strconv" +import "html/template" + +/*func init() { +ctemplates["topic"] = template_topic +}*/ + +func template_topic2(tmpl_topic_vars Page, w io.Writer) { +var extra_data TopicUser = tmpl_topic_vars.Something.(TopicUser) +w.Write([]byte(` + + + `)) +w.Write([]byte(tmpl_topic_vars.Title)) +w.Write([]byte(` + + + + + + +
+`)) +w.Write([]byte(``)) +w.Write([]byte(` +`)) +if len(tmpl_topic_vars.NoticeList) != 0 { +for _, item := range tmpl_topic_vars.NoticeList { +w.Write([]byte(`
`)) +w.Write([]byte(item)) +w.Write([]byte(`
`)) +} +} +w.Write([]byte(` +
+
+
+ `)) +w.Write([]byte(extra_data.Title)) +w.Write([]byte(` + `)) +w.Write([]byte(extra_data.Status)) +w.Write([]byte(` + Status + `)) +if tmpl_topic_vars.CurrentUser.Is_Mod { +w.Write([]byte(` + Edit + Delete + `)) +if extra_data.Sticky { +w.Write([]byte(`Unpin`)) +} else { +w.Write([]byte(`Pin`)) +} +w.Write([]byte(` + + + + + `)) +} +w.Write([]byte(` + Report +
+
+
+
+
+ `)) +w.Write([]byte(string(extra_data.Content.(template.HTML)))) +w.Write([]byte(` + +

+ `)) +w.Write([]byte(extra_data.CreatedByName)) +w.Write([]byte(` + `)) +if extra_data.Tag != "" { +w.Write([]byte(``)) +w.Write([]byte(extra_data.Tag)) +w.Write([]byte(``)) +} else { +if extra_data.URLName != "" { +w.Write([]byte(``)) +w.Write([]byte(extra_data.URLName)) +w.Write([]byte(` + `)) +w.Write([]byte(extra_data.URLPrefix)) +w.Write([]byte(``)) +} +} +w.Write([]byte(` +
+

+
+ `)) +if len(tmpl_topic_vars.ItemList) != 0 { +for _, item := range tmpl_topic_vars.ItemList { +w.Write([]byte(` +
+ `)) +w.Write([]byte(string(item.(Reply).ContentHtml))) +w.Write([]byte(` +

+ `)) +w.Write([]byte(item.(Reply).CreatedByName)) +w.Write([]byte(` + `)) +if tmpl_topic_vars.CurrentUser.Is_Mod { +w.Write([]byte(` + `)) +} +w.Write([]byte(` + + `)) +if item.(Reply).Tag != "" { +w.Write([]byte(``)) +w.Write([]byte(item.(Reply).Tag)) +w.Write([]byte(``)) +} else { +if item.(Reply).URLName != "" { +w.Write([]byte(``)) +w.Write([]byte(item.(Reply).URLName)) +w.Write([]byte(` + `)) +w.Write([]byte(item.(Reply).URLPrefix)) +w.Write([]byte(``)) +} +} +w.Write([]byte(` +
`)) +} +} +w.Write([]byte(` +
+`)) +if !tmpl_topic_vars.CurrentUser.Is_Banned { +w.Write([]byte(` +
+
+ +
+
+
+
+
+
+
+
+`)) +} +w.Write([]byte(` +`)) +w.Write([]byte(` +
+ +`)) +} diff --git a/templates.go b/templates.go index c76b85b5..617a1f78 100644 --- a/templates.go +++ b/templates.go @@ -2,6 +2,7 @@ package main import "log" import "fmt" import "strings" +import "strconv" import "reflect" import "path/filepath" import "io/ioutil" @@ -29,6 +30,9 @@ type CTemplateSet struct importMap map[string]string varList map[string]VarItem localVars map[string]map[string]VarItemReflect + stats map[string]int + pVarList string + pVarPosition int //tempVars map[string]string doImports bool expectsInt interface{} @@ -48,8 +52,12 @@ func (c *CTemplateSet) compile_template(name string, dir string, expects string, c.funcMap["lt"] = true c.funcMap["ne"] = true c.importMap = make(map[string]string) + c.importMap["io"] = "io" c.importMap["strconv"] = "strconv" c.varList = varList + //c.pVarList = "" + //c.pVarPosition = 0 + c.stats = make(map[string]int) c.expectsInt = expectsInt holdreflect := reflect.ValueOf(expectsInt) @@ -108,7 +116,16 @@ func (c *CTemplateSet) compile_template(name string, dir string, expects string, varString += "var " + varItem.Name + " " + varItem.Type + " = " + varItem.Destination + "\n" } - out = "package main\n" + importList + "\nfunc init() {\nctemplates[\"" + fname + "\"] = template_" + fname + "\n}\n\nfunc template_" + fname + "(tmpl_" + fname + "_vars " + expects + ") (tmpl string) {\n" + varString + out + "return tmpl\n}\n" + out = "package main\n" + importList + c.pVarList + "\nfunc init() {\nctemplates[\"" + fname + "\"] = template_" + fname + "\n}\n\nfunc template_" + fname + "(tmpl_" + fname + "_vars " + expects + ", w io.Writer) {\n" + varString + out + "}\n" + + out = strings.Replace(out,`)) +w.Write([]byte(`," + ",-1) + out = strings.Replace(out,"` + `","",-1) + + for index, count := range c.stats { + fmt.Println(index + ": " + strconv.Itoa(count)) + } + if debug { fmt.Println("Output!") fmt.Println(out) @@ -210,7 +227,7 @@ func (c *CTemplateSet) compile_switch(varholder string, holdreflect reflect.Valu } return c.compile_subtemplate(varholder, holdreflect, node) case *parse.TextNode: - return "tmpl += `" + string(node.Text) + "`\n" + return "w.Write([]byte(`" + string(node.Text) + "`))\n" default: panic("Unknown Node in main switch") } @@ -246,8 +263,12 @@ func (c *CTemplateSet) compile_subswitch(varholder string, holdreflect reflect.V cur = cur.FieldByName(id) if cur.Kind() == reflect.Interface { cur = cur.Elem() - if cur.Kind() == reflect.String && cur.Type().Name() != "string" { - varbit = "string(" + varbit + "." + id + ")" + /*if cur.Kind() == reflect.String && cur.Type().Name() != "string" { + varbit = "string(" + varbit + "." + id + ")"*/ + //if cur.Kind() == reflect.String && cur.Type().Name() != "string" { + if cur.Type().PkgPath() != "main" { + c.importMap["html/template"] = "html/template" + varbit += "." + id + ".(" + strings.TrimPrefix(cur.Type().PkgPath(),"html/") + "." + cur.Type().Name() + ")" } else { varbit += "." + id + ".(" + cur.Type().Name() + ")" } @@ -283,7 +304,7 @@ func (c *CTemplateSet) compile_subswitch(varholder string, holdreflect reflect.V } out, _ = c.compile_if_varsub(n.String(), varholder, template_name, holdreflect) - return "tmpl += " + out + "\n" + return "w.Write([]byte(" + out + "))\n" case *parse.StringNode: return n.Quoted default: @@ -445,30 +466,6 @@ func (c *CTemplateSet) compile_reflectswitch(varholder string, holdreflect refle fmt.Println(node.Args) } return "", outVal - /*case *parse.IdentifierNode: - fmt.Println("Identifier Node: ") - fmt.Println(node) - fmt.Println(node.Args) - - ArgLoop: - for pos, id := range node.Args { - fmt.Println(id) - switch id.String() { - case "not": - out += "!" - case "or": - out += " || " - case "and": - out += " && " - case "le": - out += c.compile_if_varsub_n(node.Args[pos + 1].String(), varholder, holdreflect) + " <= " + c.compile_if_varsub_n(node.Args[pos + 2].String(), varholder, holdreflect) - break ArgLoop - default: - fmt.Println("Variable!") - out += c.compile_if_varsub_n(id.String(), varholder, holdreflect) - } - } - return out*/ case *parse.DotNode: return varholder, holdreflect case *parse.NilNode: @@ -551,6 +548,14 @@ func (c *CTemplateSet) compile_if_varsub(varname string, varholder string, templ out = strings.Replace(out, varItem.Destination, varItem.Name, 1) } } + + _, ok := c.stats[out] + if ok { + c.stats[out]++ + } else { + c.stats[out] = 1 + } + return out, cur } @@ -578,23 +583,30 @@ func (c *CTemplateSet) compile_varsub(varname string, val reflect.Value) string } } + _, ok := c.stats[varname] + if ok { + c.stats[varname]++ + } else { + c.stats[varname] = 1 + } + if val.Kind() == reflect.Interface { val = val.Elem() } switch val.Kind() { case reflect.Int: - return "tmpl += strconv.Itoa(" + varname + ")\n" + return "w.Write([]byte(strconv.Itoa(" + varname + ")))\n" case reflect.Bool: - return "if " + varname + " {\ntmpl += \"true\"} else {\ntmpl += \"false\"\n}\n" + return "if " + varname + " {\nw.Write([]byte(\"true\"))} else {\nw.Write([]byte(\"false\"))\n}\n" case reflect.String: - if val.Type().Name() != "string" { - return "tmpl += string(" + varname + ")\n" + if val.Type().Name() != "string" && !strings.HasPrefix(varname,"string(") { + return "w.Write([]byte(string(" + varname + ")))\n" } else { - return "tmpl += " + varname + "\n" + return "w.Write([]byte(" + varname + "))\n" } case reflect.Int64: - return "tmpl += strconv.FormatInt(" + varname + ", 10)" + return "w.Write([]byte(strconv.FormatInt(" + varname + ", 10)))" default: fmt.Println("Unknown Kind: ") fmt.Println(val.Kind())