blob: 220c608955ac88a8f3ac622888ff113037f9d53b [file] [log] [blame]
Alexei Frolovdea46f72020-01-07 13:28:24 -08001// Copyright 2020 The Pigweed Authors
2//
3// Licensed under the Apache License, Version 2.0 (the "License"); you may not
4// use this file except in compliance with the License. You may obtain a copy of
5// the License at
6//
7// https://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12// License for the specific language governing permissions and limitations under
13// the License.
14
15#include "pw_protobuf/decoder.h"
16
Alexei Frolov6d9b9b42020-01-14 12:57:33 -080017#include <cstring>
18
Alexei Frolovdea46f72020-01-07 13:28:24 -080019#include "pw_varint/varint.h"
20
21namespace pw::protobuf {
22
23Status Decoder::Decode(span<const std::byte> proto) {
24 if (handler_ == nullptr || state_ != kReady) {
25 return Status::FAILED_PRECONDITION;
26 }
27
28 state_ = kDecodeInProgress;
29 proto_ = proto;
30
31 // Iterate over each field in the proto, calling the handler with the field
32 // key.
33 while (state_ == kDecodeInProgress && !proto_.empty()) {
34 const std::byte* original_cursor = proto_.data();
35
36 uint64_t key;
37 size_t bytes_read = varint::Decode(proto_, &key);
38 if (bytes_read == 0) {
39 state_ = kDecodeFailed;
40 return Status::DATA_LOSS;
41 }
42
43 uint32_t field_number = key >> kFieldNumberShift;
44 Status status = handler_->ProcessField(this, field_number);
45 if (!status.ok()) {
46 return status;
47 }
48
49 // The callback function can modify the decoder's state; check that
50 // everything is still okay.
51 if (state_ == kDecodeFailed) {
52 break;
53 }
54
55 // If the cursor has not moved, the user has not consumed the field in their
56 // callback. Skip ahead to the next field.
57 if (original_cursor == proto_.data()) {
58 SkipField();
59 }
60 }
61
62 if (state_ != kDecodeInProgress) {
63 return Status::DATA_LOSS;
64 }
65
66 state_ = kReady;
67 return Status::OK;
68}
69
70Status Decoder::ReadVarint(uint32_t field_number, uint64_t* out) {
Alexei Frolov6d9b9b42020-01-14 12:57:33 -080071 Status status = ConsumeKey(field_number, WireType::kVarint);
72 if (!status.ok()) {
73 return status;
74 }
75
76 size_t bytes_read = varint::Decode(proto_, out);
77 if (bytes_read == 0) {
78 state_ = kDecodeFailed;
79 return Status::DATA_LOSS;
80 }
81
82 // Advance to the next field.
83 proto_ = proto_.subspan(bytes_read);
84 return Status::OK;
85}
86
87Status Decoder::ReadFixed(uint32_t field_number, std::byte* out, size_t size) {
88 WireType expected_wire_type =
89 size == sizeof(uint32_t) ? WireType::kFixed32 : WireType::kFixed64;
90 Status status = ConsumeKey(field_number, expected_wire_type);
91 if (!status.ok()) {
92 return status;
93 }
94
95 if (proto_.size() < size) {
96 return Status::DATA_LOSS;
97 }
98
99 std::memcpy(out, proto_.data(), size);
100 proto_ = proto_.subspan(size);
101
102 return Status::OK;
103}
104
105Status Decoder::ReadDelimited(uint32_t field_number,
106 span<const std::byte>* out) {
107 Status status = ConsumeKey(field_number, WireType::kDelimited);
108 if (!status.ok()) {
109 return status;
110 }
111
112 uint64_t length;
113 size_t bytes_read = varint::Decode(proto_, &length);
114 if (bytes_read == 0) {
115 state_ = kDecodeFailed;
116 return Status::DATA_LOSS;
117 }
118
119 proto_ = proto_.subspan(bytes_read);
120 if (proto_.size() < length) {
121 state_ = kDecodeFailed;
122 return Status::DATA_LOSS;
123 }
124
125 *out = proto_.first(length);
126 proto_ = proto_.subspan(length);
127
128 return Status::OK;
129}
130
131Status Decoder::ConsumeKey(uint32_t field_number, WireType expected_type) {
Alexei Frolovdea46f72020-01-07 13:28:24 -0800132 if (state_ != kDecodeInProgress) {
133 return Status::FAILED_PRECONDITION;
134 }
135
136 uint64_t key;
137 size_t bytes_read = varint::Decode(proto_, &key);
138 if (bytes_read == 0) {
139 state_ = kDecodeFailed;
140 return Status::FAILED_PRECONDITION;
141 }
142
143 uint32_t field = key >> kFieldNumberShift;
144 WireType wire_type = static_cast<WireType>(key & kWireTypeMask);
145
Alexei Frolov6d9b9b42020-01-14 12:57:33 -0800146 if (field != field_number || wire_type != expected_type) {
Alexei Frolovdea46f72020-01-07 13:28:24 -0800147 state_ = kDecodeFailed;
148 return Status::FAILED_PRECONDITION;
149 }
150
Alexei Frolov6d9b9b42020-01-14 12:57:33 -0800151 // Advance past the key.
Alexei Frolovdea46f72020-01-07 13:28:24 -0800152 proto_ = proto_.subspan(bytes_read);
153 return Status::OK;
154}
155
156void Decoder::SkipField() {
157 uint64_t key;
158 proto_ = proto_.subspan(varint::Decode(proto_, &key));
159
160 WireType wire_type = static_cast<WireType>(key & kWireTypeMask);
161 size_t bytes_to_skip = 0;
162 uint64_t value = 0;
163
164 switch (wire_type) {
165 case WireType::kVarint:
166 bytes_to_skip = varint::Decode(proto_, &value);
167 break;
168
169 case WireType::kDelimited:
170 // Varint at cursor indicates size of the field.
171 bytes_to_skip += varint::Decode(proto_, &value);
172 bytes_to_skip += value;
173 break;
174
175 case WireType::kFixed32:
176 bytes_to_skip = sizeof(uint32_t);
177 break;
178
179 case WireType::kFixed64:
180 bytes_to_skip = sizeof(uint64_t);
181 break;
182 }
183
184 if (bytes_to_skip == 0) {
185 state_ = kDecodeFailed;
186 } else {
187 proto_ = proto_.subspan(bytes_to_skip);
188 }
189}
190
191} // namespace pw::protobuf