Skip to content

Commit

Permalink
[tailscale1.17] bufio: add Writer.AvailableBuffer
Browse files Browse the repository at this point in the history
This adds a new Writer.AvailableBuffer method that returns
an empty buffer with a possibly non-empty capacity for use
with append-like APIs.

The typical usage pattern is something like:
	b := bw.AvailableBuffer()
	b = appendValue(b, v)
	bw.Write(b)

It allows logic combining append-like APIs with bufio.Writer to avoid
needing to allocate and manage buffers themselves and allows the
append-like APIs to directly write into the buffer for a bufio.Writer.

Fixes golang#47527

Change-Id: I9cd169f3f8e8c7cd40818caf3daf1944c826fc66
Reviewed-on: https://go-review.googlesource.com/c/go/+/345569
Trust: Joe Tsai <joetsai@digital-static.net>
Run-TryBot: Joe Tsai <joetsai@digital-static.net>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Ian Lance Taylor <iant@golang.org>
(cherry picked from golang.org/cl/345569)
  • Loading branch information
dsnet committed Jan 19, 2022
1 parent 02953ff commit e91ed21
Show file tree
Hide file tree
Showing 3 changed files with 53 additions and 0 deletions.
8 changes: 8 additions & 0 deletions src/bufio/bufio.go
Expand Up @@ -623,6 +623,14 @@ func (b *Writer) Flush() error {
// Available returns how many bytes are unused in the buffer.
func (b *Writer) Available() int { return len(b.buf) - b.n }

// AvailableBuffer returns an empty buffer with b.Available() capacity.
// This buffer is intended to be appended to and
// passed to an immediately succeeding Write call.
// The buffer is only valid until the next write operation on b.
func (b *Writer) AvailableBuffer() []byte {
return b.buf[b.n:][:0]
}

// Buffered returns the number of bytes that have been written into the current buffer.
func (b *Writer) Buffered() int { return b.n }

Expand Down
33 changes: 33 additions & 0 deletions src/bufio/bufio_test.go
Expand Up @@ -10,6 +10,8 @@ import (
"errors"
"fmt"
"io"
"math/rand"
"strconv"
"strings"
"testing"
"testing/iotest"
Expand Down Expand Up @@ -608,6 +610,37 @@ func TestWriter(t *testing.T) {
}
}

func TestWriterAppend(t *testing.T) {
got := new(bytes.Buffer)
var want []byte
rn := rand.New(rand.NewSource(0))
w := NewWriterSize(got, 64)
for i := 0; i < 100; i++ {
// Obtain a buffer to append to.
b := w.AvailableBuffer()
if w.Available() != cap(b) {
t.Fatalf("Available() = %v, want %v", w.Available(), cap(b))
}

// While not recommended, it is valid to append to a shifted buffer.
// This forces Write to copy the the input.
if rn.Intn(8) == 0 && cap(b) > 0 {
b = b[1:1:cap(b)]
}

// Append a random integer of varying width.
n := int64(rn.Intn(1 << rn.Intn(30)))
want = append(strconv.AppendInt(want, n, 10), ' ')
b = append(strconv.AppendInt(b, n, 10), ' ')
w.Write(b)
}
w.Flush()

if !bytes.Equal(got.Bytes(), want) {
t.Errorf("output mismatch:\ngot %s\nwant %s", got.Bytes(), want)
}
}

// Check that write errors are returned properly.

type errorWriterTest struct {
Expand Down
12 changes: 12 additions & 0 deletions src/bufio/example_test.go
Expand Up @@ -20,6 +20,18 @@ func ExampleWriter() {
// Output: Hello, world!
}

func ExampleWriter_AvailableBuffer() {
w := bufio.NewWriter(os.Stdout)
for _, i := range []int64{1, 2, 3, 4} {
b := w.AvailableBuffer()
b = strconv.AppendInt(b, i, 10)
b = append(b, ' ')
w.Write(b)
}
w.Flush()
// Output: 1 2 3 4
}

// The simplest use of a Scanner, to read standard input as a set of lines.
func ExampleScanner_lines() {
scanner := bufio.NewScanner(os.Stdin)
Expand Down

0 comments on commit e91ed21

Please sign in to comment.