reduce allocs by re-using string builder
This commit is contained in:
parent
faf33ef93d
commit
93bb1d64ba
7 changed files with 40 additions and 48 deletions
|
|
@ -5,17 +5,18 @@ import (
|
||||||
"net/http"
|
"net/http"
|
||||||
"reflect"
|
"reflect"
|
||||||
"runtime"
|
"runtime"
|
||||||
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Headers = map[string]string
|
type Headers = map[string]string
|
||||||
|
|
||||||
type Partial struct {
|
type Partial struct {
|
||||||
Headers *Headers
|
Headers *Headers
|
||||||
Root string
|
Root *Element
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Partial) Render() string {
|
func (p *Partial) Render(builder *strings.Builder) {
|
||||||
return p.Root
|
p.Root.Render(builder)
|
||||||
}
|
}
|
||||||
|
|
||||||
type Page struct {
|
type Page struct {
|
||||||
|
|
@ -30,23 +31,23 @@ func NewPage(root Ren) *Page {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewPageWithHttpMethod(httpMethod string, root Ren) *Page {
|
func NewPageWithHttpMethod(httpMethod string, root *Element) *Page {
|
||||||
return &Page{
|
return &Page{
|
||||||
HttpMethod: httpMethod,
|
HttpMethod: httpMethod,
|
||||||
Root: root,
|
Root: root,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewPartialWithHeaders(headers *Headers, root Ren) *Partial {
|
func NewPartialWithHeaders(headers *Headers, root *Element) *Partial {
|
||||||
return &Partial{
|
return &Partial{
|
||||||
Headers: headers,
|
Headers: headers,
|
||||||
Root: root.Render(),
|
Root: root,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewPartial(root Ren) *Partial {
|
func NewPartial(root *Element) *Partial {
|
||||||
return &Partial{
|
return &Partial{
|
||||||
Root: root.Render(),
|
Root: root,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -69,12 +69,12 @@ func Increment(amount int) JsCommand {
|
||||||
|
|
||||||
func SetInnerHtml(r Ren) JsCommand {
|
func SetInnerHtml(r Ren) JsCommand {
|
||||||
// language=JavaScript
|
// language=JavaScript
|
||||||
return JsCommand{Command: fmt.Sprintf("this.innerHTML = `%s`", r.Render())}
|
return JsCommand{Command: fmt.Sprintf("this.innerHTML = `%s`", Render(r))}
|
||||||
}
|
}
|
||||||
|
|
||||||
func SetOuterHtml(r Ren) JsCommand {
|
func SetOuterHtml(r Ren) JsCommand {
|
||||||
// language=JavaScript
|
// language=JavaScript
|
||||||
return JsCommand{Command: fmt.Sprintf("this.outerHTML = `%s`", r.Render())}
|
return JsCommand{Command: fmt.Sprintf("this.outerHTML = `%s`", Render(r))}
|
||||||
}
|
}
|
||||||
|
|
||||||
func AddAttribute(name, value string) JsCommand {
|
func AddAttribute(name, value string) JsCommand {
|
||||||
|
|
|
||||||
|
|
@ -2,13 +2,15 @@ package h
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
func Render(node Ren) string {
|
func Render(node Ren) string {
|
||||||
start := time.Now()
|
start := time.Now()
|
||||||
html := node.Render()
|
builder := &strings.Builder{}
|
||||||
|
node.Render(builder)
|
||||||
duration := time.Since(start)
|
duration := time.Since(start)
|
||||||
fmt.Printf("render took %d microseconds\n", duration.Microseconds())
|
fmt.Printf("render took %d microseconds\n", duration.Microseconds())
|
||||||
return html
|
return builder.String()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,15 +5,13 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (node *Element) Render() string {
|
func (node *Element) Render(builder *strings.Builder) {
|
||||||
builder := &strings.Builder{}
|
|
||||||
|
|
||||||
// some elements may not have a tag, such as a Fragment
|
// some elements may not have a tag, such as a Fragment
|
||||||
if node.tag != "" {
|
if node.tag != "" {
|
||||||
builder.WriteString("<" + node.tag)
|
builder.WriteString("<" + node.tag)
|
||||||
builder.WriteString(" ")
|
builder.WriteString(" ")
|
||||||
for name, value := range node.attributes {
|
for name, value := range node.attributes {
|
||||||
builder.WriteString(NewAttribute(name, value).Render())
|
NewAttribute(name, value).Render(builder)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -34,9 +32,9 @@ func (node *Element) Render() string {
|
||||||
for _, child := range node.children {
|
for _, child := range node.children {
|
||||||
switch child.(type) {
|
switch child.(type) {
|
||||||
case *AttributeMap:
|
case *AttributeMap:
|
||||||
builder.WriteString(child.(*AttributeMap).Render())
|
child.Render(builder)
|
||||||
case *LifeCycle:
|
case *LifeCycle:
|
||||||
builder.WriteString(child.(*LifeCycle).Render())
|
child.Render(builder)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -53,52 +51,42 @@ func (node *Element) Render() string {
|
||||||
case *LifeCycle:
|
case *LifeCycle:
|
||||||
continue
|
continue
|
||||||
default:
|
default:
|
||||||
builder.WriteString(child.Render())
|
child.Render(builder)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if node.tag != "" {
|
if node.tag != "" {
|
||||||
builder.WriteString("</" + node.tag + ">")
|
builder.WriteString("</" + node.tag + ">")
|
||||||
}
|
}
|
||||||
|
|
||||||
str := builder.String()
|
|
||||||
return str
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *AttributeR) Render() string {
|
func (a *AttributeR) Render(builder *strings.Builder) {
|
||||||
return fmt.Sprintf(`%s="%s"`, a.Name, a.Value)
|
builder.WriteString(fmt.Sprintf(`%s="%s"`, a.Name, a.Value))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *TextContent) Render() string {
|
func (t *TextContent) Render(builder *strings.Builder) {
|
||||||
return t.Content
|
builder.WriteString(t.Content)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *RawContent) Render() string {
|
func (r *RawContent) Render(builder *strings.Builder) {
|
||||||
return r.Content
|
builder.WriteString(r.Content)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *ChildList) Render() string {
|
func (c *ChildList) Render(builder *strings.Builder) {
|
||||||
builder := &strings.Builder{}
|
|
||||||
for _, child := range c.Children {
|
for _, child := range c.Children {
|
||||||
builder.WriteString(child.Render())
|
child.Render(builder)
|
||||||
}
|
}
|
||||||
str := builder.String()
|
|
||||||
return str
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *AttributeMap) Render() string {
|
func (m *AttributeMap) Render(builder *strings.Builder) {
|
||||||
builder := &strings.Builder{}
|
|
||||||
m2 := m.ToMap()
|
m2 := m.ToMap()
|
||||||
|
|
||||||
for k, v := range m2 {
|
for k, v := range m2 {
|
||||||
builder.WriteString(NewAttribute(k, v).Render())
|
NewAttribute(k, v).Render(builder)
|
||||||
}
|
}
|
||||||
|
|
||||||
str := builder.String()
|
|
||||||
return str
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *LifeCycle) Render() string {
|
func (l *LifeCycle) Render(builder *strings.Builder) {
|
||||||
m := make(map[string]string)
|
m := make(map[string]string)
|
||||||
|
|
||||||
for event, commands := range l.handlers {
|
for event, commands := range l.handlers {
|
||||||
|
|
@ -114,8 +102,7 @@ func (l *LifeCycle) Render() string {
|
||||||
children = append(children, Attribute(event, js))
|
children = append(children, Attribute(event, js))
|
||||||
}
|
}
|
||||||
|
|
||||||
result := Children(children...).Render()
|
Children(children...).Render(builder)
|
||||||
return result
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *AttributeMap) ToMap() map[string]string {
|
func (m *AttributeMap) ToMap() map[string]string {
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@ type Element struct {
|
||||||
children []Ren
|
children []Ren
|
||||||
}
|
}
|
||||||
|
|
||||||
func (node *Element) AppendChild(child Ren) Ren {
|
func (node *Element) AppendChild(child Ren) *Element {
|
||||||
node.children = append(node.children, child)
|
node.children = append(node.children, child)
|
||||||
return node
|
return node
|
||||||
}
|
}
|
||||||
|
|
@ -363,7 +363,7 @@ func List[T any](items []T, mapper func(item T, index int) *Element) *Element {
|
||||||
return node
|
return node
|
||||||
}
|
}
|
||||||
|
|
||||||
func Fragment(children ...Ren) Ren {
|
func Fragment(children ...Ren) *Element {
|
||||||
return &Element{
|
return &Element{
|
||||||
tag: "",
|
tag: "",
|
||||||
children: children,
|
children: children,
|
||||||
|
|
@ -541,11 +541,11 @@ func NewSwap(selector string, content *Element) SwapArg {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func OobSwap(ctx *RequestContext, content *Element) Ren {
|
func OobSwap(ctx *RequestContext, content *Element) *Element {
|
||||||
return OobSwapWithSelector(ctx, "", content)
|
return OobSwapWithSelector(ctx, "", content)
|
||||||
}
|
}
|
||||||
|
|
||||||
func OobSwapWithSelector(ctx *RequestContext, selector string, content *Element) Ren {
|
func OobSwapWithSelector(ctx *RequestContext, selector string, content *Element) *Element {
|
||||||
if ctx == nil || ctx.Get("HX-Request") == "" {
|
if ctx == nil || ctx.Get("HX-Request") == "" {
|
||||||
return Empty()
|
return Empty()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,10 +4,11 @@ import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"github.com/labstack/echo/v4"
|
"github.com/labstack/echo/v4"
|
||||||
"net/url"
|
"net/url"
|
||||||
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Ren interface {
|
type Ren interface {
|
||||||
Render() string
|
Render(builder *strings.Builder)
|
||||||
}
|
}
|
||||||
|
|
||||||
func Ternary[T any](value bool, a T, b T) T {
|
func Ternary[T any](value bool, a T, b T) T {
|
||||||
|
|
|
||||||
|
|
@ -215,7 +215,8 @@ func UpdateName(ctx *h.RequestContext) *h.Partial {
|
||||||
return h.NewPartial(h.Div(h.Text("failed to update")))
|
return h.NewPartial(h.Div(h.Text("failed to update")))
|
||||||
}
|
}
|
||||||
|
|
||||||
return h.NewPartial(h.OobSwap(ctx, Task(task, false)))
|
return h.NewPartial(
|
||||||
|
h.OobSwap(ctx, Task(task, false)))
|
||||||
}
|
}
|
||||||
|
|
||||||
func EditNameForm(ctx *h.RequestContext) *h.Partial {
|
func EditNameForm(ctx *h.RequestContext) *h.Partial {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue