mem: add Buffer.Slice()#8977
Conversation
Codecov Report✅ All modified and coverable lines are covered by tests. 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
🚀 New features to boost your workflow:
|
a405abf to
89ce1f0
Compare
arjan-bal
left a comment
There was a problem hiding this comment.
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.
|
Please also merge the master branch to unblock some CI workflows that were changed recently. |
|
@arjan-bal All done! |
arjan-bal
left a comment
There was a problem hiding this comment.
LGTM, adding a second reviewer.
| } | ||
|
|
||
| func (s) TestBuffer_Slice(t *testing.T) { | ||
| ready := false |
There was a problem hiding this comment.
Is the ready bit really required? We only call mem.NewBuffer once. Maybe I'm missing something?
There was a problem hiding this comment.
It helps to ensure the buffer is not freed earlier than its safe to do so.
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
The code matches the existing pattern (see freed above). But I've changed it to use a channel.
| } | ||
| } | ||
|
|
||
| func (s) TestBuffer_Slice(t *testing.T) { |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
So you're saying we can delete this test? I don't see NewLeakChecker used anywhere.
There was a problem hiding this comment.
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.
| } | ||
| } | ||
|
|
||
| func (s) TestBuffer_SliceEmpty(t *testing.T) { |
There was a problem hiding this comment.
Can we do this test for all ctors as well?
There was a problem hiding this comment.
We already have a case for this {0, 0, []byte{}},. I can remove it and instead do it in this test.
There was a problem hiding this comment.
Yes, that would be nicer. Thanks.
| } | ||
| tests := []struct { | ||
| name string | ||
| buf func() mem.Buffer |
There was a problem hiding this comment.
Can we use the ctor logic here as well?
There was a problem hiding this comment.
Could you elaborate? I'm not sure what you mean.
There was a problem hiding this comment.
I wanted to see if we can do something like what we do in the other tests: for _, c := range ctors { ... }
| return mem.SliceBuffer(data) | ||
| } | ||
|
|
||
| func newEmptyBuf(_ []byte) mem.Buffer { |
There was a problem hiding this comment.
Nit: The underscore identifier can be omitted.
| } | ||
| } | ||
|
|
||
| func (s) TestBuffer_Slice(t *testing.T) { |
There was a problem hiding this comment.
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.
| } | ||
| } | ||
|
|
||
| func (s) TestBuffer_SliceEmpty(t *testing.T) { |
There was a problem hiding this comment.
Yes, that would be nicer. Thanks.
| } | ||
| tests := []struct { | ||
| name string | ||
| buf func() mem.Buffer |
There was a problem hiding this comment.
I wanted to see if we can do something like what we do in the other tests: for _, c := range ctors { ... }
Works just like Go's slice slicing.
|
@easwars Thanks for the review! I've addressed the feedback. Please let me know if I missed something. |
easwars
left a comment
There was a problem hiding this comment.
Thanks for your contribution!
I have a
CodecV2that gives me amem.BufferSlice(i.e. exposes what it gets from gRPC directly). I need to get anothermem.BufferSlicefrom 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 aSlice()method on theBuffertype would be really useful.Another use case: I want to use
BufferSlice.Readerand implement aPeek()(or similar) to get aBufferSliceof the next N bytes. I don't want a[]byteor[][]byte, I'd like to return thisBufferSlicefrom a codecv2 and let gRPC free the buffers when it no longer needs them (i.e. I don't want to wrap those individual[]byteintoBufferSlices).RELEASE NOTES:
mem.Buffer.Slice()- new method to slice the buffer like a slice.