blob: 0e803e6b332b3ef1ccca596903ce1b0fd8473862 [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
17#include "pw_varint/varint.h"
18
19namespace pw::protobuf {
20
21Status Decoder::Decode(span<const std::byte> proto) {
22 if (handler_ == nullptr || state_ != kReady) {
23 return Status::FAILED_PRECONDITION;
24 }
25
26 state_ = kDecodeInProgress;
27 proto_ = proto;
28
29 // Iterate over each field in the proto, calling the handler with the field
30 // key.
31 while (state_ == kDecodeInProgress && !proto_.empty()) {
32 const std::byte* original_cursor = proto_.data();
33
34 uint64_t key;
35 size_t bytes_read = varint::Decode(proto_, &key);
36 if (bytes_read == 0) {
37 state_ = kDecodeFailed;
38 return Status::DATA_LOSS;
39 }
40
41 uint32_t field_number = key >> kFieldNumberShift;
42 Status status = handler_->ProcessField(this, field_number);
43 if (!status.ok()) {
44 return status;
45 }
46
47 // The callback function can modify the decoder's state; check that
48 // everything is still okay.
49 if (state_ == kDecodeFailed) {
50 break;
51 }
52
53 // If the cursor has not moved, the user has not consumed the field in their
54 // callback. Skip ahead to the next field.
55 if (original_cursor == proto_.data()) {
56 SkipField();
57 }
58 }
59
60 if (state_ != kDecodeInProgress) {
61 return Status::DATA_LOSS;
62 }
63
64 state_ = kReady;
65 return Status::OK;
66}
67
68Status Decoder::ReadVarint(uint32_t field_number, uint64_t* out) {
69 if (state_ != kDecodeInProgress) {
70 return Status::FAILED_PRECONDITION;
71 }
72
73 uint64_t key;
74 size_t bytes_read = varint::Decode(proto_, &key);
75 if (bytes_read == 0) {
76 state_ = kDecodeFailed;
77 return Status::FAILED_PRECONDITION;
78 }
79
80 uint32_t field = key >> kFieldNumberShift;
81 WireType wire_type = static_cast<WireType>(key & kWireTypeMask);
82
83 if (field != field_number || wire_type != WireType::kVarint) {
84 state_ = kDecodeFailed;
85 return Status::FAILED_PRECONDITION;
86 }
87
88 // Advance to the varint value.
89 proto_ = proto_.subspan(bytes_read);
90
91 bytes_read = varint::Decode(proto_, out);
92 if (bytes_read == 0) {
93 state_ = kDecodeFailed;
94 return Status::DATA_LOSS;
95 }
96
97 // Advance to the next field.
98 proto_ = proto_.subspan(bytes_read);
99 return Status::OK;
100}
101
102void Decoder::SkipField() {
103 uint64_t key;
104 proto_ = proto_.subspan(varint::Decode(proto_, &key));
105
106 WireType wire_type = static_cast<WireType>(key & kWireTypeMask);
107 size_t bytes_to_skip = 0;
108 uint64_t value = 0;
109
110 switch (wire_type) {
111 case WireType::kVarint:
112 bytes_to_skip = varint::Decode(proto_, &value);
113 break;
114
115 case WireType::kDelimited:
116 // Varint at cursor indicates size of the field.
117 bytes_to_skip += varint::Decode(proto_, &value);
118 bytes_to_skip += value;
119 break;
120
121 case WireType::kFixed32:
122 bytes_to_skip = sizeof(uint32_t);
123 break;
124
125 case WireType::kFixed64:
126 bytes_to_skip = sizeof(uint64_t);
127 break;
128 }
129
130 if (bytes_to_skip == 0) {
131 state_ = kDecodeFailed;
132 } else {
133 proto_ = proto_.subspan(bytes_to_skip);
134 }
135}
136
137} // namespace pw::protobuf