bytes.Buffer is a tremendously useful type, but it’s a bit large1.

% sizeof -p bytes Buffer Buffer 112

… and that is just the overhead, we haven’t put any data into the buffer yet.

This Friday’s2 challenge is to write a replacement for bytes.Buffer that implements io.ReadWriter and allows the caller to discover the length and capacity of the buffer.

The smallest and most creative solution wins fame, adoration, and a first run gb sticker.

Rules

The code must continue to be correctly formatted.

Points will be deducted for arguing with the judge (me).

Everything you need to win this challenge is in the description; think laterally.

Answers

As I hoped, most readers quickly figured out a good way to save a few lines was to declare Read and Write methods on a []byte , not a struct . This would lead to some small complications dereferencing the value rather than treating it as a struct with a buf []byte field, but everyone seemed to figure that out, which is good, as these are useful skills to have in your Go toolbelt.

A few readers also spotted the deliberate loophole I left in the wording of the question around obtaining the length and the capacity of the buffer. Declaring a new type with an underlying type of a slice gives you access to the len and cap , so finding the length of a slice requires no additional methods on the type.

type Buffer []byte func main() { var b Buffer b.Write([]byte("howdy") fmt.Println(len(b)) }

Thus, the core of this challenge was to define a new slice type that had Read and Write methods, which would end up taking an overhead of 3 machine words, 24 bytes on 64bit platforms, 12 on 32bit.

One nice property of this arrangement is that if you already have a []byte slice, you can convert it into a Buffer and consume zero additional storage, as you are effectively replacing the 3 words that described the []byte with 3 words which describe your new slice type.

s := []byte{0x01, 0x02, 0x03} buf := Buffer(s)

However, as usually happens with these quizzes, a solution arrives that wipes the smug smile from my face,

Kevin, I take my imaginary hat off to you, Sir.

For the record, here was the solution I came up with last night. It is longer than I hoped it would be because of the odd contract that the standard library bytes.Buffer tests require. I think a more liberal reading of the io.Reader contract would result in a smaller entry.

// A Buffer is a variable-sized buffer of bytes with Read and Write // methods. The zero value for Buffer is an empty buffer ready to use. type Buffer []byte // Write writes len(p) bytes from p to the Buffer. func (b *Buffer) Write(p []byte) (int, error) { *b = append(*b, p...) return len(p), nil } // Read reads up to len(p) bytes into p from the Buffer. func (b *Buffer) Read(p []byte) (int, error) { if len(p) == 0 { return 0, nil } if len(*b) == 0 { return 0, io.EOF } n := copy(p, *b) *b = (*b)[n:] return n, nil }

Playground link

So, prizes and glory to @rf, Ben Lubar, and @kevingillette, with special mentions to Egon Elbre, and Dan Kortschak and Douglas Clark from G+. Some of you were more correct than others, but you were all very quick, and that’s got to count for something. I’ll be in touch with your prize.

If a reader wants to debate their solution, and possibly best ours, consider this an open challenge.