From 5c302ee313dab3cb22a09397e0a24b3fa06f8501 Mon Sep 17 00:00:00 2001 From: MsfPablo <129399053+MsfPablo@users.noreply.github.com> Date: Wed, 19 Aug 2026 05:43:22 +0200 Subject: [PATCH] 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 --- pkg/yqlib/utils.go | 2 +- pkg/yqlib/utils_test.go | 41 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 42 insertions(+), 1 deletion(-) create mode 100644 pkg/yqlib/utils_test.go diff --git a/pkg/yqlib/utils.go b/pkg/yqlib/utils.go index 2843e42d..e4036f03 100644 --- a/pkg/yqlib/utils.go +++ b/pkg/yqlib/utils.go @@ -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 } diff --git a/pkg/yqlib/utils_test.go b/pkg/yqlib/utils_test.go new file mode 100644 index 00000000..fc1b0026 --- /dev/null +++ b/pkg/yqlib/utils_test.go @@ -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) +}