/* * 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_PROTO_UTILS_H_ #define INCLUDE_PERFETTO_PROTOZERO_PROTO_UTILS_H_ #include <stddef.h> #include <cinttypes> #include <type_traits> #include "perfetto/base/logging.h" #include "perfetto/public/pb_utils.h" // Helper macro for the constexpr functions containing // the switch statement: if C++14 is supported, this macro // resolves to `constexpr` and just `inline` otherwise. #if __cpp_constexpr >= 201304 #define PERFETTO_PROTOZERO_CONSTEXPR14_OR_INLINE … #else #define PERFETTO_PROTOZERO_CONSTEXPR14_OR_INLINE … #endif namespace protozero { namespace proto_utils { // See https://developers.google.com/protocol-buffers/docs/encoding wire types. // This is a type encoded into the proto that provides just enough info to // find the length of the following value. enum class ProtoWireType : uint32_t { … }; // This is the type defined in the proto for each field. This information // is used to decide the translation strategy when writing the trace. enum class ProtoSchemaType { … }; inline const char* ProtoSchemaToString(ProtoSchemaType v) { … } // Maximum message size supported: 256 MiB (4 x 7-bit due to varint encoding). constexpr size_t kMessageLengthFieldSize = …; constexpr size_t kMaxMessageLength = …; constexpr size_t kMaxOneByteMessageLength = …; // Field tag is encoded as 32-bit varint (5 bytes at most). // Largest value of simple (not length-delimited) field is 64-bit varint // (10 bytes at most). 15 bytes buffer is enough to store a simple field. constexpr size_t kMaxTagEncodedSize = …; constexpr size_t kMaxSimpleFieldEncodedSize = …; // Proto types: (int|uint|sint)(32|64), bool, enum. constexpr uint32_t MakeTagVarInt(uint32_t field_id) { … } // Proto types: fixed64, sfixed64, fixed32, sfixed32, double, float. template <typename T> constexpr uint32_t MakeTagFixed(uint32_t field_id) { … } // Proto types: string, bytes, embedded messages. constexpr uint32_t MakeTagLengthDelimited(uint32_t field_id) { … } // Proto types: sint64, sint32. template <typename T> inline typename std::make_unsigned<T>::type ZigZagEncode(T value) { … } // Proto types: sint64, sint32. template <typename T> inline typename std::make_signed<T>::type ZigZagDecode(T value) { … } template <typename T> auto ExtendValueForVarIntSerialization(T value) -> typename std::make_unsigned< typename std::conditional<std::is_unsigned<T>::value, T, int64_t>::type>:: type { … } template <typename T> inline uint8_t* WriteVarInt(T value, uint8_t* target) { … } // Writes a fixed-size redundant encoding of the given |value|. This is // used to backfill fixed-size reservations for the length field using a // non-canonical varint encoding (e.g. \x81\x80\x80\x00 instead of \x01). // See https://github.com/google/protobuf/issues/1530. // This is used mainly in two cases: // 1) At trace writing time, when starting a nested messages. The size of a // nested message is not known until all its field have been written. // |kMessageLengthFieldSize| bytes are reserved to encode the size field and // backfilled at the end. // 2) When rewriting a message at trace filtering time, in protozero/filtering. // At that point we know only the upper bound of the length (a filtered // message is <= the original one) and we backfill after the message has been // filtered. inline void WriteRedundantVarInt(uint32_t value, uint8_t* buf, size_t size = kMessageLengthFieldSize) { … } template <uint32_t field_id> void StaticAssertSingleBytePreamble() { … } // Parses a VarInt from the encoded buffer [start, end). |end| is STL-style and // points one byte past the end of buffer. // The parsed int value is stored in the output arg |value|. Returns a pointer // to the next unconsumed byte (so start < retval <= end) or |start| if the // VarInt could not be fully parsed because there was not enough space in the // buffer. inline const uint8_t* ParseVarInt(const uint8_t* start, const uint8_t* end, uint64_t* out_value) { … } enum class RepetitionType { … }; // Provide a common base struct for all templated FieldMetadata types to allow // simple checks if a given type is a FieldMetadata or not. struct FieldMetadataBase { … }; template <uint32_t field_id, RepetitionType repetition_type, ProtoSchemaType proto_schema_type, typename CppFieldType, typename MessageType> struct FieldMetadata : public FieldMetadataBase { … }; } // namespace proto_utils } // namespace protozero #endif // INCLUDE_PERFETTO_PROTOZERO_PROTO_UTILS_H_