Add TranscoderInputStreamImpl#1078
Conversation
The class implements TranscoderInputStream from transcoding library, take input from Envoy::Buffer, this also allows Envoy::Buffer to be used as protobuf::io::ZeroCopyInputStream. For envoyproxy#501
|
#1079 for the context how this is used. |
|
@lizan Can we make a note to use this here as well: https://github.com/lyft/envoy/blob/master/source/common/grpc/rpc_channel_impl.cc#L62 Doesn't need to be done in this PR if you don't want, but would like to do a follow up. Will catch up on reviews this weekend. |
htuch
left a comment
There was a problem hiding this comment.
I think this is useful, but do we want to couple zero-copy with the transcoder? As @mattklein123 points out, this is useful in other places as well (including the active #1054 under review). Can we have a protobuf zero-copy Buffer subclass (or build this into Buffer), then use this here in the TranscoderInputStreamImply?
|
|
||
| #include "common/buffer/buffer_impl.h" | ||
|
|
||
| #include "src/transcoder_input_stream.h" |
There was a problem hiding this comment.
Can you wire up the external deps for this PR? Ideally, the grpc_transcoder library doesn't live under the src prefix, but something more like grpc_transcoder/....
There was a problem hiding this comment.
Also, do you want to add mocks for the TranscoderInputStream interface?
There was a problem hiding this comment.
grpc-ecosystem/grpc-httpjson-transcoding#9 for prefix.
Probably not need mocks, just like buffers, it is hard to EXPECT how the input stream is used, so just using real impl should be fine.
| void Move(Buffer::Instance& instance); | ||
|
|
||
| // Mark the buffer is finished | ||
| void Finish() { finished_ = true; } |
There was a problem hiding this comment.
For all methods that are not inherited from external deps, please follow the Envoy convention and start methods with lower case as per the style guide.
| } | ||
|
|
||
| Buffer::RawSlice slice; | ||
| uint64_t num_slices = buffer_.getRawSlices(&slice, 1); |
There was a problem hiding this comment.
Nit: const uint64_t num_slices = ...
| Buffer::RawSlice slice; | ||
| uint64_t num_slices = buffer_.getRawSlices(&slice, 1); | ||
|
|
||
| if (num_slices) { |
| bool TranscoderInputStreamImpl::Skip(int) { NOT_IMPLEMENTED; } | ||
|
|
||
| void TranscoderInputStreamImpl::BackUp(int count) { | ||
| GOOGLE_CHECK_GE(count, 0); |
There was a problem hiding this comment.
What are these macros? They aren't standard in the Envoy code base. Could we just use an ASSERT?
| GOOGLE_CHECK_LE(count, position_); | ||
|
|
||
| position_ -= count; | ||
| byte_count_ -= count; |
There was a problem hiding this comment.
Since I don't have the interface, I can't tell the semantics of byte_count_, but I noticed it isn't decremented if (position != 0), so in the absence of Backup, it monotonically increases forever?
There was a problem hiding this comment.
byte_count_ here is for ByteCount(), which
Returns the total number of bytes read since this object was created.
So yes it monotonically increases forever.
|
@htuch @mattklein123 refactored out |
|
|
||
| class ZeroCopyInputStreamImpl : public virtual google::protobuf::io::ZeroCopyInputStream { | ||
| public: | ||
| // Create input stream with one buffer, and finish immediately |
There was a problem hiding this comment.
I'm not sure if there is any easy way to do this, but in this case, it would be nice if we didn't have to allocate a new buffer on the stack, just to move into, and then operate. IMO we should be able to operate directly on the input buffer since it's non-const anyway. I would just put in a TODO here for a potential perf optimization.
There was a problem hiding this comment.
Seems all usage are using InstancePtr, so just changed the signature with InstancePtr in this class.
| // Create input stream with empty buffer | ||
| ZeroCopyInputStreamImpl() {} | ||
|
|
||
| virtual ~ZeroCopyInputStreamImpl() {} |
There was a problem hiding this comment.
nit: is this needed to compile? otherwise del
| } | ||
|
|
||
| Buffer::RawSlice slice; | ||
| const uint64_t num_slices = buffer_.getRawSlices(&slice, 1); |
There was a problem hiding this comment.
See comment here: https://github.com/lyft/envoy/blob/master/include/envoy/buffer/buffer.h#L67 I think this can return a slice that has no data in it. Just make sure the code below correctly handled that case. I think you probably need to also check that slice.len_ > 0 but not sure.
|
|
||
| bool ZeroCopyInputStreamImpl::Skip(int) { NOT_IMPLEMENTED; } | ||
|
|
||
| void ZeroCopyInputStreamImpl::BackUp(int count) { |
There was a problem hiding this comment.
I'm a little hazy on the proto buffer interfaces, but is this always safe? We are draining() above. Can we BackUp() into nothing? Or does that never happen. If so I would add a comment.
There was a problem hiding this comment.
Done.
The precondition of BackUp() doesn't allow them. so we're safe to draining. Added comments.
| return true; | ||
| } | ||
|
|
||
| if (!finished_) { |
There was a problem hiding this comment.
I'm a little confused what happens here. If we return true here with no data, how does the proto code know to call Next() again without just going into a spin loop? Or is this only useful in the explicit transcoding case somehow? Again might need a few more comments for those not as familiar with proto interfaces.
There was a problem hiding this comment.
It is callers responsibility to maintain. Added comment. transcoding interface add BytesAvailable() to avoid this.
mattklein123
left a comment
There was a problem hiding this comment.
LGTM pending small grammar nits and any @htuch comments.
| // Preconditions for BackUp: | ||
| // - The last method called must have been Next(). | ||
| // - count must be less than or equal to the size of the last buffer returned by Next(). | ||
| // Due to preconditions above, it is save to just adjust position_ and byte_count_ here, and |
| // - The last method called must have been Next(). | ||
| // - count must be less than or equal to the size of the last buffer returned by Next(). | ||
| // Due to preconditions above, it is save to just adjust position_ and byte_count_ here, and | ||
| // draining in Next(). |
| // google::protobuf::io::ZeroCopyInputStream | ||
| // See | ||
| // https://developers.google.com/protocol-buffers/docs/reference/cpp/google.protobuf.io.zero_copy_stream#ZeroCopyInputStream | ||
| // for each methods details. |
| // https://developers.google.com/protocol-buffers/docs/reference/cpp/google.protobuf.io.zero_copy_stream#ZeroCopyInputStream | ||
| // for each methods details. | ||
|
|
||
| // Note the Next() will return true with no data until next data available if the stream is not |
There was a problem hiding this comment.
// Note Next() will return true with no data until more data is available if the stream is not
// finished. It is the caller's responsibility to finish the stream or wrap with LimitingInputStream
// before passing to protobuf code to avoid a spin loop.
|
|
||
| namespace Buffer { | ||
|
|
||
| class ZeroCopyInputStreamImpl : public virtual google::protobuf::io::ZeroCopyInputStream { |
There was a problem hiding this comment.
Is virtual needed here? Is this because google::protobuf::io::ZeroCopyInputStream isn't pure? Do have potential diamond inheritance with this?
There was a problem hiding this comment.
Yes, TranscoderInputStreamImpl inherits this class and TranscoderInputStream from transcoding library, which inherits ZeroCopyInputStream.
|
|
||
| private: | ||
| bool finished_{false}; | ||
| int64_t byte_count_{0}; |
| ZeroCopyInputStreamImpl stream_; | ||
|
|
||
| const void* data_; | ||
| int size_; |
There was a problem hiding this comment.
Nit: Prefer fixed size types in general in Envoy .
There was a problem hiding this comment.
data_ and size_ are used to receive output from Next, so types here matches the interface.
The most recent Envoy bump requires this: envoyproxy/envoy-mobile#1076 ``` The new binary is 2.00 % different in size compared to main. The new binary is 5790759 bytes. The current size (5790759) is larger than the maximum size (5700000). ``` Signed-off-by: Michael Rebello <me@michaelrebello.com> Signed-off-by: JP Simard <jp@jpsim.com>
The most recent Envoy bump requires this: envoyproxy/envoy-mobile#1076 ``` The new binary is 2.00 % different in size compared to main. The new binary is 5790759 bytes. The current size (5790759) is larger than the maximum size (5700000). ``` Signed-off-by: Michael Rebello <me@michaelrebello.com> Signed-off-by: JP Simard <jp@jpsim.com>
**Description** This removes the unnecessary replace statement in go.mod. Signed-off-by: Takeshi Yoneda <t.y.mathetake@gmail.com>
The class implements TranscoderInputStream from transcoding library,
take input from Envoy::Buffer, this also allows Envoy::Buffer to be used
as protobuf::io::ZeroCopyInputStream.
For #501