-
Notifications
You must be signed in to change notification settings - Fork 5.3k
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 all 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,62 @@ | ||
| #include "common/buffer/zero_copy_input_stream_impl.h" | ||
|
|
||
| #include "common/buffer/buffer_impl.h" | ||
| #include "common/common/assert.h" | ||
|
|
||
| namespace Envoy { | ||
| namespace Buffer { | ||
|
|
||
| ZeroCopyInputStreamImpl::ZeroCopyInputStreamImpl() : buffer_(new Buffer::OwnedImpl) {} | ||
|
|
||
| ZeroCopyInputStreamImpl::ZeroCopyInputStreamImpl(Buffer::InstancePtr&& buffer) | ||
| : buffer_(std::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 && slice.len_ > 0) { | ||
| *data = slice.mem_; | ||
| *size = slice.len_; | ||
| position_ = slice.len_; | ||
| byte_count_ += slice.len_; | ||
| return true; | ||
| } | ||
|
|
||
| if (!finished_) { | ||
| *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_); | ||
|
|
||
| // 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 safe to just adjust position_ and byte_count_ here, and | ||
| // drain in Next(). | ||
| position_ -= count; | ||
| byte_count_ -= count; | ||
| } | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,52 @@ | ||
| #pragma once | ||
|
|
||
| #include <cstdint> | ||
| #include <string> | ||
|
|
||
| #include "envoy/buffer/buffer.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::InstancePtr&& buffer); | ||
|
|
||
| // Create input stream with empty buffer | ||
| ZeroCopyInputStreamImpl(); | ||
|
|
||
| // 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 stream is finished | ||
| void finish() { finished_ = true; } | ||
|
|
||
| // google::protobuf::io::ZeroCopyInputStream | ||
| // See | ||
| // https://developers.google.com/protocol-buffers/docs/reference/cpp/google.protobuf.io.zero_copy_stream#ZeroCopyInputStream | ||
| // for each method details. | ||
|
|
||
| // 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. | ||
| 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::InstancePtr buffer_; | ||
| uint64_t position_{0}; | ||
|
|
||
| private: | ||
| bool finished_{false}; | ||
| uint64_t byte_count_{0}; | ||
| }; | ||
|
|
||
| } // 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,79 @@ | ||
| #include "common/buffer/buffer_impl.h" | ||
| #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 | ||
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.
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.
Choose a reason for hiding this comment
The 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.