-
Notifications
You must be signed in to change notification settings - Fork 5.5k
stats: Avoid asserts in fuzz-tests by eliminating arbitrary length limits, using the new MemBlock wrapper for memcpy #8779
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 40 commits
5d40fc4
8f9ab93
beb4684
d143365
5ef1a49
c425551
f680ccc
9fee9f5
184fbed
6ac3dee
9c62a9e
4482ca7
14220bb
4d345d0
5b6f0db
70ffe33
9f1eae7
cf33e18
14b8740
eb6eb51
6d4b8bc
cf71f0b
256a857
cb17d28
7b3150e
c4ddd4a
c728651
4824977
3c9b184
5f4ea86
64c0af4
06e3fbc
b2131df
a281e28
cd98dd2
cf7b451
8069e89
9ae8144
922a860
6dcc218
006b5c7
aa70e5e
9033ac6
d19d722
ae1da1b
bc9871e
ae0b1dd
f4ef7c6
3edfac7
4f5be6c
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,136 @@ | ||
| #pragma once | ||
|
|
||
| #include <memory> | ||
| #include <vector> | ||
|
|
||
| #include "common/common/assert.h" | ||
|
|
||
| namespace Envoy { | ||
|
|
||
| // Manages a block of raw memory for objects of type T. T is generally expected | ||
| // to be a POD, where it makes sense to memcpy over it. This class carries extra | ||
| // member variables for tracking size, and a write-pointer to support safe | ||
| // appends. | ||
| // | ||
| // MemBlockBuilder is used to safely write blocks of data into a memory | ||
| // buffer. Due to two extra member variables, it is not optimal for storing in | ||
| // data structures. The intended usage model is to release the raw assembled | ||
| // memory block from the MemBlockBuilder for efficient storage. | ||
| // | ||
| // The goal for this class is to provide a usage model to replace direct usage | ||
| // of memcpy with a pattern that is easy to validate for correctness by | ||
| // inspection, asserts, and fuzzing, but when compiled for optimization is | ||
| // roughly as efficient as raw memcpy. | ||
| template <class T> class MemBlockBuilder { | ||
| public: | ||
| // Constructs a MemBlockBuilder allowing for 'capacity' instances of T. | ||
| explicit MemBlockBuilder(uint64_t capacity) | ||
| : data_(std::make_unique<T[]>(capacity)), capacity_(capacity), capacity_remaining_(capacity), | ||
| write_ptr_(data_.get()) {} | ||
| MemBlockBuilder() : capacity_(0), capacity_remaining_(0), write_ptr_(nullptr) {} | ||
|
|
||
| /** | ||
| * Populates (or repopulates) the MemBlockBuilder to make it the specified | ||
| * capacity. This does not have resize semantics; when populate() is called any | ||
| * previous contents are erased. | ||
| * | ||
| * @param capcity The number of memory elements to allocate. | ||
| */ | ||
| void populate(uint64_t capacity) { | ||
| data_ = std::make_unique<T[]>(capacity); | ||
| capacity_ = capacity; | ||
| capacity_remaining_ = capacity; | ||
| write_ptr_ = data_.get(); | ||
| } | ||
|
|
||
| /** | ||
| * @return the capacity. | ||
| */ | ||
| uint64_t capacity() const { return capacity_; } | ||
|
|
||
| /** | ||
| * Appends a single object of type T, moving an internal write-pointer | ||
| * forward. Asserts that there is room to write the object when compiled | ||
| * for debug. | ||
| * | ||
| * @param object the object to append. | ||
| */ | ||
| void appendOne(T object) { | ||
| RELEASE_ASSERT(capacity_remaining_ >= 1, "insufficient capacity"); | ||
| ASSERT(write_ptr_ < (data_.get() + capacity_)); | ||
| *write_ptr_++ = object; | ||
| --capacity_remaining_; | ||
| } | ||
|
|
||
| /** | ||
| * Appends raw data, moving an internal write-pointer forward. Asserts | ||
| * that there is room to write the block when compiled for debug. It is | ||
| * the caller's responsibility to ensure that the input data is valid. | ||
| * | ||
| * @param data The block of objects to insert. | ||
| * @param size The number of elements in the block. | ||
| */ | ||
| void appendData(const T* data, uint64_t size) { | ||
| RELEASE_ASSERT(capacity_remaining_ >= size, "insufficient capacity"); | ||
| ASSERT((write_ptr_ + size) <= (data_.get() + capacity_)); | ||
| if (size != 0) { | ||
| memcpy(write_ptr_, data, size * sizeof(T)); | ||
| } | ||
| write_ptr_ += size; | ||
| capacity_remaining_ -= size; | ||
| } | ||
|
|
||
| /** | ||
| * Appends the contents of another memory block to this one. | ||
|
Contributor
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. With a debug-only check for available capacity in the underlying implementation I think it would be good to reiterate at least some of the above comment regarding caller's responsibility that the appended (Perhaps also it should be a release assert? These are by definition risky operations.)
Contributor
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 release-assert. |
||
| * | ||
| * @param src the block to append. | ||
| */ | ||
| void appendBlock(const MemBlockBuilder& src) { appendData(src.data_.get(), src.size()); } | ||
|
|
||
| /** | ||
| * @return the number of elements remaining in the MemBlockBuilder. | ||
| */ | ||
| uint64_t capacityRemaining() const { return capacity_remaining_; } | ||
|
|
||
| /** | ||
| * Empties the contents of this. | ||
| */ | ||
| void reset() { | ||
| data_.reset(); | ||
| capacity_ = 0; | ||
| capacity_remaining_ = 0; | ||
| write_ptr_ = nullptr; | ||
| } | ||
|
|
||
| /** | ||
| * Returns the underlying storage as a unique pointer, clearing this. | ||
| * | ||
| * @return the transferred storage. | ||
| */ | ||
| std::unique_ptr<T[]> release() { | ||
| write_ptr_ = nullptr; | ||
| capacity_ = 0; | ||
| capacity_remaining_ = 0; | ||
| return std::move(data_); | ||
| } | ||
|
|
||
| /** | ||
| * @return the populated data as a vector. | ||
| * | ||
| * This is exposed to help with unit testing. | ||
| */ | ||
| std::vector<T> toVector() const { return std::vector<T>(data_.get(), write_ptr_); } | ||
|
|
||
| /** | ||
| * @return The number of elements the have been added to the builder. | ||
| */ | ||
| uint64_t size() const { return write_ptr_ - data_.get(); } | ||
|
|
||
| private: | ||
| std::unique_ptr<T[]> data_; | ||
| uint64_t capacity_; | ||
| uint64_t capacity_remaining_; | ||
| T* write_ptr_; | ||
|
Contributor
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. Would appreciate a comment here indicate that whenever it is not
Contributor
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. |
||
| }; | ||
|
|
||
| } // 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.
Can we split this out into a distinct PR for review? Thanks.
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.
done --> #9306