xref: /aosp_15_r20/external/pigweed/pw_protobuf/decoder.cc (revision 61c4878ac05f98d0ceed94b57d316916de578985)
1*61c4878aSAndroid Build Coastguard Worker // Copyright 2020 The Pigweed Authors
2*61c4878aSAndroid Build Coastguard Worker //
3*61c4878aSAndroid Build Coastguard Worker // Licensed under the Apache License, Version 2.0 (the "License"); you may not
4*61c4878aSAndroid Build Coastguard Worker // use this file except in compliance with the License. You may obtain a copy of
5*61c4878aSAndroid Build Coastguard Worker // the License at
6*61c4878aSAndroid Build Coastguard Worker //
7*61c4878aSAndroid Build Coastguard Worker //     https://www.apache.org/licenses/LICENSE-2.0
8*61c4878aSAndroid Build Coastguard Worker //
9*61c4878aSAndroid Build Coastguard Worker // Unless required by applicable law or agreed to in writing, software
10*61c4878aSAndroid Build Coastguard Worker // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11*61c4878aSAndroid Build Coastguard Worker // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12*61c4878aSAndroid Build Coastguard Worker // License for the specific language governing permissions and limitations under
13*61c4878aSAndroid Build Coastguard Worker // the License.
14*61c4878aSAndroid Build Coastguard Worker 
15*61c4878aSAndroid Build Coastguard Worker #include "pw_protobuf/decoder.h"
16*61c4878aSAndroid Build Coastguard Worker 
17*61c4878aSAndroid Build Coastguard Worker #include <cstring>
18*61c4878aSAndroid Build Coastguard Worker 
19*61c4878aSAndroid Build Coastguard Worker #include "pw_assert/check.h"
20*61c4878aSAndroid Build Coastguard Worker #include "pw_varint/varint.h"
21*61c4878aSAndroid Build Coastguard Worker 
22*61c4878aSAndroid Build Coastguard Worker namespace pw::protobuf {
23*61c4878aSAndroid Build Coastguard Worker 
Next()24*61c4878aSAndroid Build Coastguard Worker Status Decoder::Next() {
25*61c4878aSAndroid Build Coastguard Worker   if (!previous_field_consumed_) {
26*61c4878aSAndroid Build Coastguard Worker     if (Status status = SkipField(); !status.ok()) {
27*61c4878aSAndroid Build Coastguard Worker       return status;
28*61c4878aSAndroid Build Coastguard Worker     }
29*61c4878aSAndroid Build Coastguard Worker   }
30*61c4878aSAndroid Build Coastguard Worker   if (proto_.empty()) {
31*61c4878aSAndroid Build Coastguard Worker     return Status::OutOfRange();
32*61c4878aSAndroid Build Coastguard Worker   }
33*61c4878aSAndroid Build Coastguard Worker   previous_field_consumed_ = false;
34*61c4878aSAndroid Build Coastguard Worker   return GetFieldSize().ok() ? OkStatus() : Status::DataLoss();
35*61c4878aSAndroid Build Coastguard Worker }
36*61c4878aSAndroid Build Coastguard Worker 
SkipField()37*61c4878aSAndroid Build Coastguard Worker Status Decoder::SkipField() {
38*61c4878aSAndroid Build Coastguard Worker   if (proto_.empty()) {
39*61c4878aSAndroid Build Coastguard Worker     return Status::OutOfRange();
40*61c4878aSAndroid Build Coastguard Worker   }
41*61c4878aSAndroid Build Coastguard Worker 
42*61c4878aSAndroid Build Coastguard Worker   size_t bytes_to_skip = GetFieldSize().total();
43*61c4878aSAndroid Build Coastguard Worker   if (bytes_to_skip == 0) {
44*61c4878aSAndroid Build Coastguard Worker     return Status::DataLoss();
45*61c4878aSAndroid Build Coastguard Worker   }
46*61c4878aSAndroid Build Coastguard Worker 
47*61c4878aSAndroid Build Coastguard Worker   proto_ = proto_.subspan(bytes_to_skip);
48*61c4878aSAndroid Build Coastguard Worker   return proto_.empty() ? Status::OutOfRange() : OkStatus();
49*61c4878aSAndroid Build Coastguard Worker }
50*61c4878aSAndroid Build Coastguard Worker 
FieldNumber() const51*61c4878aSAndroid Build Coastguard Worker uint32_t Decoder::FieldNumber() const {
52*61c4878aSAndroid Build Coastguard Worker   uint64_t key;
53*61c4878aSAndroid Build Coastguard Worker   varint::Decode(proto_, &key);
54*61c4878aSAndroid Build Coastguard Worker   if (!FieldKey::IsValidKey(key)) {
55*61c4878aSAndroid Build Coastguard Worker     return 0;
56*61c4878aSAndroid Build Coastguard Worker   }
57*61c4878aSAndroid Build Coastguard Worker   PW_DCHECK(key <= std::numeric_limits<uint32_t>::max());
58*61c4878aSAndroid Build Coastguard Worker   return FieldKey(static_cast<uint32_t>(key)).field_number();
59*61c4878aSAndroid Build Coastguard Worker }
60*61c4878aSAndroid Build Coastguard Worker 
ReadUint32(uint32_t * out)61*61c4878aSAndroid Build Coastguard Worker Status Decoder::ReadUint32(uint32_t* out) {
62*61c4878aSAndroid Build Coastguard Worker   uint64_t value = 0;
63*61c4878aSAndroid Build Coastguard Worker   Status status = ReadUint64(&value);
64*61c4878aSAndroid Build Coastguard Worker   if (!status.ok()) {
65*61c4878aSAndroid Build Coastguard Worker     return status;
66*61c4878aSAndroid Build Coastguard Worker   }
67*61c4878aSAndroid Build Coastguard Worker   if (value > std::numeric_limits<uint32_t>::max()) {
68*61c4878aSAndroid Build Coastguard Worker     return Status::OutOfRange();
69*61c4878aSAndroid Build Coastguard Worker   }
70*61c4878aSAndroid Build Coastguard Worker   *out = static_cast<uint32_t>(value);
71*61c4878aSAndroid Build Coastguard Worker   return OkStatus();
72*61c4878aSAndroid Build Coastguard Worker }
73*61c4878aSAndroid Build Coastguard Worker 
ReadSint32(int32_t * out)74*61c4878aSAndroid Build Coastguard Worker Status Decoder::ReadSint32(int32_t* out) {
75*61c4878aSAndroid Build Coastguard Worker   int64_t value = 0;
76*61c4878aSAndroid Build Coastguard Worker   Status status = ReadSint64(&value);
77*61c4878aSAndroid Build Coastguard Worker   if (!status.ok()) {
78*61c4878aSAndroid Build Coastguard Worker     return status;
79*61c4878aSAndroid Build Coastguard Worker   }
80*61c4878aSAndroid Build Coastguard Worker   if (value > std::numeric_limits<int32_t>::max()) {
81*61c4878aSAndroid Build Coastguard Worker     return Status::OutOfRange();
82*61c4878aSAndroid Build Coastguard Worker   }
83*61c4878aSAndroid Build Coastguard Worker   *out = static_cast<uint32_t>(value);
84*61c4878aSAndroid Build Coastguard Worker   return OkStatus();
85*61c4878aSAndroid Build Coastguard Worker }
86*61c4878aSAndroid Build Coastguard Worker 
ReadSint64(int64_t * out)87*61c4878aSAndroid Build Coastguard Worker Status Decoder::ReadSint64(int64_t* out) {
88*61c4878aSAndroid Build Coastguard Worker   uint64_t value = 0;
89*61c4878aSAndroid Build Coastguard Worker   Status status = ReadUint64(&value);
90*61c4878aSAndroid Build Coastguard Worker   if (!status.ok()) {
91*61c4878aSAndroid Build Coastguard Worker     return status;
92*61c4878aSAndroid Build Coastguard Worker   }
93*61c4878aSAndroid Build Coastguard Worker   *out = varint::ZigZagDecode(value);
94*61c4878aSAndroid Build Coastguard Worker   return OkStatus();
95*61c4878aSAndroid Build Coastguard Worker }
96*61c4878aSAndroid Build Coastguard Worker 
ReadBool(bool * out)97*61c4878aSAndroid Build Coastguard Worker Status Decoder::ReadBool(bool* out) {
98*61c4878aSAndroid Build Coastguard Worker   uint64_t value = 0;
99*61c4878aSAndroid Build Coastguard Worker   Status status = ReadUint64(&value);
100*61c4878aSAndroid Build Coastguard Worker   if (!status.ok()) {
101*61c4878aSAndroid Build Coastguard Worker     return status;
102*61c4878aSAndroid Build Coastguard Worker   }
103*61c4878aSAndroid Build Coastguard Worker   *out = value;
104*61c4878aSAndroid Build Coastguard Worker   return OkStatus();
105*61c4878aSAndroid Build Coastguard Worker }
106*61c4878aSAndroid Build Coastguard Worker 
ReadString(std::string_view * out)107*61c4878aSAndroid Build Coastguard Worker Status Decoder::ReadString(std::string_view* out) {
108*61c4878aSAndroid Build Coastguard Worker   span<const std::byte> bytes;
109*61c4878aSAndroid Build Coastguard Worker   Status status = ReadDelimited(&bytes);
110*61c4878aSAndroid Build Coastguard Worker   if (!status.ok()) {
111*61c4878aSAndroid Build Coastguard Worker     return status;
112*61c4878aSAndroid Build Coastguard Worker   }
113*61c4878aSAndroid Build Coastguard Worker   *out = std::string_view(reinterpret_cast<const char*>(bytes.data()),
114*61c4878aSAndroid Build Coastguard Worker                           bytes.size());
115*61c4878aSAndroid Build Coastguard Worker   return OkStatus();
116*61c4878aSAndroid Build Coastguard Worker }
117*61c4878aSAndroid Build Coastguard Worker 
GetFieldSize() const118*61c4878aSAndroid Build Coastguard Worker Decoder::FieldSize Decoder::GetFieldSize() const {
119*61c4878aSAndroid Build Coastguard Worker   uint64_t key;
120*61c4878aSAndroid Build Coastguard Worker   size_t key_size = varint::Decode(proto_, &key);
121*61c4878aSAndroid Build Coastguard Worker   if (key_size == 0 || !FieldKey::IsValidKey(key)) {
122*61c4878aSAndroid Build Coastguard Worker     return FieldSize::Invalid();
123*61c4878aSAndroid Build Coastguard Worker   }
124*61c4878aSAndroid Build Coastguard Worker 
125*61c4878aSAndroid Build Coastguard Worker   span<const std::byte> remainder = proto_.subspan(key_size);
126*61c4878aSAndroid Build Coastguard Worker   uint64_t value = 0;
127*61c4878aSAndroid Build Coastguard Worker   size_t expected_size = 0;
128*61c4878aSAndroid Build Coastguard Worker 
129*61c4878aSAndroid Build Coastguard Worker   PW_DCHECK(key <= std::numeric_limits<uint32_t>::max());
130*61c4878aSAndroid Build Coastguard Worker   switch (FieldKey(static_cast<uint32_t>(key)).wire_type()) {
131*61c4878aSAndroid Build Coastguard Worker     case WireType::kVarint:
132*61c4878aSAndroid Build Coastguard Worker       expected_size = varint::Decode(remainder, &value);
133*61c4878aSAndroid Build Coastguard Worker       if (expected_size == 0) {
134*61c4878aSAndroid Build Coastguard Worker         return FieldSize::Invalid();
135*61c4878aSAndroid Build Coastguard Worker       }
136*61c4878aSAndroid Build Coastguard Worker       break;
137*61c4878aSAndroid Build Coastguard Worker 
138*61c4878aSAndroid Build Coastguard Worker     case WireType::kDelimited: {
139*61c4878aSAndroid Build Coastguard Worker       // Varint at cursor indicates size of the field.
140*61c4878aSAndroid Build Coastguard Worker       const size_t delimited_size = varint::Decode(remainder, &value);
141*61c4878aSAndroid Build Coastguard Worker       if (delimited_size == 0) {
142*61c4878aSAndroid Build Coastguard Worker         return FieldSize::Invalid();
143*61c4878aSAndroid Build Coastguard Worker       }
144*61c4878aSAndroid Build Coastguard Worker       key_size += delimited_size;
145*61c4878aSAndroid Build Coastguard Worker       expected_size += value;
146*61c4878aSAndroid Build Coastguard Worker       break;
147*61c4878aSAndroid Build Coastguard Worker     }
148*61c4878aSAndroid Build Coastguard Worker     case WireType::kFixed32:
149*61c4878aSAndroid Build Coastguard Worker       expected_size = sizeof(uint32_t);
150*61c4878aSAndroid Build Coastguard Worker       break;
151*61c4878aSAndroid Build Coastguard Worker 
152*61c4878aSAndroid Build Coastguard Worker     case WireType::kFixed64:
153*61c4878aSAndroid Build Coastguard Worker       expected_size = sizeof(uint64_t);
154*61c4878aSAndroid Build Coastguard Worker       break;
155*61c4878aSAndroid Build Coastguard Worker   }
156*61c4878aSAndroid Build Coastguard Worker 
157*61c4878aSAndroid Build Coastguard Worker   if (remainder.size() < expected_size) {
158*61c4878aSAndroid Build Coastguard Worker     return FieldSize::Invalid();
159*61c4878aSAndroid Build Coastguard Worker   }
160*61c4878aSAndroid Build Coastguard Worker 
161*61c4878aSAndroid Build Coastguard Worker   return FieldSize{key_size, expected_size};
162*61c4878aSAndroid Build Coastguard Worker }
163*61c4878aSAndroid Build Coastguard Worker 
ConsumeKey(WireType expected_type)164*61c4878aSAndroid Build Coastguard Worker Status Decoder::ConsumeKey(WireType expected_type) {
165*61c4878aSAndroid Build Coastguard Worker   uint64_t key;
166*61c4878aSAndroid Build Coastguard Worker   size_t bytes_read = varint::Decode(proto_, &key);
167*61c4878aSAndroid Build Coastguard Worker   if (bytes_read == 0) {
168*61c4878aSAndroid Build Coastguard Worker     return Status::FailedPrecondition();
169*61c4878aSAndroid Build Coastguard Worker   }
170*61c4878aSAndroid Build Coastguard Worker 
171*61c4878aSAndroid Build Coastguard Worker   if (!FieldKey::IsValidKey(key)) {
172*61c4878aSAndroid Build Coastguard Worker     return Status::DataLoss();
173*61c4878aSAndroid Build Coastguard Worker   }
174*61c4878aSAndroid Build Coastguard Worker 
175*61c4878aSAndroid Build Coastguard Worker   PW_DCHECK(key <= std::numeric_limits<uint32_t>::max());
176*61c4878aSAndroid Build Coastguard Worker   if (FieldKey(static_cast<uint32_t>(key)).wire_type() != expected_type) {
177*61c4878aSAndroid Build Coastguard Worker     return Status::FailedPrecondition();
178*61c4878aSAndroid Build Coastguard Worker   }
179*61c4878aSAndroid Build Coastguard Worker 
180*61c4878aSAndroid Build Coastguard Worker   // Advance past the key.
181*61c4878aSAndroid Build Coastguard Worker   proto_ = proto_.subspan(bytes_read);
182*61c4878aSAndroid Build Coastguard Worker   return OkStatus();
183*61c4878aSAndroid Build Coastguard Worker }
184*61c4878aSAndroid Build Coastguard Worker 
ReadVarint(uint64_t * out)185*61c4878aSAndroid Build Coastguard Worker Status Decoder::ReadVarint(uint64_t* out) {
186*61c4878aSAndroid Build Coastguard Worker   if (Status status = ConsumeKey(WireType::kVarint); !status.ok()) {
187*61c4878aSAndroid Build Coastguard Worker     return status;
188*61c4878aSAndroid Build Coastguard Worker   }
189*61c4878aSAndroid Build Coastguard Worker 
190*61c4878aSAndroid Build Coastguard Worker   size_t bytes_read = varint::Decode(proto_, out);
191*61c4878aSAndroid Build Coastguard Worker   if (bytes_read == 0) {
192*61c4878aSAndroid Build Coastguard Worker     return Status::DataLoss();
193*61c4878aSAndroid Build Coastguard Worker   }
194*61c4878aSAndroid Build Coastguard Worker 
195*61c4878aSAndroid Build Coastguard Worker   // Advance to the next field.
196*61c4878aSAndroid Build Coastguard Worker   proto_ = proto_.subspan(bytes_read);
197*61c4878aSAndroid Build Coastguard Worker   previous_field_consumed_ = true;
198*61c4878aSAndroid Build Coastguard Worker   return OkStatus();
199*61c4878aSAndroid Build Coastguard Worker }
200*61c4878aSAndroid Build Coastguard Worker 
ReadFixed(std::byte * out,size_t size)201*61c4878aSAndroid Build Coastguard Worker Status Decoder::ReadFixed(std::byte* out, size_t size) {
202*61c4878aSAndroid Build Coastguard Worker   WireType expected_wire_type =
203*61c4878aSAndroid Build Coastguard Worker       size == sizeof(uint32_t) ? WireType::kFixed32 : WireType::kFixed64;
204*61c4878aSAndroid Build Coastguard Worker   Status status = ConsumeKey(expected_wire_type);
205*61c4878aSAndroid Build Coastguard Worker   if (!status.ok()) {
206*61c4878aSAndroid Build Coastguard Worker     return status;
207*61c4878aSAndroid Build Coastguard Worker   }
208*61c4878aSAndroid Build Coastguard Worker 
209*61c4878aSAndroid Build Coastguard Worker   if (proto_.size() < size) {
210*61c4878aSAndroid Build Coastguard Worker     return Status::DataLoss();
211*61c4878aSAndroid Build Coastguard Worker   }
212*61c4878aSAndroid Build Coastguard Worker 
213*61c4878aSAndroid Build Coastguard Worker   std::memcpy(out, proto_.data(), size);
214*61c4878aSAndroid Build Coastguard Worker   proto_ = proto_.subspan(size);
215*61c4878aSAndroid Build Coastguard Worker   previous_field_consumed_ = true;
216*61c4878aSAndroid Build Coastguard Worker 
217*61c4878aSAndroid Build Coastguard Worker   return OkStatus();
218*61c4878aSAndroid Build Coastguard Worker }
219*61c4878aSAndroid Build Coastguard Worker 
ReadDelimited(span<const std::byte> * out)220*61c4878aSAndroid Build Coastguard Worker Status Decoder::ReadDelimited(span<const std::byte>* out) {
221*61c4878aSAndroid Build Coastguard Worker   Status status = ConsumeKey(WireType::kDelimited);
222*61c4878aSAndroid Build Coastguard Worker   if (!status.ok()) {
223*61c4878aSAndroid Build Coastguard Worker     return status;
224*61c4878aSAndroid Build Coastguard Worker   }
225*61c4878aSAndroid Build Coastguard Worker 
226*61c4878aSAndroid Build Coastguard Worker   uint64_t length;
227*61c4878aSAndroid Build Coastguard Worker   size_t bytes_read = varint::Decode(proto_, &length);
228*61c4878aSAndroid Build Coastguard Worker   if (bytes_read == 0) {
229*61c4878aSAndroid Build Coastguard Worker     return Status::DataLoss();
230*61c4878aSAndroid Build Coastguard Worker   }
231*61c4878aSAndroid Build Coastguard Worker 
232*61c4878aSAndroid Build Coastguard Worker   proto_ = proto_.subspan(bytes_read);
233*61c4878aSAndroid Build Coastguard Worker   if (proto_.size() < length) {
234*61c4878aSAndroid Build Coastguard Worker     return Status::DataLoss();
235*61c4878aSAndroid Build Coastguard Worker   }
236*61c4878aSAndroid Build Coastguard Worker 
237*61c4878aSAndroid Build Coastguard Worker   *out = proto_.first(length);
238*61c4878aSAndroid Build Coastguard Worker   proto_ = proto_.subspan(length);
239*61c4878aSAndroid Build Coastguard Worker   previous_field_consumed_ = true;
240*61c4878aSAndroid Build Coastguard Worker 
241*61c4878aSAndroid Build Coastguard Worker   return OkStatus();
242*61c4878aSAndroid Build Coastguard Worker }
243*61c4878aSAndroid Build Coastguard Worker 
Decode(span<const std::byte> proto)244*61c4878aSAndroid Build Coastguard Worker Status CallbackDecoder::Decode(span<const std::byte> proto) {
245*61c4878aSAndroid Build Coastguard Worker   if (handler_ == nullptr || state_ != kReady) {
246*61c4878aSAndroid Build Coastguard Worker     return Status::FailedPrecondition();
247*61c4878aSAndroid Build Coastguard Worker   }
248*61c4878aSAndroid Build Coastguard Worker 
249*61c4878aSAndroid Build Coastguard Worker   state_ = kDecodeInProgress;
250*61c4878aSAndroid Build Coastguard Worker   decoder_.Reset(proto);
251*61c4878aSAndroid Build Coastguard Worker 
252*61c4878aSAndroid Build Coastguard Worker   // Iterate the proto, calling the handler with each field number.
253*61c4878aSAndroid Build Coastguard Worker   while (state_ == kDecodeInProgress) {
254*61c4878aSAndroid Build Coastguard Worker     if (Status status = decoder_.Next(); !status.ok()) {
255*61c4878aSAndroid Build Coastguard Worker       if (status.IsOutOfRange()) {
256*61c4878aSAndroid Build Coastguard Worker         // Reached the end of the proto.
257*61c4878aSAndroid Build Coastguard Worker         break;
258*61c4878aSAndroid Build Coastguard Worker       }
259*61c4878aSAndroid Build Coastguard Worker 
260*61c4878aSAndroid Build Coastguard Worker       // Proto data is malformed.
261*61c4878aSAndroid Build Coastguard Worker       return status;
262*61c4878aSAndroid Build Coastguard Worker     }
263*61c4878aSAndroid Build Coastguard Worker 
264*61c4878aSAndroid Build Coastguard Worker     Status status = handler_->ProcessField(*this, decoder_.FieldNumber());
265*61c4878aSAndroid Build Coastguard Worker     if (!status.ok()) {
266*61c4878aSAndroid Build Coastguard Worker       state_ = status.IsCancelled() ? kDecodeCancelled : kDecodeFailed;
267*61c4878aSAndroid Build Coastguard Worker       return status;
268*61c4878aSAndroid Build Coastguard Worker     }
269*61c4878aSAndroid Build Coastguard Worker 
270*61c4878aSAndroid Build Coastguard Worker     // The callback function can modify the decoder's state; check that
271*61c4878aSAndroid Build Coastguard Worker     // everything is still okay.
272*61c4878aSAndroid Build Coastguard Worker     if (state_ == kDecodeFailed) {
273*61c4878aSAndroid Build Coastguard Worker       break;
274*61c4878aSAndroid Build Coastguard Worker     }
275*61c4878aSAndroid Build Coastguard Worker   }
276*61c4878aSAndroid Build Coastguard Worker 
277*61c4878aSAndroid Build Coastguard Worker   if (state_ != kDecodeInProgress) {
278*61c4878aSAndroid Build Coastguard Worker     return Status::DataLoss();
279*61c4878aSAndroid Build Coastguard Worker   }
280*61c4878aSAndroid Build Coastguard Worker 
281*61c4878aSAndroid Build Coastguard Worker   state_ = kReady;
282*61c4878aSAndroid Build Coastguard Worker   return OkStatus();
283*61c4878aSAndroid Build Coastguard Worker }
284*61c4878aSAndroid Build Coastguard Worker 
285*61c4878aSAndroid Build Coastguard Worker }  // namespace pw::protobuf
286