1 // Protocol Buffers - Google's data interchange format
2 // Copyright 2008 Google Inc.  All rights reserved.
3 // https://developers.google.com/protocol-buffers/
4 //
5 // Redistribution and use in source and binary forms, with or without
6 // modification, are permitted provided that the following conditions are
7 // met:
8 //
9 //     * Redistributions of source code must retain the above copyright
10 // notice, this list of conditions and the following disclaimer.
11 //     * Redistributions in binary form must reproduce the above
12 // copyright notice, this list of conditions and the following disclaimer
13 // in the documentation and/or other materials provided with the
14 // distribution.
15 //     * Neither the name of Google Inc. nor the names of its
16 // contributors may be used to endorse or promote products derived from
17 // this software without specific prior written permission.
18 //
19 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 
31 #include <google/protobuf/compiler/cpp/cpp_map_field.h>
32 #include <google/protobuf/compiler/cpp/cpp_helpers.h>
33 #include <google/protobuf/io/printer.h>
34 #include <google/protobuf/wire_format.h>
35 #include <google/protobuf/stubs/strutil.h>
36 
37 namespace google {
38 namespace protobuf {
39 namespace compiler {
40 namespace cpp {
41 
IsProto3Field(const FieldDescriptor * field_descriptor)42 bool IsProto3Field(const FieldDescriptor* field_descriptor) {
43   const FileDescriptor* file_descriptor = field_descriptor->file();
44   return file_descriptor->syntax() == FileDescriptor::SYNTAX_PROTO3;
45 }
46 
SetMessageVariables(const FieldDescriptor * descriptor,map<string,string> * variables,const Options & options)47 void SetMessageVariables(const FieldDescriptor* descriptor,
48                          map<string, string>* variables,
49                          const Options& options) {
50   SetCommonFieldVariables(descriptor, variables, options);
51   (*variables)["type"] = FieldMessageTypeName(descriptor);
52   (*variables)["stream_writer"] =
53       (*variables)["declared_type"] +
54       (HasFastArraySerialization(descriptor->message_type()->file(), options)
55            ? "MaybeToArray"
56            : "");
57   (*variables)["full_name"] = descriptor->full_name();
58 
59   const FieldDescriptor* key =
60       descriptor->message_type()->FindFieldByName("key");
61   const FieldDescriptor* val =
62       descriptor->message_type()->FindFieldByName("value");
63   (*variables)["key_cpp"] = PrimitiveTypeName(key->cpp_type());
64   switch (val->cpp_type()) {
65     case FieldDescriptor::CPPTYPE_MESSAGE:
66       (*variables)["val_cpp"] = FieldMessageTypeName(val);
67       (*variables)["wrapper"] = "EntryWrapper";
68       break;
69     case FieldDescriptor::CPPTYPE_ENUM:
70       (*variables)["val_cpp"] = ClassName(val->enum_type(), true);
71       (*variables)["wrapper"] = "EnumEntryWrapper";
72       break;
73     default:
74       (*variables)["val_cpp"] = PrimitiveTypeName(val->cpp_type());
75       (*variables)["wrapper"] = "EntryWrapper";
76   }
77   (*variables)["key_wire_type"] =
78       "::google::protobuf::internal::WireFormatLite::TYPE_" +
79       ToUpper(DeclaredTypeMethodName(key->type()));
80   (*variables)["val_wire_type"] =
81       "::google::protobuf::internal::WireFormatLite::TYPE_" +
82       ToUpper(DeclaredTypeMethodName(val->type()));
83   (*variables)["map_classname"] = ClassName(descriptor->message_type(), false);
84   (*variables)["number"] = SimpleItoa(descriptor->number());
85   (*variables)["tag"] = SimpleItoa(internal::WireFormat::MakeTag(descriptor));
86 
87   if (HasDescriptorMethods(descriptor->file(), options)) {
88     (*variables)["lite"] = "";
89   } else {
90     (*variables)["lite"] = "Lite";
91   }
92 
93   if (!IsProto3Field(descriptor) &&
94       val->type() == FieldDescriptor::TYPE_ENUM) {
95     const EnumValueDescriptor* default_value = val->default_value_enum();
96     (*variables)["default_enum_value"] = Int32ToString(default_value->number());
97   } else {
98     (*variables)["default_enum_value"] = "0";
99   }
100 }
101 
MapFieldGenerator(const FieldDescriptor * descriptor,const Options & options)102 MapFieldGenerator::MapFieldGenerator(const FieldDescriptor* descriptor,
103                                      const Options& options)
104     : FieldGenerator(options),
105       descriptor_(descriptor),
106       dependent_field_(options.proto_h && IsFieldDependent(descriptor)) {
107   SetMessageVariables(descriptor, &variables_, options);
108 }
109 
~MapFieldGenerator()110 MapFieldGenerator::~MapFieldGenerator() {}
111 
112 void MapFieldGenerator::
GeneratePrivateMembers(io::Printer * printer) const113 GeneratePrivateMembers(io::Printer* printer) const {
114   printer->Print(variables_,
115       "typedef ::google::protobuf::internal::MapEntryLite<\n"
116       "    $key_cpp$, $val_cpp$,\n"
117       "    $key_wire_type$,\n"
118       "    $val_wire_type$,\n"
119       "    $default_enum_value$ >\n"
120       "    $map_classname$;\n"
121       "::google::protobuf::internal::MapField$lite$<\n"
122       "    $key_cpp$, $val_cpp$,\n"
123       "    $key_wire_type$,\n"
124       "    $val_wire_type$,\n"
125       "    $default_enum_value$ > $name$_;\n");
126 }
127 
128 void MapFieldGenerator::
GenerateAccessorDeclarations(io::Printer * printer) const129 GenerateAccessorDeclarations(io::Printer* printer) const {
130   printer->Print(variables_,
131       "$deprecated_attr$const ::google::protobuf::Map< $key_cpp$, $val_cpp$ >&\n"
132       "    $name$() const;\n"
133       "$deprecated_attr$::google::protobuf::Map< $key_cpp$, $val_cpp$ >*\n"
134       "    mutable_$name$();\n");
135 }
136 
137 void MapFieldGenerator::
GenerateInlineAccessorDefinitions(io::Printer * printer,bool is_inline) const138 GenerateInlineAccessorDefinitions(io::Printer* printer,
139                                   bool is_inline) const {
140   map<string, string> variables(variables_);
141   variables["inline"] = is_inline ? "inline" : "";
142   printer->Print(variables,
143       "$inline$ const ::google::protobuf::Map< $key_cpp$, $val_cpp$ >&\n"
144       "$classname$::$name$() const {\n"
145       "  // @@protoc_insertion_point(field_map:$full_name$)\n"
146       "  return $name$_.GetMap();\n"
147       "}\n"
148       "$inline$ ::google::protobuf::Map< $key_cpp$, $val_cpp$ >*\n"
149       "$classname$::mutable_$name$() {\n"
150       "  // @@protoc_insertion_point(field_mutable_map:$full_name$)\n"
151       "  return $name$_.MutableMap();\n"
152       "}\n");
153 }
154 
155 void MapFieldGenerator::
GenerateClearingCode(io::Printer * printer) const156 GenerateClearingCode(io::Printer* printer) const {
157   map<string, string> variables(variables_);
158   variables["this_message"] = dependent_field_ ? DependentBaseDownCast() : "";
159   printer->Print(variables, "$this_message$$name$_.Clear();\n");
160 }
161 
162 void MapFieldGenerator::
GenerateMergingCode(io::Printer * printer) const163 GenerateMergingCode(io::Printer* printer) const {
164   printer->Print(variables_, "$name$_.MergeFrom(from.$name$_);\n");
165 }
166 
167 void MapFieldGenerator::
GenerateSwappingCode(io::Printer * printer) const168 GenerateSwappingCode(io::Printer* printer) const {
169   printer->Print(variables_, "$name$_.Swap(&other->$name$_);\n");
170 }
171 
172 void MapFieldGenerator::
GenerateConstructorCode(io::Printer * printer) const173 GenerateConstructorCode(io::Printer* printer) const {
174   if (HasDescriptorMethods(descriptor_->file(), options_)) {
175     printer->Print(variables_,
176         "$name$_.SetAssignDescriptorCallback(\n"
177         "    protobuf_AssignDescriptorsOnce);\n"
178         "$name$_.SetEntryDescriptor(\n"
179         "    &$type$_descriptor_);\n");
180   }
181 }
182 
183 void MapFieldGenerator::
GenerateMergeFromCodedStream(io::Printer * printer) const184 GenerateMergeFromCodedStream(io::Printer* printer) const {
185     const FieldDescriptor* key_field =
186         descriptor_->message_type()->FindFieldByName("key");
187   const FieldDescriptor* value_field =
188       descriptor_->message_type()->FindFieldByName("value");
189   bool using_entry = false;
190   string key;
191   string value;
192   if (IsProto3Field(descriptor_) ||
193       value_field->type() != FieldDescriptor::TYPE_ENUM) {
194     printer->Print(variables_,
195         "$map_classname$::Parser< ::google::protobuf::internal::MapField$lite$<\n"
196                    "    $key_cpp$, $val_cpp$,\n"
197                    "    $key_wire_type$,\n"
198                    "    $val_wire_type$,\n"
199                    "    $default_enum_value$ >,\n"
200                    "  ::google::protobuf::Map< $key_cpp$, $val_cpp$ > >"
201         " parser(&$name$_);\n"
202         "DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtual(\n"
203         "    input, &parser));\n");
204     key = "parser.key()";
205     value = "parser.value()";
206   } else {
207     using_entry = true;
208     key = "entry->key()";
209     value = "entry->value()";
210     printer->Print(variables_,
211         "::google::protobuf::scoped_ptr<$map_classname$> entry($name$_.NewEntry());\n");
212     printer->Print(variables_,
213         "{\n"
214         "  ::std::string data;\n"
215         "  DO_(::google::protobuf::internal::WireFormatLite::ReadString(input, &data));\n"
216         "  DO_(entry->ParseFromString(data));\n"
217         "  if ($val_cpp$_IsValid(*entry->mutable_value())) {\n"
218         "    (*mutable_$name$())[entry->key()] =\n"
219         "        static_cast< $val_cpp$ >(*entry->mutable_value());\n"
220         "  } else {\n");
221     if (HasDescriptorMethods(descriptor_->file(), options_)) {
222       printer->Print(variables_,
223           "    mutable_unknown_fields()"
224           "->AddLengthDelimited($number$, data);\n");
225     } else {
226       printer->Print(variables_,
227           "    unknown_fields_stream.WriteVarint32($tag$);\n"
228           "    unknown_fields_stream.WriteVarint32(data.size());\n"
229           "    unknown_fields_stream.WriteString(data);\n");
230     }
231 
232     printer->Print(variables_,
233         "  }\n"
234         "}\n");
235   }
236 
237   if (key_field->type() == FieldDescriptor::TYPE_STRING) {
238     GenerateUtf8CheckCodeForString(
239     key_field, options_, true, variables_,
240         StrCat(key, ".data(), ", key, ".length(),\n").data(), printer);
241   }
242   if (value_field->type() == FieldDescriptor::TYPE_STRING) {
243     GenerateUtf8CheckCodeForString(value_field, options_, true, variables_,
244         StrCat(value, ".data(), ", value, ".length(),\n").data(), printer);
245   }
246 
247   // If entry is allocated by arena, its desctructor should be avoided.
248   if (using_entry && SupportsArenas(descriptor_)) {
249     printer->Print(variables_,
250         "if (entry->GetArena() != NULL) entry.release();\n");
251   }
252 }
253 
GenerateSerializationLoop(io::Printer * printer,const map<string,string> & variables,bool supports_arenas,const string & utf8_check,const string & loop_header,const string & ptr,bool loop_via_iterators)254 static void GenerateSerializationLoop(io::Printer* printer,
255                                       const map<string, string>& variables,
256                                       bool supports_arenas,
257                                       const string& utf8_check,
258                                       const string& loop_header,
259                                       const string& ptr,
260                                       bool loop_via_iterators) {
261   printer->Print(variables,
262       StrCat("::google::protobuf::scoped_ptr<$map_classname$> entry;\n",
263              loop_header, " {\n").c_str());
264   printer->Indent();
265 
266   printer->Print(variables, StrCat(
267       "entry.reset($name$_.New$wrapper$(\n"
268       "    ", ptr, "->first, ", ptr, "->second));\n"
269       "$write_entry$;\n").c_str());
270 
271   // If entry is allocated by arena, its desctructor should be avoided.
272   if (supports_arenas) {
273     printer->Print(
274         "if (entry->GetArena() != NULL) {\n"
275         "  entry.release();\n"
276         "}\n");
277   }
278 
279   if (!utf8_check.empty()) {
280     // If loop_via_iterators is true then ptr is actually an iterator, and we
281     // create a pointer by prefixing it with "&*".
282     printer->Print(
283         StrCat(utf8_check, "(", (loop_via_iterators ? "&*" : ""), ptr, ");\n")
284             .c_str());
285   }
286 
287   printer->Outdent();
288   printer->Print(
289       "}\n");
290 }
291 
292 void MapFieldGenerator::
GenerateSerializeWithCachedSizes(io::Printer * printer) const293 GenerateSerializeWithCachedSizes(io::Printer* printer) const {
294   map<string, string> variables(variables_);
295   variables["write_entry"] = "::google::protobuf::internal::WireFormatLite::Write" +
296                              variables["stream_writer"] + "(\n            " +
297                              variables["number"] + ", *entry, output)";
298   variables["deterministic"] = "output->IsSerializationDeterminstic()";
299   GenerateSerializeWithCachedSizes(printer, variables);
300 }
301 
302 void MapFieldGenerator::
GenerateSerializeWithCachedSizesToArray(io::Printer * printer) const303 GenerateSerializeWithCachedSizesToArray(io::Printer* printer) const {
304   map<string, string> variables(variables_);
305   variables["write_entry"] =
306       "target = ::google::protobuf::internal::WireFormatLite::\n"
307       "                   InternalWrite" + variables["declared_type"] +
308       "NoVirtualToArray(\n                       " + variables["number"] +
309       ", *entry, deterministic, target);\n";
310   variables["deterministic"] = "deterministic";
311   GenerateSerializeWithCachedSizes(printer, variables);
312 }
313 
GenerateSerializeWithCachedSizes(io::Printer * printer,const map<string,string> & variables) const314 void MapFieldGenerator::GenerateSerializeWithCachedSizes(
315     io::Printer* printer, const map<string, string>& variables) const {
316   printer->Print(variables,
317       "if (!this->$name$().empty()) {\n");
318   printer->Indent();
319   const FieldDescriptor* key_field =
320       descriptor_->message_type()->FindFieldByName("key");
321   const FieldDescriptor* value_field =
322       descriptor_->message_type()->FindFieldByName("value");
323   const bool string_key = key_field->type() == FieldDescriptor::TYPE_STRING;
324   const bool string_value = value_field->type() == FieldDescriptor::TYPE_STRING;
325 
326   printer->Print(variables,
327       "typedef ::google::protobuf::Map< $key_cpp$, $val_cpp$ >::const_pointer\n"
328       "    ConstPtr;\n");
329   if (string_key) {
330     printer->Print(variables,
331         "typedef ConstPtr SortItem;\n"
332         "typedef ::google::protobuf::internal::"
333         "CompareByDerefFirst<SortItem> Less;\n");
334   } else {
335     printer->Print(variables,
336         "typedef ::google::protobuf::internal::SortItem< $key_cpp$, ConstPtr > "
337         "SortItem;\n"
338         "typedef ::google::protobuf::internal::CompareByFirstField<SortItem> Less;\n");
339   }
340   string utf8_check;
341   if (string_key || string_value) {
342     printer->Print(
343         "struct Utf8Check {\n"
344         "  static void Check(ConstPtr p) {\n");
345     printer->Indent();
346     printer->Indent();
347     if (string_key) {
348       GenerateUtf8CheckCodeForString(key_field, options_, false, variables,
349                                      "p->first.data(), p->first.length(),\n",
350                                      printer);
351     }
352     if (string_value) {
353       GenerateUtf8CheckCodeForString(value_field, options_, false, variables,
354                                      "p->second.data(), p->second.length(),\n",
355                                      printer);
356     }
357     printer->Outdent();
358     printer->Outdent();
359     printer->Print(
360         "  }\n"
361         "};\n");
362     utf8_check = "Utf8Check::Check";
363   }
364 
365   printer->Print(variables,
366       "\n"
367       "if ($deterministic$ &&\n"
368       "    this->$name$().size() > 1) {\n"
369       "  ::google::protobuf::scoped_array<SortItem> items(\n"
370       "      new SortItem[this->$name$().size()]);\n"
371       "  typedef ::google::protobuf::Map< $key_cpp$, $val_cpp$ >::size_type size_type;\n"
372       "  size_type n = 0;\n"
373       "  for (::google::protobuf::Map< $key_cpp$, $val_cpp$ >::const_iterator\n"
374       "      it = this->$name$().begin();\n"
375       "      it != this->$name$().end(); ++it, ++n) {\n"
376       "    items[n] = SortItem(&*it);\n"
377       "  }\n"
378       "  ::std::sort(&items[0], &items[n], Less());\n");
379   printer->Indent();
380   GenerateSerializationLoop(printer, variables, SupportsArenas(descriptor_),
381                             utf8_check, "for (size_type i = 0; i < n; i++)",
382                             string_key ? "items[i]" : "items[i].second", false);
383   printer->Outdent();
384   printer->Print(
385       "} else {\n");
386   printer->Indent();
387   GenerateSerializationLoop(
388       printer, variables, SupportsArenas(descriptor_), utf8_check,
389       "for (::google::protobuf::Map< $key_cpp$, $val_cpp$ >::const_iterator\n"
390       "    it = this->$name$().begin();\n"
391       "    it != this->$name$().end(); ++it)",
392       "it", true);
393   printer->Outdent();
394   printer->Print("}\n");
395   printer->Outdent();
396   printer->Print("}\n");
397 }
398 
399 void MapFieldGenerator::
GenerateByteSize(io::Printer * printer) const400 GenerateByteSize(io::Printer* printer) const {
401   printer->Print(variables_,
402       "total_size += $tag_size$ * this->$name$_size();\n"
403       "{\n"
404       "  ::google::protobuf::scoped_ptr<$map_classname$> entry;\n"
405       "  for (::google::protobuf::Map< $key_cpp$, $val_cpp$ >::const_iterator\n"
406       "      it = this->$name$().begin();\n"
407       "      it != this->$name$().end(); ++it) {\n");
408 
409   // If entry is allocated by arena, its desctructor should be avoided.
410   if (SupportsArenas(descriptor_)) {
411     printer->Print(variables_,
412         "    if (entry.get() != NULL && entry->GetArena() != NULL) {\n"
413         "      entry.release();\n"
414         "    }\n");
415   }
416 
417   printer->Print(variables_,
418       "    entry.reset($name$_.New$wrapper$(it->first, it->second));\n"
419       "    total_size += ::google::protobuf::internal::WireFormatLite::\n"
420       "        $declared_type$SizeNoVirtual(*entry);\n"
421       "  }\n");
422 
423   // If entry is allocated by arena, its desctructor should be avoided.
424   if (SupportsArenas(descriptor_)) {
425     printer->Print(variables_,
426         "  if (entry.get() != NULL && entry->GetArena() != NULL) {\n"
427         "    entry.release();\n"
428         "  }\n");
429   }
430 
431   printer->Print("}\n");
432 }
433 
434 }  // namespace cpp
435 }  // namespace compiler
436 }  // namespace protobuf
437 }  // namespace google
438