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