1 /* 2 * Copyright (C) 2017 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 #ifndef INCLUDE_PERFETTO_PROTOZERO_SCATTERED_HEAP_BUFFER_H_ 18 #define INCLUDE_PERFETTO_PROTOZERO_SCATTERED_HEAP_BUFFER_H_ 19 20 #include <memory> 21 #include <string> 22 #include <vector> 23 24 #include "perfetto/base/export.h" 25 #include "perfetto/base/logging.h" 26 #include "perfetto/protozero/scattered_stream_writer.h" 27 28 namespace protozero { 29 30 class Message; 31 32 class PERFETTO_EXPORT ScatteredHeapBuffer 33 : public protozero::ScatteredStreamWriter::Delegate { 34 public: 35 class PERFETTO_EXPORT Slice { 36 public: 37 Slice(); 38 explicit Slice(size_t size); 39 Slice(Slice&& slice) noexcept; 40 ~Slice(); 41 Slice& operator=(Slice&&); 42 GetTotalRange()43 inline protozero::ContiguousMemoryRange GetTotalRange() const { 44 return {buffer_.get(), buffer_.get() + size_}; 45 } 46 GetUsedRange()47 inline protozero::ContiguousMemoryRange GetUsedRange() const { 48 return {buffer_.get(), buffer_.get() + size_ - unused_bytes_}; 49 } 50 start()51 uint8_t* start() const { return buffer_.get(); } size()52 size_t size() const { return size_; } unused_bytes()53 size_t unused_bytes() const { return unused_bytes_; } set_unused_bytes(size_t unused_bytes)54 void set_unused_bytes(size_t unused_bytes) { 55 PERFETTO_DCHECK(unused_bytes_ <= size_); 56 unused_bytes_ = unused_bytes; 57 } 58 59 void Clear(); 60 61 private: 62 std::unique_ptr<uint8_t[]> buffer_; 63 size_t size_; 64 size_t unused_bytes_; 65 }; 66 67 ScatteredHeapBuffer(size_t initial_slice_size_bytes = 128, 68 size_t maximum_slice_size_bytes = 128 * 1024); 69 ~ScatteredHeapBuffer() override; 70 71 // protozero::ScatteredStreamWriter::Delegate implementation. 72 protozero::ContiguousMemoryRange GetNewBuffer() override; 73 74 // Stitch all the slices into a single contiguous buffer. 75 std::vector<uint8_t> StitchSlices(); 76 77 // Note that the returned ranges point back to this buffer and thus cannot 78 // outlive it. 79 std::vector<protozero::ContiguousMemoryRange> GetRanges(); 80 slices()81 const std::vector<Slice>& slices() const { return slices_; } 82 set_writer(protozero::ScatteredStreamWriter * writer)83 void set_writer(protozero::ScatteredStreamWriter* writer) { 84 writer_ = writer; 85 } 86 87 // Update unused_bytes() of the current |Slice| based on the writer's state. 88 void AdjustUsedSizeOfCurrentSlice(); 89 90 // Returns the total size the slices occupy in heap memory (including unused). 91 size_t GetTotalSize(); 92 93 // Reset the contents of this buffer but retain one slice allocation (if it 94 // exists) to be reused for future writes. 95 void Reset(); 96 97 private: 98 size_t next_slice_size_; 99 const size_t maximum_slice_size_; 100 protozero::ScatteredStreamWriter* writer_ = nullptr; 101 std::vector<Slice> slices_; 102 103 // Used to keep an allocated slice around after this buffer is reset. 104 Slice cached_slice_; 105 }; 106 107 // Helper function to create heap-based protozero messages in one line. 108 // Useful when manually serializing a protozero message (primarily in 109 // tests/utilities). So instead of the following: 110 // protozero::MyMessage msg; 111 // protozero::ScatteredHeapBuffer shb; 112 // protozero::ScatteredStreamWriter writer(&shb); 113 // shb.set_writer(&writer); 114 // msg.Reset(&writer); 115 // ... 116 // You can write: 117 // protozero::HeapBuffered<protozero::MyMessage> msg; 118 // msg->set_stuff(...); 119 // msg.SerializeAsString(); 120 template <typename T = ::protozero::Message> 121 class HeapBuffered { 122 public: HeapBuffered()123 HeapBuffered() : HeapBuffered(4096, 4096) {} HeapBuffered(size_t initial_slice_size_bytes,size_t maximum_slice_size_bytes)124 HeapBuffered(size_t initial_slice_size_bytes, size_t maximum_slice_size_bytes) 125 : shb_(initial_slice_size_bytes, maximum_slice_size_bytes), 126 writer_(&shb_) { 127 shb_.set_writer(&writer_); 128 msg_.Reset(&writer_); 129 } 130 131 // This can't be neither copied nor moved because Message hands out pointers 132 // to itself when creating submessages. 133 HeapBuffered(const HeapBuffered&) = delete; 134 HeapBuffered& operator=(const HeapBuffered&) = delete; 135 HeapBuffered(HeapBuffered&&) = delete; 136 HeapBuffered& operator=(HeapBuffered&&) = delete; 137 get()138 T* get() { return &msg_; } 139 T* operator->() { return &msg_; } 140 empty()141 bool empty() const { return shb_.slices().empty(); } 142 SerializeAsArray()143 std::vector<uint8_t> SerializeAsArray() { 144 msg_.Finalize(); 145 return shb_.StitchSlices(); 146 } 147 SerializeAsString()148 std::string SerializeAsString() { 149 auto vec = SerializeAsArray(); 150 return std::string(reinterpret_cast<const char*>(vec.data()), vec.size()); 151 } 152 GetRanges()153 std::vector<protozero::ContiguousMemoryRange> GetRanges() { 154 msg_.Finalize(); 155 return shb_.GetRanges(); 156 } 157 Reset()158 void Reset() { 159 shb_.Reset(); 160 writer_.Reset(protozero::ContiguousMemoryRange{}); 161 msg_.Reset(&writer_); 162 PERFETTO_DCHECK(empty()); 163 } 164 165 private: 166 ScatteredHeapBuffer shb_; 167 ScatteredStreamWriter writer_; 168 T msg_; 169 }; 170 171 } // namespace protozero 172 173 #endif // INCLUDE_PERFETTO_PROTOZERO_SCATTERED_HEAP_BUFFER_H_ 174