-
Notifications
You must be signed in to change notification settings - Fork 5.5k
Add TranscoderInputStreamImpl #1078
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
Changes from 9 commits
6572487
a8f8f33
579d342
118d1e7
ca46f94
8034159
77f9c00
dd68af1
8d235bd
98c5ccf
547b92f
8f1f307
b88b0d5
9850c9b
53fcf47
6be5d0d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,54 @@ | ||
| #include "common/buffer/zero_copy_input_stream_impl.h" | ||
|
|
||
| #include "common/common/assert.h" | ||
|
|
||
| namespace Envoy { | ||
| namespace Buffer { | ||
|
|
||
| ZeroCopyInputStreamImpl::ZeroCopyInputStreamImpl(Buffer::Instance& buffer) { | ||
| move(buffer); | ||
| finish(); | ||
| } | ||
|
|
||
| void ZeroCopyInputStreamImpl::move(Buffer::Instance& instance) { | ||
| ASSERT(!finished_); | ||
|
|
||
| buffer_.move(instance); | ||
| } | ||
|
|
||
| bool ZeroCopyInputStreamImpl::Next(const void** data, int* size) { | ||
| if (position_ != 0) { | ||
| buffer_.drain(position_); | ||
| position_ = 0; | ||
| } | ||
|
|
||
| Buffer::RawSlice slice; | ||
| const uint64_t num_slices = buffer_.getRawSlices(&slice, 1); | ||
|
|
||
| if (num_slices > 0) { | ||
| *data = slice.mem_; | ||
| *size = slice.len_; | ||
| position_ = slice.len_; | ||
| byte_count_ += slice.len_; | ||
| return true; | ||
| } | ||
|
|
||
| if (!finished_) { | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 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.
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It is callers responsibility to maintain. Added comment. transcoding interface add BytesAvailable() to avoid this. |
||
| *data = nullptr; | ||
| *size = 0; | ||
| return true; | ||
| } | ||
| return false; | ||
| } | ||
|
|
||
| bool ZeroCopyInputStreamImpl::Skip(int) { NOT_IMPLEMENTED; } | ||
|
|
||
| void ZeroCopyInputStreamImpl::BackUp(int count) { | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 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.
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done. The precondition of |
||
| ASSERT(count > 0); | ||
| ASSERT(uint64_t(count) < position_); | ||
|
|
||
| position_ -= count; | ||
| byte_count_ -= count; | ||
| } | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,47 @@ | ||
| #pragma once | ||
|
|
||
| #include <cstdint> | ||
| #include <string> | ||
|
|
||
| #include "common/buffer/buffer_impl.h" | ||
|
|
||
| #include "google/protobuf/io/zero_copy_stream.h" | ||
|
|
||
| namespace Envoy { | ||
|
|
||
| namespace Buffer { | ||
|
|
||
| class ZeroCopyInputStreamImpl : public virtual google::protobuf::io::ZeroCopyInputStream { | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, |
||
| public: | ||
| // Create input stream with one buffer, and finish immediately | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 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.
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Seems all usage are using InstancePtr, so just changed the signature with InstancePtr in this class. |
||
| ZeroCopyInputStreamImpl(Buffer::Instance& buffer); | ||
|
|
||
| // Create input stream with empty buffer | ||
| ZeroCopyInputStreamImpl() {} | ||
|
|
||
| virtual ~ZeroCopyInputStreamImpl() {} | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: is this needed to compile? otherwise del
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done |
||
|
|
||
| // Add a buffer to input stream, will consume all buffer from parameter | ||
| // if the stream is not finished | ||
| void move(Buffer::Instance& instance); | ||
|
|
||
| // Mark the buffer is finished | ||
| void finish() { finished_ = true; } | ||
|
|
||
| // google::protobuf::io::ZeroCopyInputStream | ||
| virtual bool Next(const void** data, int* size) override; | ||
| virtual void BackUp(int count) override; | ||
| virtual bool Skip(int count) override; // Not implemented | ||
| virtual google::protobuf::int64 ByteCount() const override { return byte_count_; } | ||
|
|
||
| protected: | ||
| Buffer::OwnedImpl buffer_; | ||
| uint64_t position_{0}; | ||
|
|
||
| private: | ||
| bool finished_{false}; | ||
| int64_t byte_count_{0}; | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This can be negative?
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. changed to uint |
||
| }; | ||
|
|
||
| } // namespace Buffer | ||
| } // namespace Envoy | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,9 @@ | ||
| #include "common/grpc/transcoder_input_stream_impl.h" | ||
|
|
||
| namespace Envoy { | ||
| namespace Grpc { | ||
|
|
||
| int64_t TranscoderInputStreamImpl::BytesAvailable() const { return buffer_.length() - position_; } | ||
|
|
||
| } // namespace Grpc | ||
| } // namespace Envoy |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,18 @@ | ||
| #pragma once | ||
|
|
||
| #include "common/buffer/zero_copy_input_stream_impl.h" | ||
|
|
||
| #include "grpc_transcoding/transcoder_input_stream.h" | ||
|
|
||
| namespace Envoy { | ||
| namespace Grpc { | ||
|
|
||
| class TranscoderInputStreamImpl : public Buffer::ZeroCopyInputStreamImpl, | ||
| public google::grpc::transcoding::TranscoderInputStream { | ||
| public: | ||
| // TranscoderInputStream | ||
| virtual int64_t BytesAvailable() const override; | ||
| }; | ||
|
|
||
| } // namespace Grpc | ||
| } // namespace Envoy |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,17 @@ | ||
| licenses(["notice"]) # Apache 2 | ||
|
|
||
| load( | ||
| "//bazel:envoy_build_system.bzl", | ||
| "envoy_cc_test", | ||
| "envoy_package", | ||
| ) | ||
|
|
||
| envoy_package() | ||
|
|
||
| envoy_cc_test( | ||
| name = "zero_copy_input_stream_test", | ||
| srcs = ["zero_copy_input_stream_test.cc"], | ||
| deps = [ | ||
| "//source/common/buffer:zero_copy_input_stream_lib", | ||
| ], | ||
| ) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,78 @@ | ||
| #include "common/buffer/zero_copy_input_stream_impl.h" | ||
|
|
||
| #include "gtest/gtest.h" | ||
|
|
||
| namespace Envoy { | ||
| namespace Buffer { | ||
| namespace { | ||
|
|
||
| class ZeroCopyInputStreamTest : public testing::Test { | ||
| public: | ||
| ZeroCopyInputStreamTest() { | ||
| Buffer::OwnedImpl buffer{"abcd"}; | ||
| stream_.move(buffer); | ||
| } | ||
|
|
||
| std::string slice_data_{"abcd"}; | ||
| ZeroCopyInputStreamImpl stream_; | ||
|
|
||
| const void* data_; | ||
| int size_; | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nit: Prefer fixed size types in general in Envoy .
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
| }; | ||
|
|
||
| TEST_F(ZeroCopyInputStreamTest, Move) { | ||
| Buffer::OwnedImpl buffer{"abcd"}; | ||
| stream_.move(buffer); | ||
|
|
||
| EXPECT_EQ(0, buffer.length()); | ||
| } | ||
|
|
||
| TEST_F(ZeroCopyInputStreamTest, Next) { | ||
| EXPECT_TRUE(stream_.Next(&data_, &size_)); | ||
| EXPECT_EQ(4, size_); | ||
| EXPECT_EQ(0, memcmp(slice_data_.data(), data_, size_)); | ||
| } | ||
|
|
||
| TEST_F(ZeroCopyInputStreamTest, TwoSlices) { | ||
| Buffer::OwnedImpl buffer("efgh"); | ||
|
|
||
| stream_.move(buffer); | ||
|
|
||
| EXPECT_TRUE(stream_.Next(&data_, &size_)); | ||
| EXPECT_EQ(4, size_); | ||
| EXPECT_EQ(0, memcmp(slice_data_.data(), data_, size_)); | ||
| EXPECT_TRUE(stream_.Next(&data_, &size_)); | ||
| EXPECT_EQ(4, size_); | ||
| EXPECT_EQ(0, memcmp("efgh", data_, size_)); | ||
| } | ||
|
|
||
| TEST_F(ZeroCopyInputStreamTest, BackUp) { | ||
| EXPECT_TRUE(stream_.Next(&data_, &size_)); | ||
| EXPECT_EQ(4, size_); | ||
|
|
||
| stream_.BackUp(3); | ||
| EXPECT_EQ(1, stream_.ByteCount()); | ||
|
|
||
| EXPECT_TRUE(stream_.Next(&data_, &size_)); | ||
| EXPECT_EQ(3, size_); | ||
| EXPECT_EQ(0, memcmp("bcd", data_, size_)); | ||
| EXPECT_EQ(4, stream_.ByteCount()); | ||
| } | ||
|
|
||
| TEST_F(ZeroCopyInputStreamTest, ByteCount) { | ||
| EXPECT_EQ(0, stream_.ByteCount()); | ||
| EXPECT_TRUE(stream_.Next(&data_, &size_)); | ||
| EXPECT_EQ(4, stream_.ByteCount()); | ||
| } | ||
|
|
||
| TEST_F(ZeroCopyInputStreamTest, Finish) { | ||
| EXPECT_TRUE(stream_.Next(&data_, &size_)); | ||
| EXPECT_TRUE(stream_.Next(&data_, &size_)); | ||
| EXPECT_EQ(0, size_); | ||
| stream_.finish(); | ||
| EXPECT_FALSE(stream_.Next(&data_, &size_)); | ||
| } | ||
|
|
||
| } // namespace | ||
| } // namespace Buffer | ||
| } // namespace Envoy | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,47 @@ | ||
| #include "common/grpc/transcoder_input_stream_impl.h" | ||
|
|
||
| #include "gtest/gtest.h" | ||
|
|
||
| namespace Envoy { | ||
| namespace Grpc { | ||
| namespace { | ||
|
|
||
| class TranscoderInputStreamTest : public testing::Test { | ||
| public: | ||
| TranscoderInputStreamTest() { | ||
| Buffer::OwnedImpl buffer{"abcd"}; | ||
| stream_.move(buffer); | ||
| } | ||
|
|
||
| std::string slice_data_{"abcd"}; | ||
| TranscoderInputStreamImpl stream_; | ||
|
|
||
| const void* data_; | ||
| int size_; | ||
| }; | ||
|
|
||
| TEST_F(TranscoderInputStreamTest, BytesAvailable) { | ||
| Buffer::OwnedImpl buffer{"abcd"}; | ||
|
|
||
| stream_.move(buffer); | ||
| EXPECT_EQ(8, stream_.BytesAvailable()); | ||
| } | ||
|
|
||
| TEST_F(TranscoderInputStreamTest, TwoSlices) { | ||
| Buffer::OwnedImpl buffer("efghi"); | ||
|
|
||
| stream_.move(buffer); | ||
| EXPECT_EQ(9, stream_.BytesAvailable()); | ||
| } | ||
|
|
||
| TEST_F(TranscoderInputStreamTest, BackUp) { | ||
| EXPECT_TRUE(stream_.Next(&data_, &size_)); | ||
| EXPECT_EQ(4, size_); | ||
|
|
||
| stream_.BackUp(3); | ||
| EXPECT_EQ(3, stream_.BytesAvailable()); | ||
| } | ||
|
|
||
| } // namespace | ||
| } // namespace Grpc | ||
| } // namespace Envoy |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.