1 /* 2 * Copyright (C) 2016 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 // This file implements a simple protobuf encoder and decoder. The high-level 18 // idea is to use C++ structs as data containers corresponding to protobuf 19 // messages. A descriptor must be provided for a struct via a 20 // |nvram::DescriptorForType| specialization that declares the protobuf fields 21 // to encode and decode. 22 // * Encoding works by going through the declared fields, and encode the 23 // corresponding struct members in protobuf wire format. 24 // * Decoding scans through the binary encoded message. It looks at the wire 25 // tag decoded form the message to recover the field number as well as 26 // the protobuf wire type (i.e. kind of encoding). The field number is then 27 // used to locate the struct field declaration so the appropriate decoding 28 // logic for the corresponding struct member can be invoked. 29 // * The main dispatch point that ties member types to decoding and encoding 30 // logic is the |nvram::proto::detail::Codec| template. The idea is that 31 // |Codec<Type>| provides encoding and decoding logic for |Type|. 32 // 33 // The API for encoding and decoding is straightforward. Consider the following 34 // Employee struct and its descriptor: 35 // type: 36 // 37 // struct Employee { 38 // uint32_t id; 39 // std::string name; 40 // std::vector<uint32_t> reports; 41 // }; 42 // 43 // template <> 44 // struct DescriptorForType<Employee> { 45 // static constexpr auto kFields = 46 // MakeFieldList(MakeField(1, &Employee::id), 47 // MakeField(2, &Employee::name), 48 // MakeField(3, &Employee::reports)); 49 // }; 50 // 51 // Encoding is simple: 52 // 53 // Employee employee; 54 // uint8_t buffer[SIZE]; 55 // nvram::OutputStream stream(buffer, sizeof(buffer)); 56 // if (!nvram::proto::Encode(employee, &stream)) { 57 // // Handle encoding failure. 58 // } 59 // 60 // Note that |nvram::proto::GetSize()| can be used to determine a sufficient 61 // buffer size. 62 // 63 // Decoding is similar: 64 // 65 // Employee employee; 66 // nvram::InputStreamBuffer stream(buffer_start, buffer_size); 67 // if (!nvram::proto::Decode(&employee, &stream)) { 68 // // Handle decoding failure. 69 // } 70 // 71 // Note that this file is not meant as a header to be included by all code that 72 // needs to encode or decode messages. Rather, this header should only be 73 // included by a .cpp file which can then instantiate the 74 // |nvram::proto::Encode()| and |nvram::proto::Decode()| templates to obtain 75 // encoders and decoders for the relevant message types. This approach results 76 // in decode and encode logic getting compiled in only one translation unit, 77 // which other code can link against. 78 79 #ifndef NVRAM_MESSAGES_PROTO_HPP_ 80 #define NVRAM_MESSAGES_PROTO_HPP_ 81 82 extern "C" { 83 #include <stdint.h> 84 } 85 86 #include <nvram/messages/blob.h> 87 #include <nvram/messages/compiler.h> 88 #include <nvram/messages/io.h> 89 #include <nvram/messages/message_codec.h> 90 #include <nvram/messages/optional.h> 91 #include <nvram/messages/struct.h> 92 #include <nvram/messages/tagged_union.h> 93 #include <nvram/messages/type_traits.h> 94 #include <nvram/messages/vector.h> 95 96 namespace nvram { 97 namespace proto { 98 99 namespace detail { 100 101 // A class template that performs encoding and decoding of a protobuf message 102 // field of the C++ type |Type|. The base template is left undefined here, 103 // specific implementations for relevant |Type|s are provided by template 104 // specializations. Each specialization needs to provide the following members: 105 // * |static constexpr WireType kWireType| indicates the wire type used for 106 // encoded field data. 107 // * |static bool Encode(const Type& object, ProtoWriter* writer)| writes the 108 // encoded form of |object| to |writer|. 109 // * |static bool Decode(Type& object, ProtoReader* reader)| decodes a field 110 // from |reader| and places recovered data in |object|. 111 // 112 // |Codec| specializations are provided below for commonly-used types such as 113 // integral and enum types, as well as structs with corresponding descriptors. 114 // Additional specializations can be added as needed. 115 template <typename Type, typename Enable = void> 116 struct Codec { 117 // The assert below fails unconditionally, but must depend on the |Type| 118 // parameter so it only triggers at instantiation time. If this assert fires, 119 // then you are attempting to encode or decode a struct that contains a field 120 // of a C++ type for which there exists no code that implements encoding and 121 // decoding for that type. To add encoding/decoding support for a type, you 122 // can provide a Codec specialization. 123 static_assert(sizeof(Type) == 0, 124 "A Codec specialization must be provided for types " 125 "that are to be used with the protobuf encoder."); 126 }; 127 128 namespace { 129 130 // Codec specific message field encoding function. Note that this is marked 131 // noinline to prevent the compiler from inlining |Codec::Encode| for every 132 // occurrence of a field of type |Type|. 133 template <typename Codec, typename Type> 134 NVRAM_NOINLINE bool EncodeField(const Type& value, ProtoWriter* writer) { 135 return Codec::Encode(value, writer); 136 } 137 138 // Codec specific message field decoding function. Note that this is marked 139 // noinline to prevent the compiler from inlining |Codec::Decode| for every 140 // occurrence of a field of type |Type|. 141 template <typename Codec, typename Type> 142 NVRAM_NOINLINE bool DecodeField(Type& value, ProtoReader* reader) { 143 return Codec::Decode(value, reader); 144 } 145 146 } // namespace 147 148 // |Codec| specialization for Blob. 149 template <> 150 struct Codec<Blob> { 151 static constexpr WireType kWireType = WireType::kLengthDelimited; 152 153 static bool Encode(const Blob& blob, ProtoWriter* writer) { 154 return writer->WriteLengthDelimited(blob.data(), blob.size()); 155 } 156 157 static bool Decode(Blob& blob, ProtoReader* reader) { 158 return blob.Resize(reader->field_size()) && 159 reader->ReadLengthDelimited(blob.data(), blob.size()); 160 } 161 }; 162 163 // A helper to test whether a given |Type| should be handled by the Varint 164 // |Codec| specialization. The |Type| needs to allow conversion from and to 165 // |uint64_t|. This checks for static_cast conversion behavior instead of 166 // implicit conversion in order to also match scoped enums. 167 template <typename Type> 168 struct IsVarintCompatible { 169 template <typename From, typename To> 170 struct IsCastConvertible { 171 template <typename T> 172 static decltype(static_cast<T>(declval<From>()), true_type()) test(int); 173 174 template <typename T> 175 static false_type test(...); 176 177 static constexpr bool value = decltype(test<To>(0))::value; 178 }; 179 180 static constexpr bool value = IsCastConvertible<Type, uint64_t>::value && 181 IsCastConvertible<uint64_t, Type>::value; 182 }; 183 184 // |Codec| specialization for varint-encoded numeric fields. 185 template <typename Type> 186 struct Codec<Type, typename enable_if<IsVarintCompatible<Type>::value>::Type> { 187 static constexpr WireType kWireType = WireType::kVarint; 188 189 static bool Encode(const Type& value, ProtoWriter* writer) { 190 return writer->WriteVarint(static_cast<uint64_t>(value)); 191 } 192 193 static bool Decode(Type& value, ProtoReader* reader) { 194 uint64_t raw_value; 195 if (!reader->ReadVarint(&raw_value)) { 196 return false; 197 } 198 value = static_cast<Type>(raw_value); 199 return static_cast<uint64_t>(value) == raw_value; 200 } 201 }; 202 203 // |Codec| specialization for |Vector|. 204 template <typename ElementType> 205 struct Codec<Vector<ElementType>> { 206 using ElementCodec = Codec<ElementType>; 207 static constexpr WireType kWireType = ElementCodec::kWireType; 208 209 static bool Encode(const Vector<ElementType>& vector, ProtoWriter* writer) { 210 for (const ElementType& elem : vector) { 211 if (!EncodeField<ElementCodec>(elem, writer)) { 212 return false; 213 } 214 } 215 return true; 216 } 217 218 static bool Decode(Vector<ElementType>& vector, ProtoReader* reader) { 219 return vector.Resize(vector.size() + 1) && 220 DecodeField<ElementCodec>(vector[vector.size() - 1], reader); 221 } 222 }; 223 224 // |Codec| specialization for |Optional|. 225 template <typename ValueType> 226 struct Codec<Optional<ValueType>> { 227 using ValueCodec = Codec<ValueType>; 228 static constexpr WireType kWireType = ValueCodec::kWireType; 229 230 static bool Encode(const Optional<ValueType>& value, ProtoWriter* writer) { 231 return !value.valid() || EncodeField<ValueCodec>(value.value(), writer); 232 } 233 234 static bool Decode(Optional<ValueType>& value, ProtoReader* reader) { 235 return DecodeField<ValueCodec>(value.Activate(), reader); 236 } 237 }; 238 239 namespace { 240 241 // |StructDescriptor| provides the |FieldDescriptor| table corresponding to 242 // |StructType|. The table contains information about each field in the protobuf 243 // encoding, e.g. field number and wire type. 244 // 245 // The |IndexSequence| template parameter is present purely for technical 246 // reasons. It provides a sequence of indices, one for each entry in the field 247 // declaration list for |StructType|. Having the index available simplifies 248 // generation of the descriptor table entries. 249 template < 250 typename StructType, 251 typename IndexSequence = decltype( 252 make_index_sequence<DescriptorForType<StructType>::kFields.kSize>())> 253 struct StructDescriptor; 254 255 template <typename StructType, size_t... indices> 256 struct StructDescriptor<StructType, index_sequence<indices...>> { 257 private: 258 static constexpr auto kFieldSpecList = 259 DescriptorForType<StructType>::kFields; 260 using FieldSpecs = typename remove_const<decltype(kFieldSpecList)>::Type; 261 262 // A helper function used to preform a compile-time sanity check on the 263 // declared field numbers to ensure that they're positive, unique and in 264 // ascending order. 265 template <typename FieldSpecList> 266 static constexpr bool CheckFieldNumbersAscending( 267 FieldSpecList list, 268 uint32_t previous_field_number) { 269 return list.kFieldSpec.kFieldNumber > previous_field_number && 270 CheckFieldNumbersAscending(list.kTail, list.kFieldSpec.kFieldNumber); 271 } 272 static constexpr bool CheckFieldNumbersAscending(FieldSpecList<>, uint32_t) { 273 return true; 274 } 275 276 // If this fails, check your struct field declarations for the following: 277 // * Field numbers must be positive. 278 // * Field numbers must be unique. 279 // * Fields must be declared in ascending field number order. 280 static_assert(CheckFieldNumbersAscending(kFieldSpecList, 0), 281 "Field numbers must be positive, unique and declared in " 282 "ascending order."); 283 284 // Provides the |FieldDescriptor| instance for the field specified by |index|. 285 // Note that |index| is *not* the proto field number, but the zero-based index 286 // in the field declaration list. 287 template <size_t index> 288 class FieldDescriptorBuilder { 289 static constexpr auto kFieldSpec = kFieldSpecList.template Get<index>(); 290 using FieldSpecType = typename remove_const<decltype(kFieldSpec)>::Type; 291 using MemberType = typename FieldSpecType::MemberType; 292 293 // Determines the Codec type to use for the field. The default is to use 294 // |Codec<MemberType>|, which is appropriate for simple fields declared via 295 // |FieldSpec|. 296 template <typename FieldSpec> 297 struct MemberCodecLookup { 298 using Type = Codec<MemberType>; 299 }; 300 301 // |TaggedUnion| members require a special codec implementation that takes 302 // into account the case, so encoding only takes place if the respective 303 // union member is active and decoding activates the requested member before 304 // decoding data. 305 template <typename Struct, typename TagType, typename... Member> 306 struct MemberCodecLookup< 307 OneOfFieldSpec<Struct, TagType, Member...>> { 308 static constexpr TagType kTag = kFieldSpec.kTag; 309 310 struct Type { 311 using TaggedUnionType = TaggedUnion<TagType, Member...>; 312 using TaggedUnionMemberType = 313 typename TaggedUnionType::template MemberLookup<kTag>::Type::Type; 314 using TaggedUnionMemberCodec = Codec<TaggedUnionMemberType>; 315 static constexpr WireType kWireType = TaggedUnionMemberCodec::kWireType; 316 317 static bool Encode(const TaggedUnionType& object, ProtoWriter* writer) { 318 const TaggedUnionMemberType* member = object.template get<kTag>(); 319 if (member) { 320 return EncodeField<TaggedUnionMemberCodec>(*member, writer); 321 } 322 return true; 323 } 324 325 static bool Decode(TaggedUnionType& object, ProtoReader* reader) { 326 return DecodeField<TaggedUnionMemberCodec>( 327 object.template Activate<kTag>(), reader); 328 } 329 }; 330 }; 331 332 using MemberCodec = typename MemberCodecLookup<FieldSpecType>::Type; 333 334 // Encodes a member. Retrieves a reference to the member within |object| and 335 // calls the appropriate encoder. 336 static bool EncodeMember(const void* object, ProtoWriter* writer) { 337 constexpr auto spec = kFieldSpec; 338 return EncodeField<MemberCodec>( 339 spec.Get(*static_cast<const StructType*>(object)), writer); 340 }; 341 342 // Decodes a member. Retrieves a const reference to the member within 343 // |object| and calls the appropriate decoder. 344 static bool DecodeMember(void* object, ProtoReader* reader) { 345 constexpr auto spec = kFieldSpec; 346 return DecodeField<MemberCodec>( 347 spec.Get(*static_cast<StructType*>(object)), reader); 348 }; 349 350 public: 351 // Assemble the actual descriptor for the field. Note that this is still a 352 // compile-time constant (i.e. has no linkage). However, the constant is 353 // used below to initialize the entry in the static descriptor table. 354 static constexpr FieldDescriptor kDescriptor = 355 FieldDescriptor(kFieldSpec.kFieldNumber, 356 MemberCodec::kWireType, 357 &EncodeMember, 358 &DecodeMember); 359 }; 360 361 public: 362 // Descriptor table size. 363 static constexpr size_t kNumDescriptors = kFieldSpecList.kSize; 364 365 // The actual descriptor table. 366 static constexpr FieldDescriptor kDescriptors[] = { 367 FieldDescriptorBuilder<indices>::kDescriptor...}; 368 }; 369 370 // Provide a definition of the |kDescriptors| array such that the descriptor 371 // table gets emitted to static data. 372 template <typename StructType, size_t... index> 373 constexpr FieldDescriptor 374 StructDescriptor<StructType, index_sequence<index...>>::kDescriptors[]; 375 376 // Note that G++ versions before 5.0 have a bug in handling parameter pack 377 // expansions that result in an empty array initializer. To work around this, 378 // the following specialization is provided for empty field lists. 379 template <typename StructType> 380 struct StructDescriptor<StructType, index_sequence<>> { 381 static constexpr size_t kNumDescriptors = 0; 382 static constexpr FieldDescriptor* kDescriptors = nullptr; 383 }; 384 385 // A convenience class to initialize |MessageEncoderBase| with the descriptor 386 // table corresponding to |StructType| as determined by |StructDescriptor|. 387 template <typename StructType> 388 class MessageEncoder : public MessageEncoderBase { 389 public: 390 explicit MessageEncoder(const StructType& object) 391 : MessageEncoderBase(&object, 392 StructDescriptor<StructType>::kDescriptors, 393 StructDescriptor<StructType>::kNumDescriptors) {} 394 395 static bool Encode(const StructType& object, ProtoWriter* writer) { 396 return MessageEncoderBase::Encode( 397 &object, writer, StructDescriptor<StructType>::kDescriptors, 398 StructDescriptor<StructType>::kNumDescriptors); 399 } 400 }; 401 402 // A convenience class to initialize |MessageDecoderBase| with the descriptor 403 // table corresponding to |StructType| as determined by |StructDescriptor|. 404 template <typename StructType> 405 class MessageDecoder : public MessageDecoderBase { 406 public: 407 explicit MessageDecoder(StructType& object) 408 : MessageDecoderBase(&object, 409 StructDescriptor<StructType>::kDescriptors, 410 StructDescriptor<StructType>::kNumDescriptors) {} 411 412 static bool Decode(StructType& object, ProtoReader* reader) { 413 return MessageDecoderBase::Decode( 414 &object, reader, StructDescriptor<StructType>::kDescriptors, 415 StructDescriptor<StructType>::kNumDescriptors); 416 } 417 }; 418 419 } // namespace 420 421 // |Codec| specialization for struct types. The second template parameter 422 // evaluates to |void| if the appropriate |DescriptorForType| specialization 423 // exists, enabling the |Codec| specialization for that case. 424 // 425 // Note that this template generates code for each struct type that needs to be 426 // encoded and decoded. To avoid bloating the binary, we keep the type-dependent 427 // code at the absolute minimum. The |MessageEncoder| and |MessageDecoder| 428 // templates merely obtain the appropriate descriptor table for the struct type 429 // and then invoke the type-agnostic encoder and decoder base classes. 430 template <typename StructType> 431 struct Codec<StructType, 432 decltype( 433 static_cast<void>(DescriptorForType<StructType>::kFields))> { 434 static constexpr WireType kWireType = WireType::kLengthDelimited; 435 436 static bool Encode(const StructType& object, ProtoWriter* writer) { 437 return MessageEncoder<StructType>::Encode(object, writer); 438 } 439 440 static bool Decode(StructType& object, ProtoReader* reader) { 441 return MessageDecoder<StructType>::Decode(object, reader); 442 } 443 }; 444 445 } // namespace detail 446 447 // Get the encoded size of an object. 448 template <typename Struct> 449 size_t GetSize(const Struct& object) { 450 detail::MessageEncoder<Struct> encoder(object); 451 return encoder.GetSize(); 452 } 453 454 // Encode |object| and write the result to |stream|. Returns true if successful, 455 // false if encoding fails. Encoding may fail because |stream| doesn't have 456 // enough room to hold the encoded data. 457 template <typename Struct> 458 bool Encode(const Struct& object, OutputStreamBuffer* stream) { 459 ProtoWriter writer(stream); 460 detail::MessageEncoder<Struct> encoder(object); 461 return encoder.EncodeData(&writer); 462 } 463 464 // Decode |stream| and update |object| with the decoded information. Returns 465 // true if successful, false if encoding fails. Failure conditions include: 466 // * Binary data isn't valid with respect to the protobuf wire format. 467 // * |stream| ends prematurely. 468 // * Memory allocation in |object| to hold decoded data fails. 469 template <typename Struct> 470 bool Decode(Struct* object, InputStreamBuffer* stream) { 471 ProtoReader reader(stream); 472 detail::MessageDecoder<Struct> decoder(*object); 473 return decoder.DecodeData(&reader); 474 } 475 476 } // namespace proto 477 } // namespace nvram 478 479 #endif // NVRAM_MESSAGES_PROTO_HPP_ 480