• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2018 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "tools/cddl/codegen.h"
6 
7 #include <cinttypes>
8 #include <iostream>
9 #include <limits>
10 #include <set>
11 #include <sstream>
12 #include <string>
13 #include <utility>
14 #include <vector>
15 
16 #include "absl/algorithm/container.h"
17 #include "absl/types/optional.h"
18 
19 // Convert '-' to '_' to use a CDDL identifier as a C identifier.
ToUnderscoreId(const std::string & x)20 std::string ToUnderscoreId(const std::string& x) {
21   std::string result(x);
22   for (auto& c : result) {
23     if (c == '-')
24       c = '_';
25   }
26   return result;
27 }
28 
29 // Convert a CDDL identifier to camel case for use as a C typename.  E.g.
30 // presentation-connection-message to PresentationConnectionMessage.
ToCamelCase(const std::string & x)31 std::string ToCamelCase(const std::string& x) {
32   std::string result(x);
33   result[0] = toupper(result[0]);
34   size_t new_size = 1;
35   size_t result_size = result.size();
36   for (size_t i = 1; i < result_size; ++i, ++new_size) {
37     if (result[i] == '-') {
38       ++i;
39       if (i < result_size)
40         result[new_size] = toupper(result[i]);
41     } else {
42       result[new_size] = result[i];
43     }
44   }
45   result.resize(new_size);
46   return result;
47 }
48 
49 // Returns a string which represents the C++ type of |cpp_type|.  Returns an
50 // empty string if there is no valid representation for |cpp_type| (e.g. a
51 // vector with an invalid element type).
CppTypeToString(const CppType & cpp_type)52 std::string CppTypeToString(const CppType& cpp_type) {
53   switch (cpp_type.which) {
54     case CppType::Which::kUint64:
55       return "uint64_t";
56     case CppType::Which::kString:
57       return "std::string";
58     case CppType::Which::kBytes: {
59       if (cpp_type.bytes_type.fixed_size) {
60         std::string size_string =
61             std::to_string(cpp_type.bytes_type.fixed_size.value());
62         return "std::array<uint8_t, " + size_string + ">";
63       } else {
64         return "std::vector<uint8_t>";
65       }
66     }
67     case CppType::Which::kVector: {
68       std::string element_string =
69           CppTypeToString(*cpp_type.vector_type.element_type);
70       if (element_string.empty())
71         return std::string();
72       return "std::vector<" + element_string + ">";
73     }
74     case CppType::Which::kEnum:
75       return ToCamelCase(cpp_type.name);
76     case CppType::Which::kStruct:
77       return ToCamelCase(cpp_type.name);
78     case CppType::Which::kTaggedType:
79       return CppTypeToString(*cpp_type.tagged_type.real_type);
80     default:
81       return std::string();
82   }
83 }
84 
WriteEnumEqualityOperatorSwitchCases(int fd,const CppType & parent,std::string child_name,std::string parent_name)85 bool WriteEnumEqualityOperatorSwitchCases(int fd,
86                                           const CppType& parent,
87                                           std::string child_name,
88                                           std::string parent_name) {
89   for (const auto& x : parent.enum_type.members) {
90     std::string enum_value = "k" + ToCamelCase(x.first);
91     dprintf(fd, "    case %s::%s: return parent == %s::%s;\n",
92             child_name.c_str(), enum_value.c_str(), parent_name.c_str(),
93             enum_value.c_str());
94   }
95 
96   return absl::c_all_of(parent.enum_type.sub_members,
97                         [&fd, &child_name, &parent_name](CppType* new_parent) {
98                           return WriteEnumEqualityOperatorSwitchCases(
99                               fd, *new_parent, child_name, parent_name);
100                         });
101 }
102 
103 // Write the equality operators for comparing an enum and its parent types.
WriteEnumEqualityOperator(int fd,const CppType & type,const CppType & parent)104 bool WriteEnumEqualityOperator(int fd,
105                                const CppType& type,
106                                const CppType& parent) {
107   std::string name = ToCamelCase(type.name);
108   std::string parent_name = ToCamelCase(parent.name);
109 
110   // Define type == parentType.
111   dprintf(fd, "inline bool operator==(const %s& child, const %s& parent) {\n",
112           name.c_str(), parent_name.c_str());
113   dprintf(fd, "  switch (child) {\n");
114   if (!WriteEnumEqualityOperatorSwitchCases(fd, parent, name, parent_name)) {
115     return false;
116   }
117   dprintf(fd, "    default: return false;\n");
118   dprintf(fd, "  }\n}\n");
119 
120   // Define parentType == type.
121   dprintf(fd, "inline bool operator==(const %s& parent, const %s& child) {\n",
122           parent_name.c_str(), name.c_str());
123   dprintf(fd, "  return child == parent;\n}\n");
124 
125   // Define type != parentType.
126   dprintf(fd, "inline bool operator!=(const %s& child, const %s& parent) {\n",
127           name.c_str(), parent_name.c_str());
128   dprintf(fd, "  return !(child == parent);\n}\n");
129 
130   // Define parentType != type.
131   dprintf(fd, "inline bool operator!=(const %s& parent, const %s& child) {\n",
132           parent_name.c_str(), name.c_str());
133   dprintf(fd, "  return !(parent == child);\n}\n");
134 
135   return true;
136 }
137 
WriteEnumStreamOperatorSwitchCases(int fd,const CppType & type,std::string name)138 bool WriteEnumStreamOperatorSwitchCases(int fd,
139                                         const CppType& type,
140                                         std::string name) {
141   for (const auto& x : type.enum_type.members) {
142     std::string enum_value = "k" + ToCamelCase(x.first);
143     dprintf(fd, "    case %s::%s: os << \"%s\"; break;\n", name.c_str(),
144             enum_value.c_str(), enum_value.c_str());
145   }
146 
147   return absl::c_all_of(
148       type.enum_type.sub_members, [&fd, &name](CppType* parent) {
149         return WriteEnumStreamOperatorSwitchCases(fd, *parent, name);
150       });
151 }
152 
WriteEnumOperators(int fd,const CppType & type)153 bool WriteEnumOperators(int fd, const CppType& type) {
154   // Write << operator.
155   std::string name = ToCamelCase(type.name);
156   dprintf(
157       fd,
158       "inline std::ostream& operator<<(std::ostream& os, const %s& val) {\n",
159       name.c_str());
160   dprintf(fd, "  switch (val) {\n");
161   if (!WriteEnumStreamOperatorSwitchCases(fd, type, name)) {
162     return false;
163   }
164   dprintf(fd,
165           "    default: os << \"Unknown Value: \" << static_cast<int>(val);"
166           "\n      break;\n    }\n  return os;\n}\n");
167 
168   // Write equality operators.
169   return absl::c_all_of(type.enum_type.sub_members,
170                         [&fd, &type](CppType* parent) {
171                           return WriteEnumEqualityOperator(fd, type, *parent);
172                         });
173 }
174 
175 // Writes the equality operator for a specific Discriminated Union.
WriteDiscriminatedUnionEqualityOperator(int fd,const CppType & type,const std::string & name_prefix="")176 bool WriteDiscriminatedUnionEqualityOperator(
177     int fd,
178     const CppType& type,
179     const std::string& name_prefix = "") {
180   const std::string name = name_prefix + ToCamelCase(type.name);
181   dprintf(fd, "\nbool %s::operator==(const %s& other) const {\n", name.c_str(),
182           name.c_str());
183   dprintf(fd, "  return this->which == other.which");
184   for (auto* union_member : type.discriminated_union.members) {
185     dprintf(fd, " &&\n         ");
186     switch (union_member->which) {
187       case CppType::Which::kUint64:
188         dprintf(fd,
189                 "(this->which != Which::kUint64 || this->uint == other.uint)");
190         break;
191       case CppType::Which::kString:
192         dprintf(fd,
193                 "(this->which != Which::kString || this->str == other.str)");
194         break;
195       case CppType::Which::kBytes:
196         dprintf(fd,
197                 "(this->which != Which::kBytes || this->bytes == other.bytes)");
198         break;
199       default:
200         return false;
201     }
202   }
203   dprintf(fd, ";\n}\n");
204   dprintf(fd, "bool %s::operator!=(const %s& other) const {\n", name.c_str(),
205           name.c_str());
206   dprintf(fd, "  return !(*this == other);\n}\n");
207   return true;
208 }
209 
210 // Writes the equality operator for a specific C++ struct.
WriteStructEqualityOperator(int fd,const CppType & type,const std::string & name_prefix="")211 bool WriteStructEqualityOperator(int fd,
212                                  const CppType& type,
213                                  const std::string& name_prefix = "") {
214   const std::string name = name_prefix + ToCamelCase(type.name);
215   dprintf(fd, "\nbool %s::operator==(const %s& other) const {\n", name.c_str(),
216           name.c_str());
217   for (size_t i = 0; i < type.struct_type.members.size(); i++) {
218     if (i == 0) {
219       dprintf(fd, "  return ");
220     } else {
221       dprintf(fd, " &&\n         ");
222     }
223     auto name = ToUnderscoreId(type.struct_type.members[i].name);
224     dprintf(fd, "this->%s == other.%s", name.c_str(), name.c_str());
225   }
226   dprintf(fd, ";\n}");
227   dprintf(fd, "\nbool %s::operator!=(const %s& other) const {\n", name.c_str(),
228           name.c_str());
229   dprintf(fd, "  return !(*this == other);\n}\n");
230   std::string new_prefix = name_prefix + ToCamelCase(type.name) + "::";
231   for (const auto& x : type.struct_type.members) {
232     // NOTE: Don't need to call recursively on struct members, since all structs
233     // are handled in the calling method.
234     if (x.type->which == CppType::Which::kDiscriminatedUnion) {
235       if (!WriteDiscriminatedUnionEqualityOperator(fd, *x.type, new_prefix)) {
236         return false;
237       }
238     }
239   }
240   return true;
241 }
242 
243 // Write the C++ struct member definitions of every type in |members| to the
244 // file descriptor |fd|.
WriteStructMembers(int fd,const std::string & name,const std::vector<CppType::Struct::CppMember> & members)245 bool WriteStructMembers(
246     int fd,
247     const std::string& name,
248     const std::vector<CppType::Struct::CppMember>& members) {
249   for (const auto& x : members) {
250     std::string type_string;
251     switch (x.type->which) {
252       case CppType::Which::kStruct: {
253         if (x.type->struct_type.key_type ==
254             CppType::Struct::KeyType::kPlainGroup) {
255           if (!WriteStructMembers(fd, x.type->name,
256                                   x.type->struct_type.members))
257             return false;
258           continue;
259         } else {
260           type_string = ToCamelCase(x.name);
261         }
262       } break;
263       case CppType::Which::kOptional: {
264         // TODO(btolsch): Make this optional<T> when one lands.
265         dprintf(fd, "  bool has_%s;\n", ToUnderscoreId(x.name).c_str());
266         type_string = CppTypeToString(*x.type->optional_type);
267       } break;
268       case CppType::Which::kDiscriminatedUnion: {
269         std::string cid = ToUnderscoreId(x.name);
270         type_string = ToCamelCase(x.name);
271         dprintf(fd, "  struct %s {\n", type_string.c_str());
272         dprintf(fd, "    %s();\n    ~%s();\n\n", type_string.c_str(),
273                 type_string.c_str());
274 
275         dprintf(fd, "  bool operator==(const %s& other) const;\n",
276                 type_string.c_str());
277         dprintf(fd, "  bool operator!=(const %s& other) const;\n\n",
278                 type_string.c_str());
279         dprintf(fd, "  enum class Which {\n");
280         for (auto* union_member : x.type->discriminated_union.members) {
281           switch (union_member->which) {
282             case CppType::Which::kUint64:
283               dprintf(fd, "    kUint64,\n");
284               break;
285             case CppType::Which::kString:
286               dprintf(fd, "    kString,\n");
287               break;
288             case CppType::Which::kBytes:
289               dprintf(fd, "    kBytes,\n");
290               break;
291             default:
292               return false;
293           }
294         }
295         dprintf(fd, "    kUninitialized,\n");
296         dprintf(fd, "  } which;\n");
297         dprintf(fd, "  union {\n");
298         for (auto* union_member : x.type->discriminated_union.members) {
299           switch (union_member->which) {
300             case CppType::Which::kUint64:
301               dprintf(fd, "    uint64_t uint;\n");
302               break;
303             case CppType::Which::kString:
304               dprintf(fd, "    std::string str;\n");
305               break;
306             case CppType::Which::kBytes:
307               dprintf(fd, "    std::vector<uint8_t> bytes;\n");
308               break;
309             default:
310               return false;
311           }
312         }
313         // NOTE: This member allows the union to be easily constructed in an
314         // effectively uninitialized state.  Its value should never be used.
315         dprintf(fd, "    bool placeholder_;\n");
316         dprintf(fd, "  };\n");
317         dprintf(fd, "  };\n");
318       } break;
319       default:
320         type_string = CppTypeToString(*x.type);
321         break;
322     }
323     if (type_string.empty())
324       return false;
325     dprintf(fd, "  %s %s;\n", type_string.c_str(),
326             ToUnderscoreId(x.name).c_str());
327   }
328   return true;
329 }
330 
WriteEnumMembers(int fd,const CppType & type)331 void WriteEnumMembers(int fd, const CppType& type) {
332   for (const auto& x : type.enum_type.members) {
333     dprintf(fd, "  k%s = %" PRIu64 "ull,\n", ToCamelCase(x.first).c_str(),
334             x.second);
335   }
336   for (const auto* x : type.enum_type.sub_members) {
337     WriteEnumMembers(fd, *x);
338   }
339 }
340 
341 // Writes a C++ type definition for |type| to the file descriptor |fd|.  This
342 // only generates a definition for enums and structs.
WriteTypeDefinition(int fd,const CppType & type)343 bool WriteTypeDefinition(int fd, const CppType& type) {
344   std::string name = ToCamelCase(type.name);
345   switch (type.which) {
346     case CppType::Which::kEnum: {
347       dprintf(fd, "\nenum class %s : uint64_t {\n", name.c_str());
348       WriteEnumMembers(fd, type);
349       dprintf(fd, "};\n");
350       if (!WriteEnumOperators(fd, type))
351         return false;
352     } break;
353     case CppType::Which::kStruct: {
354       dprintf(fd, "\nstruct %s {\n", name.c_str());
355       if (type.type_key != absl::nullopt) {
356         dprintf(fd, "  // type key: %" PRIu64 "\n", type.type_key.value());
357       }
358       dprintf(fd, "  bool operator==(const %s& other) const;\n", name.c_str());
359       dprintf(fd, "  bool operator!=(const %s& other) const;\n\n",
360               name.c_str());
361       if (!WriteStructMembers(fd, type.name, type.struct_type.members))
362         return false;
363       dprintf(fd, "};\n");
364     } break;
365     default:
366       break;
367   }
368   return true;
369 }
370 
371 // Ensures that any dependencies within |cpp_type| are written to the file
372 // descriptor |fd| before writing |cpp_type| to the file descriptor |fd|.  This
373 // is done by walking the tree of types defined by |cpp_type| (e.g. all the
374 // members for a struct).  |defs| contains the names of types that have already
375 // been written.  If a type hasn't been written and needs to be, its name will
376 // also be added to |defs|.
EnsureDependentTypeDefinitionsWritten(int fd,const CppType & cpp_type,std::set<std::string> * defs)377 bool EnsureDependentTypeDefinitionsWritten(int fd,
378                                            const CppType& cpp_type,
379                                            std::set<std::string>* defs) {
380   switch (cpp_type.which) {
381     case CppType::Which::kVector: {
382       return EnsureDependentTypeDefinitionsWritten(
383           fd, *cpp_type.vector_type.element_type, defs);
384     } break;
385     case CppType::Which::kEnum: {
386       if (defs->find(cpp_type.name) != defs->end())
387         return true;
388       for (const auto* x : cpp_type.enum_type.sub_members)
389         if (!EnsureDependentTypeDefinitionsWritten(fd, *x, defs))
390           return false;
391       defs->emplace(cpp_type.name);
392       WriteTypeDefinition(fd, cpp_type);
393     } break;
394     case CppType::Which::kStruct: {
395       if (cpp_type.struct_type.key_type !=
396           CppType::Struct::KeyType::kPlainGroup) {
397         if (defs->find(cpp_type.name) != defs->end())
398           return true;
399         for (const auto& x : cpp_type.struct_type.members)
400           if (!EnsureDependentTypeDefinitionsWritten(fd, *x.type, defs))
401             return false;
402         defs->emplace(cpp_type.name);
403         WriteTypeDefinition(fd, cpp_type);
404       }
405     } break;
406     case CppType::Which::kOptional: {
407       return EnsureDependentTypeDefinitionsWritten(fd, *cpp_type.optional_type,
408                                                    defs);
409     } break;
410     case CppType::Which::kDiscriminatedUnion: {
411       for (const auto* x : cpp_type.discriminated_union.members)
412         if (!EnsureDependentTypeDefinitionsWritten(fd, *x, defs))
413           return false;
414     } break;
415     case CppType::Which::kTaggedType: {
416       if (!EnsureDependentTypeDefinitionsWritten(
417               fd, *cpp_type.tagged_type.real_type, defs)) {
418         return false;
419       }
420     } break;
421     default:
422       break;
423   }
424   return true;
425 }
426 
427 // Writes the type definition for every C++ type in |table|.  This function
428 // makes sure to write them in such an order that all type dependencies are
429 // written before they are need so the resulting text in the file descriptor
430 // |fd| will compile without modification.  For example, the following would be
431 // bad output:
432 //
433 // struct Foo {
434 //   Bar bar;
435 //   int x;
436 // };
437 //
438 // struct Bar {
439 //   int alpha;
440 // };
441 //
442 // This function ensures that Bar would be written sometime before Foo.
WriteTypeDefinitions(int fd,CppSymbolTable * table)443 bool WriteTypeDefinitions(int fd, CppSymbolTable* table) {
444   std::set<std::string> defs;
445   for (const std::unique_ptr<CppType>& real_type : table->cpp_types) {
446     if (real_type->which != CppType::Which::kStruct ||
447         real_type->struct_type.key_type ==
448             CppType::Struct::KeyType::kPlainGroup) {
449       continue;
450     }
451     if (!EnsureDependentTypeDefinitionsWritten(fd, *real_type, &defs))
452       return false;
453   }
454 
455   dprintf(fd, "\nenum class Type : uint64_t {\n");
456   dprintf(fd, "    kUnknown = 0ull,\n");
457   for (CppType* type : table->TypesWithId()) {
458     dprintf(fd, "    k%s = %" PRIu64 "ull,\n", ToCamelCase(type->name).c_str(),
459             type->type_key.value());
460   }
461   dprintf(fd, "};\n");
462   return true;
463 }
464 
465 // Writes a parser that takes in a uint64_t and outputs the corresponding Type
466 // if one matches up, or Type::kUnknown if none does.
467 // NOTE: In future, this could be changes to use a Trie, which would allow for
468 // manufacturers to more easily add their own type ids to ours.
WriteTypeParserDefinition(int fd,CppSymbolTable * table)469 bool WriteTypeParserDefinition(int fd, CppSymbolTable* table) {
470   dprintf(fd, "\n//static\n");
471   dprintf(fd, "Type TypeEnumValidator::SafeCast(uint64_t type_id) {\n");
472   dprintf(fd, "  switch (type_id) {\n");
473   for (CppType* type : table->TypesWithId()) {
474     dprintf(fd, "    case uint64_t{%" PRIu64 "}: return Type::k%s;\n",
475             type->type_key.value(), ToCamelCase(type->name).c_str());
476   }
477   dprintf(fd, "    default: return Type::kUnknown;\n");
478   dprintf(fd, "  }\n}\n");
479   return true;
480 }
481 
482 // Writes the function prototypes for the encode and decode functions for each
483 // type in |table| to the file descriptor |fd|.
WriteFunctionDeclarations(int fd,CppSymbolTable * table)484 bool WriteFunctionDeclarations(int fd, CppSymbolTable* table) {
485   for (CppType* real_type : table->TypesWithId()) {
486     const auto& name = real_type->name;
487     if (real_type->which != CppType::Which::kStruct ||
488         real_type->struct_type.key_type ==
489             CppType::Struct::KeyType::kPlainGroup) {
490       return false;
491     }
492     std::string cpp_name = ToCamelCase(name);
493     dprintf(fd, "\nbool Encode%s(\n", cpp_name.c_str());
494     dprintf(fd, "    const %s& data,\n", cpp_name.c_str());
495     dprintf(fd, "    CborEncodeBuffer* buffer);\n");
496     dprintf(fd, "ssize_t Encode%s(\n", cpp_name.c_str());
497     dprintf(fd, "    const %s& data,\n", cpp_name.c_str());
498     dprintf(fd, "    uint8_t* buffer,\n    size_t length);\n");
499     dprintf(fd, "ssize_t Decode%s(\n", cpp_name.c_str());
500     dprintf(fd, "    const uint8_t* buffer,\n    size_t length,\n");
501     dprintf(fd, "    %s* data);\n", cpp_name.c_str());
502   }
503   return true;
504 }
505 
506 bool WriteMapEncoder(int fd,
507                      const std::string& name,
508                      const std::vector<CppType::Struct::CppMember>& members,
509                      const std::string& nested_type_scope,
510                      int encoder_depth = 1);
511 bool WriteArrayEncoder(int fd,
512                        const std::string& name,
513                        const std::vector<CppType::Struct::CppMember>& members,
514                        const std::string& nested_type_scope,
515                        int encoder_depth = 1);
516 
517 // Writes the encoding function for the C++ type |cpp_type| to the file
518 // descriptor |fd|.  |name| is the C++ variable name that needs to be encoded.
519 // |nested_type_scope| is the closest C++ scope name (i.e. struct name), which
520 // may be used to access local enum constants.  |encoder_depth| is used to
521 // independently name independent cbor encoders that need to be created.
WriteEncoder(int fd,const std::string & name,const CppType & cpp_type,const std::string & nested_type_scope,int encoder_depth)522 bool WriteEncoder(int fd,
523                   const std::string& name,
524                   const CppType& cpp_type,
525                   const std::string& nested_type_scope,
526                   int encoder_depth) {
527   switch (cpp_type.which) {
528     case CppType::Which::kStruct:
529       if (cpp_type.struct_type.key_type == CppType::Struct::KeyType::kMap) {
530         if (!WriteMapEncoder(fd, name, cpp_type.struct_type.members,
531                              cpp_type.name, encoder_depth + 1)) {
532           return false;
533         }
534         return true;
535       } else if (cpp_type.struct_type.key_type ==
536                  CppType::Struct::KeyType::kArray) {
537         if (!WriteArrayEncoder(fd, name, cpp_type.struct_type.members,
538                                cpp_type.name, encoder_depth + 1)) {
539           return false;
540         }
541         return true;
542       } else {
543         for (const auto& x : cpp_type.struct_type.members) {
544           if (x.integer_key.has_value()) {
545             dprintf(fd,
546                     "  CBOR_RETURN_ON_ERROR(cbor_encode_uint("
547                     "&encoder%d, %" PRIu64 ");\n",
548                     encoder_depth, x.integer_key.value());
549           } else {
550             dprintf(fd,
551                     "  CBOR_RETURN_ON_ERROR(cbor_encode_text_string("
552                     "&encoder%d, \"%s\", sizeof(\"%s\") - 1));\n",
553                     encoder_depth, x.name.c_str(), x.name.c_str());
554           }
555           if (!WriteEncoder(fd, name + "." + ToUnderscoreId(x.name), *x.type,
556                             nested_type_scope, encoder_depth)) {
557             return false;
558           }
559         }
560         return true;
561       }
562       break;
563     case CppType::Which::kUint64:
564       dprintf(fd, "  CBOR_RETURN_ON_ERROR(cbor_encode_uint(&encoder%d, %s));\n",
565               encoder_depth, ToUnderscoreId(name).c_str());
566       return true;
567       break;
568     case CppType::Which::kString: {
569       std::string cid = ToUnderscoreId(name);
570       dprintf(fd, "  if (!IsValidUtf8(%s)) {\n", cid.c_str());
571       dprintf(fd, "    return -CborErrorInvalidUtf8TextString;\n");
572       dprintf(fd, "  }\n");
573       dprintf(fd,
574               "  CBOR_RETURN_ON_ERROR(cbor_encode_text_string(&encoder%d, "
575               "%s.c_str(), %s.size()));\n",
576               encoder_depth, cid.c_str(), cid.c_str());
577       return true;
578     } break;
579     case CppType::Which::kBytes: {
580       std::string cid = ToUnderscoreId(name);
581       dprintf(fd,
582               "  CBOR_RETURN_ON_ERROR(cbor_encode_byte_string(&encoder%d, "
583               "%s.data(), "
584               "%s.size()));\n",
585               encoder_depth, cid.c_str(), cid.c_str());
586       return true;
587     } break;
588     case CppType::Which::kVector: {
589       std::string cid = ToUnderscoreId(name);
590       dprintf(fd, "  {\n");
591       if (cpp_type.vector_type.min_length !=
592           CppType::Vector::kMinLengthUnbounded) {
593         dprintf(fd, "  if (%s.size() < %d) {\n", cid.c_str(),
594                 cpp_type.vector_type.min_length);
595         dprintf(fd, "    return -CborErrorTooFewItems;\n");
596         dprintf(fd, "  }\n");
597       }
598       if (cpp_type.vector_type.max_length !=
599           CppType::Vector::kMaxLengthUnbounded) {
600         dprintf(fd, "  if (%s.size() > %d) {\n", cid.c_str(),
601                 cpp_type.vector_type.max_length);
602         dprintf(fd, "    return -CborErrorTooManyItems;\n");
603         dprintf(fd, "  }\n");
604       }
605       dprintf(fd, "  CborEncoder encoder%d;\n", encoder_depth + 1);
606       dprintf(fd,
607               "  CBOR_RETURN_ON_ERROR(cbor_encoder_create_array(&encoder%d, "
608               "&encoder%d, %s.size()));\n",
609               encoder_depth, encoder_depth + 1, cid.c_str());
610       dprintf(fd, "  for (const auto& x : %s) {\n", cid.c_str());
611       if (!WriteEncoder(fd, "x", *cpp_type.vector_type.element_type,
612                         nested_type_scope, encoder_depth + 1)) {
613         return false;
614       }
615       dprintf(fd, "  }\n");
616       dprintf(fd,
617               "  CBOR_RETURN_ON_ERROR(cbor_encoder_close_container(&encoder%d, "
618               "&encoder%d));\n",
619               encoder_depth, encoder_depth + 1);
620       dprintf(fd, "  }\n");
621       return true;
622     } break;
623     case CppType::Which::kEnum: {
624       dprintf(fd,
625               "  CBOR_RETURN_ON_ERROR(cbor_encode_uint(&encoder%d, "
626               "static_cast<uint64_t>(%s)));\n",
627               encoder_depth, ToUnderscoreId(name).c_str());
628       return true;
629     } break;
630     case CppType::Which::kDiscriminatedUnion: {
631       for (const auto* union_member : cpp_type.discriminated_union.members) {
632         switch (union_member->which) {
633           case CppType::Which::kUint64:
634             dprintf(fd, "  case %s::%s::Which::kUint64:\n",
635                     ToCamelCase(nested_type_scope).c_str(),
636                     ToCamelCase(cpp_type.name).c_str());
637             if (!WriteEncoder(fd, ToUnderscoreId(name + ".uint"), *union_member,
638                               nested_type_scope, encoder_depth)) {
639               return false;
640             }
641             dprintf(fd, "    break;\n");
642             break;
643           case CppType::Which::kString:
644             dprintf(fd, "  case %s::%s::Which::kString:\n",
645                     ToCamelCase(nested_type_scope).c_str(),
646                     ToCamelCase(cpp_type.name).c_str());
647             if (!WriteEncoder(fd, ToUnderscoreId(name + ".str"), *union_member,
648                               nested_type_scope, encoder_depth)) {
649               return false;
650             }
651             dprintf(fd, "    break;\n");
652             break;
653           case CppType::Which::kBytes:
654             dprintf(fd, "  case %s::%s::Which::kBytes:\n",
655                     ToCamelCase(nested_type_scope).c_str(),
656                     ToCamelCase(cpp_type.name).c_str());
657             if (!WriteEncoder(fd, ToUnderscoreId(name + ".bytes"),
658                               *union_member, nested_type_scope,
659                               encoder_depth)) {
660               return false;
661             }
662             dprintf(fd, "    break;\n");
663             break;
664           default:
665             return false;
666         }
667       }
668       dprintf(fd, "  case %s::%s::Which::kUninitialized:\n",
669               ToCamelCase(nested_type_scope).c_str(),
670               ToCamelCase(cpp_type.name).c_str());
671       dprintf(fd, "    return -CborUnknownError;\n");
672       return true;
673     } break;
674     case CppType::Which::kTaggedType: {
675       dprintf(fd,
676               "  CBOR_RETURN_ON_ERROR(cbor_encode_tag(&encoder%d, %" PRIu64
677               "ull));\n",
678               encoder_depth, cpp_type.tagged_type.tag);
679       if (!WriteEncoder(fd, name, *cpp_type.tagged_type.real_type,
680                         nested_type_scope, encoder_depth)) {
681         return false;
682       }
683       return true;
684     } break;
685     default:
686       break;
687   }
688   return false;
689 }
690 
691 struct MemberCountResult {
692   int num_required;
693   int num_optional;
694 };
695 
CountMemberTypes(int fd,const std::string & name_id,const std::vector<CppType::Struct::CppMember> & members)696 MemberCountResult CountMemberTypes(
697     int fd,
698     const std::string& name_id,
699     const std::vector<CppType::Struct::CppMember>& members) {
700   int num_required = 0;
701   int num_optional = 0;
702   for (const auto& x : members) {
703     if (x.type->which == CppType::Which::kOptional) {
704       std::string x_id = ToUnderscoreId(x.name);
705       if (num_optional == 0) {
706         dprintf(fd, "  int num_optionals_present = %s.has_%s;\n",
707                 name_id.c_str(), x_id.c_str());
708       } else {
709         dprintf(fd, "  num_optionals_present += %s.has_%s;\n", name_id.c_str(),
710                 x_id.c_str());
711       }
712       ++num_optional;
713     } else {
714       ++num_required;
715     }
716   }
717   return MemberCountResult{num_required, num_optional};
718 }
719 
720 // Writes the encoding function for a CBOR map with the C++ type members in
721 // |members| to the file descriptor |fd|.  |name| is the C++ variable name that
722 // needs to be encoded.  |nested_type_scope| is the closest C++ scope name (i.e.
723 // struct name), which may be used to access local enum constants.
724 // |encoder_depth| is used to independently name independent cbor encoders that
725 // need to be created.
WriteMapEncoder(int fd,const std::string & name,const std::vector<CppType::Struct::CppMember> & members,const std::string & nested_type_scope,int encoder_depth)726 bool WriteMapEncoder(int fd,
727                      const std::string& name,
728                      const std::vector<CppType::Struct::CppMember>& members,
729                      const std::string& nested_type_scope,
730                      int encoder_depth) {
731   std::string name_id = ToUnderscoreId(name);
732   dprintf(fd, "  CborEncoder encoder%d;\n", encoder_depth);
733   MemberCountResult member_counts = CountMemberTypes(fd, name_id, members);
734   if (member_counts.num_optional == 0) {
735     dprintf(fd,
736             "  CBOR_RETURN_ON_ERROR(cbor_encoder_create_map(&encoder%d, "
737             "&encoder%d, "
738             "%d));\n",
739             encoder_depth - 1, encoder_depth, member_counts.num_required);
740   } else {
741     dprintf(fd,
742             "  CBOR_RETURN_ON_ERROR(cbor_encoder_create_map(&encoder%d, "
743             "&encoder%d, "
744             "%d + num_optionals_present));\n",
745             encoder_depth - 1, encoder_depth, member_counts.num_required);
746   }
747 
748   for (const auto& x : members) {
749     std::string fullname = name;
750     CppType* member_type = x.type;
751     if (x.type->which != CppType::Which::kStruct ||
752         x.type->struct_type.key_type != CppType::Struct::KeyType::kPlainGroup) {
753       if (x.type->which == CppType::Which::kOptional) {
754         member_type = x.type->optional_type;
755         dprintf(fd, "  if (%s.has_%s) {\n", name_id.c_str(),
756                 ToUnderscoreId(x.name).c_str());
757       }
758 
759       if (x.integer_key.has_value()) {
760         dprintf(fd,
761                 "  CBOR_RETURN_ON_ERROR(cbor_encode_uint(&encoder%d, %" PRIu64
762                 "));\n",
763                 encoder_depth, x.integer_key.value());
764       } else {
765         dprintf(fd,
766                 "  CBOR_RETURN_ON_ERROR(cbor_encode_text_string(&encoder%d, "
767                 "\"%s\", sizeof(\"%s\") - 1));\n",
768                 encoder_depth, x.name.c_str(), x.name.c_str());
769       }
770       if (x.type->which == CppType::Which::kDiscriminatedUnion) {
771         dprintf(fd, "  switch (%s.%s.which) {\n", fullname.c_str(),
772                 x.name.c_str());
773       }
774       fullname = fullname + "." + x.name;
775     }
776     if (!WriteEncoder(fd, fullname, *member_type, nested_type_scope,
777                       encoder_depth)) {
778       return false;
779     }
780     if (x.type->which == CppType::Which::kOptional ||
781         x.type->which == CppType::Which::kDiscriminatedUnion) {
782       dprintf(fd, "  }\n");
783     }
784   }
785 
786   dprintf(fd,
787           "  CBOR_RETURN_ON_ERROR(cbor_encoder_close_container(&encoder%d, "
788           "&encoder%d));\n",
789           encoder_depth - 1, encoder_depth);
790   return true;
791 }
792 
793 // Writes the encoding function for a CBOR array with the C++ type members in
794 // |members| to the file descriptor |fd|.  |name| is the C++ variable name that
795 // needs to be encoded.  |nested_type_scope| is the closest C++ scope name (i.e.
796 // struct name), which may be used to access local enum constants.
797 // |encoder_depth| is used to independently name independent cbor encoders that
798 // need to be created.
WriteArrayEncoder(int fd,const std::string & name,const std::vector<CppType::Struct::CppMember> & members,const std::string & nested_type_scope,int encoder_depth)799 bool WriteArrayEncoder(int fd,
800                        const std::string& name,
801                        const std::vector<CppType::Struct::CppMember>& members,
802                        const std::string& nested_type_scope,
803                        int encoder_depth) {
804   std::string name_id = ToUnderscoreId(name);
805   dprintf(fd, "  CborEncoder encoder%d;\n", encoder_depth);
806   MemberCountResult member_counts = CountMemberTypes(fd, name_id, members);
807   if (member_counts.num_optional == 0) {
808     dprintf(fd,
809             "  CBOR_RETURN_ON_ERROR(cbor_encoder_create_array(&encoder%d, "
810             "&encoder%d, %d));\n",
811             encoder_depth - 1, encoder_depth, member_counts.num_required);
812   } else {
813     dprintf(fd,
814             "  CBOR_RETURN_ON_ERROR(cbor_encoder_create_array(&encoder%d, "
815             "&encoder%d, %d + num_optionals_present));\n",
816             encoder_depth - 1, encoder_depth, member_counts.num_required);
817   }
818 
819   for (const auto& x : members) {
820     std::string fullname = name;
821     CppType* member_type = x.type;
822     if (x.type->which != CppType::Which::kStruct ||
823         x.type->struct_type.key_type != CppType::Struct::KeyType::kPlainGroup) {
824       if (x.type->which == CppType::Which::kOptional) {
825         member_type = x.type->optional_type;
826         dprintf(fd, "  if (%s.has_%s) {\n", name_id.c_str(),
827                 ToUnderscoreId(x.name).c_str());
828       }
829       if (x.type->which == CppType::Which::kDiscriminatedUnion) {
830         dprintf(fd, "  switch (%s.%s.which) {\n", fullname.c_str(),
831                 x.name.c_str());
832       }
833       fullname = fullname + "." + x.name;
834     }
835     if (!WriteEncoder(fd, fullname, *member_type, nested_type_scope,
836                       encoder_depth)) {
837       return false;
838     }
839     if (x.type->which == CppType::Which::kOptional ||
840         x.type->which == CppType::Which::kDiscriminatedUnion) {
841       dprintf(fd, "  }\n");
842     }
843   }
844 
845   dprintf(fd,
846           "  CBOR_RETURN_ON_ERROR(cbor_encoder_close_container(&encoder%d, "
847           "&encoder%d));\n",
848           encoder_depth - 1, encoder_depth);
849   return true;
850 }
851 
GetByte(uint64_t value,size_t byte)852 uint8_t GetByte(uint64_t value, size_t byte) {
853   return static_cast<uint8_t>((value >> (byte * 8)) & 0xFF);
854 }
855 
GetEncodedTypeKey(const CppType & type)856 std::string GetEncodedTypeKey(const CppType& type) {
857   if (type.type_key == absl::nullopt) {
858     return "";
859   }
860 
861   // Determine all constants needed for calculating the encoded id bytes.
862   uint64_t type_id = type.type_key.value();
863   uint8_t encoding_size;
864   uint8_t start_processing_byte;
865   if (type_id < 0x1 << 6) {
866     encoding_size = 0x0;
867     start_processing_byte = 0;
868   } else if (type_id < 0x1 << 14) {
869     encoding_size = 0x01;
870     start_processing_byte = 1;
871   } else if (type_id < 0x1 << 30) {
872     encoding_size = 0x02;
873     start_processing_byte = 3;
874   } else if (type_id < uint64_t{0x1} << 62) {
875     encoding_size = 0x03;
876     start_processing_byte = 7;
877   } else {
878     return "";
879   }
880 
881   // Parse the encoded id into a string;
882   std::stringstream ss;
883   uint8_t first_byte =
884       encoding_size << 6 | GetByte(type_id, start_processing_byte);
885   ss << "{0x" << std::hex << uint32_t{first_byte};
886   for (int i = start_processing_byte - 1; i >= 0; i--) {
887     ss << ", 0x" << std::hex << uint32_t{GetByte(type_id, i)};
888   }
889   ss << "}";
890   return ss.str();
891 }
892 
893 // Writes encoding functions for each type in |table| to the file descriptor
894 // |fd|.
WriteEncoders(int fd,CppSymbolTable * table)895 bool WriteEncoders(int fd, CppSymbolTable* table) {
896   for (CppType* real_type : table->TypesWithId()) {
897     const auto& name = real_type->name;
898     if (real_type->which != CppType::Which::kStruct ||
899         real_type->struct_type.key_type ==
900             CppType::Struct::KeyType::kPlainGroup) {
901       return false;
902     }
903     std::string cpp_name = ToCamelCase(name);
904 
905     for (const auto& x : real_type->struct_type.members) {
906       if (x.type->which != CppType::Which::kDiscriminatedUnion)
907         continue;
908       std::string dunion_cpp_name = ToCamelCase(x.name);
909       dprintf(fd, "\n%s::%s::%s()\n", cpp_name.c_str(), dunion_cpp_name.c_str(),
910               dunion_cpp_name.c_str());
911       std::string cid = ToUnderscoreId(x.name);
912       std::string type_name = ToCamelCase(x.name);
913       dprintf(fd,
914               "    : which(Which::kUninitialized), placeholder_(false) {}\n");
915 
916       dprintf(fd, "\n%s::%s::~%s() {\n", cpp_name.c_str(),
917               dunion_cpp_name.c_str(), dunion_cpp_name.c_str());
918       dprintf(fd, "  switch (which) {\n");
919       for (const auto* y : x.type->discriminated_union.members) {
920         switch (y->which) {
921           case CppType::Which::kUint64: {
922             dprintf(fd, " case Which::kUint64: break;\n");
923           } break;
924           case CppType::Which::kString: {
925             dprintf(fd, "  case Which::kString:\n");
926             dprintf(fd, "    str.std::string::~basic_string();\n");
927             dprintf(fd, "    break;\n");
928           } break;
929           case CppType::Which::kBytes: {
930             dprintf(fd, "  case Which::kBytes:\n");
931             dprintf(fd, "    bytes.std::vector<uint8_t>::~vector();\n");
932             dprintf(fd, "    break;\n");
933           } break;
934           default:
935             return false;
936         }
937       }
938       dprintf(fd, " case Which::kUninitialized: break;\n");
939       dprintf(fd, "  }\n");
940       dprintf(fd, "}\n");
941     }
942 
943     static const char vector_encode_function[] =
944         R"(
945 bool Encode%1$s(
946     const %1$s& data,
947     CborEncodeBuffer* buffer) {
948   if (buffer->AvailableLength() == 0 &&
949       !buffer->Append(CborEncodeBuffer::kDefaultInitialEncodeBufferSize))
950     return false;
951   const uint8_t type_id[] = %2$s;
952   if(!buffer->SetType(type_id, sizeof(type_id))) {
953     return false;
954   }
955   while (true) {
956     size_t available_length = buffer->AvailableLength();
957     ssize_t error_or_size = msgs::Encode%1$s(
958         data, buffer->Position(), available_length);
959     if (IsError(error_or_size)) {
960       return false;
961     } else if (error_or_size > static_cast<ssize_t>(available_length)) {
962       if (!buffer->ResizeBy(error_or_size - available_length))
963         return false;
964     } else {
965       buffer->ResizeBy(error_or_size - available_length);
966       return true;
967     }
968   }
969 }
970 )";
971 
972     std::string encoded_id = GetEncodedTypeKey(*real_type);
973     if (encoded_id.empty()) {
974       return false;
975     }
976 
977     dprintf(fd, vector_encode_function, cpp_name.c_str(), encoded_id.c_str());
978     dprintf(fd, "\nssize_t Encode%s(\n", cpp_name.c_str());
979     dprintf(fd, "    const %s& data,\n", cpp_name.c_str());
980     dprintf(fd, "    uint8_t* buffer,\n    size_t length) {\n");
981     dprintf(fd, "  CborEncoder encoder0;\n");
982     dprintf(fd, "  cbor_encoder_init(&encoder0, buffer, length, 0);\n");
983 
984     if (real_type->struct_type.key_type == CppType::Struct::KeyType::kMap) {
985       if (!WriteMapEncoder(fd, "data", real_type->struct_type.members, name))
986         return false;
987     } else {
988       if (!WriteArrayEncoder(fd, "data", real_type->struct_type.members,
989                              name)) {
990         return false;
991       }
992     }
993 
994     dprintf(fd,
995             "  size_t extra_bytes_needed = "
996             "cbor_encoder_get_extra_bytes_needed(&encoder0);\n");
997     dprintf(fd, "  if (extra_bytes_needed) {\n");
998     dprintf(fd,
999             "    return static_cast<ssize_t>(length + extra_bytes_needed);\n");
1000     dprintf(fd, "  } else {\n");
1001     dprintf(fd,
1002             "    return "
1003             "static_cast<ssize_t>(cbor_encoder_get_buffer_size(&encoder0, "
1004             "buffer));\n");
1005     dprintf(fd, "  }\n");
1006     dprintf(fd, "}\n");
1007   }
1008   return true;
1009 }
1010 
1011 bool WriteMapDecoder(int fd,
1012                      const std::string& name,
1013                      const std::string& member_accessor,
1014                      const std::vector<CppType::Struct::CppMember>& members,
1015                      int decoder_depth,
1016                      int* temporary_count);
1017 bool WriteArrayDecoder(int fd,
1018                        const std::string& name,
1019                        const std::string& member_accessor,
1020                        const std::vector<CppType::Struct::CppMember>& members,
1021                        int decoder_depth,
1022                        int* temporary_count);
1023 
1024 // Writes the decoding function for the C++ type |cpp_type| to the file
1025 // descriptor |fd|.  |name| is the C++ variable name that needs to be encoded.
1026 // |member_accessor| is either "." or "->" depending on whether |name| is a
1027 // pointer type.  |decoder_depth| is used to independently name independent cbor
1028 // decoders that need to be created.  |temporary_count| is used to ensure
1029 // temporaries get unique names by appending an automatically incremented
1030 // integer.
WriteDecoder(int fd,const std::string & name,const std::string & member_accessor,const CppType & cpp_type,int decoder_depth,int * temporary_count)1031 bool WriteDecoder(int fd,
1032                   const std::string& name,
1033                   const std::string& member_accessor,
1034                   const CppType& cpp_type,
1035                   int decoder_depth,
1036                   int* temporary_count) {
1037   switch (cpp_type.which) {
1038     case CppType::Which::kUint64: {
1039       dprintf(fd,
1040               "  CBOR_RETURN_ON_ERROR(cbor_value_get_uint64(&it%d, &%s));\n",
1041               decoder_depth, name.c_str());
1042       dprintf(fd, "  CBOR_RETURN_ON_ERROR(cbor_value_advance_fixed(&it%d));\n",
1043               decoder_depth);
1044       return true;
1045     } break;
1046     case CppType::Which::kString: {
1047       int temp_length = (*temporary_count)++;
1048       dprintf(fd, "  size_t length%d = 0;", temp_length);
1049       dprintf(fd,
1050               "  CBOR_RETURN_ON_ERROR(cbor_value_validate(&it%d, "
1051               "CborValidateUtf8));\n",
1052               decoder_depth);
1053       dprintf(fd, "  if (cbor_value_is_length_known(&it%d)) {\n",
1054               decoder_depth);
1055       dprintf(fd,
1056               "    CBOR_RETURN_ON_ERROR(cbor_value_get_string_length(&it%d, "
1057               "&length%d));\n",
1058               decoder_depth, temp_length);
1059       dprintf(fd, "  } else {\n");
1060       dprintf(
1061           fd,
1062           "    CBOR_RETURN_ON_ERROR(cbor_value_calculate_string_length(&it%d, "
1063           "&length%d));\n",
1064           decoder_depth, temp_length);
1065       dprintf(fd, "  }\n");
1066       dprintf(fd, "  %s%sresize(length%d);\n", name.c_str(),
1067               member_accessor.c_str(), temp_length);
1068       dprintf(fd,
1069               "  CBOR_RETURN_ON_ERROR(cbor_value_copy_text_string(&it%d, "
1070               "const_cast<char*>(%s%sdata()), &length%d, nullptr));\n",
1071               decoder_depth, name.c_str(), member_accessor.c_str(),
1072               temp_length);
1073       dprintf(fd, "  CBOR_RETURN_ON_ERROR(cbor_value_advance(&it%d));\n",
1074               decoder_depth);
1075       return true;
1076     } break;
1077     case CppType::Which::kBytes: {
1078       int temp_length = (*temporary_count)++;
1079       dprintf(fd, "  size_t length%d = 0;", temp_length);
1080       dprintf(fd, "  if (cbor_value_is_length_known(&it%d)) {\n",
1081               decoder_depth);
1082       dprintf(fd,
1083               "    CBOR_RETURN_ON_ERROR(cbor_value_get_string_length(&it%d, "
1084               "&length%d));\n",
1085               decoder_depth, temp_length);
1086       dprintf(fd, "  } else {\n");
1087       dprintf(
1088           fd,
1089           "    CBOR_RETURN_ON_ERROR(cbor_value_calculate_string_length(&it%d, "
1090           "&length%d));\n",
1091           decoder_depth, temp_length);
1092       dprintf(fd, "  }\n");
1093       if (!cpp_type.bytes_type.fixed_size) {
1094         dprintf(fd, "  %s%sresize(length%d);\n", name.c_str(),
1095                 member_accessor.c_str(), temp_length);
1096       } else {
1097         dprintf(fd, "  if (length%d < %d) {\n", temp_length,
1098                 static_cast<int>(cpp_type.bytes_type.fixed_size.value()));
1099         dprintf(fd, "    return -CborErrorTooFewItems;\n");
1100         dprintf(fd, "  } else if (length%d > %d) {\n", temp_length,
1101                 static_cast<int>(cpp_type.bytes_type.fixed_size.value()));
1102         dprintf(fd, "    return -CborErrorTooManyItems;\n");
1103         dprintf(fd, "  }\n");
1104       }
1105       dprintf(fd,
1106               "  CBOR_RETURN_ON_ERROR(cbor_value_copy_byte_string(&it%d, "
1107               "const_cast<uint8_t*>(%s%sdata()), &length%d, nullptr));\n",
1108               decoder_depth, name.c_str(), member_accessor.c_str(),
1109               temp_length);
1110       dprintf(fd, "  CBOR_RETURN_ON_ERROR(cbor_value_advance(&it%d));\n",
1111               decoder_depth);
1112       return true;
1113     } break;
1114     case CppType::Which::kVector: {
1115       dprintf(fd, "  if (cbor_value_get_type(&it%d) != CborArrayType) {\n",
1116               decoder_depth);
1117       dprintf(fd, "    return -1;\n");
1118       dprintf(fd, "  }\n");
1119       dprintf(fd, "  {\n");
1120       dprintf(fd, "  CborValue it%d;\n", decoder_depth + 1);
1121       dprintf(fd, "  size_t it%d_length = 0;\n", decoder_depth + 1);
1122       dprintf(fd,
1123               "  CBOR_RETURN_ON_ERROR(cbor_value_get_array_length(&it%d, "
1124               "&it%d_length));\n",
1125               decoder_depth, decoder_depth + 1);
1126       if (cpp_type.vector_type.min_length !=
1127           CppType::Vector::kMinLengthUnbounded) {
1128         dprintf(fd, "  if (it%d_length < %d) {\n", decoder_depth + 1,
1129                 cpp_type.vector_type.min_length);
1130         dprintf(fd, "    return -CborErrorTooFewItems;\n");
1131         dprintf(fd, "  }\n");
1132       }
1133       if (cpp_type.vector_type.max_length !=
1134           CppType::Vector::kMaxLengthUnbounded) {
1135         dprintf(fd, "  if (it%d_length > %d) {\n", decoder_depth + 1,
1136                 cpp_type.vector_type.max_length);
1137         dprintf(fd, "    return -CborErrorTooManyItems;\n");
1138         dprintf(fd, "  }\n");
1139       }
1140       dprintf(fd, "  %s%sresize(it%d_length);\n", name.c_str(),
1141               member_accessor.c_str(), decoder_depth + 1);
1142       dprintf(
1143           fd,
1144           "  CBOR_RETURN_ON_ERROR(cbor_value_enter_container(&it%d, &it%d));\n",
1145           decoder_depth, decoder_depth + 1);
1146       dprintf(fd, "  for (auto i = %s%sbegin(); i != %s%send(); ++i) {\n",
1147               name.c_str(), member_accessor.c_str(), name.c_str(),
1148               member_accessor.c_str());
1149       if (!WriteDecoder(fd, "(*i)", ".", *cpp_type.vector_type.element_type,
1150                         decoder_depth + 1, temporary_count)) {
1151         return false;
1152       }
1153       dprintf(fd, "  }\n");
1154       dprintf(
1155           fd,
1156           "  CBOR_RETURN_ON_ERROR(cbor_value_leave_container(&it%d, &it%d));\n",
1157           decoder_depth, decoder_depth + 1);
1158       dprintf(fd, "  }\n");
1159       return true;
1160     } break;
1161     case CppType::Which::kEnum: {
1162       dprintf(fd,
1163               "  CBOR_RETURN_ON_ERROR(cbor_value_get_uint64(&it%d, "
1164               "reinterpret_cast<uint64_t*>(&%s)));\n",
1165               decoder_depth, name.c_str());
1166       dprintf(fd, "  CBOR_RETURN_ON_ERROR(cbor_value_advance_fixed(&it%d));\n",
1167               decoder_depth);
1168       // TODO(btolsch): Validate against enum members.
1169       return true;
1170     } break;
1171     case CppType::Which::kStruct: {
1172       if (cpp_type.struct_type.key_type == CppType::Struct::KeyType::kMap) {
1173         return WriteMapDecoder(fd, name, member_accessor,
1174                                cpp_type.struct_type.members, decoder_depth + 1,
1175                                temporary_count);
1176       } else if (cpp_type.struct_type.key_type ==
1177                  CppType::Struct::KeyType::kArray) {
1178         return WriteArrayDecoder(fd, name, member_accessor,
1179                                  cpp_type.struct_type.members,
1180                                  decoder_depth + 1, temporary_count);
1181       }
1182     } break;
1183     case CppType::Which::kDiscriminatedUnion: {
1184       int temp_value_type = (*temporary_count)++;
1185       dprintf(fd, "  CborType type%d = cbor_value_get_type(&it%d);\n",
1186               temp_value_type, decoder_depth);
1187       bool first = true;
1188       for (const auto* x : cpp_type.discriminated_union.members) {
1189         if (first)
1190           first = false;
1191         else
1192           dprintf(fd, " else ");
1193         switch (x->which) {
1194           case CppType::Which::kUint64:
1195             dprintf(fd,
1196                     "  if (type%d == CborIntegerType && (it%d.flags & "
1197                     "CborIteratorFlag_NegativeInteger) == 0) {\n",
1198                     temp_value_type, decoder_depth);
1199             dprintf(fd, "  %s.which = decltype(%s)::Which::kUint64;\n",
1200                     name.c_str(), name.c_str());
1201             if (!WriteDecoder(fd, name + ".uint", ".", *x, decoder_depth,
1202                               temporary_count)) {
1203               return false;
1204             }
1205             break;
1206           case CppType::Which::kString: {
1207             dprintf(fd, "  if (type%d == CborTextStringType) {\n",
1208                     temp_value_type);
1209             dprintf(fd, "  %s.which = decltype(%s)::Which::kString;\n",
1210                     name.c_str(), name.c_str());
1211             std::string str_name = name + ".str";
1212             dprintf(fd, "  new (&%s) std::string();\n", str_name.c_str());
1213             if (!WriteDecoder(fd, str_name, ".", *x, decoder_depth,
1214                               temporary_count)) {
1215               return false;
1216             }
1217           } break;
1218           case CppType::Which::kBytes: {
1219             dprintf(fd, "  if (type%d == CborByteStringType) {\n",
1220                     temp_value_type);
1221             std::string bytes_name = name + ".bytes";
1222             dprintf(fd, "  %s.which = decltype(%s)::Which::kBytes;\n",
1223                     name.c_str(), name.c_str());
1224             dprintf(fd, "  new (&%s) std::vector<uint8_t>();\n",
1225                     bytes_name.c_str());
1226             if (!WriteDecoder(fd, bytes_name, ".", *x, decoder_depth,
1227                               temporary_count)) {
1228               return false;
1229             }
1230           } break;
1231           default:
1232             return false;
1233         }
1234         dprintf(fd, "  }\n");
1235       }
1236       dprintf(fd, " else { return -1; }\n");
1237       return true;
1238     } break;
1239     case CppType::Which::kTaggedType: {
1240       int temp_tag = (*temporary_count)++;
1241       dprintf(fd, "  uint64_t tag%d = 0;\n", temp_tag);
1242       dprintf(fd, "  cbor_value_get_tag(&it%d, &tag%d);\n", decoder_depth,
1243               temp_tag);
1244       dprintf(fd, "  if (tag%d != %" PRIu64 "ull) {\n", temp_tag,
1245               cpp_type.tagged_type.tag);
1246       dprintf(fd, "    return -1;\n");
1247       dprintf(fd, "  }\n");
1248       dprintf(fd, "  CBOR_RETURN_ON_ERROR(cbor_value_advance_fixed(&it%d));\n",
1249               decoder_depth);
1250       if (!WriteDecoder(fd, name, member_accessor,
1251                         *cpp_type.tagged_type.real_type, decoder_depth,
1252                         temporary_count)) {
1253         return false;
1254       }
1255       return true;
1256     } break;
1257     default:
1258       break;
1259   }
1260   return false;
1261 }
1262 
1263 // Writes the decoding function for the CBOR map with members in |members| to
1264 // the file descriptor |fd|.  |name| is the C++ variable name that needs to be
1265 // encoded.  |member_accessor| is either "." or "->" depending on whether |name|
1266 // is a pointer type.  |decoder_depth| is used to independently name independent
1267 // cbor decoders that need to be created.  |temporary_count| is used to ensure
1268 // temporaries get unique names by appending an automatically incremented
1269 // integer.
WriteMapDecoder(int fd,const std::string & name,const std::string & member_accessor,const std::vector<CppType::Struct::CppMember> & members,int decoder_depth,int * temporary_count)1270 bool WriteMapDecoder(int fd,
1271                      const std::string& name,
1272                      const std::string& member_accessor,
1273                      const std::vector<CppType::Struct::CppMember>& members,
1274                      int decoder_depth,
1275                      int* temporary_count) {
1276   dprintf(fd, "  if (cbor_value_get_type(&it%d) != CborMapType) {\n",
1277           decoder_depth - 1);
1278   dprintf(fd, "    return -1;\n");
1279   dprintf(fd, "  }\n");
1280   dprintf(fd, "  CborValue it%d;\n", decoder_depth);
1281   dprintf(fd, "  size_t it%d_length = 0;\n", decoder_depth);
1282   dprintf(fd,
1283           "  CBOR_RETURN_ON_ERROR(cbor_value_get_map_length(&it%d, "
1284           "&it%d_length));\n",
1285           decoder_depth - 1, decoder_depth);
1286   int optional_members = 0;
1287   for (const auto& member : members) {
1288     if (member.type->which == CppType::Which::kOptional) {
1289       ++optional_members;
1290     }
1291   }
1292   dprintf(fd, "  if (it%d_length != %d", decoder_depth,
1293           static_cast<int>(members.size()));
1294   for (int i = 0; i < optional_members; ++i) {
1295     dprintf(fd, " && it%d_length != %d", decoder_depth,
1296             static_cast<int>(members.size()) - i - 1);
1297   }
1298   dprintf(fd, ") {\n");
1299   dprintf(fd, "    return -1;\n");
1300   dprintf(fd, "  }\n");
1301   dprintf(fd,
1302           "  CBOR_RETURN_ON_ERROR(cbor_value_enter_container(&it%d, &it%d));\n",
1303           decoder_depth - 1, decoder_depth);
1304   int member_pos = 0;
1305   for (const auto& x : members) {
1306     std::string cid = ToUnderscoreId(x.name);
1307     std::string fullname = name + member_accessor + cid;
1308     if (x.type->which == CppType::Which::kOptional) {
1309       // TODO(btolsch): This is wrong for the same reason as arrays, but will be
1310       // easier to handle when doing out-of-order keys.
1311       dprintf(fd, "  if (it%d_length > %d) {\n", decoder_depth, member_pos);
1312 
1313       if (x.integer_key.has_value()) {
1314         dprintf(fd,
1315                 "  CBOR_RETURN_ON_ERROR(EXPECT_INT_KEY_CONSTANT(&it%d, %" PRIu64
1316                 "));\n",
1317                 decoder_depth, x.integer_key.value());
1318       } else {
1319         dprintf(fd,
1320                 "  CBOR_RETURN_ON_ERROR(EXPECT_KEY_CONSTANT(&it%d, \"%s\"));\n",
1321                 decoder_depth, x.name.c_str());
1322       }
1323       dprintf(fd, "    %s%shas_%s = true;\n", name.c_str(),
1324               member_accessor.c_str(), cid.c_str());
1325       if (!WriteDecoder(fd, fullname, ".", *x.type->optional_type,
1326                         decoder_depth, temporary_count)) {
1327         return false;
1328       }
1329       dprintf(fd, "  } else {\n");
1330       dprintf(fd, "    %s%shas_%s = false;\n", name.c_str(),
1331               member_accessor.c_str(), cid.c_str());
1332       dprintf(fd, "  }\n");
1333     } else {
1334       if (x.integer_key.has_value()) {
1335         dprintf(fd,
1336                 "  CBOR_RETURN_ON_ERROR(EXPECT_INT_KEY_CONSTANT(&it%d, %" PRIu64
1337                 "));\n",
1338                 decoder_depth, x.integer_key.value());
1339       } else {
1340         dprintf(fd,
1341                 "  CBOR_RETURN_ON_ERROR(EXPECT_KEY_CONSTANT(&it%d, \"%s\"));\n",
1342                 decoder_depth, x.name.c_str());
1343       }
1344       if (!WriteDecoder(fd, fullname, ".", *x.type, decoder_depth,
1345                         temporary_count)) {
1346         return false;
1347       }
1348     }
1349     ++member_pos;
1350   }
1351   dprintf(fd,
1352           "  CBOR_RETURN_ON_ERROR(cbor_value_leave_container(&it%d, &it%d));\n",
1353           decoder_depth - 1, decoder_depth);
1354   return true;
1355 }
1356 
1357 // Writes the decoding function for the CBOR array with members in |members| to
1358 // the file descriptor |fd|.  |name| is the C++ variable name that needs to be
1359 // encoded.  |member_accessor| is either "." or "->" depending on whether |name|
1360 // is a pointer type.  |decoder_depth| is used to independently name independent
1361 // cbor decoders that need to be created.  |temporary_count| is used to ensure
1362 // temporaries get unique names by appending an automatically incremented
1363 // integer.
WriteArrayDecoder(int fd,const std::string & name,const std::string & member_accessor,const std::vector<CppType::Struct::CppMember> & members,int decoder_depth,int * temporary_count)1364 bool WriteArrayDecoder(int fd,
1365                        const std::string& name,
1366                        const std::string& member_accessor,
1367                        const std::vector<CppType::Struct::CppMember>& members,
1368                        int decoder_depth,
1369                        int* temporary_count) {
1370   dprintf(fd, "  if (cbor_value_get_type(&it%d) != CborArrayType) {\n",
1371           decoder_depth - 1);
1372   dprintf(fd, "    return -1;\n");
1373   dprintf(fd, "  }\n");
1374   dprintf(fd, "  CborValue it%d;\n", decoder_depth);
1375   dprintf(fd, "  size_t it%d_length = 0;\n", decoder_depth);
1376   dprintf(fd,
1377           "  CBOR_RETURN_ON_ERROR(cbor_value_get_array_length(&it%d, "
1378           "&it%d_length));\n",
1379           decoder_depth - 1, decoder_depth);
1380   int optional_members = 0;
1381   for (const auto& member : members) {
1382     if (member.type->which == CppType::Which::kOptional) {
1383       ++optional_members;
1384     }
1385   }
1386   dprintf(fd, "  if (it%d_length != %d", decoder_depth,
1387           static_cast<int>(members.size()));
1388   for (int i = 0; i < optional_members; ++i) {
1389     dprintf(fd, " && it%d_length != %d", decoder_depth,
1390             static_cast<int>(members.size()) - i - 1);
1391   }
1392   dprintf(fd, ") {\n");
1393   dprintf(fd, "    return -1;\n");
1394   dprintf(fd, "  }\n");
1395   dprintf(fd,
1396           "  CBOR_RETURN_ON_ERROR(cbor_value_enter_container(&it%d, &it%d));\n",
1397           decoder_depth - 1, decoder_depth);
1398   int member_pos = 0;
1399   for (const auto& x : members) {
1400     std::string cid = ToUnderscoreId(x.name);
1401     std::string fullname = name + member_accessor + cid;
1402     if (x.type->which == CppType::Which::kOptional) {
1403       // TODO(btolsch): This only handles a single block of optionals and only
1404       // the ones present form a contiguous range from the start of the block.
1405       // However, we likely don't really need more than one optional for arrays
1406       // for the foreseeable future.  The proper approach would be to have a set
1407       // of possible types for the next element and a map for the member to
1408       // which each corresponds.
1409       dprintf(fd, "  if (it%d_length > %d) {\n", decoder_depth, member_pos);
1410       dprintf(fd, "    %s%shas_%s = true;\n", name.c_str(),
1411               member_accessor.c_str(), cid.c_str());
1412       if (!WriteDecoder(fd, fullname, ".", *x.type->optional_type,
1413                         decoder_depth, temporary_count)) {
1414         return false;
1415       }
1416       dprintf(fd, "  } else {\n");
1417       dprintf(fd, "    %s%shas_%s = false;\n", name.c_str(),
1418               member_accessor.c_str(), cid.c_str());
1419       dprintf(fd, "  }\n");
1420     } else {
1421       if (!WriteDecoder(fd, fullname, ".", *x.type, decoder_depth,
1422                         temporary_count)) {
1423         return false;
1424       }
1425     }
1426     ++member_pos;
1427   }
1428   dprintf(fd,
1429           "  CBOR_RETURN_ON_ERROR(cbor_value_leave_container(&it%d, &it%d));\n",
1430           decoder_depth - 1, decoder_depth);
1431   return true;
1432 }
1433 
1434 // Writes the equality operators for all structs.
WriteEqualityOperators(int fd,CppSymbolTable * table)1435 bool WriteEqualityOperators(int fd, CppSymbolTable* table) {
1436   for (const auto& pair : table->cpp_type_map) {
1437     CppType* real_type = pair.second;
1438     if (real_type->which == CppType::Which::kStruct &&
1439         real_type->struct_type.key_type !=
1440             CppType::Struct::KeyType::kPlainGroup) {
1441       if (!WriteStructEqualityOperator(fd, *real_type)) {
1442         return false;
1443       }
1444     }
1445   }
1446   return true;
1447 }
1448 
1449 // Writes a decoder function definition for every type in |table| to the file
1450 // descriptor |fd|.
WriteDecoders(int fd,CppSymbolTable * table)1451 bool WriteDecoders(int fd, CppSymbolTable* table) {
1452   if (!WriteTypeParserDefinition(fd, table)) {
1453     return false;
1454   }
1455   for (CppType* real_type : table->TypesWithId()) {
1456     const auto& name = real_type->name;
1457     int temporary_count = 0;
1458     if (real_type->which != CppType::Which::kStruct ||
1459         real_type->struct_type.key_type ==
1460             CppType::Struct::KeyType::kPlainGroup) {
1461       continue;
1462     }
1463     std::string cpp_name = ToCamelCase(name);
1464     dprintf(fd, "\nssize_t Decode%s(\n", cpp_name.c_str());
1465     dprintf(fd, "    const uint8_t* buffer,\n    size_t length,\n");
1466     dprintf(fd, "    %s* data) {\n", cpp_name.c_str());
1467     dprintf(fd, "  CborParser parser;\n");
1468     dprintf(fd, "  CborValue it0;\n");
1469     dprintf(
1470         fd,
1471         "  CBOR_RETURN_ON_ERROR(cbor_parser_init(buffer, length, 0, &parser, "
1472         "&it0));\n");
1473     if (real_type->struct_type.key_type == CppType::Struct::KeyType::kMap) {
1474       if (!WriteMapDecoder(fd, "data", "->", real_type->struct_type.members, 1,
1475                            &temporary_count)) {
1476         return false;
1477       }
1478     } else {
1479       if (!WriteArrayDecoder(fd, "data", "->", real_type->struct_type.members,
1480                              1, &temporary_count)) {
1481         return false;
1482       }
1483     }
1484     dprintf(
1485         fd,
1486         "  auto result = static_cast<ssize_t>(cbor_value_get_next_byte(&it0) - "
1487         "buffer);\n");
1488     dprintf(fd, "  return result;\n");
1489     dprintf(fd, "}\n");
1490   }
1491   return true;
1492 }
1493 
1494 // Converts the filename |header_filename| to a preprocessor token that can be
1495 // used as a header guard macro name.
ToHeaderGuard(const std::string & header_filename)1496 std::string ToHeaderGuard(const std::string& header_filename) {
1497   std::string result = header_filename;
1498   for (auto& c : result) {
1499     if (c == '/' || c == '.')
1500       c = '_';
1501     else
1502       c = toupper(c);
1503   }
1504   result += "_";
1505   return result;
1506 }
1507 
WriteHeaderPrologue(int fd,const std::string & header_filename)1508 bool WriteHeaderPrologue(int fd, const std::string& header_filename) {
1509   static const char prologue[] =
1510       R"(#ifndef %s
1511 #define %s
1512 
1513 #include <array>
1514 #include <cstdint>
1515 #include <iostream>
1516 #include <string>
1517 #include <vector>
1518 
1519 #include "third_party/tinycbor/src/src/cbor.h"
1520 
1521 namespace openscreen {
1522 namespace msgs {
1523 
1524 enum CborErrors {
1525   kParserEOF = -CborErrorUnexpectedEOF,
1526 };
1527 
1528 class CborEncodeBuffer;
1529 )";
1530   std::string header_guard = ToHeaderGuard(header_filename);
1531   dprintf(fd, prologue, header_guard.c_str(), header_guard.c_str());
1532   return true;
1533 }
1534 
WriteHeaderEpilogue(int fd,const std::string & header_filename)1535 bool WriteHeaderEpilogue(int fd, const std::string& header_filename) {
1536   static const char epilogue[] = R"(
1537 class TypeEnumValidator {
1538  public:
1539   static Type SafeCast(uint64_t type_id);
1540 };
1541 
1542 class CborEncodeBuffer {
1543  public:
1544   static constexpr size_t kDefaultInitialEncodeBufferSize = 250;
1545   static constexpr size_t kDefaultMaxEncodeBufferSize = 64000;
1546 
1547   CborEncodeBuffer();
1548   CborEncodeBuffer(size_t initial_size, size_t max_size);
1549   ~CborEncodeBuffer();
1550 
1551   bool Append(size_t length);
1552   bool ResizeBy(ssize_t length);
1553   bool SetType(const uint8_t encoded_id[], size_t size);
1554 
1555   const uint8_t* data() const { return data_.data(); }
1556   size_t size() const { return data_.size(); }
1557 
1558   uint8_t* Position() { return &data_[0] + position_; }
1559   size_t AvailableLength() { return data_.size() - position_; }
1560 
1561  private:
1562   size_t max_size_;
1563   size_t position_;
1564   std::vector<uint8_t> data_;
1565 };
1566 
1567 CborError ExpectKey(CborValue* it, const uint64_t key);
1568 CborError ExpectKey(CborValue* it, const char* key, size_t key_length);
1569 
1570 }  // namespace msgs
1571 }  // namespace openscreen
1572 #endif  // %s)";
1573   std::string header_guard = ToHeaderGuard(header_filename);
1574   dprintf(fd, epilogue, header_guard.c_str());
1575   return true;
1576 }
1577 
WriteSourcePrologue(int fd,const std::string & header_filename)1578 bool WriteSourcePrologue(int fd, const std::string& header_filename) {
1579   static const char prologue[] =
1580       R"(#include "%s"
1581 
1582 #include "third_party/tinycbor/src/src/utf8_p.h"
1583 #include "util/osp_logging.h"
1584 
1585 namespace openscreen {
1586 namespace msgs {
1587 namespace {
1588 
1589 #define CBOR_RETURN_WHAT_ON_ERROR(stmt, what)                           \
1590   {                                                                     \
1591     CborError error = stmt;                                             \
1592     /* Encoder-specific errors, so it's fine to check these even in the \
1593      * parser.                                                          \
1594      */                                                                 \
1595     OSP_DCHECK_NE(error, CborErrorTooFewItems);                             \
1596     OSP_DCHECK_NE(error, CborErrorTooManyItems);                            \
1597     OSP_DCHECK_NE(error, CborErrorDataTooLarge);                            \
1598     if (error != CborNoError && error != CborErrorOutOfMemory)          \
1599       return what;                                                      \
1600   }
1601 #define CBOR_RETURN_ON_ERROR_INTERNAL(stmt) \
1602   CBOR_RETURN_WHAT_ON_ERROR(stmt, error)
1603 #define CBOR_RETURN_ON_ERROR(stmt) CBOR_RETURN_WHAT_ON_ERROR(stmt, -error)
1604 
1605 #define EXPECT_KEY_CONSTANT(it, key) ExpectKey(it, key, sizeof(key) - 1)
1606 #define EXPECT_INT_KEY_CONSTANT(it, key) ExpectKey(it, key)
1607 
1608 bool IsValidUtf8(const std::string& s) {
1609   const uint8_t* buffer = reinterpret_cast<const uint8_t*>(s.data());
1610   const uint8_t* end = buffer + s.size();
1611   while (buffer < end) {
1612     // TODO(btolsch): This is an implementation detail of tinycbor so we should
1613     // eventually replace this call with our own utf8 validation.
1614     if (get_utf8(&buffer, end) == ~0u)
1615       return false;
1616   }
1617   return true;
1618 }
1619 }  // namespace
1620 
1621 CborError ExpectKey(CborValue* it, const uint64_t key) {
1622   if  (!cbor_value_is_unsigned_integer(it))
1623     return CborErrorImproperValue;
1624   uint64_t observed_key;
1625   CBOR_RETURN_ON_ERROR_INTERNAL(cbor_value_get_uint64(it, &observed_key));
1626   if (observed_key != key)
1627     return CborErrorImproperValue;
1628   CBOR_RETURN_ON_ERROR_INTERNAL(cbor_value_advance_fixed(it));
1629   return CborNoError;
1630 }
1631 
1632 CborError ExpectKey(CborValue* it, const char* key, size_t key_length) {
1633   if(!cbor_value_is_text_string(it))
1634     return CborErrorImproperValue;
1635   size_t observed_length = 0;
1636   CBOR_RETURN_ON_ERROR_INTERNAL(
1637       cbor_value_get_string_length(it, &observed_length));
1638   if (observed_length != key_length)
1639     return CborErrorImproperValue;
1640   std::string observed_key(key_length, 0);
1641   CBOR_RETURN_ON_ERROR_INTERNAL(cbor_value_copy_text_string(
1642       it, const_cast<char*>(observed_key.data()), &observed_length, nullptr));
1643   if (observed_key != key)
1644     return CborErrorImproperValue;
1645   CBOR_RETURN_ON_ERROR_INTERNAL(cbor_value_advance(it));
1646   return CborNoError;
1647 }
1648 
1649 // static
1650 constexpr size_t CborEncodeBuffer::kDefaultInitialEncodeBufferSize;
1651 
1652 // static
1653 constexpr size_t CborEncodeBuffer::kDefaultMaxEncodeBufferSize;
1654 
1655 CborEncodeBuffer::CborEncodeBuffer()
1656     : max_size_(kDefaultMaxEncodeBufferSize),
1657       position_(0),
1658       data_(kDefaultInitialEncodeBufferSize) {}
1659 CborEncodeBuffer::CborEncodeBuffer(size_t initial_size, size_t max_size)
1660     : max_size_(max_size), position_(0), data_(initial_size) {}
1661 CborEncodeBuffer::~CborEncodeBuffer() = default;
1662 
1663 bool CborEncodeBuffer::SetType(const uint8_t encoded_id[], size_t size) {
1664   if (this->AvailableLength() < size) {
1665     if (!this->ResizeBy(size)) {
1666       return false;
1667     }
1668   }
1669   memcpy(&data_[position_], encoded_id, size);
1670   position_ += size;
1671   return true;
1672 }
1673 
1674 bool CborEncodeBuffer::Append(size_t length) {
1675   if (length == 0)
1676     return false;
1677   if ((data_.size() + length) > max_size_) {
1678     length = max_size_ - data_.size();
1679     if (length == 0)
1680       return false;
1681   }
1682   size_t append_area = data_.size();
1683   data_.resize(append_area + length);
1684   position_ = append_area;
1685   return true;
1686 }
1687 
1688 bool CborEncodeBuffer::ResizeBy(ssize_t delta) {
1689   if (delta == 0)
1690     return true;
1691   if (delta < 0 && static_cast<size_t>(-delta) > data_.size())
1692     return false;
1693   if (delta > 0 && (data_.size() + delta) > max_size_)
1694     return false;
1695   data_.resize(data_.size() + delta);
1696   return true;
1697 }
1698 
1699 bool IsError(ssize_t x) {
1700   return x < 0;
1701 }
1702 )";
1703   dprintf(fd, prologue, header_filename.c_str());
1704   return true;
1705 }
1706 
WriteSourceEpilogue(int fd)1707 bool WriteSourceEpilogue(int fd) {
1708   static const char epilogue[] = R"(
1709 }  // namespace msgs
1710 }  // namespace openscreen)";
1711   dprintf(fd, epilogue);
1712   return true;
1713 }
1714