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