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