Skip to content

proposal: bytes: add Buffer.Peek #73794

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
icholy opened this issue May 20, 2025 · 4 comments · May be fixed by #73795
Open

proposal: bytes: add Buffer.Peek #73794

icholy opened this issue May 20, 2025 · 4 comments · May be fixed by #73795
Labels
LibraryProposal Issues describing a requested change to the Go standard library or x/ libraries, but not to a tool Proposal
Milestone

Comments

@icholy
Copy link

icholy commented May 20, 2025

Proposal Details

Add a Peek method to bytes.Buffer to avoid unnecessary wrapping when passing a buffer to image.Decode.

package bytes

// Peek returns the next n bytes without advancing the buffer.
// If there are fewer than n bytes in the buffer, it returns error [io.EOF].
// The slice is only valid until the next buffer modification.
func (b *Buffer) Peek(n int) ([]byte, error)

See:

@gopherbot gopherbot added this to the Proposal milestone May 20, 2025
icholy added a commit to icholy/go that referenced this issue May 20, 2025
@icholy icholy linked a pull request May 20, 2025 that will close this issue
@gopherbot
Copy link
Contributor

Change https://go.dev/cl/674415 mentions this issue: bytes: add Buffer.Peek

@gabyhelp
Copy link

Related Issues

Related Code Changes

(Emoji vote if this was helpful or unhelpful; more detailed feedback welcome in this discussion.)

@gabyhelp gabyhelp added the LibraryProposal Issues describing a requested change to the Go standard library or x/ libraries, but not to a tool label May 20, 2025
icholy added a commit to icholy/go that referenced this issue May 20, 2025
@adonovan adonovan moved this to Incoming in Proposals May 20, 2025
@CAFxX
Copy link
Contributor

CAFxX commented May 20, 2025

Or maybe we can just add something like this?

func asReader(r io.Reader) reader {
	if rr, ok := r.(reader); ok {
		return rr
	} else if bb, ok := r.(*bytes.Buffer); ok {
		return bytesBufferAdapter{bb}
	}
	return bufio.NewReader(r)
}

type bytesBufferAdapter struct{ *bytes.Buffer }

func (a bytesBufferAdapter) Peek(n int) ([]byte, error) {
	b := a.Bytes()
	if len(b) < n {
		return b, io.EOF
	}
	return b[:n], nil
}

bufio already imports bytes anyway.

Alternatively you could just do the same when passing the *bytes.Buffer...

@apparentlymart
Copy link

I think this functionality makes sense, but I was initially confused between this and the existing Buffer.AvailableBuffer method, since the noun "buffer" is representing all sorts of different things here!

In retrospect I guess I might've named Buffer.AvailableBuffer as Buffer.ExtraCapacity instead, to distinguish more strongly between the active part of the buffer and its tail of unused additional capacity.

But since that ship has already sailed, perhaps this can be addressed with just some careful wording in the documentation, possibly including an analogy to Buffer.Read.

Thinking down that path made me also wonder about a slightly different behavior:

  • If there's at least one byte remaining in the buffer, returns a slice covering either the first n bytes of the buffer or the entire buffer (whichever is shortest) and no error.
  • If there are zero bytes remaining in the buffer, returns io.EOF.

This definition makes it more directly analogous to Read, such that it could potentially be defined as "Like Buffer.Read, except that it does not advance the buffer's read position.".

That would make it slightly different than the current reader.Peek in package image, but it seems like the code that uses it would tolerate a "short peek" already:

go/src/image/format.go

Lines 59 to 69 in c8bf388

func match(magic string, b []byte) bool {
if len(magic) != len(b) {
return false
}
for i, c := range b {
if magic[i] != c && magic[i] != '?' {
return false
}
}
return true
}

(the if len(magic) != len(b) branch handles that case)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
LibraryProposal Issues describing a requested change to the Go standard library or x/ libraries, but not to a tool Proposal
Projects
Status: Incoming
Development

Successfully merging a pull request may close this issue.

5 participants