• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 #include "perfetto/protozero/proto_utils.h"
18 
19 #include <string.h>
20 
21 #include <limits>
22 
23 #include "perfetto/base/logging.h"
24 #include "perfetto/base/utils.h"
25 
26 #define PERFETTO_CHECK_PTR_LE(a, b)                \
27   PERFETTO_CHECK(reinterpret_cast<uintptr_t>(a) <= \
28                  reinterpret_cast<uintptr_t>(b))
29 
30 namespace protozero {
31 namespace proto_utils {
32 
33 #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
34 #define BYTE_SWAP_TO_LE32(x) (x)
35 #define BYTE_SWAP_TO_LE64(x) (x)
36 #else
37 #error Unimplemented for big endian archs.
38 #endif
39 
ParseVarInt(const uint8_t * start,const uint8_t * end,uint64_t * value)40 const uint8_t* ParseVarInt(const uint8_t* start,
41                            const uint8_t* end,
42                            uint64_t* value) {
43   const uint8_t* pos = start;
44   uint64_t shift = 0;
45   *value = 0;
46   do {
47     if (PERFETTO_UNLIKELY(pos >= end)) {
48       *value = 0;
49       break;
50     }
51     PERFETTO_DCHECK(shift < 64ull);
52     *value |= static_cast<uint64_t>(*pos & 0x7f) << shift;
53     shift += 7;
54   } while (*pos++ & 0x80);
55   return pos;
56 }
57 
ParseField(const uint8_t * start,const uint8_t * end,uint32_t * field_id,FieldType * field_type,uint64_t * field_intvalue)58 const uint8_t* ParseField(const uint8_t* start,
59                           const uint8_t* end,
60                           uint32_t* field_id,
61                           FieldType* field_type,
62                           uint64_t* field_intvalue) {
63   // The first byte of a proto field is structured as follows:
64   // The least 3 significant bits determine the field type.
65   // The most 5 significant bits determine the field id. If MSB == 1, the
66   // field id continues on the next bytes following the VarInt encoding.
67   const uint8_t kFieldTypeNumBits = 3;
68   const uint8_t kFieldTypeMask = (1 << kFieldTypeNumBits) - 1;  // 0000 0111;
69 
70   const uint8_t* pos = start;
71   PERFETTO_CHECK_PTR_LE(pos, end - 1);
72   *field_type = static_cast<FieldType>(*pos & kFieldTypeMask);
73 
74   uint64_t raw_field_id;
75   pos = ParseVarInt(pos, end, &raw_field_id);
76   raw_field_id >>= kFieldTypeNumBits;
77 
78   PERFETTO_DCHECK(raw_field_id <= std::numeric_limits<uint32_t>::max());
79   *field_id = static_cast<uint32_t>(raw_field_id);
80 
81   switch (*field_type) {
82     case kFieldTypeFixed64: {
83       PERFETTO_CHECK_PTR_LE(pos + sizeof(uint64_t), end);
84       memcpy(field_intvalue, pos, sizeof(uint64_t));
85       *field_intvalue = BYTE_SWAP_TO_LE64(*field_intvalue);
86       pos += sizeof(uint64_t);
87       break;
88     }
89     case kFieldTypeFixed32: {
90       PERFETTO_CHECK_PTR_LE(pos + sizeof(uint32_t), end);
91       uint32_t tmp;
92       memcpy(&tmp, pos, sizeof(uint32_t));
93       *field_intvalue = BYTE_SWAP_TO_LE32(tmp);
94       pos += sizeof(uint32_t);
95       break;
96     }
97     case kFieldTypeVarInt: {
98       pos = ParseVarInt(pos, end, field_intvalue);
99       break;
100     }
101     case kFieldTypeLengthDelimited: {
102       pos = ParseVarInt(pos, end, field_intvalue);
103       pos += *field_intvalue;
104       PERFETTO_CHECK_PTR_LE(pos, end);
105       break;
106     }
107   }
108   return pos;
109 }
110 
111 }  // namespace proto_utils
112 }  // namespace protozero
113