1 /* 2 * Copyright (C) 2019 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_PACKED_REPEATED_FIELDS_H_ 18 #define INCLUDE_PERFETTO_PROTOZERO_PACKED_REPEATED_FIELDS_H_ 19 20 #include <stdint.h> 21 22 #include <array> 23 #include <memory> 24 #include <type_traits> 25 26 #include "perfetto/base/logging.h" 27 #include "perfetto/protozero/proto_utils.h" 28 29 namespace protozero { 30 31 // This file contains classes used when encoding packed repeated fields. 32 // To encode such a field, the caller is first expected to accumulate all of the 33 // values in one of the following types (depending on the wire type of the 34 // individual elements), defined below: 35 // * protozero::PackedVarInt 36 // * protozero::PackedFixedSizeInt</*element_type=*/ uint32_t> 37 // Then that buffer is passed to the protozero-generated setters as an argument. 38 // After calling the setter, the buffer can be destroyed. 39 // 40 // An example of encoding a packed field: 41 // protozero::HeapBuffered<protozero::Message> msg; 42 // protozero::PackedVarInt buf; 43 // buf.Append(42); 44 // buf.Append(-1); 45 // msg->set_fieldname(buf); 46 // msg.SerializeAsString(); 47 48 class PackedBufferBase { 49 public: PackedBufferBase()50 PackedBufferBase() { Reset(); } 51 52 // Copy or move is disabled due to pointers to stack addresses. 53 PackedBufferBase(const PackedBufferBase&) = delete; 54 PackedBufferBase(PackedBufferBase&&) = delete; 55 PackedBufferBase& operator=(const PackedBufferBase&) = delete; 56 PackedBufferBase& operator=(PackedBufferBase&&) = delete; 57 58 void Reset(); 59 data()60 const uint8_t* data() const { return storage_begin_; } 61 size()62 size_t size() const { 63 return static_cast<size_t>(write_ptr_ - storage_begin_); 64 } 65 66 protected: GrowIfNeeded()67 void GrowIfNeeded() { 68 PERFETTO_DCHECK(write_ptr_ >= storage_begin_ && write_ptr_ <= storage_end_); 69 if (PERFETTO_UNLIKELY(write_ptr_ + kMaxElementSize > storage_end_)) { 70 GrowSlowpath(); 71 } 72 } 73 74 void GrowSlowpath(); 75 76 // max(uint64_t varint encoding, biggest fixed type (uint64)). 77 static constexpr size_t kMaxElementSize = 10; 78 79 // So sizeof(this) == 8k. 80 static constexpr size_t kOnStackStorageSize = 8192 - 32; 81 82 uint8_t* storage_begin_; 83 uint8_t* storage_end_; 84 uint8_t* write_ptr_; 85 std::unique_ptr<uint8_t[]> heap_buf_; 86 alignas(uint64_t) uint8_t stack_buf_[kOnStackStorageSize]; 87 }; 88 89 class PackedVarInt : public PackedBufferBase { 90 public: 91 template <typename T> Append(T value)92 void Append(T value) { 93 GrowIfNeeded(); 94 write_ptr_ = proto_utils::WriteVarInt(value, write_ptr_); 95 } 96 }; 97 98 template <typename T /* e.g. uint32_t for Fixed32 */> 99 class PackedFixedSizeInt : public PackedBufferBase { 100 public: Append(T value)101 void Append(T value) { 102 static_assert(sizeof(T) == 4 || sizeof(T) == 8, 103 "PackedFixedSizeInt should be used only with 32/64-bit ints"); 104 static_assert(sizeof(T) <= kMaxElementSize, 105 "kMaxElementSize needs to be updated"); 106 GrowIfNeeded(); 107 PERFETTO_DCHECK(reinterpret_cast<size_t>(write_ptr_) % alignof(T) == 0); 108 memcpy(reinterpret_cast<T*>(write_ptr_), &value, sizeof(T)); 109 write_ptr_ += sizeof(T); 110 } 111 }; 112 113 } // namespace protozero 114 115 #endif // INCLUDE_PERFETTO_PROTOZERO_PACKED_REPEATED_FIELDS_H_ 116