More strings.Builder and io.Pipe

This commit is contained in:
Jan-Lukas Else 2023-01-24 14:30:53 +01:00
parent ffe7065621
commit e8a5e9eb0e
11 changed files with 68 additions and 96 deletions

View File

@ -13,7 +13,6 @@ import (
"github.com/samber/lo"
"github.com/spf13/cast"
"go.goblog.app/app/pkgs/bufferpool"
"go.goblog.app/app/pkgs/contenttype"
"gopkg.in/yaml.v3"
)
@ -76,14 +75,12 @@ func (a *goBlog) serveMicropubQuery(w http.ResponseWriter, r *http.Request) {
a.serve404(w, r)
return
}
buf := bufferpool.Get()
defer bufferpool.Put(buf)
if err := json.NewEncoder(buf).Encode(result); err != nil {
a.serveError(w, r, "Failed to encode json", http.StatusInternalServerError)
return
}
pr, pw := io.Pipe()
go func() {
_ = pw.CloseWithError(json.NewEncoder(pw).Encode(result))
}()
w.Header().Set(contentType, contenttype.JSONUTF8)
_ = a.min.Get().Minify(contenttype.JSON, w, buf)
_ = pr.CloseWithError(a.min.Get().Minify(contenttype.JSON, w, pr))
}
func (a *goBlog) getMicropubChannelsMap() []map[string]any {

View File

@ -5,29 +5,24 @@ import (
"io"
"net/http"
"go.goblog.app/app/pkgs/bufferpool"
"go.goblog.app/app/pkgs/contenttype"
)
func (a *goBlog) serveNodeInfoDiscover(w http.ResponseWriter, r *http.Request) {
buf := bufferpool.Get()
defer bufferpool.Put(buf)
err := json.NewEncoder(buf).Encode(map[string]any{
result := map[string]any{
"links": []map[string]any{
{
"href": a.getFullAddress("/nodeinfo"),
"rel": "http://nodeinfo.diaspora.software/ns/schema/2.1",
},
},
})
if err != nil {
a.serveError(w, r, "", http.StatusInternalServerError)
return
}
pr, pw := io.Pipe()
go func() {
_ = pw.CloseWithError(json.NewEncoder(pw).Encode(result))
}()
w.Header().Set(contentType, contenttype.JSONUTF8)
mw := a.min.Get().Writer(contenttype.JSON, w)
_, _ = io.Copy(mw, buf)
_ = mw.Close()
_ = pr.CloseWithError(a.min.Get().Minify(contenttype.JSON, w, pr))
}
func (a *goBlog) serveNodeInfo(w http.ResponseWriter, r *http.Request) {
@ -35,9 +30,7 @@ func (a *goBlog) serveNodeInfo(w http.ResponseWriter, r *http.Request) {
status: []postStatus{statusPublished},
visibility: []postVisibility{visibilityPublic},
})
buf := bufferpool.Get()
defer bufferpool.Put(buf)
if err := json.NewEncoder(buf).Encode(map[string]any{
result := map[string]any{
"version": "2.1",
"software": map[string]any{
"name": "goblog",
@ -55,10 +48,11 @@ func (a *goBlog) serveNodeInfo(w http.ResponseWriter, r *http.Request) {
"webmention",
},
"metadata": map[string]any{},
}); err != nil {
a.serveError(w, r, "", http.StatusInternalServerError)
return
}
pr, pw := io.Pipe()
go func() {
_ = pw.CloseWithError(json.NewEncoder(pw).Encode(result))
}()
w.Header().Set(contentType, contenttype.JSONUTF8)
_ = a.min.Get().Minify(contenttype.JSON, w, buf)
_ = pr.CloseWithError(a.min.Get().Minify(contenttype.JSON, w, pr))
}

View File

@ -2,9 +2,9 @@ package main
import (
"encoding/xml"
"io"
"net/http"
"go.goblog.app/app/pkgs/bufferpool"
"go.goblog.app/app/pkgs/contenttype"
)
@ -49,15 +49,13 @@ func (a *goBlog) serveOpenSearch(w http.ResponseWriter, r *http.Request) {
},
SearchForm: sURL,
}
buf := bufferpool.Get()
defer bufferpool.Put(buf)
_, _ = buf.WriteString(xml.Header)
if err := xml.NewEncoder(buf).Encode(openSearch); err != nil {
a.serveError(w, r, "", http.StatusInternalServerError)
return
}
pr, pw := io.Pipe()
go func() {
_, _ = io.WriteString(pw, xml.Header)
_ = pw.CloseWithError(xml.NewEncoder(pw).Encode(openSearch))
}()
w.Header().Set(contentType, "application/opensearchdescription+xml"+contenttype.CharsetUtf8Suffix)
_ = a.min.Get().Minify(contenttype.XML, w, buf)
_ = pr.CloseWithError(a.min.Get().Minify(contenttype.XML, w, pr))
}
func openSearchUrl(b *configBlog) string {

View File

@ -2,13 +2,13 @@ package highlighting
import (
"io"
"strings"
"github.com/yuin/goldmark"
"github.com/yuin/goldmark/ast"
"github.com/yuin/goldmark/renderer"
"github.com/yuin/goldmark/renderer/html"
"github.com/yuin/goldmark/util"
"go.goblog.app/app/pkgs/bufferpool"
"github.com/alecthomas/chroma/v2"
chromahtml "github.com/alecthomas/chroma/v2/formatters/html"
@ -60,8 +60,7 @@ func (r *htmlRenderer) renderFencedCodeBlock(w util.BufWriter, source []byte, no
n := node.(*ast.FencedCodeBlock)
// Read code block content.
buf := bufferpool.Get()
defer bufferpool.Put(buf)
var buf strings.Builder
for _, line := range n.Lines().Sliced(0, n.Lines().Len()) {
buf.Write(line.Value(source))
}
@ -70,7 +69,7 @@ func (r *htmlRenderer) renderFencedCodeBlock(w util.BufWriter, source []byte, no
if highlight(w, buf.String(), string(n.Language(source)), r.formatter) != nil {
// Highlight failed, fallback to plain text.
_, _ = w.WriteString("<pre><code>")
r.Writer.RawWrite(w, buf.Bytes())
r.Writer.RawWrite(w, []byte(buf.String()))
_, _ = w.WriteString("</code></pre>\n")
}

View File

@ -206,8 +206,7 @@ func (db *database) savePost(p *post, o *postCreationOptions) error {
db.pcm.Lock()
defer db.pcm.Unlock()
// Build SQL
sqlBuilder := bufferpool.Get()
defer bufferpool.Put(sqlBuilder)
var sqlBuilder strings.Builder
var sqlArgs = []any{dbNoCache}
// Start transaction
sqlBuilder.WriteString("begin;")
@ -336,7 +335,7 @@ func (db *database) replacePostParam(path, param string, values []string) error
db.pcm.Lock()
defer db.pcm.Unlock()
// Build SQL
sqlBuilder := bufferpool.Get()
var sqlBuilder strings.Builder
var sqlArgs = []any{dbNoCache}
// Start transaction
sqlBuilder.WriteString("begin;")
@ -352,7 +351,6 @@ func (db *database) replacePostParam(path, param string, values []string) error
sqlBuilder.WriteString("commit;")
// Execute
_, err := db.Exec(sqlBuilder.String(), sqlArgs...)
bufferpool.Put(sqlBuilder)
if err != nil {
return err
}
@ -387,8 +385,7 @@ type postsRequestConfig struct {
}
func buildPostsQuery(c *postsRequestConfig, selection string) (query string, args []any) {
queryBuilder := bufferpool.Get()
defer bufferpool.Put(queryBuilder)
var queryBuilder strings.Builder
// Selection
queryBuilder.WriteString("select ")
queryBuilder.WriteString(selection)
@ -413,11 +410,11 @@ func buildPostsQuery(c *postsRequestConfig, selection string) (query string, arg
queryBuilder.WriteString(", ")
}
named := "status" + strconv.Itoa(i)
queryBuilder.WriteByte('@')
queryBuilder.WriteString("@")
queryBuilder.WriteString(named)
args = append(args, sql.Named(named, status))
}
queryBuilder.WriteByte(')')
queryBuilder.WriteString(")")
}
if c.visibility != nil && len(c.visibility) > 0 {
queryBuilder.WriteString(" and visibility in (")
@ -426,11 +423,11 @@ func buildPostsQuery(c *postsRequestConfig, selection string) (query string, arg
queryBuilder.WriteString(", ")
}
named := "visibility" + strconv.Itoa(i)
queryBuilder.WriteByte('@')
queryBuilder.WriteString("@")
queryBuilder.WriteString(named)
args = append(args, sql.Named(named, visibility))
}
queryBuilder.WriteByte(')')
queryBuilder.WriteString(")")
}
if c.blog != "" {
queryBuilder.WriteString(" and blog = @blog")
@ -451,7 +448,7 @@ func buildPostsQuery(c *postsRequestConfig, selection string) (query string, arg
queryBuilder.WriteString(", ")
}
named := "param" + strconv.Itoa(i)
queryBuilder.WriteByte('@')
queryBuilder.WriteString("@")
queryBuilder.WriteString(named)
args = append(args, param)
}
@ -477,11 +474,11 @@ func buildPostsQuery(c *postsRequestConfig, selection string) (query string, arg
queryBuilder.WriteString(", ")
}
named := "section" + strconv.Itoa(i)
queryBuilder.WriteByte('@')
queryBuilder.WriteString("@")
queryBuilder.WriteString(named)
args = append(args, sql.Named(named, section))
}
queryBuilder.WriteByte(')')
queryBuilder.WriteString(")")
}
if c.publishedYear != 0 {
queryBuilder.WriteString(" and substr(tolocal(published), 1, 4) = @publishedyear")
@ -522,8 +519,7 @@ func (d *database) loadPostParameters(posts []*post, parameters ...string) (err
}
// Build query
sqlArgs := make([]any, 0)
queryBuilder := bufferpool.Get()
defer bufferpool.Put(queryBuilder)
var queryBuilder strings.Builder
queryBuilder.WriteString("select path, parameter, value from post_parameters where")
// Paths
queryBuilder.WriteString(" path in (")
@ -532,11 +528,11 @@ func (d *database) loadPostParameters(posts []*post, parameters ...string) (err
queryBuilder.WriteString(", ")
}
named := "path" + strconv.Itoa(i)
queryBuilder.WriteByte('@')
queryBuilder.WriteString("@")
queryBuilder.WriteString(named)
sqlArgs = append(sqlArgs, sql.Named(named, p.Path))
}
queryBuilder.WriteByte(')')
queryBuilder.WriteString(")")
// Parameters
if len(parameters) > 0 {
queryBuilder.WriteString(" and parameter in (")
@ -545,11 +541,11 @@ func (d *database) loadPostParameters(posts []*post, parameters ...string) (err
queryBuilder.WriteString(", ")
}
named := "param" + strconv.Itoa(i)
queryBuilder.WriteByte('@')
queryBuilder.WriteString("@")
queryBuilder.WriteString(named)
sqlArgs = append(sqlArgs, sql.Named(named, p))
}
queryBuilder.WriteByte(')')
queryBuilder.WriteString(")")
}
// Order
queryBuilder.WriteString(" order by id")
@ -695,7 +691,7 @@ group by name;
func (db *database) usesOfMediaFile(names ...string) (counts []int, err error) {
sqlArgs := []any{dbNoCache}
nameValues := bufferpool.Get()
var nameValues strings.Builder
for i, n := range names {
if i > 0 {
nameValues.WriteString(", ")
@ -703,11 +699,10 @@ func (db *database) usesOfMediaFile(names ...string) (counts []int, err error) {
named := "name" + strconv.Itoa(i)
nameValues.WriteString("(@")
nameValues.WriteString(named)
nameValues.WriteByte(')')
nameValues.WriteString(")")
sqlArgs = append(sqlArgs, sql.Named(named, n))
}
rows, err := db.Query(fmt.Sprintf(mediaUseSql, nameValues.String()), sqlArgs...)
bufferpool.Put(nameValues)
if err != nil {
return nil, err
}

View File

@ -5,10 +5,10 @@ import (
"errors"
"io"
"net/http"
"strings"
"github.com/dgraph-io/ristretto"
"github.com/samber/lo"
"go.goblog.app/app/pkgs/bufferpool"
"go.goblog.app/app/pkgs/contenttype"
)
@ -88,15 +88,12 @@ func (a *goBlog) getReactions(w http.ResponseWriter, r *http.Request) {
a.serveError(w, r, "", http.StatusInternalServerError)
return
}
buf := bufferpool.Get()
defer bufferpool.Put(buf)
err = json.NewEncoder(buf).Encode(reactions)
if err != nil {
a.serveError(w, r, "", http.StatusInternalServerError)
return
}
pr, pw := io.Pipe()
go func() {
_ = pw.CloseWithError(json.NewEncoder(pw).Encode(reactions))
}()
w.Header().Set(contentType, contenttype.JSONUTF8)
_, _ = io.Copy(w, buf)
_ = pr.CloseWithError(a.min.Get().Minify(contenttype.JSON, w, pr))
}
func (a *goBlog) getReactionsFromDatabase(path string) (map[string]int, error) {
@ -110,8 +107,7 @@ func (a *goBlog) getReactionsFromDatabase(path string) (map[string]int, error) {
// Get reactions
res, err, _ := a.reactionsSfg.Do(path, func() (any, error) {
// Build query
sqlBuf := bufferpool.Get()
defer bufferpool.Put(sqlBuf)
var sqlBuf strings.Builder
sqlArgs := []any{}
sqlBuf.WriteString("select reaction, count from reactions where path=? and reaction in (")
sqlArgs = append(sqlArgs, path)

View File

@ -136,13 +136,13 @@ func Test_reactionsHighLevel(t *testing.T) {
rec = httptest.NewRecorder()
app.getReactions(rec, req)
assert.Equal(t, http.StatusOK, rec.Code)
assert.Equal(t, "{\"❤️\":1}\n", rec.Body.String())
assert.Equal(t, "{\"❤️\":1}", rec.Body.String())
// Get reactions for a non-existing post
req = httptest.NewRequest(http.MethodGet, "/?path=/non-existing-post", nil)
rec = httptest.NewRecorder()
app.getReactions(rec, req)
assert.Equal(t, http.StatusOK, rec.Code)
assert.Equal(t, "{}\n", rec.Body.String())
assert.Equal(t, "{}", rec.Body.String())
}

View File

@ -3,12 +3,12 @@ package main
import (
"database/sql"
"encoding/xml"
"io"
"net/http"
"time"
"github.com/araddon/dateparse"
"github.com/snabb/sitemap"
"go.goblog.app/app/pkgs/bufferpool"
"go.goblog.app/app/pkgs/contenttype"
)
@ -174,18 +174,16 @@ func (a *goBlog) serveSitemapBlogPosts(w http.ResponseWriter, r *http.Request) {
}
func (a *goBlog) writeSitemapXML(w http.ResponseWriter, r *http.Request, sm any) {
buf := bufferpool.Get()
defer bufferpool.Put(buf)
_, _ = buf.WriteString(xml.Header)
_, _ = buf.WriteString(`<?xml-stylesheet type="text/xsl" href="`)
_, _ = buf.WriteString(a.assetFileName("sitemap.xsl"))
_, _ = buf.WriteString(`" ?>`)
if err := xml.NewEncoder(buf).Encode(sm); err != nil {
a.serveError(w, r, "Failed to encode sitemap", http.StatusInternalServerError)
return
}
pr, pw := io.Pipe()
go func() {
_, _ = io.WriteString(pw, xml.Header)
_, _ = io.WriteString(pw, `<?xml-stylesheet type="text/xsl" href="`)
_, _ = io.WriteString(pw, a.assetFileName("sitemap.xsl"))
_, _ = io.WriteString(pw, `" ?>`)
_ = pw.CloseWithError(xml.NewEncoder(pw).Encode(sm))
}()
w.Header().Set(contentType, contenttype.XMLUTF8)
_ = a.min.Get().Minify(contenttype.XML, w, buf)
_ = pr.CloseWithError(a.min.Get().Minify(contenttype.XML, w, pr))
}
const sitemapDatePathsSql = `

View File

@ -5,9 +5,9 @@ import (
"log"
"net/url"
"strconv"
"strings"
tgbotapi "github.com/go-telegram-bot-api/telegram-bot-api/v5"
"go.goblog.app/app/pkgs/bufferpool"
)
func (a *goBlog) initTelegram() {
@ -135,8 +135,7 @@ func (tg *configTelegram) generateHTML(title, fullURL, shortURL string) (html st
if !tg.enabled() {
return ""
}
message := bufferpool.Get()
defer bufferpool.Put(message)
var message strings.Builder
if title != "" {
message.WriteString(tgbotapi.EscapeText(tgbotapi.ModeHTML, title))
message.WriteString("\n\n")

View File

@ -6,7 +6,6 @@ import (
"time"
"github.com/samber/lo"
"go.goblog.app/app/pkgs/bufferpool"
"go.goblog.app/app/pkgs/htmlbuilder"
)
@ -65,7 +64,7 @@ func (a *goBlog) renderSummary(hb *htmlbuilder.HtmlBuilder, bc *configBlog, p *p
}
// Show link to full post
hb.WriteElementOpen("p")
prefix := bufferpool.Get()
var prefix strings.Builder
if len(photos) > 0 {
// Contains photos
prefix.WriteString("🖼️")
@ -75,10 +74,9 @@ func (a *goBlog) renderSummary(hb *htmlbuilder.HtmlBuilder, bc *configBlog, p *p
prefix.WriteString("🗺️")
}
if prefix.Len() > 0 {
prefix.WriteRune(' ')
prefix.WriteString(" ")
hb.WriteEscaped(prefix.String())
}
bufferpool.Put(prefix)
hb.WriteElementOpen("a", "class", "u-url", "href", p.Path)
hb.WriteEscaped(a.ts.GetTemplateStringVariant(bc.Lang, "view"))
hb.WriteElementClose("a")

View File

@ -8,7 +8,6 @@ import (
"strings"
"time"
"go.goblog.app/app/pkgs/bufferpool"
"go.goblog.app/app/pkgs/contenttype"
)
@ -231,8 +230,7 @@ type webmentionsRequestConfig struct {
}
func buildWebmentionsQuery(config *webmentionsRequestConfig) (query string, args []any) {
queryBuilder := bufferpool.Get()
defer bufferpool.Put(queryBuilder)
var queryBuilder strings.Builder
queryBuilder.WriteString("select id, source, target, url, created, title, content, author, status from webmentions ")
if config != nil {
queryBuilder.WriteString("where 1")