Line data Source code
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 : #ifdef _MSC_VER
32 : #include <io.h>
33 : #else
34 : #include <unistd.h>
35 : #endif
36 : #include <climits>
37 : #include <errno.h>
38 : #include <fcntl.h>
39 : #include <fstream>
40 : #include <iostream>
41 : #include <sstream>
42 : #include <stdlib.h>
43 : #include <vector>
44 :
45 : #include <google/protobuf/stubs/hash.h>
46 : #include <google/protobuf/compiler/objectivec/objectivec_helpers.h>
47 : #include <google/protobuf/io/coded_stream.h>
48 : #include <google/protobuf/io/zero_copy_stream_impl.h>
49 : #include <google/protobuf/descriptor.pb.h>
50 : #include <google/protobuf/stubs/common.h>
51 : #include <google/protobuf/stubs/strutil.h>
52 :
53 : // NOTE: src/google/protobuf/compiler/plugin.cc makes use of cerr for some
54 : // error cases, so it seems to be ok to use as a back door for errors.
55 :
56 : namespace google {
57 : namespace protobuf {
58 : namespace compiler {
59 : namespace objectivec {
60 :
61 : namespace {
62 :
63 34 : hash_set<string> MakeWordsMap(const char* const words[], size_t num_words) {
64 : hash_set<string> result;
65 2669 : for (int i = 0; i < num_words; i++) {
66 7905 : result.insert(words[i]);
67 : }
68 34 : return result;
69 : }
70 :
71 : const char* const kUpperSegmentsList[] = {"url", "http", "https"};
72 :
73 17 : hash_set<string> kUpperSegments =
74 : MakeWordsMap(kUpperSegmentsList, GOOGLE_ARRAYSIZE(kUpperSegmentsList));
75 :
76 : // Internal helper for name handing.
77 : // Do not expose this outside of helpers, stick to having functions for specific
78 : // cases (ClassName(), FieldName()), so there is always consistent suffix rules.
79 0 : string UnderscoresToCamelCase(const string& input, bool first_capitalized) {
80 : vector<string> values;
81 : string current;
82 :
83 0 : bool last_char_was_number = false;
84 0 : bool last_char_was_lower = false;
85 0 : bool last_char_was_upper = false;
86 0 : for (int i = 0; i < input.size(); i++) {
87 0 : char c = input[i];
88 0 : if (ascii_isdigit(c)) {
89 0 : if (!last_char_was_number) {
90 0 : values.push_back(current);
91 : current = "";
92 : }
93 0 : current += c;
94 : last_char_was_number = last_char_was_lower = last_char_was_upper = false;
95 : last_char_was_number = true;
96 0 : } else if (ascii_islower(c)) {
97 : // lowercase letter can follow a lowercase or uppercase letter
98 0 : if (!last_char_was_lower && !last_char_was_upper) {
99 0 : values.push_back(current);
100 : current = "";
101 : }
102 0 : current += c; // already lower
103 : last_char_was_number = last_char_was_lower = last_char_was_upper = false;
104 : last_char_was_lower = true;
105 0 : } else if (ascii_isupper(c)) {
106 0 : if (!last_char_was_upper) {
107 0 : values.push_back(current);
108 : current = "";
109 : }
110 0 : current += ascii_tolower(c);
111 : last_char_was_number = last_char_was_lower = last_char_was_upper = false;
112 : last_char_was_upper = true;
113 : } else {
114 : last_char_was_number = last_char_was_lower = last_char_was_upper = false;
115 : }
116 : }
117 0 : values.push_back(current);
118 :
119 0 : for (vector<string>::iterator i = values.begin(); i != values.end(); ++i) {
120 0 : string value = *i;
121 0 : bool all_upper = (kUpperSegments.count(value) > 0);
122 0 : for (int j = 0; j < value.length(); j++) {
123 0 : if (j == 0 || all_upper) {
124 0 : value[j] = ascii_toupper(value[j]);
125 : } else {
126 : // Nothing, already in lower.
127 : }
128 : }
129 0 : *i = value;
130 : }
131 : string result;
132 0 : for (vector<string>::iterator i = values.begin(); i != values.end(); ++i) {
133 0 : result += *i;
134 : }
135 0 : if ((result.length() != 0) && !first_capitalized) {
136 0 : result[0] = ascii_tolower(result[0]);
137 : }
138 0 : return result;
139 : }
140 :
141 : const char* const kReservedWordList[] = {
142 : // Objective C "keywords" that aren't in C
143 : // From
144 : // http://stackoverflow.com/questions/1873630/reserved-keywords-in-objective-c
145 : "id", "_cmd", "super", "in", "out", "inout", "bycopy", "byref", "oneway",
146 : "self",
147 :
148 : // C/C++ keywords (Incl C++ 0x11)
149 : // From http://en.cppreference.com/w/cpp/keywords
150 : "and", "and_eq", "alignas", "alignof", "asm", "auto", "bitand", "bitor",
151 : "bool", "break", "case", "catch", "char", "char16_t", "char32_t", "class",
152 : "compl", "const", "constexpr", "const_cast", "continue", "decltype",
153 : "default", "delete", "double", "dynamic_cast", "else", "enum", "explicit",
154 : "export", "extern ", "false", "float", "for", "friend", "goto", "if",
155 : "inline", "int", "long", "mutable", "namespace", "new", "noexcept", "not",
156 : "not_eq", "nullptr", "operator", "or", "or_eq", "private", "protected",
157 : "public", "register", "reinterpret_cast", "return", "short", "signed",
158 : "sizeof", "static", "static_assert", "static_cast", "struct", "switch",
159 : "template", "this", "thread_local", "throw", "true", "try", "typedef",
160 : "typeid", "typename", "union", "unsigned", "using", "virtual", "void",
161 : "volatile", "wchar_t", "while", "xor", "xor_eq",
162 :
163 : // C99 keywords
164 : // From
165 : // http://publib.boulder.ibm.com/infocenter/lnxpcomp/v8v101/index.jsp?topic=%2Fcom.ibm.xlcpp8l.doc%2Flanguage%2Fref%2Fkeyw.htm
166 : "restrict",
167 :
168 : // Objective-C Runtime typedefs
169 : // From <obc/runtime.h>
170 : "Category", "Ivar", "Method", "Protocol",
171 :
172 : // NSObject Methods
173 : // new is covered by C++ keywords.
174 : "description", "debugDescription", "finalize", "hash", "dealloc", "init",
175 : "class", "superclass", "retain", "release", "autorelease", "retainCount",
176 : "zone", "isProxy", "copy", "mutableCopy", "classForCoder",
177 :
178 : // GPBMessage Methods
179 : // Only need to add instance methods that may conflict with
180 : // method declared in protos. The main cases are methods
181 : // that take no arguments, or setFoo:/hasFoo: type methods.
182 : "clear", "data", "delimitedData", "descriptor", "extensionRegistry",
183 : "extensionsCurrentlySet", "isInitialized", "serializedSize",
184 : "sortedExtensionsInUse", "unknownFields",
185 :
186 : // MacTypes.h names
187 : "Fixed", "Fract", "Size", "LogicalAddress", "PhysicalAddress", "ByteCount",
188 : "ByteOffset", "Duration", "AbsoluteTime", "OptionBits", "ItemCount",
189 : "PBVersion", "ScriptCode", "LangCode", "RegionCode", "OSType",
190 : "ProcessSerialNumber", "Point", "Rect", "FixedPoint", "FixedRect", "Style",
191 : "StyleParameter", "StyleField", "TimeScale", "TimeBase", "TimeRecord",
192 : };
193 :
194 17 : hash_set<string> kReservedWords =
195 : MakeWordsMap(kReservedWordList, GOOGLE_ARRAYSIZE(kReservedWordList));
196 :
197 0 : string SanitizeNameForObjC(const string& input, const string& extension) {
198 0 : if (kReservedWords.count(input) > 0) {
199 0 : return input + extension;
200 : }
201 0 : return input;
202 : }
203 :
204 0 : string NameFromFieldDescriptor(const FieldDescriptor* field) {
205 0 : if (field->type() == FieldDescriptor::TYPE_GROUP) {
206 0 : return field->message_type()->name();
207 : } else {
208 0 : return field->name();
209 : }
210 : }
211 :
212 : // Escape C++ trigraphs by escaping question marks to \?
213 0 : string EscapeTrigraphs(const string& to_escape) {
214 0 : return StringReplace(to_escape, "?", "\\?", true);
215 : }
216 :
217 0 : void PathSplit(const string& path, string* directory, string* basename) {
218 0 : string::size_type last_slash = path.rfind('/');
219 0 : if (last_slash == string::npos) {
220 0 : if (directory) {
221 : *directory = "";
222 : }
223 0 : if (basename) {
224 : *basename = path;
225 : }
226 : } else {
227 0 : if (directory) {
228 0 : *directory = path.substr(0, last_slash);
229 : }
230 0 : if (basename) {
231 0 : *basename = path.substr(last_slash + 1);
232 : }
233 : }
234 0 : }
235 :
236 0 : bool IsSpecialName(const string& name, const string* special_names,
237 : size_t count) {
238 0 : for (size_t i = 0; i < count; ++i) {
239 0 : size_t length = special_names[i].length();
240 0 : if (name.compare(0, length, special_names[i]) == 0) {
241 0 : if (name.length() > length) {
242 : // If name is longer than the retained_name[i] that it matches
243 : // the next character must be not lower case (newton vs newTon vs
244 : // new_ton).
245 0 : return !ascii_islower(name[length]);
246 : } else {
247 : return true;
248 : }
249 : }
250 : }
251 : return false;
252 : }
253 :
254 : } // namespace
255 :
256 0 : string StripProto(const string& filename) {
257 0 : if (HasSuffixString(filename, ".protodevel")) {
258 0 : return StripSuffixString(filename, ".protodevel");
259 : } else {
260 0 : return StripSuffixString(filename, ".proto");
261 : }
262 : }
263 :
264 0 : bool IsRetainedName(const string& name) {
265 : // List of prefixes from
266 : // http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/MemoryMgmt/Articles/mmRules.html
267 : static const string retained_names[] = {"new", "alloc", "copy",
268 0 : "mutableCopy"};
269 : return IsSpecialName(name, retained_names,
270 0 : sizeof(retained_names) / sizeof(retained_names[0]));
271 : }
272 :
273 0 : bool IsInitName(const string& name) {
274 0 : static const string init_names[] = {"init"};
275 : return IsSpecialName(name, init_names,
276 0 : sizeof(init_names) / sizeof(init_names[0]));
277 : }
278 :
279 0 : string BaseFileName(const FileDescriptor* file) {
280 : string basename;
281 0 : PathSplit(file->name(), NULL, &basename);
282 0 : return basename;
283 : }
284 :
285 0 : string FileName(const FileDescriptor* file) {
286 0 : string path = FilePath(file);
287 : string basename;
288 0 : PathSplit(path, NULL, &basename);
289 0 : return basename;
290 : }
291 :
292 0 : string FilePath(const FileDescriptor* file) {
293 : string output;
294 : string basename;
295 : string directory;
296 0 : PathSplit(file->name(), &directory, &basename);
297 0 : if (directory.length() > 0) {
298 0 : output = directory + "/";
299 : }
300 0 : basename = StripProto(basename);
301 :
302 : // CamelCase to be more ObjC friendly.
303 0 : basename = UnderscoresToCamelCase(basename, true);
304 :
305 : output += basename;
306 0 : return output;
307 : }
308 :
309 0 : string FileClassPrefix(const FileDescriptor* file) {
310 : // Default is empty string, no need to check has_objc_class_prefix.
311 0 : string result = file->options().objc_class_prefix();
312 0 : return result;
313 : }
314 :
315 0 : string FileClassName(const FileDescriptor* file) {
316 0 : string name = FileClassPrefix(file);
317 0 : name += UnderscoresToCamelCase(StripProto(BaseFileName(file)), true);
318 : name += "Root";
319 : // There aren't really any reserved words that end in "Root", but playing
320 : // it safe and checking.
321 0 : return SanitizeNameForObjC(name, "_RootClass");
322 : }
323 :
324 0 : string ClassNameWorker(const Descriptor* descriptor) {
325 : string name;
326 0 : if (descriptor->containing_type() != NULL) {
327 0 : name = ClassNameWorker(descriptor->containing_type());
328 : name += "_";
329 : }
330 0 : return name + descriptor->name();
331 : }
332 :
333 0 : string ClassNameWorker(const EnumDescriptor* descriptor) {
334 : string name;
335 0 : if (descriptor->containing_type() != NULL) {
336 0 : name = ClassNameWorker(descriptor->containing_type());
337 : name += "_";
338 : }
339 0 : return name + descriptor->name();
340 : }
341 :
342 0 : string ClassName(const Descriptor* descriptor) {
343 : // 1. Message names are used as is (style calls for CamelCase, trust it).
344 : // 2. Check for reserved word at the very end and then suffix things.
345 0 : string prefix = FileClassPrefix(descriptor->file());
346 0 : string name = ClassNameWorker(descriptor);
347 0 : return SanitizeNameForObjC(prefix + name, "_Class");
348 : }
349 :
350 0 : string EnumName(const EnumDescriptor* descriptor) {
351 : // 1. Enum names are used as is (style calls for CamelCase, trust it).
352 : // 2. Check for reserved word at the every end and then suffix things.
353 : // message Fixed {
354 : // message Size {...}
355 : // enum Mumble {...}
356 : // ...
357 : // }
358 : // yields Fixed_Class, Fixed_Size.
359 0 : string name = FileClassPrefix(descriptor->file());
360 0 : name += ClassNameWorker(descriptor);
361 0 : return SanitizeNameForObjC(name, "_Enum");
362 : }
363 :
364 0 : string EnumValueName(const EnumValueDescriptor* descriptor) {
365 : // Because of the Switch enum compatibility, the name on the enum has to have
366 : // the suffix handing, so it slightly diverges from how nested classes work.
367 : // enum Fixed {
368 : // FOO = 1
369 : // }
370 : // yields Fixed_Enum and Fixed_Enum_Foo (not Fixed_Foo).
371 0 : const string& class_name = EnumName(descriptor->type());
372 0 : const string& value_str = UnderscoresToCamelCase(descriptor->name(), true);
373 0 : const string& name = class_name + "_" + value_str;
374 : // There aren't really any reserved words with an underscore and a leading
375 : // capital letter, but playing it safe and checking.
376 0 : return SanitizeNameForObjC(name, "_Value");
377 : }
378 :
379 0 : string EnumValueShortName(const EnumValueDescriptor* descriptor) {
380 : // Enum value names (EnumValueName above) are the enum name turned into
381 : // a class name and then the value name is CamelCased and concatenated; the
382 : // whole thing then gets sanitized for reserved words.
383 : // The "short name" is intended to be the final leaf, the value name; but
384 : // you can't simply send that off to sanitize as that could result in it
385 : // getting modified when the full name didn't. For example enum
386 : // "StorageModes" has a value "retain". So the full name is
387 : // "StorageModes_Retain", but if we sanitize "retain" it would become
388 : // "RetainValue".
389 : // So the right way to get the short name is to take the full enum name
390 : // and then strip off the enum name (leaving the value name and anything
391 : // done by sanitize).
392 0 : const string& class_name = EnumName(descriptor->type());
393 0 : const string& long_name_prefix = class_name + "_";
394 0 : const string& long_name = EnumValueName(descriptor);
395 0 : return StripPrefixString(long_name, long_name_prefix);
396 : }
397 :
398 0 : string UnCamelCaseEnumShortName(const string& name) {
399 : string result;
400 0 : for (int i = 0; i < name.size(); i++) {
401 0 : char c = name[i];
402 0 : if (i > 0 && ascii_isupper(c)) {
403 : result += '_';
404 : }
405 0 : result += ascii_toupper(c);
406 : }
407 0 : return result;
408 : }
409 :
410 0 : string ExtensionMethodName(const FieldDescriptor* descriptor) {
411 0 : const string& name = NameFromFieldDescriptor(descriptor);
412 0 : const string& result = UnderscoresToCamelCase(name, false);
413 0 : return SanitizeNameForObjC(result, "_Extension");
414 : }
415 :
416 0 : string FieldName(const FieldDescriptor* field) {
417 0 : const string& name = NameFromFieldDescriptor(field);
418 0 : string result = UnderscoresToCamelCase(name, false);
419 0 : if (field->is_repeated() && !field->is_map()) {
420 : // Add "Array" before do check for reserved worlds.
421 : result += "Array";
422 : } else {
423 : // If it wasn't repeated, but ends in "Array", force on the _p suffix.
424 0 : if (HasSuffixString(result, "Array")) {
425 : result += "_p";
426 : }
427 : }
428 0 : return SanitizeNameForObjC(result, "_p");
429 : }
430 :
431 0 : string FieldNameCapitalized(const FieldDescriptor* field) {
432 : // Want the same suffix handling, so upcase the first letter of the other
433 : // name.
434 0 : string result = FieldName(field);
435 0 : if (result.length() > 0) {
436 0 : result[0] = ascii_toupper(result[0]);
437 : }
438 0 : return result;
439 : }
440 :
441 0 : string OneofEnumName(const OneofDescriptor* descriptor) {
442 0 : const Descriptor* fieldDescriptor = descriptor->containing_type();
443 0 : string name = ClassName(fieldDescriptor);
444 0 : name += "_" + UnderscoresToCamelCase(descriptor->name(), true) + "_OneOfCase";
445 : // No sanitize needed because the OS never has names that end in _OneOfCase.
446 0 : return name;
447 : }
448 :
449 0 : string OneofName(const OneofDescriptor* descriptor) {
450 0 : string name = UnderscoresToCamelCase(descriptor->name(), false);
451 : // No sanitize needed because it gets OneOfCase added and that shouldn't
452 : // ever conflict.
453 0 : return name;
454 : }
455 :
456 0 : string OneofNameCapitalized(const OneofDescriptor* descriptor) {
457 : // Use the common handling and then up-case the first letter.
458 0 : string result = OneofName(descriptor);
459 0 : if (result.length() > 0) {
460 0 : result[0] = ascii_toupper(result[0]);
461 : }
462 0 : return result;
463 : }
464 :
465 0 : string UnCamelCaseFieldName(const string& name, const FieldDescriptor* field) {
466 0 : string worker(name);
467 0 : if (HasSuffixString(worker, "_p")) {
468 0 : worker = StripSuffixString(worker, "_p");
469 : }
470 0 : if (field->is_repeated() && HasSuffixString(worker, "Array")) {
471 0 : worker = StripSuffixString(worker, "Array");
472 : }
473 0 : if (field->type() == FieldDescriptor::TYPE_GROUP) {
474 0 : if (worker.length() > 0) {
475 0 : if (ascii_islower(worker[0])) {
476 0 : worker[0] = ascii_toupper(worker[0]);
477 : }
478 : }
479 0 : return worker;
480 : } else {
481 : string result;
482 0 : for (int i = 0; i < worker.size(); i++) {
483 0 : char c = worker[i];
484 0 : if (ascii_isupper(c)) {
485 0 : if (i > 0) {
486 : result += '_';
487 : }
488 0 : result += ascii_tolower(c);
489 : } else {
490 0 : result += c;
491 : }
492 : }
493 0 : return result;
494 : }
495 : }
496 :
497 0 : string GetCapitalizedType(const FieldDescriptor* field) {
498 0 : switch (field->type()) {
499 : case FieldDescriptor::TYPE_INT32:
500 0 : return "Int32";
501 : case FieldDescriptor::TYPE_UINT32:
502 0 : return "UInt32";
503 : case FieldDescriptor::TYPE_SINT32:
504 0 : return "SInt32";
505 : case FieldDescriptor::TYPE_FIXED32:
506 0 : return "Fixed32";
507 : case FieldDescriptor::TYPE_SFIXED32:
508 0 : return "SFixed32";
509 : case FieldDescriptor::TYPE_INT64:
510 0 : return "Int64";
511 : case FieldDescriptor::TYPE_UINT64:
512 0 : return "UInt64";
513 : case FieldDescriptor::TYPE_SINT64:
514 0 : return "SInt64";
515 : case FieldDescriptor::TYPE_FIXED64:
516 0 : return "Fixed64";
517 : case FieldDescriptor::TYPE_SFIXED64:
518 0 : return "SFixed64";
519 : case FieldDescriptor::TYPE_FLOAT:
520 0 : return "Float";
521 : case FieldDescriptor::TYPE_DOUBLE:
522 0 : return "Double";
523 : case FieldDescriptor::TYPE_BOOL:
524 0 : return "Bool";
525 : case FieldDescriptor::TYPE_STRING:
526 0 : return "String";
527 : case FieldDescriptor::TYPE_BYTES:
528 0 : return "Bytes";
529 : case FieldDescriptor::TYPE_ENUM:
530 0 : return "Enum";
531 : case FieldDescriptor::TYPE_GROUP:
532 0 : return "Group";
533 : case FieldDescriptor::TYPE_MESSAGE:
534 0 : return "Message";
535 : }
536 :
537 : // Some compilers report reaching end of function even though all cases of
538 : // the enum are handed in the switch.
539 0 : GOOGLE_LOG(FATAL) << "Can't get here.";
540 0 : return NULL;
541 : }
542 :
543 0 : ObjectiveCType GetObjectiveCType(FieldDescriptor::Type field_type) {
544 0 : switch (field_type) {
545 : case FieldDescriptor::TYPE_INT32:
546 : case FieldDescriptor::TYPE_SINT32:
547 : case FieldDescriptor::TYPE_SFIXED32:
548 : return OBJECTIVECTYPE_INT32;
549 :
550 : case FieldDescriptor::TYPE_UINT32:
551 : case FieldDescriptor::TYPE_FIXED32:
552 0 : return OBJECTIVECTYPE_UINT32;
553 :
554 : case FieldDescriptor::TYPE_INT64:
555 : case FieldDescriptor::TYPE_SINT64:
556 : case FieldDescriptor::TYPE_SFIXED64:
557 0 : return OBJECTIVECTYPE_INT64;
558 :
559 : case FieldDescriptor::TYPE_UINT64:
560 : case FieldDescriptor::TYPE_FIXED64:
561 0 : return OBJECTIVECTYPE_UINT64;
562 :
563 : case FieldDescriptor::TYPE_FLOAT:
564 0 : return OBJECTIVECTYPE_FLOAT;
565 :
566 : case FieldDescriptor::TYPE_DOUBLE:
567 0 : return OBJECTIVECTYPE_DOUBLE;
568 :
569 : case FieldDescriptor::TYPE_BOOL:
570 0 : return OBJECTIVECTYPE_BOOLEAN;
571 :
572 : case FieldDescriptor::TYPE_STRING:
573 0 : return OBJECTIVECTYPE_STRING;
574 :
575 : case FieldDescriptor::TYPE_BYTES:
576 0 : return OBJECTIVECTYPE_DATA;
577 :
578 : case FieldDescriptor::TYPE_ENUM:
579 0 : return OBJECTIVECTYPE_ENUM;
580 :
581 : case FieldDescriptor::TYPE_GROUP:
582 : case FieldDescriptor::TYPE_MESSAGE:
583 0 : return OBJECTIVECTYPE_MESSAGE;
584 : }
585 :
586 : // Some compilers report reaching end of function even though all cases of
587 : // the enum are handed in the switch.
588 0 : GOOGLE_LOG(FATAL) << "Can't get here.";
589 0 : return OBJECTIVECTYPE_INT32;
590 : }
591 :
592 0 : bool IsPrimitiveType(const FieldDescriptor* field) {
593 0 : ObjectiveCType type = GetObjectiveCType(field);
594 0 : switch (type) {
595 : case OBJECTIVECTYPE_INT32:
596 : case OBJECTIVECTYPE_UINT32:
597 : case OBJECTIVECTYPE_INT64:
598 : case OBJECTIVECTYPE_UINT64:
599 : case OBJECTIVECTYPE_FLOAT:
600 : case OBJECTIVECTYPE_DOUBLE:
601 : case OBJECTIVECTYPE_BOOLEAN:
602 : case OBJECTIVECTYPE_ENUM:
603 : return true;
604 : break;
605 : default:
606 0 : return false;
607 : }
608 : }
609 :
610 0 : bool IsReferenceType(const FieldDescriptor* field) {
611 0 : return !IsPrimitiveType(field);
612 : }
613 :
614 0 : static string HandleExtremeFloatingPoint(string val, bool add_float_suffix) {
615 0 : if (val == "nan") {
616 0 : return "NAN";
617 0 : } else if (val == "inf") {
618 0 : return "INFINITY";
619 0 : } else if (val == "-inf") {
620 0 : return "-INFINITY";
621 : } else {
622 : // float strings with ., e or E need to have f appended
623 0 : if (add_float_suffix &&
624 0 : (val.find(".") != string::npos || val.find("e") != string::npos ||
625 0 : val.find("E") != string::npos)) {
626 : val += "f";
627 : }
628 0 : return val;
629 : }
630 : }
631 :
632 0 : string GPBGenericValueFieldName(const FieldDescriptor* field) {
633 : // Returns the field within the GPBGenericValue union to use for the given
634 : // field.
635 0 : if (field->is_repeated()) {
636 0 : return "valueMessage";
637 : }
638 0 : switch (field->cpp_type()) {
639 : case FieldDescriptor::CPPTYPE_INT32:
640 0 : return "valueInt32";
641 : case FieldDescriptor::CPPTYPE_UINT32:
642 0 : return "valueUInt32";
643 : case FieldDescriptor::CPPTYPE_INT64:
644 0 : return "valueInt64";
645 : case FieldDescriptor::CPPTYPE_UINT64:
646 0 : return "valueUInt64";
647 : case FieldDescriptor::CPPTYPE_FLOAT:
648 0 : return "valueFloat";
649 : case FieldDescriptor::CPPTYPE_DOUBLE:
650 0 : return "valueDouble";
651 : case FieldDescriptor::CPPTYPE_BOOL:
652 0 : return "valueBool";
653 : case FieldDescriptor::CPPTYPE_STRING:
654 0 : if (field->type() == FieldDescriptor::TYPE_BYTES) {
655 0 : return "valueData";
656 : } else {
657 0 : return "valueString";
658 : }
659 : case FieldDescriptor::CPPTYPE_ENUM:
660 0 : return "valueEnum";
661 : case FieldDescriptor::CPPTYPE_MESSAGE:
662 0 : return "valueMessage";
663 : }
664 :
665 : // Some compilers report reaching end of function even though all cases of
666 : // the enum are handed in the switch.
667 0 : GOOGLE_LOG(FATAL) << "Can't get here.";
668 0 : return NULL;
669 : }
670 :
671 :
672 0 : string DefaultValue(const FieldDescriptor* field) {
673 : // Repeated fields don't have defaults.
674 0 : if (field->is_repeated()) {
675 0 : return "nil";
676 : }
677 :
678 : // Switch on cpp_type since we need to know which default_value_* method
679 : // of FieldDescriptor to call.
680 0 : switch (field->cpp_type()) {
681 : case FieldDescriptor::CPPTYPE_INT32:
682 : // gcc and llvm reject the decimal form of kint32min and kint64min.
683 0 : if (field->default_value_int32() == INT_MIN) {
684 0 : return "-0x80000000";
685 : }
686 0 : return SimpleItoa(field->default_value_int32());
687 : case FieldDescriptor::CPPTYPE_UINT32:
688 0 : return SimpleItoa(field->default_value_uint32()) + "U";
689 : case FieldDescriptor::CPPTYPE_INT64:
690 : // gcc and llvm reject the decimal form of kint32min and kint64min.
691 0 : if (field->default_value_int64() == LLONG_MIN) {
692 0 : return "-0x8000000000000000LL";
693 : }
694 0 : return SimpleItoa(field->default_value_int64()) + "LL";
695 : case FieldDescriptor::CPPTYPE_UINT64:
696 0 : return SimpleItoa(field->default_value_uint64()) + "ULL";
697 : case FieldDescriptor::CPPTYPE_DOUBLE:
698 : return HandleExtremeFloatingPoint(
699 0 : SimpleDtoa(field->default_value_double()), false);
700 : case FieldDescriptor::CPPTYPE_FLOAT:
701 : return HandleExtremeFloatingPoint(
702 0 : SimpleFtoa(field->default_value_float()), true);
703 : case FieldDescriptor::CPPTYPE_BOOL:
704 0 : return field->default_value_bool() ? "YES" : "NO";
705 : case FieldDescriptor::CPPTYPE_STRING: {
706 0 : const bool has_default_value = field->has_default_value();
707 0 : const string& default_string = field->default_value_string();
708 0 : if (!has_default_value || default_string.length() == 0) {
709 : // If the field is defined as being the empty string,
710 : // then we will just assign to nil, as the empty string is the
711 : // default for both strings and data.
712 0 : return "nil";
713 : }
714 0 : if (field->type() == FieldDescriptor::TYPE_BYTES) {
715 : // We want constant fields in our data structures so we can
716 : // declare them as static. To achieve this we cheat and stuff
717 : // a escaped c string (prefixed with a length) into the data
718 : // field, and cast it to an (NSData*) so it will compile.
719 : // The runtime library knows how to handle it.
720 :
721 : // Must convert to a standard byte order for packing length into
722 : // a cstring.
723 0 : uint32 length = ghtonl(default_string.length());
724 0 : string bytes((const char*)&length, sizeof(length));
725 0 : bytes.append(default_string);
726 0 : return "(NSData*)\"" + CEscape(bytes) + "\"";
727 : } else {
728 0 : return "@\"" + EscapeTrigraphs(CEscape(default_string)) + "\"";
729 : }
730 : }
731 : case FieldDescriptor::CPPTYPE_ENUM:
732 0 : return EnumValueName(field->default_value_enum());
733 : case FieldDescriptor::CPPTYPE_MESSAGE:
734 0 : return "nil";
735 : }
736 :
737 : // Some compilers report reaching end of function even though all cases of
738 : // the enum are handed in the switch.
739 0 : GOOGLE_LOG(FATAL) << "Can't get here.";
740 0 : return NULL;
741 : }
742 :
743 0 : string BuildFlagsString(const vector<string>& strings) {
744 0 : if (strings.size() == 0) {
745 0 : return "0";
746 : }
747 : string string;
748 0 : for (size_t i = 0; i != strings.size(); ++i) {
749 0 : if (i > 0) {
750 0 : string.append(" | ");
751 : }
752 0 : string.append(strings[i]);
753 : }
754 0 : return string;
755 : }
756 :
757 0 : string BuildCommentsString(const SourceLocation& location) {
758 0 : const string& comments = location.leading_comments.empty()
759 : ? location.trailing_comments
760 0 : : location.leading_comments;
761 : vector<string> lines;
762 0 : SplitStringAllowEmpty(comments, "\n", &lines);
763 0 : while (!lines.empty() && lines.back().empty()) {
764 0 : lines.pop_back();
765 : }
766 0 : string prefix("//");
767 0 : string suffix("\n");
768 : string final_comments;
769 0 : for (int i = 0; i < lines.size(); i++) {
770 : // We use $ for delimiters, so replace comments with dollars with
771 : // html escaped version.
772 : // None of the other compilers handle this (as of this writing) but we
773 : // ran into it once, so just to be safe.
774 : final_comments +=
775 0 : prefix + StringReplace(lines[i], "$", "$", true) + suffix;
776 : }
777 0 : return final_comments;
778 : }
779 :
780 : namespace {
781 :
782 : // Internal helper class that parses the expected package to prefix mappings
783 : // file.
784 0 : class Parser {
785 : public:
786 : Parser(map<string, string>* inout_package_to_prefix_map)
787 0 : : prefix_map_(inout_package_to_prefix_map), line_(0) {}
788 :
789 : // Parses a check of input, returning success/failure.
790 : bool ParseChunk(StringPiece chunk);
791 :
792 : // Should be called to finish parsing (after all input has been provided via
793 : // ParseChunk()). Returns success/failure.
794 : bool Finish();
795 :
796 : int last_line() const { return line_; }
797 0 : string error_str() const { return error_str_; }
798 :
799 : private:
800 : bool ParseLoop();
801 :
802 : map<string, string>* prefix_map_;
803 : int line_;
804 : string error_str_;
805 : StringPiece p_;
806 : string leftover_;
807 : };
808 :
809 0 : bool Parser::ParseChunk(StringPiece chunk) {
810 0 : if (!leftover_.empty()) {
811 0 : chunk.AppendToString(&leftover_);
812 0 : p_ = StringPiece(leftover_);
813 : } else {
814 0 : p_ = chunk;
815 : }
816 0 : bool result = ParseLoop();
817 0 : if (p_.empty()) {
818 0 : leftover_.clear();
819 : } else {
820 0 : leftover_ = p_.ToString();
821 : }
822 0 : return result;
823 : }
824 :
825 0 : bool Parser::Finish() {
826 0 : if (leftover_.empty()) {
827 : return true;
828 : }
829 : // Force a newline onto the end to finish parsing.
830 0 : p_ = StringPiece(leftover_ + "\n");
831 0 : if (!ParseLoop()) {
832 : return false;
833 : }
834 0 : return p_.empty(); // Everything used?
835 : }
836 :
837 0 : static bool ascii_isnewline(char c) { return c == '\n' || c == '\r'; }
838 :
839 0 : bool ReadLine(StringPiece* input, StringPiece* line) {
840 0 : for (int len = 0; len < input->size(); ++len) {
841 0 : if (ascii_isnewline((*input)[len])) {
842 0 : *line = StringPiece(input->data(), len);
843 0 : ++len; // advance over the newline
844 0 : *input = StringPiece(input->data() + len, input->size() - len);
845 0 : return true;
846 : }
847 : }
848 : return false; // Ran out of input with no newline.
849 : }
850 :
851 0 : void TrimWhitespace(StringPiece* input) {
852 0 : while (!input->empty() && ascii_isspace(*input->data())) {
853 : input->remove_prefix(1);
854 : }
855 0 : while (!input->empty() && ascii_isspace((*input)[input->length() - 1])) {
856 0 : input->remove_suffix(1);
857 : }
858 0 : }
859 :
860 0 : void RemoveComment(StringPiece* input) {
861 0 : int offset = input->find('#');
862 0 : if (offset != StringPiece::npos) {
863 0 : input->remove_suffix(input->length() - offset);
864 : }
865 0 : }
866 :
867 0 : bool Parser::ParseLoop() {
868 : StringPiece line;
869 0 : while (ReadLine(&p_, &line)) {
870 0 : ++line_;
871 0 : RemoveComment(&line);
872 0 : TrimWhitespace(&line);
873 0 : if (line.size() == 0) {
874 0 : continue; // Blank line.
875 : }
876 0 : int offset = line.find('=');
877 0 : if (offset == StringPiece::npos) {
878 0 : error_str_ =
879 0 : string("Line without equal sign: '") + line.ToString() + "'.";
880 0 : return false;
881 : }
882 0 : StringPiece package(line, 0, offset);
883 0 : StringPiece prefix(line, offset + 1, line.length() - offset - 1);
884 0 : TrimWhitespace(&package);
885 0 : TrimWhitespace(&prefix);
886 : // Don't really worry about error checking the the package/prefix for
887 : // being valid. Assume the file is validated when it is created/edited.
888 0 : (*prefix_map_)[package.ToString()] = prefix.ToString();
889 : }
890 : return true;
891 : }
892 :
893 0 : bool LoadExpectedPackagePrefixes(map<string, string>* prefix_map,
894 : string* out_expect_file_path,
895 : string* out_error) {
896 0 : const char* file_path = getenv("GPB_OBJC_EXPECTED_PACKAGE_PREFIXES");
897 0 : if (file_path == NULL) {
898 : return true;
899 : }
900 :
901 : int fd;
902 0 : do {
903 0 : fd = open(file_path, O_RDONLY);
904 0 : } while (fd < 0 && errno == EINTR);
905 0 : if (fd < 0) {
906 : *out_error =
907 0 : string(file_path) + ":0:0: error: Unable to open." + strerror(errno);
908 0 : return false;
909 : }
910 0 : io::FileInputStream file_stream(fd);
911 : file_stream.SetCloseOnDelete(true);
912 : *out_expect_file_path = file_path;
913 :
914 0 : Parser parser(prefix_map);
915 : const void* buf;
916 : int buf_len;
917 0 : while (file_stream.Next(&buf, &buf_len)) {
918 0 : if (buf_len == 0) {
919 : continue;
920 : }
921 :
922 0 : if (!parser.ParseChunk(StringPiece(static_cast<const char*>(buf), buf_len))) {
923 0 : *out_error = string(file_path) + ":" + SimpleItoa(parser.last_line()) +
924 0 : ":0: error: " + parser.error_str();
925 0 : return false;
926 : }
927 : }
928 0 : return parser.Finish();
929 : }
930 :
931 : } // namespace
932 :
933 0 : bool ValidateObjCClassPrefix(const FileDescriptor* file, string* out_error) {
934 0 : const string prefix = file->options().objc_class_prefix();
935 0 : const string package = file->package();
936 :
937 : // NOTE: src/google/protobuf/compiler/plugin.cc makes use of cerr for some
938 : // error cases, so it seems to be ok to use as a back door for warnings.
939 :
940 : // First Check: Warning - if there is a prefix, ensure it is is a reasonable
941 : // value according to Apple's rules.
942 0 : if (prefix.length()) {
943 0 : if (!ascii_isupper(prefix[0])) {
944 : cerr << endl
945 0 : << "protoc:0: warning: Invalid 'option objc_class_prefix = \""
946 0 : << prefix << "\";' in '" << file->name() << "';"
947 0 : << " it should start with a capital letter." << endl;
948 0 : cerr.flush();
949 : }
950 0 : if (prefix.length() < 3) {
951 : cerr << endl
952 0 : << "protoc:0: warning: Invalid 'option objc_class_prefix = \""
953 0 : << prefix << "\";' in '" << file->name() << "';"
954 0 : << " Apple recommends they should be at least 3 characters long."
955 : << endl;
956 0 : cerr.flush();
957 : }
958 : }
959 :
960 : // Load any expected package prefixes to validate against those.
961 : map<string, string> expected_package_prefixes;
962 : string expect_file_path;
963 0 : if (!LoadExpectedPackagePrefixes(&expected_package_prefixes,
964 0 : &expect_file_path, out_error)) {
965 : return false;
966 : }
967 :
968 : // If there are no expected prefixes, out of here.
969 0 : if (expected_package_prefixes.size() == 0) {
970 : return true;
971 : }
972 :
973 : // Second Check: Error - See if there was an expected prefix for the package
974 : // and report if it doesn't match.
975 : map<string, string>::iterator package_match =
976 0 : expected_package_prefixes.find(package);
977 0 : if (package_match != expected_package_prefixes.end()) {
978 : // There was an entry, and...
979 0 : if (package_match->second == prefix) {
980 : // ...it matches. All good, out of here!
981 : return true;
982 : } else {
983 : // ...it didn't match!
984 0 : *out_error = "protoc:0: error: Expected 'option objc_class_prefix = \"" +
985 0 : package_match->second + "\";' in '" + file->name() + "'";
986 0 : if (prefix.length()) {
987 0 : *out_error += "; but found '" + prefix + "' instead";
988 : }
989 : *out_error += ".";
990 : return false;
991 : }
992 : }
993 :
994 : // Third Check: Error - If there was a prefix make sure it wasn't expected
995 : // for a different package instead (overlap is allowed, but it has to be
996 : // listed as an expected overlap).
997 0 : if (prefix.length()) {
998 0 : for (map<string, string>::iterator i = expected_package_prefixes.begin();
999 0 : i != expected_package_prefixes.end(); ++i) {
1000 0 : if (i->second == prefix) {
1001 : *out_error =
1002 0 : "protoc:0: error: Found 'option objc_class_prefix = \"" + prefix +
1003 0 : "\";' in '" + file->name() +
1004 0 : "'; that prefix is already used for 'package " + i->first +
1005 0 : ";'. It can only be reused by listing it in the expected file (" +
1006 0 : expect_file_path + ").";
1007 : return false; // Only report first usage of the prefix.
1008 : }
1009 : }
1010 : }
1011 :
1012 : // Fourth Check: Warning - If there was a prefix, and it wasn't expected,
1013 : // issue a warning suggesting it gets added to the file.
1014 0 : if (prefix.length()) {
1015 : cerr << endl
1016 0 : << "protoc:0: warning: Found 'option objc_class_prefix = \"" << prefix
1017 0 : << "\";' in '" << file->name() << "';"
1018 0 : << " should you add it to the expected prefixes file ("
1019 0 : << expect_file_path << ")?" << endl;
1020 0 : cerr.flush();
1021 : }
1022 :
1023 : return true;
1024 : }
1025 :
1026 0 : void TextFormatDecodeData::AddString(int32 key,
1027 : const string& input_for_decode,
1028 : const string& desired_output) {
1029 0 : for (vector<DataEntry>::const_iterator i = entries_.begin();
1030 0 : i != entries_.end(); ++i) {
1031 0 : if (i->first == key) {
1032 0 : cerr << "error: duplicate key (" << key
1033 0 : << ") making TextFormat data, input: \"" << input_for_decode
1034 0 : << "\", desired: \"" << desired_output << "\"." << endl;
1035 0 : cerr.flush();
1036 0 : abort();
1037 : }
1038 : }
1039 :
1040 : const string& data = TextFormatDecodeData::DecodeDataForString(
1041 0 : input_for_decode, desired_output);
1042 0 : entries_.push_back(DataEntry(key, data));
1043 0 : }
1044 :
1045 0 : string TextFormatDecodeData::Data() const {
1046 0 : ostringstream data_stringstream;
1047 :
1048 0 : if (num_entries() > 0) {
1049 0 : io::OstreamOutputStream data_outputstream(&data_stringstream);
1050 0 : io::CodedOutputStream output_stream(&data_outputstream);
1051 :
1052 0 : output_stream.WriteVarint32(num_entries());
1053 0 : for (vector<DataEntry>::const_iterator i = entries_.begin();
1054 0 : i != entries_.end(); ++i) {
1055 0 : output_stream.WriteVarint32(i->first);
1056 0 : output_stream.WriteString(i->second);
1057 0 : }
1058 : }
1059 :
1060 0 : data_stringstream.flush();
1061 0 : return data_stringstream.str();
1062 : }
1063 :
1064 : namespace {
1065 :
1066 : // Helper to build up the decode data for a string.
1067 0 : class DecodeDataBuilder {
1068 : public:
1069 0 : DecodeDataBuilder() { Reset(); }
1070 :
1071 : bool AddCharacter(const char desired, const char input);
1072 : void AddUnderscore() {
1073 0 : Push();
1074 0 : need_underscore_ = true;
1075 : }
1076 : string Finish() {
1077 0 : Push();
1078 0 : return decode_data_;
1079 : }
1080 :
1081 : private:
1082 : static const uint8 kAddUnderscore = 0x80;
1083 :
1084 : static const uint8 kOpAsIs = 0x00;
1085 : static const uint8 kOpFirstUpper = 0x40;
1086 : static const uint8 kOpFirstLower = 0x20;
1087 : static const uint8 kOpAllUpper = 0x60;
1088 :
1089 : static const int kMaxSegmentLen = 0x1f;
1090 :
1091 : void AddChar(const char desired) {
1092 0 : ++segment_len_;
1093 0 : is_all_upper_ &= ascii_isupper(desired);
1094 : }
1095 :
1096 0 : void Push() {
1097 0 : uint8 op = (op_ | segment_len_);
1098 0 : if (need_underscore_) op |= kAddUnderscore;
1099 0 : if (op != 0) {
1100 0 : decode_data_ += (char)op;
1101 : }
1102 : Reset();
1103 0 : }
1104 :
1105 0 : bool AddFirst(const char desired, const char input) {
1106 0 : if (desired == input) {
1107 0 : op_ = kOpAsIs;
1108 0 : } else if (desired == ascii_toupper(input)) {
1109 0 : op_ = kOpFirstUpper;
1110 0 : } else if (desired == ascii_tolower(input)) {
1111 0 : op_ = kOpFirstLower;
1112 : } else {
1113 : // Can't be transformed to match.
1114 : return false;
1115 : }
1116 0 : AddChar(desired);
1117 0 : return true;
1118 : }
1119 :
1120 : void Reset() {
1121 0 : need_underscore_ = false;
1122 0 : op_ = 0;
1123 0 : segment_len_ = 0;
1124 0 : is_all_upper_ = true;
1125 : }
1126 :
1127 : bool need_underscore_;
1128 : bool is_all_upper_;
1129 : uint8 op_;
1130 : int segment_len_;
1131 :
1132 : string decode_data_;
1133 : };
1134 :
1135 0 : bool DecodeDataBuilder::AddCharacter(const char desired, const char input) {
1136 : // If we've hit the max size, push to start a new segment.
1137 0 : if (segment_len_ == kMaxSegmentLen) {
1138 0 : Push();
1139 : }
1140 0 : if (segment_len_ == 0) {
1141 0 : return AddFirst(desired, input);
1142 : }
1143 :
1144 : // Desired and input match...
1145 0 : if (desired == input) {
1146 : // If we aren't transforming it, or we're upper casing it and it is
1147 : // supposed to be uppercase; just add it to the segment.
1148 0 : if ((op_ != kOpAllUpper) || ascii_isupper(desired)) {
1149 0 : AddChar(desired);
1150 0 : return true;
1151 : }
1152 :
1153 : // Add the current segment, and start the next one.
1154 0 : Push();
1155 0 : return AddFirst(desired, input);
1156 : }
1157 :
1158 : // If we need to uppercase, and everything so far has been uppercase,
1159 : // promote op to AllUpper.
1160 0 : if ((desired == ascii_toupper(input)) && is_all_upper_) {
1161 0 : op_ = kOpAllUpper;
1162 0 : AddChar(desired);
1163 0 : return true;
1164 : }
1165 :
1166 : // Give up, push and start a new segment.
1167 0 : Push();
1168 0 : return AddFirst(desired, input);
1169 : }
1170 :
1171 : // If decode data can't be generated, a directive for the raw string
1172 : // is used instead.
1173 0 : string DirectDecodeString(const string& str) {
1174 : string result;
1175 : result += (char)'\0'; // Marker for full string.
1176 : result += str;
1177 : result += (char)'\0'; // End of string.
1178 0 : return result;
1179 : }
1180 :
1181 : } // namespace
1182 :
1183 : // static
1184 0 : string TextFormatDecodeData::DecodeDataForString(const string& input_for_decode,
1185 : const string& desired_output) {
1186 0 : if ((input_for_decode.size() == 0) || (desired_output.size() == 0)) {
1187 0 : cerr << "error: got empty string for making TextFormat data, input: \""
1188 0 : << input_for_decode << "\", desired: \"" << desired_output << "\"."
1189 : << endl;
1190 0 : cerr.flush();
1191 0 : abort();
1192 : }
1193 0 : if ((input_for_decode.find('\0') != string::npos) ||
1194 0 : (desired_output.find('\0') != string::npos)) {
1195 0 : cerr << "error: got a null char in a string for making TextFormat data,"
1196 0 : << " input: \"" << CEscape(input_for_decode) << "\", desired: \""
1197 0 : << CEscape(desired_output) << "\"." << endl;
1198 0 : cerr.flush();
1199 0 : abort();
1200 : }
1201 :
1202 : DecodeDataBuilder builder;
1203 :
1204 : // Walk the output building it from the input.
1205 0 : int x = 0;
1206 0 : for (int y = 0; y < desired_output.size(); y++) {
1207 0 : const char d = desired_output[y];
1208 0 : if (d == '_') {
1209 : builder.AddUnderscore();
1210 : continue;
1211 : }
1212 :
1213 0 : if (x >= input_for_decode.size()) {
1214 : // Out of input, no way to encode it, just return a full decode.
1215 0 : return DirectDecodeString(desired_output);
1216 : }
1217 0 : if (builder.AddCharacter(d, input_for_decode[x])) {
1218 0 : ++x; // Consumed one input
1219 : } else {
1220 : // Couldn't transform for the next character, just return a full decode.
1221 0 : return DirectDecodeString(desired_output);
1222 : }
1223 : }
1224 :
1225 0 : if (x != input_for_decode.size()) {
1226 : // Extra input (suffix from name sanitizing?), just return a full decode.
1227 0 : return DirectDecodeString(desired_output);
1228 : }
1229 :
1230 : // Add the end marker.
1231 0 : return builder.Finish() + (char)'\0';
1232 : }
1233 :
1234 : } // namespace objectivec
1235 : } // namespace compiler
1236 : } // namespace protobuf
1237 51 : } // namespace google
|