Home | History | Annotate | Download | only in protozero
      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_PROTO_UTILS_H_
     18 #define INCLUDE_PERFETTO_PROTOZERO_PROTO_UTILS_H_
     19 
     20 #include <inttypes.h>
     21 #include <stddef.h>
     22 
     23 #include <type_traits>
     24 
     25 namespace protozero {
     26 namespace proto_utils {
     27 
     28 // See https://developers.google.com/protocol-buffers/docs/encoding wire types.
     29 
     30 enum FieldType : uint32_t {
     31   kFieldTypeVarInt = 0,
     32   kFieldTypeFixed64 = 1,
     33   kFieldTypeLengthDelimited = 2,
     34   kFieldTypeFixed32 = 5,
     35 };
     36 
     37 // Maximum message size supported: 256 MiB (4 x 7-bit due to varint encoding).
     38 constexpr size_t kMessageLengthFieldSize = 4;
     39 constexpr size_t kMaxMessageLength = (1u << (kMessageLengthFieldSize * 7)) - 1;
     40 
     41 // Field tag is encoded as 32-bit varint (5 bytes at most).
     42 // Largest value of simple (not length-delimited) field is 64-bit varint
     43 // (10 bytes at most). 15 bytes buffer is enough to store a simple field.
     44 constexpr size_t kMaxTagEncodedSize = 5;
     45 constexpr size_t kMaxSimpleFieldEncodedSize = kMaxTagEncodedSize + 10;
     46 
     47 // Proto types: (int|uint|sint)(32|64), bool, enum.
     48 constexpr uint32_t MakeTagVarInt(uint32_t field_id) {
     49   return (field_id << 3) | kFieldTypeVarInt;
     50 }
     51 
     52 // Proto types: fixed64, sfixed64, fixed32, sfixed32, double, float.
     53 template <typename T>
     54 constexpr uint32_t MakeTagFixed(uint32_t field_id) {
     55   static_assert(sizeof(T) == 8 || sizeof(T) == 4, "Value must be 4 or 8 bytes");
     56   return (field_id << 3) |
     57          (sizeof(T) == 8 ? kFieldTypeFixed64 : kFieldTypeFixed32);
     58 }
     59 
     60 // Proto types: string, bytes, embedded messages.
     61 constexpr uint32_t MakeTagLengthDelimited(uint32_t field_id) {
     62   return (field_id << 3) | kFieldTypeLengthDelimited;
     63 }
     64 
     65 // Proto types: sint64, sint32.
     66 template <typename T>
     67 inline typename std::make_unsigned<T>::type ZigZagEncode(T value) {
     68   return static_cast<typename std::make_unsigned<T>::type>(
     69       (value << 1) ^ (value >> (sizeof(T) * 8 - 1)));
     70 }
     71 
     72 template <typename T>
     73 inline uint8_t* WriteVarInt(T value, uint8_t* target) {
     74   // Avoid arithmetic (sign expanding) shifts.
     75   using UnsignedType = typename std::make_unsigned<T>::type;
     76   UnsignedType unsigned_value = static_cast<UnsignedType>(value);
     77 
     78   while (unsigned_value >= 0x80) {
     79     *target++ = static_cast<uint8_t>(unsigned_value) | 0x80;
     80     unsigned_value >>= 7;
     81   }
     82   *target = static_cast<uint8_t>(unsigned_value);
     83   return target + 1;
     84 }
     85 
     86 // Writes a fixed-size redundant encoding of the given |value|. This is
     87 // used to backfill fixed-size reservations for the length field using a
     88 // non-canonical varint encoding (e.g. \x81\x80\x80\x00 instead of \x01).
     89 // See https://github.com/google/protobuf/issues/1530.
     90 // In particular, this is used for nested messages. The size of a nested message
     91 // is not known until all its field have been written. |kMessageLengthFieldSize|
     92 // bytes are reserved to encode the size field and backfilled at the end.
     93 inline void WriteRedundantVarInt(uint32_t value, uint8_t* buf) {
     94   for (size_t i = 0; i < kMessageLengthFieldSize; ++i) {
     95     const uint8_t msb = (i < kMessageLengthFieldSize - 1) ? 0x80 : 0;
     96     buf[i] = static_cast<uint8_t>(value) | msb;
     97     value >>= 7;
     98   }
     99 }
    100 
    101 template <uint32_t field_id>
    102 void StaticAssertSingleBytePreamble() {
    103   static_assert(field_id < 16,
    104                 "Proto field id too big to fit in a single byte preamble");
    105 };
    106 
    107 // Parses a VarInt from the encoded buffer [start, end). |end| is STL-style and
    108 // points one byte past the end of buffer.
    109 // The parsed int value is stored in the output arg |value|. Returns a pointer
    110 // to the next unconsumed byte (so start < retval <= end).
    111 const uint8_t* ParseVarInt(const uint8_t* start,
    112                            const uint8_t* end,
    113                            uint64_t* value);
    114 
    115 // Parses a protobuf field and computes its id, type and value.
    116 // Returns a pointer to the next unconsumed byte (|start| < retval <= end) that
    117 // is either the beginning of the next field or the end of the parent message.
    118 // In the case of a kFieldTypeLengthDelimited field, |field_intvalue| will
    119 // store the length of the payload (either a string or a nested message). In
    120 // this case, the start of the payload will be at (return value) -
    121 // |field_intvalue|.
    122 const uint8_t* ParseField(const uint8_t* start,
    123                           const uint8_t* end,
    124                           uint32_t* field_id,
    125                           FieldType* field_type,
    126                           uint64_t* field_intvalue);
    127 
    128 }  // namespace proto_utils
    129 }  // namespace protozero
    130 
    131 #endif  // INCLUDE_PERFETTO_PROTOZERO_PROTO_UTILS_H_
    132