proto.hpp revision bb621b95ea636314c87b885107c8d5331992f9bb
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
82extern "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
96namespace nvram {
97namespace proto {
98
99namespace 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.
115template <typename Type, typename Enable = void>
116struct 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
128namespace {
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|.
133template <typename Codec, typename Type>
134NVRAM_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|.
141template <typename Codec, typename Type>
142NVRAM_NOINLINE bool DecodeField(Type& value, ProtoReader* reader) {
143  return Codec::Decode(value, reader);
144}
145
146}  // namespace
147
148// |Codec| specialization for Blob.
149template <>
150struct 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.
167template <typename Type>
168struct 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.
185template <typename Type>
186struct 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|.
204template <typename ElementType>
205struct 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|.
225template <typename ValueType>
226struct 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
239namespace {
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.
249template <
250    typename StructType,
251    typename IndexSequence = decltype(
252        make_index_sequence<DescriptorForType<StructType>::kFields.kSize>())>
253struct StructDescriptor;
254
255template <typename StructType, size_t... indices>
256struct 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.
372template <typename StructType, size_t... index>
373constexpr 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.
379template <typename StructType>
380struct 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|.
387template <typename StructType>
388class 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|.
404template <typename StructType>
405class 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.
430template <typename StructType>
431struct 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.
448template <typename Struct>
449size_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.
457template <typename Struct>
458bool 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.
469template <typename Struct>
470bool 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