utilities: Implemented an ostream that writes to a user provided buffer #13797
utilities: Implemented an ostream that writes to a user provided buffer #13797mattklein123 merged 7 commits intoenvoyproxy:masterfrom
Conversation
…cates if we surpass the buffer size. Signed-off-by: Kevin Baichoo <kbaichoo@google.com>
|
/assign @akonradi |
test/common/common/utility_test.cc
Outdated
| // Can write to buffer | ||
| { | ||
| std::string data = "123"; | ||
| std::string buffer(3, 'x'); |
There was a problem hiding this comment.
This and the others could be std::array; that saves you a little on runtime since the contents of the buffer before streaming don't actually matter.
There was a problem hiding this comment.
You're right the contents don't matter.
With std::array I'd actually have to loop when calling << which is an antipattern when using ostreams.
What I'll do is just declare a string, and resize() instead.
There was a problem hiding this comment.
I don't understand the std::array issue. I was suggesting declaring std::array<uint8, 3> buffer and using std::array::data() and size() below to access the raw bytes.
There was a problem hiding this comment.
Now use std::array<char, N> for the buffer, thanks for clarifying
source/common/common/utility.h
Outdated
| /** | ||
| * std::ostream class that serializes writes into the provided buffer. | ||
| */ | ||
| class OutputBufferStream : public virtual FixedSizeStreamBuffer, public std::ostream { |
There was a problem hiding this comment.
This class should probably have a way to provide the number of bytes that were written into the buffer.
Signed-off-by: Kevin Baichoo <kbaichoo@google.com>
test/common/common/utility_test.cc
Outdated
| // Can write to buffer | ||
| { | ||
| std::string data = "123"; | ||
| std::string buffer(3, 'x'); |
There was a problem hiding this comment.
I don't understand the std::array issue. I was suggesting declaring std::array<uint8, 3> buffer and using std::array::data() and size() below to access the raw bytes.
Signed-off-by: Kevin Baichoo <kbaichoo@google.com>
|
PTAL @envoyproxy/senior-maintainers |
|
We mostly haven't used the iostream model in envoy. What's the motivation/justification for introducing it here? Or stated a different way: why not have a class with a |
|
The motivation for using iostream here is due to the ScopedTrackedObject interface. One of the use cases for the |
We need a way to capture the output generated by dumpState. The other alternative is to replace use of ostream by dumpState with something else. |
The benefit of using ostream is that objects implement it in order to be serializable, and the serialization is decoupled from the storage medium (it could be a fixed sized buffer as in this case, it could be a file, etc.) |
Sounds like a reasonable justification to me. |
|
/retest |
|
Retrying Azure Pipelines: |
|
friendly ping @ggreenway since the PR #13676 which this will be useful for has merged |
|
Sorry, I didn't know you were waiting on me for this, and somehow it never got assigned to anyone. I've been pretty busy. @antoniovicente can you take a look? |
|
Oh, I see what happened: you assigned it to a non-maintainer, so it was never seen in the view for "unassigned PRs" to get assigned to a maintainer. |
antoniovicente
left a comment
There was a problem hiding this comment.
Very nice. I expected this to be more complicated to implement. Still some regrets about this not being something provided out of the box by C++ standard libraries, since it does provide all the tools needed to do this.
|
|
||
| TEST(OutputBufferStream, CanWriteToBuffer) { | ||
| constexpr char data[] = "123"; | ||
| std::array<char, 3> buffer; |
There was a problem hiding this comment.
I forget if ASAN is able to find issues with data members like this allocated in the stack. Consider instead:
std::unique_ptr<char[]> buffer(new char[3]);
There was a problem hiding this comment.
ASAN doesn't complain about it.
There was a problem hiding this comment.
If you were to do something like:
OutputBufferStream ostream{buffer.data(), buffer.size()+1};
ostream << "lots of bytes";
Would ASAN detect the problem?
There was a problem hiding this comment.
Yes ASAN detected that.
We're saying the buffer is bigger than it is (i.e. buffer.size() + 1) and trying to write to memory we don't have in that case.
There was a problem hiding this comment.
Great. std::array works for me.
source/common/common/utility.cc
Outdated
| } | ||
|
|
||
| OutputBufferStream::OutputBufferStream(char* data, size_t size) | ||
| : FixedSizeStreamBuffer{data, size}, std::ostream{static_cast<std::streambuf*>(this)} {} |
There was a problem hiding this comment.
Does the call to setp need to happen before a pointer is given to the ostream constructor?
This seems to work:
/**
* std::ostream class that serializes writes into the provided buffer.
*/
class OutputBufferStream : private std::streambuf, public std::ostream {
public:
OutputBufferStream(char* data, size_t size);
...
}
OutputBufferStream::OutputBufferStream(char* data, size_t size)
: std::ostream(this) {
setp(data, data + size);
}
There was a problem hiding this comment.
The call to setp doesn't need to occur first since streambuf are more focused on managing some storage (buffer, file, ...) and ostreams are more focused on serializing objects. ostream defers the actual writing / storage management to the streambuf.
I organized it like this to be consistent with the InputConstMemoryStream. If you want I can flatten it (removing FixedSizeStreamBuffer)
There was a problem hiding this comment.
I would consider using the name MutableMemoryStreamBuffer
I don't feel strongly about flattening it. Flattening it seems slightly cleaner but both approaches are fine.
Signed-off-by: Kevin Baichoo <kbaichoo@google.com>
Signed-off-by: Kevin Baichoo <kbaichoo@google.com>
KBaichoo
left a comment
There was a problem hiding this comment.
Thanks for the review!
|
|
||
| TEST(OutputBufferStream, CanWriteToBuffer) { | ||
| constexpr char data[] = "123"; | ||
| std::array<char, 3> buffer; |
There was a problem hiding this comment.
ASAN doesn't complain about it.
source/common/common/utility.cc
Outdated
| } | ||
|
|
||
| OutputBufferStream::OutputBufferStream(char* data, size_t size) | ||
| : FixedSizeStreamBuffer{data, size}, std::ostream{static_cast<std::streambuf*>(this)} {} |
There was a problem hiding this comment.
The call to setp doesn't need to occur first since streambuf are more focused on managing some storage (buffer, file, ...) and ostreams are more focused on serializing objects. ostream defers the actual writing / storage management to the streambuf.
I organized it like this to be consistent with the InputConstMemoryStream. If you want I can flatten it (removing FixedSizeStreamBuffer)
source/common/common/utility.cc
Outdated
| } | ||
|
|
||
| OutputBufferStream::OutputBufferStream(char* data, size_t size) | ||
| : FixedSizeStreamBuffer{data, size}, std::ostream{static_cast<std::streambuf*>(this)} {} |
There was a problem hiding this comment.
I would consider using the name MutableMemoryStreamBuffer
I don't feel strongly about flattening it. Flattening it seems slightly cleaner but both approaches are fine.
|
|
||
| TEST(OutputBufferStream, CanWriteToBuffer) { | ||
| constexpr char data[] = "123"; | ||
| std::array<char, 3> buffer; |
There was a problem hiding this comment.
If you were to do something like:
OutputBufferStream ostream{buffer.data(), buffer.size()+1};
ostream << "lots of bytes";
Would ASAN detect the problem?
test/common/common/utility_test.cc
Outdated
| EXPECT_EQ(ostream.contents(), "12"); | ||
| } | ||
|
|
||
| TEST(OutputBufferStream, ReturnsNumberOfBytesWrittenIntoBuffer) { |
There was a problem hiding this comment.
This test seems very similar to OutputBufferStream.CanWriteToBuffer
There was a problem hiding this comment.
Folded into that test and the overflow test.
Signed-off-by: Kevin Baichoo <kbaichoo@google.com>
KBaichoo
left a comment
There was a problem hiding this comment.
Thanks for the review.
test/common/common/utility_test.cc
Outdated
| EXPECT_EQ(ostream.contents(), "12"); | ||
| } | ||
|
|
||
| TEST(OutputBufferStream, ReturnsNumberOfBytesWrittenIntoBuffer) { |
There was a problem hiding this comment.
Folded into that test and the overflow test.
source/common/common/utility.cc
Outdated
| } | ||
|
|
||
| OutputBufferStream::OutputBufferStream(char* data, size_t size) | ||
| : FixedSizeStreamBuffer{data, size}, std::ostream{static_cast<std::streambuf*>(this)} {} |
|
|
||
| TEST(OutputBufferStream, CanWriteToBuffer) { | ||
| constexpr char data[] = "123"; | ||
| std::array<char, 3> buffer; |
There was a problem hiding this comment.
Yes ASAN detected that.
We're saying the buffer is bigger than it is (i.e. buffer.size() + 1) and trying to write to memory we don't have in that case.
|
|
||
| TEST(OutputBufferStream, CanWriteToBuffer) { | ||
| constexpr char data[] = "123"; | ||
| std::array<char, 3> buffer; |
There was a problem hiding this comment.
Great. std::array works for me.
|
Could you merge master to see if that fixes the coverage error? 2020-11-20T22:29:10.8563882Z Per-extension coverage failed: |
Signed-off-by: Kevin Baichoo <kbaichoo@google.com>
|
cc @asraa Thoughts on what may be up with linux_x64 fuzz_coverage? Run seems to still be pending after more than 5 hours. |
|
I'm going to guess it's an AZP issue. The build aborted midway I'll re-run and keep the tab open. |
…er (envoyproxy#13797) Signed-off-by: Kevin Baichoo <kbaichoo@google.com> Signed-off-by: Qin Qin <qqin@google.com>
Implemented an ostream that writes to a user provided buffer and truncates if we surpass the buffer size.
Signed-off-by: Kevin Baichoo kbaichoo@google.com
For an explanation of how to fill out the fields, please see the relevant section
in PULL_REQUESTS.md
Commit Message: Implemented an ostream that writes to a user provided buffer and truncates if we surpass the buffer size.
Additional Description: Will be useful for Fatal Actions (see PR #13676 ), and other situations where we want a fixed-sized ostream.
Risk Level: low
Testing: unit tests
Docs Changes: NA
Release Notes: NA
Platform Specific Features: NA