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