Home | History | Annotate | Download | only in messages
      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