perf: use io.WriteString in writeString to avoid heap allocation (#2809)

writeString converted every string to []byte before calling
io.Writer.Write, which Go escape analysis reports as escaping to the
heap. io.WriteString uses the io.StringWriter fast path when available
(the standard printer writer is a *bufio.Writer) and falls back to
Write([]byte(txt)) otherwise.

Fixes #2807

Co-authored-by: Pablo Garcia <pablito@MacBook-Neo-de-Pablo.local>
This commit is contained in:
MsfPablo
2026-08-19 13:43:22 +10:00
committed by GitHub
co-authored by Pablo Garcia
parent d6543ac3fe
commit 5c302ee313
2 changed files with 42 additions and 1 deletions
+1 -1
View File
@@ -50,7 +50,7 @@ func readStream(filename string) (io.Reader, error) {
}
func writeString(writer io.Writer, txt string) error {
_, errorWriting := writer.Write([]byte(txt))
_, errorWriting := io.WriteString(writer, txt)
return errorWriting
}
+41
View File
@@ -0,0 +1,41 @@
package yqlib
import (
"bufio"
"bytes"
"io"
"testing"
"github.com/mikefarah/yq/v4/test"
)
// plainWriter only implements io.Writer, so io.WriteString must fall back to Write.
type plainWriter struct {
buf bytes.Buffer
}
func (w *plainWriter) Write(p []byte) (int, error) {
return w.buf.Write(p)
}
func TestWriteStringToStringWriter(t *testing.T) {
var buf bytes.Buffer
writer := bufio.NewWriter(&buf)
test.AssertResult(t, nil, writeString(writer, "hello world"))
test.AssertResult(t, nil, writer.Flush())
test.AssertResult(t, "hello world", buf.String())
}
func TestWriteStringToPlainWriter(t *testing.T) {
writer := &plainWriter{}
test.AssertResult(t, nil, writeString(writer, "hello world"))
test.AssertResult(t, "hello world", writer.buf.String())
}
func TestWriteStringDoesNotAllocate(t *testing.T) {
writer := bufio.NewWriter(io.Discard)
allocations := testing.AllocsPerRun(100, func() {
_ = writeString(writer, "hello world")
})
test.AssertResult(t, 0.0, allocations)
}