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>
EncodeField(const Type & value,ProtoWriter * writer)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>
DecodeField(Type & value,ProtoReader * reader)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 
Encodenvram::proto::detail::Codec153   static bool Encode(const Blob& blob, ProtoWriter* writer) {
154     return writer->WriteLengthDelimited(blob.data(), blob.size());
155   }
156 
Decodenvram::proto::detail::Codec157   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 
Encodenvram::proto::detail::Codec189   static bool Encode(const Type& value, ProtoWriter* writer) {
190     return writer->WriteVarint(static_cast<uint64_t>(value));
191   }
192 
Decodenvram::proto::detail::Codec193   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 
Encodenvram::proto::detail::Codec209   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 
Decodenvram::proto::detail::Codec218   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 
Encodenvram::proto::detail::Codec230   static bool Encode(const Optional<ValueType>& value, ProtoWriter* writer) {
231     return !value.valid() || EncodeField<ValueCodec>(value.value(), writer);
232   }
233 
Decodenvram::proto::detail::Codec234   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>
CheckFieldNumbersAscendingnvram::proto::detail::__anond1a572440211::StructDescriptor266   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   }
CheckFieldNumbersAscendingnvram::proto::detail::__anond1a572440211::StructDescriptor272   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 
Encodenvram::proto::detail::__anond1a572440211::StructDescriptor::FieldDescriptorBuilder::MemberCodecLookup::Type317         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 
Decodenvram::proto::detail::__anond1a572440211::StructDescriptor::FieldDescriptorBuilder::MemberCodecLookup::Type325         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.
EncodeMember(const void * object,ProtoWriter * writer)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.
DecodeMember(void * object,ProtoReader * reader)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:
MessageEncoder(const StructType & object)390   explicit MessageEncoder(const StructType& object)
391       : MessageEncoderBase(&object,
392                            StructDescriptor<StructType>::kDescriptors,
393                            StructDescriptor<StructType>::kNumDescriptors) {}
394 
Encode(const StructType & object,ProtoWriter * writer)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:
MessageDecoder(StructType & object)407   explicit MessageDecoder(StructType& object)
408       : MessageDecoderBase(&object,
409                            StructDescriptor<StructType>::kDescriptors,
410                            StructDescriptor<StructType>::kNumDescriptors) {}
411 
Decode(StructType & object,ProtoReader * reader)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 
Encodenvram::proto::detail::Codec436   static bool Encode(const StructType& object, ProtoWriter* writer) {
437     return MessageEncoder<StructType>::Encode(object, writer);
438   }
439 
Decodenvram::proto::detail::Codec440   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>
GetSize(const Struct & object)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>
Encode(const Struct & object,OutputStreamBuffer * stream)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>
Decode(Struct * object,InputStreamBuffer * stream)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