1 // 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 #pragma once 15 16 #include <string_view> 17 18 #include "pw_bytes/span.h" 19 #include "pw_protobuf/wire_format.h" 20 #include "pw_result/result.h" 21 #include "pw_span/span.h" 22 #include "pw_status/status.h" 23 #include "pw_varint/varint.h" 24 25 // This file defines a low-level event-based protobuf wire format decoder. 26 // The decoder processes an encoded message by iterating over its fields. The 27 // caller can extract the values of any fields it cares about. 28 // 29 // The decoder does not provide any in-memory data structures to represent a 30 // protobuf message's data. More sophisticated APIs can be built on top of the 31 // low-level decoder to provide additional functionality, if desired. 32 // 33 // Example usage: 34 // 35 // Decoder decoder(proto); 36 // while (decoder.Next().ok()) { 37 // switch (decoder.FieldNumber()) { 38 // case 1: 39 // decoder.ReadUint32(&my_uint32); 40 // break; 41 // // ... and other fields. 42 // } 43 // } 44 // 45 namespace pw::protobuf { 46 47 // TODO(frolv): Rename this to MemoryDecoder to match the encoder naming. 48 class Decoder { 49 public: Decoder(span<const std::byte> proto)50 constexpr Decoder(span<const std::byte> proto) 51 : proto_(proto), previous_field_consumed_(true) {} 52 53 Decoder(const Decoder& other) = delete; 54 Decoder& operator=(const Decoder& other) = delete; 55 56 // Advances to the next field in the proto. 57 // 58 // If Next() returns OK, there is guaranteed to be a valid protobuf field at 59 // the current cursor position. 60 // 61 // Return values: 62 // 63 // OK: Advanced to a valid proto field. 64 // OUT_OF_RANGE: Reached the end of the proto message. 65 // DATA_LOSS: Invalid protobuf data. 66 // 67 Status Next(); 68 69 // Returns the field number of the field at the current cursor position. 70 // 71 // A return value of 0 indicates that the field number is invalid. An invalid 72 // field number terminates the decode operation; any subsequent calls to 73 // Next() or Read*() will return DATA_LOSS. 74 // 75 // TODO(frolv): This should be refactored to return a Result<uint32_t>. 76 uint32_t FieldNumber() const; 77 78 // Reads a proto int32 value from the current cursor. ReadInt32(int32_t * out)79 Status ReadInt32(int32_t* out) { 80 return ReadUint32(reinterpret_cast<uint32_t*>(out)); 81 } 82 83 // Reads a proto uint32 value from the current cursor. 84 Status ReadUint32(uint32_t* out); 85 86 // Reads a proto int64 value from the current cursor. ReadInt64(int64_t * out)87 Status ReadInt64(int64_t* out) { 88 return ReadVarint(reinterpret_cast<uint64_t*>(out)); 89 } 90 91 // Reads a proto uint64 value from the current cursor. ReadUint64(uint64_t * out)92 Status ReadUint64(uint64_t* out) { return ReadVarint(out); } 93 94 // Reads a proto sint32 value from the current cursor. 95 Status ReadSint32(int32_t* out); 96 97 // Reads a proto sint64 value from the current cursor. 98 Status ReadSint64(int64_t* out); 99 100 // Reads a proto bool value from the current cursor. 101 Status ReadBool(bool* out); 102 103 // Reads a proto fixed32 value from the current cursor. ReadFixed32(uint32_t * out)104 Status ReadFixed32(uint32_t* out) { return ReadFixed(out); } 105 106 // Reads a proto fixed64 value from the current cursor. ReadFixed64(uint64_t * out)107 Status ReadFixed64(uint64_t* out) { return ReadFixed(out); } 108 109 // Reads a proto sfixed32 value from the current cursor. ReadSfixed32(int32_t * out)110 Status ReadSfixed32(int32_t* out) { 111 return ReadFixed32(reinterpret_cast<uint32_t*>(out)); 112 } 113 114 // Reads a proto sfixed64 value from the current cursor. ReadSfixed64(int64_t * out)115 Status ReadSfixed64(int64_t* out) { 116 return ReadFixed64(reinterpret_cast<uint64_t*>(out)); 117 } 118 119 // Reads a proto float value from the current cursor. ReadFloat(float * out)120 Status ReadFloat(float* out) { 121 static_assert(sizeof(float) == sizeof(uint32_t), 122 "Float and uint32_t must be the same size for protobufs"); 123 return ReadFixed(out); 124 } 125 126 // Reads a proto double value from the current cursor. ReadDouble(double * out)127 Status ReadDouble(double* out) { 128 static_assert(sizeof(double) == sizeof(uint64_t), 129 "Double and uint64_t must be the same size for protobufs"); 130 return ReadFixed(out); 131 } 132 133 // Reads a proto string value from the current cursor and returns a view of it 134 // in `out`. The raw protobuf data must outlive `out`. If the string field is 135 // invalid, `out` is not modified. 136 Status ReadString(std::string_view* out); 137 138 // Reads a proto bytes value from the current cursor and returns a view of it 139 // in `out`. The raw protobuf data must outlive the `out` span. If the 140 // bytes field is invalid, `out` is not modified. ReadBytes(span<const std::byte> * out)141 Status ReadBytes(span<const std::byte>* out) { return ReadDelimited(out); } 142 143 // Resets the decoder to start reading a new proto message. Reset(span<const std::byte> proto)144 void Reset(span<const std::byte> proto) { 145 proto_ = proto; 146 previous_field_consumed_ = true; 147 } 148 149 private: 150 // Allow only the FindRaw function to access the raw bytes of the field. 151 friend Result<ConstByteSpan> FindRaw(ConstByteSpan, uint32_t); 152 153 // Returns the raw field value. The decoder MUST be at a valid field. RawFieldBytes()154 ConstByteSpan RawFieldBytes() { return GetFieldSize().ValueBytes(proto_); } 155 156 // Advances the cursor to the next field in the proto. 157 Status SkipField(); 158 159 // Stores the size of a field. 160 class FieldSize { 161 public: Invalid()162 static constexpr FieldSize Invalid() { return {0, 0}; } 163 FieldSize(size_t key_size,size_t value_size)164 constexpr FieldSize(size_t key_size, size_t value_size) 165 : key_size_bytes_(key_size), value_size_bytes_(value_size) {} 166 ok()167 bool ok() const { return key_size_bytes_ != 0; } 168 169 // Total size of the field (key and value); 0 if ok() is false. total()170 size_t total() const { return key_size_bytes_ + value_size_bytes_; } 171 ValueBytes(ConstByteSpan field)172 ConstByteSpan ValueBytes(ConstByteSpan field) const { 173 PW_DASSERT(ok()); 174 return field.subspan(key_size_bytes_, value_size_bytes_); 175 } 176 177 private: 178 size_t key_size_bytes_; // size of key + length (if delimited field) 179 size_t value_size_bytes_; // size of raw value only 180 }; 181 182 // Returns the size of the current field as a FieldSize object. 183 FieldSize GetFieldSize() const; 184 185 Status ConsumeKey(WireType expected_type); 186 187 // Reads a varint key-value pair from the current cursor position. 188 Status ReadVarint(uint64_t* out); 189 190 // Reads a fixed-size key-value pair from the current cursor position. 191 Status ReadFixed(std::byte* out, size_t size); 192 193 template <typename T> ReadFixed(T * out)194 Status ReadFixed(T* out) { 195 static_assert( 196 sizeof(T) == sizeof(uint32_t) || sizeof(T) == sizeof(uint64_t), 197 "Protobuf fixed-size fields must be 32- or 64-bit"); 198 return ReadFixed(reinterpret_cast<std::byte*>(out), sizeof(T)); 199 } 200 201 Status ReadDelimited(span<const std::byte>* out); 202 203 span<const std::byte> proto_; 204 bool previous_field_consumed_; 205 }; 206 207 class DecodeHandler; 208 209 // A protobuf decoder that iterates over an encoded protobuf, calling a handler 210 // for each field it encounters. 211 // 212 // Example usage: 213 // 214 // class FooProtoHandler : public DecodeHandler { 215 // public: 216 // Status ProcessField(CallbackDecoder& decoder, 217 // uint32_t field_number) override { 218 // switch (field_number) { 219 // case FooFields::kBar: 220 // if (!decoder.ReadSint32(&bar).ok()) { 221 // bar = 0; 222 // } 223 // break; 224 // case FooFields::kBaz: 225 // if (!decoder.ReadUint32(&baz).ok()) { 226 // baz = 0; 227 // } 228 // break; 229 // } 230 // 231 // return OkStatus(); 232 // } 233 // 234 // int bar; 235 // unsigned int baz; 236 // }; 237 // 238 // void DecodeFooProto(span<std::byte> raw_proto) { 239 // Decoder decoder; 240 // FooProtoHandler handler; 241 // 242 // decoder.set_handler(&handler); 243 // if (!decoder.Decode(raw_proto).ok()) { 244 // LOG_FATAL("Invalid foo message!"); 245 // } 246 // 247 // LOG_INFO("Read Foo proto message; bar: %d baz: %u", 248 // handler.bar, handler.baz); 249 // } 250 // 251 class CallbackDecoder { 252 public: CallbackDecoder()253 constexpr CallbackDecoder() 254 : decoder_({}), handler_(nullptr), state_(kReady) {} 255 256 CallbackDecoder(const CallbackDecoder& other) = delete; 257 CallbackDecoder& operator=(const CallbackDecoder& other) = delete; 258 set_handler(DecodeHandler * handler)259 void set_handler(DecodeHandler* handler) { handler_ = handler; } 260 261 // Decodes the specified protobuf data. The registered handler's ProcessField 262 // function is called on each field found in the data. 263 Status Decode(span<const std::byte> proto); 264 265 // Reads a proto int32 value from the current cursor. ReadInt32(int32_t * out)266 Status ReadInt32(int32_t* out) { return decoder_.ReadInt32(out); } 267 268 // Reads a proto uint32 value from the current cursor. ReadUint32(uint32_t * out)269 Status ReadUint32(uint32_t* out) { return decoder_.ReadUint32(out); } 270 271 // Reads a proto int64 value from the current cursor. ReadInt64(int64_t * out)272 Status ReadInt64(int64_t* out) { return decoder_.ReadInt64(out); } 273 274 // Reads a proto uint64 value from the current cursor. ReadUint64(uint64_t * out)275 Status ReadUint64(uint64_t* out) { return decoder_.ReadUint64(out); } 276 277 // Reads a proto sint64 value from the current cursor. ReadSint32(int32_t * out)278 Status ReadSint32(int32_t* out) { return decoder_.ReadSint32(out); } 279 280 // Reads a proto sint64 value from the current cursor. ReadSint64(int64_t * out)281 Status ReadSint64(int64_t* out) { return decoder_.ReadSint64(out); } 282 283 // Reads a proto bool value from the current cursor. ReadBool(bool * out)284 Status ReadBool(bool* out) { return decoder_.ReadBool(out); } 285 286 // Reads a proto fixed32 value from the current cursor. ReadFixed32(uint32_t * out)287 Status ReadFixed32(uint32_t* out) { return decoder_.ReadFixed32(out); } 288 289 // Reads a proto fixed64 value from the current cursor. ReadFixed64(uint64_t * out)290 Status ReadFixed64(uint64_t* out) { return decoder_.ReadFixed64(out); } 291 292 // Reads a proto sfixed32 value from the current cursor. ReadSfixed32(int32_t * out)293 Status ReadSfixed32(int32_t* out) { return decoder_.ReadSfixed32(out); } 294 295 // Reads a proto sfixed64 value from the current cursor. ReadSfixed64(int64_t * out)296 Status ReadSfixed64(int64_t* out) { return decoder_.ReadSfixed64(out); } 297 298 // Reads a proto float value from the current cursor. ReadFloat(float * out)299 Status ReadFloat(float* out) { return decoder_.ReadFloat(out); } 300 301 // Reads a proto double value from the current cursor. ReadDouble(double * out)302 Status ReadDouble(double* out) { return decoder_.ReadDouble(out); } 303 304 // Reads a proto string value from the current cursor and returns a view of it 305 // in `out`. The raw protobuf data must outlive `out`. If the string field is 306 // invalid, `out` is not modified. ReadString(std::string_view * out)307 Status ReadString(std::string_view* out) { return decoder_.ReadString(out); } 308 309 // Reads a proto bytes value from the current cursor and returns a view of it 310 // in `out`. The raw protobuf data must outlive the `out` span. If the 311 // bytes field is invalid, `out` is not modified. ReadBytes(span<const std::byte> * out)312 Status ReadBytes(span<const std::byte>* out) { 313 return decoder_.ReadBytes(out); 314 } 315 cancelled()316 bool cancelled() const { return state_ == kDecodeCancelled; } 317 318 private: 319 enum State { 320 kReady, 321 kDecodeInProgress, 322 kDecodeCancelled, 323 kDecodeFailed, 324 }; 325 326 Decoder decoder_; 327 DecodeHandler* handler_; 328 329 State state_; 330 }; 331 332 // The event-handling interface implemented for a proto callback decoding 333 // operation. 334 class DecodeHandler { 335 public: 336 virtual ~DecodeHandler() = default; 337 338 // Callback called for each field encountered in the decoded proto message. 339 // Receives a pointer to the decoder object, allowing the handler to call 340 // the appropriate method to extract the field's data. 341 // 342 // If the status returned is not OkStatus(), the decode operation is exited 343 // with the provided status. Returning Status::Cancelled() allows a convenient 344 // way of stopping a decode early (for example, if a desired field is found). 345 virtual Status ProcessField(CallbackDecoder& decoder, 346 uint32_t field_number) = 0; 347 }; 348 349 } // namespace pw::protobuf 350