blob: eab565d0ef2203139a1580b79d87427b33c19edb [file] [log] [blame]
Lalit Maganti45172a82018-06-01 03:04:43 +01001/*
2 * Copyright (C) 2018 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_decoder.h"
18
19#include <string.h>
20
21#include "perfetto/base/logging.h"
22
23namespace protozero {
24
25using namespace proto_utils;
26
27#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
28#define BYTE_SWAP_TO_LE32(x) (x)
29#define BYTE_SWAP_TO_LE64(x) (x)
30#else
31#error Unimplemented for big endian archs.
32#endif
33
34ProtoDecoder::ProtoDecoder(const uint8_t* buffer, uint64_t length)
35 : buffer_(buffer), length_(length), current_position_(buffer) {}
36
37ProtoDecoder::Field ProtoDecoder::ReadField() {
38 Field field{};
39
40 // The first byte of a proto field is structured as follows:
41 // The least 3 significant bits determine the field type.
42 // The most 5 significant bits determine the field id. If MSB == 1, the
43 // field id continues on the next bytes following the VarInt encoding.
44 const uint8_t kFieldTypeNumBits = 3;
45 const uint64_t kFieldTypeMask = (1 << kFieldTypeNumBits) - 1; // 0000 0111;
46
47 const uint8_t* end = buffer_ + length_;
48 const uint8_t* pos = current_position_;
49 PERFETTO_DCHECK(pos >= buffer_);
50 PERFETTO_DCHECK(pos <= end);
51
Lalit Magantidf3e9262018-06-04 17:45:00 +010052 // If we've already hit the end, just return an invalid field.
53 if (pos >= end) {
54 return field;
55 }
56
Lalit Maganti45172a82018-06-01 03:04:43 +010057 uint64_t raw_field_id = 0;
58 pos = ParseVarInt(pos, end, &raw_field_id);
59
60 uint32_t field_id = static_cast<uint32_t>(raw_field_id >> kFieldTypeNumBits);
61 if (field_id == 0 || pos >= end) {
62 return field;
63 }
64 field.type = static_cast<FieldType>(raw_field_id & kFieldTypeMask);
65
Lalit Magantif1c27b12018-06-05 12:45:29 +010066 const uint8_t* new_pos = nullptr;
Lalit Maganti45172a82018-06-01 03:04:43 +010067 uint64_t field_intvalue = 0;
68 switch (field.type) {
69 case kFieldTypeFixed64: {
70 if (pos + sizeof(uint64_t) > end) {
71 return field;
72 }
73 memcpy(&field_intvalue, pos, sizeof(uint64_t));
74 field.int_value = BYTE_SWAP_TO_LE64(field_intvalue);
75 pos += sizeof(uint64_t);
76 break;
77 }
78 case kFieldTypeFixed32: {
79 if (pos + sizeof(uint32_t) > end) {
80 return field;
81 }
82 uint32_t tmp;
83 memcpy(&tmp, pos, sizeof(uint32_t));
84 field.int_value = BYTE_SWAP_TO_LE32(tmp);
85 pos += sizeof(uint32_t);
86 break;
87 }
88 case kFieldTypeVarInt: {
Lalit Magantif1c27b12018-06-05 12:45:29 +010089 new_pos = ParseVarInt(pos, end, &field.int_value);
Lalit Maganti45172a82018-06-01 03:04:43 +010090
Lalit Magantif1c27b12018-06-05 12:45:29 +010091 // new_pos not being greater than pos means ParseVarInt could not fully
92 // parse the number. This is because we are out of space in the buffer.
93 // Set the id to zero and return but don't update the offset so a future
94 // read can read this field.
95 if (new_pos == pos) {
96 return field;
Lalit Maganti45172a82018-06-01 03:04:43 +010097 }
Lalit Magantif1c27b12018-06-05 12:45:29 +010098 pos = new_pos;
Lalit Maganti45172a82018-06-01 03:04:43 +010099 break;
100 }
101 case kFieldTypeLengthDelimited: {
Lalit Magantif1c27b12018-06-05 12:45:29 +0100102 new_pos = ParseVarInt(pos, end, &field_intvalue);
Lalit Maganti45172a82018-06-01 03:04:43 +0100103
Lalit Magantif1c27b12018-06-05 12:45:29 +0100104 // new_pos not being greater than pos means ParseVarInt could not fully
105 // parse the number. This is because we are out of space in the buffer.
106 // Alternatively, we may not have space to fully read the length
107 // delimited field. Set the id to zero and return but don't update the
108 // offset so a future read can read this field.
109 if (new_pos == pos || pos + field_intvalue > end) {
110 return field;
Lalit Maganti45172a82018-06-01 03:04:43 +0100111 }
Lalit Magantif1c27b12018-06-05 12:45:29 +0100112 pos = new_pos;
113 field.length_limited.data = pos;
114 field.length_limited.length = field_intvalue;
115 pos += field_intvalue;
Lalit Maganti45172a82018-06-01 03:04:43 +0100116 break;
117 }
118 }
119 // Set the field id to make the returned value valid and update the current
120 // position in the buffer.
121 field.id = field_id;
122 current_position_ = pos;
123 return field;
124}
125
126bool ProtoDecoder::IsEndOfBuffer() {
127 PERFETTO_DCHECK(current_position_ >= buffer_);
128 return length_ == static_cast<uint64_t>(current_position_ - buffer_);
129}
130
Lalit Magantidf3e9262018-06-04 17:45:00 +0100131void ProtoDecoder::Reset() {
132 current_position_ = buffer_;
133}
134
Lalit Maganti45172a82018-06-01 03:04:43 +0100135} // namespace protozero