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