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
30namespace protozero {
31namespace 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
40const 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
58const 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