From 3951e6049509628778ba47a0f6c2a93b92af058a Mon Sep 17 00:00:00 2001 From: Saddam Azy Date: Thu, 27 Feb 2025 08:48:50 +0700 Subject: [PATCH] increase performance by migrating from bytes.Buffer to strings.Builder --- bench_example_test.go | 8 ++++---- property.go | 13 ++++++------- utilities.go | 21 ++++++++++----------- 3 files changed, 20 insertions(+), 22 deletions(-) diff --git a/bench_example_test.go b/bench_example_test.go index ba9ebe5..e7eb5a1 100644 --- a/bench_example_test.go +++ b/bench_example_test.go @@ -1,15 +1,15 @@ package goquery import ( - "bytes" "fmt" "strconv" + "strings" "testing" ) func BenchmarkMetalReviewExample(b *testing.B) { var n int - var buf bytes.Buffer + var builder strings.Builder b.StopTimer() doc := loadDoc("metalreview.html") @@ -27,12 +27,12 @@ func BenchmarkMetalReviewExample(b *testing.B) { if score, e = strconv.ParseFloat(s.Find(".score").Text(), 64); e != nil { // Not a valid float, ignore score if n <= 4 { - buf.WriteString(fmt.Sprintf("Review %d: %s - %s.\n", i, band, title)) + builder.WriteString(fmt.Sprintf("Review %d: %s - %s.\n", i, band, title)) } } else { // Print all, including score if n <= 4 { - buf.WriteString(fmt.Sprintf("Review %d: %s - %s (%2.1f).\n", i, band, title, score)) + builder.WriteString(fmt.Sprintf("Review %d: %s - %s (%2.1f).\n", i, band, title, score)) } } }) diff --git a/property.go b/property.go index 411126d..f1c80b9 100644 --- a/property.go +++ b/property.go @@ -1,7 +1,6 @@ package goquery import ( - "bytes" "regexp" "strings" @@ -60,14 +59,14 @@ func (s *Selection) SetAttr(attrName, val string) *Selection { // Text gets the combined text contents of each element in the set of matched // elements, including their descendants. func (s *Selection) Text() string { - var buf bytes.Buffer + var builder strings.Builder // Slightly optimized vs calling Each: no single selection object created var f func(*html.Node) f = func(n *html.Node) { if n.Type == html.TextNode { // Keep newlines and spaces, like jQuery - buf.WriteString(n.Data) + builder.WriteString(n.Data) } if n.FirstChild != nil { for c := n.FirstChild; c != nil; c = c.NextSibling { @@ -79,7 +78,7 @@ func (s *Selection) Text() string { f(n) } - return buf.String() + return builder.String() } // Size is an alias for Length. @@ -97,16 +96,16 @@ func (s *Selection) Length() int { func (s *Selection) Html() (ret string, e error) { // Since there is no .innerHtml, the HTML content must be re-created from // the nodes using html.Render. - var buf bytes.Buffer + var builder strings.Builder if len(s.Nodes) > 0 { for c := s.Nodes[0].FirstChild; c != nil; c = c.NextSibling { - e = html.Render(&buf, c) + e = html.Render(&builder, c) if e != nil { return } } - ret = buf.String() + ret = builder.String() } return diff --git a/utilities.go b/utilities.go index ecd3453..361795b 100644 --- a/utilities.go +++ b/utilities.go @@ -1,8 +1,8 @@ package goquery import ( - "bytes" "io" + "strings" "golang.org/x/net/html" ) @@ -26,13 +26,12 @@ var nodeNames = []string{ // Go's net/html package defines the following node types, listed with // the corresponding returned value from this function: // -// ErrorNode : #error -// TextNode : #text -// DocumentNode : #document -// ElementNode : the element's tag name -// CommentNode : #comment -// DoctypeNode : the name of the document type -// +// ErrorNode : #error +// TextNode : #text +// DocumentNode : #document +// ElementNode : the element's tag name +// CommentNode : #comment +// DoctypeNode : the name of the document type func NodeName(s *Selection) string { if s.Length() == 0 { return "" @@ -77,11 +76,11 @@ func Render(w io.Writer, s *Selection) error { // because this is not a jQuery method (in javascript-land, this is // a property provided by the DOM). func OuterHtml(s *Selection) (string, error) { - var buf bytes.Buffer - if err := Render(&buf, s); err != nil { + var builder strings.Builder + if err := Render(&builder, s); err != nil { return "", err } - return buf.String(), nil + return builder.String(), nil } // Loop through all container nodes to search for the target node.