Skip to content

Commit 668041e

Browse files
committed
fmt: add Append, Appendln, Appendf
These are straightforward variants of the existing Sprintf etc., but append the resulting bytes to a provided buffer rather than returning a string. Internally, there is potentially some allocation because the package uses a pool of buffers to build its output. We make no attempt to override that, so the result is first printed into the pool and then copied to the output. Since it is a managed pool, asymptotically there should be no extra allocation. Fixes #47579 RELNOTE=yes Change-Id: Icef797f9b6f0c84d03e7035d95c06cdb819e2649 Reviewed-on: https://go-review.googlesource.com/c/go/+/406177 Reviewed-by: Ian Lance Taylor <[email protected]> Run-TryBot: Ian Lance Taylor <[email protected]> Reviewed-by: Michael Knyszek <[email protected]> TryBot-Result: Gopher Robot <[email protected]>
1 parent 076039a commit 668041e

File tree

3 files changed

+78
-0
lines changed

3 files changed

+78
-0
lines changed

api/next/47579.txt

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
pkg fmt, func Append([]uint8, ...interface{}) []uint8 #47579
2+
pkg fmt, func Appendf([]uint8, string, ...interface{}) []uint8 #47579
3+
pkg fmt, func Appendln([]uint8, ...interface{}) []uint8 #47579

src/fmt/fmt_test.go

+44
Original file line numberDiff line numberDiff line change
@@ -1896,3 +1896,47 @@ func TestParsenum(t *testing.T) {
18961896
}
18971897
}
18981898
}
1899+
1900+
// Test the various Append printers. The details are well tested above;
1901+
// here we just make sure the byte slice is updated.
1902+
1903+
const (
1904+
appendResult = "hello world, 23"
1905+
hello = "hello "
1906+
)
1907+
1908+
func TestAppendf(t *testing.T) {
1909+
b := make([]byte, 100)
1910+
b = b[:copy(b, hello)]
1911+
got := Appendf(b, "world, %d", 23)
1912+
if string(got) != appendResult {
1913+
t.Fatalf("Appendf returns %q not %q", got, appendResult)
1914+
}
1915+
if &b[0] != &got[0] {
1916+
t.Fatalf("Appendf allocated a new slice")
1917+
}
1918+
}
1919+
1920+
func TestAppend(t *testing.T) {
1921+
b := make([]byte, 100)
1922+
b = b[:copy(b, hello)]
1923+
got := Append(b, "world", ", ", 23)
1924+
if string(got) != appendResult {
1925+
t.Fatalf("Append returns %q not %q", got, appendResult)
1926+
}
1927+
if &b[0] != &got[0] {
1928+
t.Fatalf("Append allocated a new slice")
1929+
}
1930+
}
1931+
1932+
func TestAppendln(t *testing.T) {
1933+
b := make([]byte, 100)
1934+
b = b[:copy(b, hello)]
1935+
got := Appendln(b, "world,", 23)
1936+
if string(got) != appendResult+"\n" {
1937+
t.Fatalf("Appendln returns %q not %q", got, appendResult+"\n")
1938+
}
1939+
if &b[0] != &got[0] {
1940+
t.Fatalf("Appendln allocated a new slice")
1941+
}
1942+
}

src/fmt/print.go

+31
Original file line numberDiff line numberDiff line change
@@ -222,6 +222,16 @@ func Sprintf(format string, a ...any) string {
222222
return s
223223
}
224224

225+
// Appendf formats according to a format specifier, appends the result to the byte
226+
// slice, and returns the updated slice.
227+
func Appendf(b []byte, format string, a ...any) []byte {
228+
p := newPrinter()
229+
p.doPrintf(format, a)
230+
b = append(b, p.buf...)
231+
p.free()
232+
return b
233+
}
234+
225235
// These routines do not take a format string
226236

227237
// Fprint formats using the default formats for its operands and writes to w.
@@ -252,6 +262,16 @@ func Sprint(a ...any) string {
252262
return s
253263
}
254264

265+
// Append formats using the default formats for its operands, appends the result to
266+
// the byte slice, and returns the updated slice.
267+
func Append(b []byte, a ...any) []byte {
268+
p := newPrinter()
269+
p.doPrint(a)
270+
b = append(b, p.buf...)
271+
p.free()
272+
return b
273+
}
274+
255275
// These routines end in 'ln', do not take a format string,
256276
// always add spaces between operands, and add a newline
257277
// after the last operand.
@@ -284,6 +304,17 @@ func Sprintln(a ...any) string {
284304
return s
285305
}
286306

307+
// Appendln formats using the default formats for its operands, appends the result
308+
// to the byte slice, and returns the updated slice. Spaces are always added
309+
// between operands and a newline is appended.
310+
func Appendln(b []byte, a ...any) []byte {
311+
p := newPrinter()
312+
p.doPrintln(a)
313+
b = append(b, p.buf...)
314+
p.free()
315+
return b
316+
}
317+
287318
// getField gets the i'th field of the struct value.
288319
// If the field is itself is an interface, return a value for
289320
// the thing inside the interface, not the interface itself.

0 commit comments

Comments
 (0)