| /* |
| * Copyright (C) 2017 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #ifndef INCLUDE_PERFETTO_PROTOZERO_SCATTERED_HEAP_BUFFER_H_ |
| #define INCLUDE_PERFETTO_PROTOZERO_SCATTERED_HEAP_BUFFER_H_ |
| |
| #include <memory> |
| #include <string> |
| #include <vector> |
| |
| #include "perfetto/base/export.h" |
| #include "perfetto/base/logging.h" |
| #include "perfetto/protozero/root_message.h" |
| #include "perfetto/protozero/scattered_stream_writer.h" |
| |
| namespace protozero { |
| |
| class Message; |
| |
| class PERFETTO_EXPORT_COMPONENT ScatteredHeapBuffer |
| : public protozero::ScatteredStreamWriter::Delegate { |
| public: |
| class PERFETTO_EXPORT_COMPONENT Slice { |
| public: |
| Slice(); |
| explicit Slice(size_t size); |
| Slice(Slice&& slice) noexcept; |
| ~Slice(); |
| Slice& operator=(Slice&&); |
| |
| inline protozero::ContiguousMemoryRange GetTotalRange() const { |
| return {buffer_.get(), buffer_.get() + size_}; |
| } |
| |
| inline protozero::ContiguousMemoryRange GetUsedRange() const { |
| return {buffer_.get(), buffer_.get() + size_ - unused_bytes_}; |
| } |
| |
| uint8_t* start() const { return buffer_.get(); } |
| size_t size() const { return size_; } |
| size_t unused_bytes() const { return unused_bytes_; } |
| void set_unused_bytes(size_t unused_bytes) { |
| PERFETTO_DCHECK(unused_bytes_ <= size_); |
| unused_bytes_ = unused_bytes; |
| } |
| |
| void Clear(); |
| |
| private: |
| std::unique_ptr<uint8_t[]> buffer_; |
| size_t size_; |
| size_t unused_bytes_; |
| }; |
| |
| ScatteredHeapBuffer(size_t initial_slice_size_bytes = 128, |
| size_t maximum_slice_size_bytes = 128 * 1024); |
| ~ScatteredHeapBuffer() override; |
| |
| // protozero::ScatteredStreamWriter::Delegate implementation. |
| protozero::ContiguousMemoryRange GetNewBuffer() override; |
| |
| // Return the slices backing this buffer, adjusted for the number of bytes the |
| // writer has written. |
| const std::vector<Slice>& GetSlices(); |
| |
| // Stitch all the slices into a single contiguous buffer. |
| std::vector<uint8_t> StitchSlices(); |
| |
| // Note that the returned ranges point back to this buffer and thus cannot |
| // outlive it. |
| std::vector<protozero::ContiguousMemoryRange> GetRanges(); |
| |
| // Note that size of the last slice isn't updated to reflect the number of |
| // bytes written by the trace writer. |
| const std::vector<Slice>& slices() const { return slices_; } |
| |
| void set_writer(protozero::ScatteredStreamWriter* writer) { |
| writer_ = writer; |
| } |
| |
| // Update unused_bytes() of the current |Slice| based on the writer's state. |
| void AdjustUsedSizeOfCurrentSlice(); |
| |
| // Returns the total size the slices occupy in heap memory (including unused). |
| size_t GetTotalSize(); |
| |
| // Reset the contents of this buffer but retain one slice allocation (if it |
| // exists) to be reused for future writes. |
| void Reset(); |
| |
| private: |
| size_t next_slice_size_; |
| const size_t maximum_slice_size_; |
| protozero::ScatteredStreamWriter* writer_ = nullptr; |
| std::vector<Slice> slices_; |
| |
| // Used to keep an allocated slice around after this buffer is reset. |
| Slice cached_slice_; |
| }; |
| |
| // Helper function to create heap-based protozero messages in one line. |
| // Useful when manually serializing a protozero message (primarily in |
| // tests/utilities). So instead of the following: |
| // protozero::MyMessage msg; |
| // protozero::ScatteredHeapBuffer shb; |
| // protozero::ScatteredStreamWriter writer(&shb); |
| // shb.set_writer(&writer); |
| // msg.Reset(&writer); |
| // ... |
| // You can write: |
| // protozero::HeapBuffered<protozero::MyMessage> msg; |
| // msg->set_stuff(...); |
| // msg.SerializeAsString(); |
| template <typename T = ::protozero::Message> |
| class HeapBuffered { |
| public: |
| HeapBuffered() : HeapBuffered(4096, 4096) {} |
| HeapBuffered(size_t initial_slice_size_bytes, size_t maximum_slice_size_bytes) |
| : shb_(initial_slice_size_bytes, maximum_slice_size_bytes), |
| writer_(&shb_) { |
| shb_.set_writer(&writer_); |
| msg_.Reset(&writer_); |
| } |
| |
| // This can't be neither copied nor moved because Message hands out pointers |
| // to itself when creating submessages. |
| HeapBuffered(const HeapBuffered&) = delete; |
| HeapBuffered& operator=(const HeapBuffered&) = delete; |
| HeapBuffered(HeapBuffered&&) = delete; |
| HeapBuffered& operator=(HeapBuffered&&) = delete; |
| |
| T* get() { return &msg_; } |
| T* operator->() { return &msg_; } |
| |
| bool empty() const { return shb_.slices().empty(); } |
| |
| std::vector<uint8_t> SerializeAsArray() { |
| msg_.Finalize(); |
| return shb_.StitchSlices(); |
| } |
| |
| std::string SerializeAsString() { |
| auto vec = SerializeAsArray(); |
| return std::string(reinterpret_cast<const char*>(vec.data()), vec.size()); |
| } |
| |
| std::vector<protozero::ContiguousMemoryRange> GetRanges() { |
| msg_.Finalize(); |
| return shb_.GetRanges(); |
| } |
| |
| const std::vector<ScatteredHeapBuffer::Slice>& GetSlices() { |
| msg_.Finalize(); |
| return shb_.GetSlices(); |
| } |
| |
| void Reset() { |
| shb_.Reset(); |
| writer_.Reset(protozero::ContiguousMemoryRange{}); |
| msg_.Reset(&writer_); |
| PERFETTO_DCHECK(empty()); |
| } |
| |
| private: |
| ScatteredHeapBuffer shb_; |
| ScatteredStreamWriter writer_; |
| RootMessage<T> msg_; |
| }; |
| |
| } // namespace protozero |
| |
| #endif // INCLUDE_PERFETTO_PROTOZERO_SCATTERED_HEAP_BUFFER_H_ |