Skip to content

mem: add Buffer.Slice()#8977

Merged
easwars merged 6 commits into
grpc:masterfrom
ash2k:ash2k/slice
Apr 7, 2026
Merged

mem: add Buffer.Slice()#8977
easwars merged 6 commits into
grpc:masterfrom
ash2k:ash2k/slice

Conversation

@ash2k

@ash2k ash2k commented Mar 17, 2026

Copy link
Copy Markdown
Contributor

I have a CodecV2 that gives me a mem.BufferSlice (i.e. exposes what it gets from gRPC directly). I need to get another mem.BufferSlice from it that is a subslice. In some scenarios I need to slice the first and/or the last buffer to get only the subset of data. Hence, having a Slice() method on the Buffer type would be really useful.

Another use case: I want to use BufferSlice.Reader and implement a Peek()(or similar) to get a BufferSlice of the next N bytes. I don't want a []byte or [][]byte, I'd like to return this BufferSlice from a codecv2 and let gRPC free the buffers when it no longer needs them (i.e. I don't want to wrap those individual []byte into BufferSlices).

RELEASE NOTES:

  • mem: add mem.Buffer.Slice() - new method to slice the buffer like a slice.

@codecov

codecov Bot commented Mar 17, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 83.18%. Comparing base (aa4d281) to head (f57d232).
⚠️ Report is 1 commits behind head on master.

Additional details and impacted files
@@            Coverage Diff             @@
##           master    #8977      +/-   ##
==========================================
+ Coverage   83.02%   83.18%   +0.15%     
==========================================
  Files         413      413              
  Lines       33229    33249      +20     
==========================================
+ Hits        27589    27658      +69     
+ Misses       4221     4190      -31     
+ Partials     1419     1401      -18     
Files with missing lines Coverage Δ
mem/buffer_slice.go 96.45% <100.00%> (ø)
mem/buffers.go 90.26% <100.00%> (+2.09%) ⬆️

... and 29 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@ash2k ash2k force-pushed the ash2k/slice branch 2 times, most recently from a405abf to 89ce1f0 Compare March 17, 2026 06:16
@easwars easwars requested a review from arjan-bal March 24, 2026 03:46

@arjan-bal arjan-bal left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The new API looks good. I left a few minor comments.

I was also planning to introduce something similar in this diff to eliminate the copy performed in bufio.Reader.

Just FYI, the zero-copy buffer struct used in Rust also has a very similar API.

Comment thread mem/buffers.go
Comment thread mem/buffers.go Outdated
Comment thread mem/buffers.go
Comment thread mem/buffers_test.go Outdated
Comment thread mem/buffers_test.go Outdated
Comment thread mem/buffers_test.go Outdated
Comment thread mem/buffers_test.go Outdated
@arjan-bal

Copy link
Copy Markdown
Contributor

Please also merge the master branch to unblock some CI workflows that were changed recently.

@arjan-bal arjan-bal assigned ash2k and easwars and unassigned arjan-bal Mar 24, 2026
@arjan-bal arjan-bal added this to the 1.81 Release milestone Mar 24, 2026
@arjan-bal arjan-bal added the Type: Feature New features or improvements in behavior label Mar 24, 2026
@ash2k

ash2k commented Mar 25, 2026

Copy link
Copy Markdown
Contributor Author

@arjan-bal All done!

@arjan-bal arjan-bal assigned arjan-bal and unassigned ash2k Mar 26, 2026

@arjan-bal arjan-bal left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, adding a second reviewer.

@arjan-bal arjan-bal removed their assignment Mar 26, 2026
Comment thread mem/buffers_test.go Outdated
Comment thread mem/buffers_test.go Outdated
}

func (s) TestBuffer_Slice(t *testing.T) {
ready := false

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is the ready bit really required? We only call mem.NewBuffer once. Maybe I'm missing something?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It helps to ensure the buffer is not freed earlier than its safe to do so.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It just feels a little weird to have a value that it set by the test goroutine and the value is checked here in the Put method of the pool. Instead, if you can have a channel that is closed when the Put method is called, or a grpcsync.Event that is fired when the Put method is called, the main test goroutine can do the following:

  • after the first slice is freed, ensure that the event hasn't fired (or if using a channel, ensure that a read on the channel does not return with a short deadline)
  • after the second slice is freed, do the same thing
  • after the root buffer is freed, ensure that the event fires or the channel is closed

Would that work?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The code matches the existing pattern (see freed above). But I've changed it to use a channel.

Comment thread mem/buffers_test.go
Comment thread mem/buffers_test.go Outdated
Comment thread mem/buffers_test.go
}
}

func (s) TestBuffer_Slice(t *testing.T) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, do we need this test at all?

Can't we fold this into TestBuffer_SliceBasic and TestBuffer_SliceSubSlice?

Like, we don't have to check if slice is freed too early by explicitly checking in the ctor. We can let ReadOnlyData detect that.

And similarly, we don't have to check if the buffer is freed explicitly, as long as we use the default buffer pool. The leakchecker will do that for us.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So you're saying we can delete this test? I don't see NewLeakChecker used anywhere.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The leakchecker that I mentioned is implemented here: https://github.com/grpc/grpc-go/tree/master/internal/leakcheck and it gets invoked on all tests.

But I'm fine with leaving this test as is.

Comment thread mem/buffers_test.go
}
}

func (s) TestBuffer_SliceEmpty(t *testing.T) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we do this test for all ctors as well?

@ash2k ash2k Mar 27, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We already have a case for this {0, 0, []byte{}},. I can remove it and instead do it in this test.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, that would be nicer. Thanks.

Comment thread mem/buffers_test.go Outdated
}
tests := []struct {
name string
buf func() mem.Buffer

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we use the ctor logic here as well?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you elaborate? I'm not sure what you mean.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wanted to see if we can do something like what we do in the other tests: for _, c := range ctors { ... }

@easwars easwars assigned ash2k and unassigned easwars Mar 26, 2026
@Pranjali-2501 Pranjali-2501 assigned easwars and unassigned ash2k Mar 31, 2026
Comment thread mem/buffers_test.go Outdated
return mem.SliceBuffer(data)
}

func newEmptyBuf(_ []byte) mem.Buffer {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: The underscore identifier can be omitted.

Comment thread mem/buffers_test.go
}
}

func (s) TestBuffer_Slice(t *testing.T) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The leakchecker that I mentioned is implemented here: https://github.com/grpc/grpc-go/tree/master/internal/leakcheck and it gets invoked on all tests.

But I'm fine with leaving this test as is.

Comment thread mem/buffers_test.go
}
}

func (s) TestBuffer_SliceEmpty(t *testing.T) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, that would be nicer. Thanks.

Comment thread mem/buffers_test.go Outdated
}
tests := []struct {
name string
buf func() mem.Buffer

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wanted to see if we can do something like what we do in the other tests: for _, c := range ctors { ... }

@easwars easwars assigned ash2k and unassigned easwars Mar 31, 2026
@ash2k

ash2k commented Apr 3, 2026

Copy link
Copy Markdown
Contributor Author

@easwars Thanks for the review! I've addressed the feedback. Please let me know if I missed something.

@arjan-bal arjan-bal assigned easwars and unassigned ash2k Apr 7, 2026

@easwars easwars left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for your contribution!

@easwars easwars merged commit 5fdb6d0 into grpc:master Apr 7, 2026
14 checks passed
@ash2k ash2k deleted the ash2k/slice branch April 8, 2026 05:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Type: Feature New features or improvements in behavior

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants