1 // Protocol Buffers - Google's data interchange format
2 // Copyright 2008 Google Inc.  All rights reserved.
3 // http://code.google.com/p/protobuf/
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 // Author: kenton@google.com (Kenton Varda)
32 //  Based on original Protocol Buffers design by
33 //  Sanjay Ghemawat, Jeff Dean, and others.
34 
35 // Copyright (c) 2008-2013, Dave Benson.  All rights reserved.
36 //
37 // Redistribution and use in source and binary forms, with or without
38 // modification, are permitted provided that the following conditions are
39 // met:
40 //
41 //     * Redistributions of source code must retain the above copyright
42 // notice, this list of conditions and the following disclaimer.
43 //
44 //     * Redistributions in binary form must reproduce the above
45 // copyright notice, this list of conditions and the following disclaimer
46 // in the documentation and/or other materials provided with the
47 // distribution.
48 //
49 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
50 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
51 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
52 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
53 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
54 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
55 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
56 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
57 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
58 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
59 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
60 
61 // Modified to implement C code by Dave Benson.
62 
63 #include <set>
64 #include <map>
65 
66 #include <protoc-c/c_enum.h>
67 #include <protoc-c/c_helpers.h>
68 #include <google/protobuf/io/printer.h>
69 
70 namespace google {
71 namespace protobuf {
72 namespace compiler {
73 namespace c {
74 
EnumGenerator(const EnumDescriptor * descriptor,const std::string & dllexport_decl)75 EnumGenerator::EnumGenerator(const EnumDescriptor* descriptor,
76                              const std::string& dllexport_decl)
77   : descriptor_(descriptor),
78     dllexport_decl_(dllexport_decl) {
79 }
80 
~EnumGenerator()81 EnumGenerator::~EnumGenerator() {}
82 
GenerateDefinition(io::Printer * printer)83 void EnumGenerator::GenerateDefinition(io::Printer* printer) {
84   std::map<std::string, std::string> vars;
85   vars["classname"] = FullNameToC(descriptor_->full_name(), descriptor_->file());
86   vars["shortname"] = descriptor_->name();
87   vars["uc_name"] = FullNameToUpper(descriptor_->full_name(), descriptor_->file());
88 
89   SourceLocation sourceLoc;
90   descriptor_->GetSourceLocation(&sourceLoc);
91   PrintComment (printer, sourceLoc.leading_comments);
92 
93   printer->Print(vars, "typedef enum _$classname$ {\n");
94   printer->Indent();
95 
96   const EnumValueDescriptor* min_value = descriptor_->value(0);
97   const EnumValueDescriptor* max_value = descriptor_->value(0);
98 
99 
100   vars["opt_comma"] = ",";
101   vars["prefix"] = FullNameToUpper(descriptor_->full_name(), descriptor_->file()) + "__";
102   for (int i = 0; i < descriptor_->value_count(); i++) {
103     vars["name"] = descriptor_->value(i)->name();
104     vars["number"] = SimpleItoa(descriptor_->value(i)->number());
105     if (i + 1 == descriptor_->value_count())
106       vars["opt_comma"] = "";
107 
108     SourceLocation valSourceLoc;
109     descriptor_->value(i)->GetSourceLocation(&valSourceLoc);
110 
111     PrintComment (printer, valSourceLoc.leading_comments);
112     PrintComment (printer, valSourceLoc.trailing_comments);
113     printer->Print(vars, "$prefix$$name$ = $number$$opt_comma$\n");
114 
115     if (descriptor_->value(i)->number() < min_value->number()) {
116       min_value = descriptor_->value(i);
117     }
118     if (descriptor_->value(i)->number() > max_value->number()) {
119       max_value = descriptor_->value(i);
120     }
121   }
122 
123   printer->Print(vars, "  PROTOBUF_C__FORCE_ENUM_TO_BE_INT_SIZE($uc_name$)\n");
124   printer->Outdent();
125   printer->Print(vars, "} $classname$;\n");
126 }
127 
GenerateDescriptorDeclarations(io::Printer * printer)128 void EnumGenerator::GenerateDescriptorDeclarations(io::Printer* printer) {
129   std::map<std::string, std::string> vars;
130   if (dllexport_decl_.empty()) {
131     vars["dllexport"] = "";
132   } else {
133     vars["dllexport"] = dllexport_decl_ + " ";
134   }
135   vars["classname"] = FullNameToC(descriptor_->full_name(), descriptor_->file());
136   vars["lcclassname"] = FullNameToLower(descriptor_->full_name(), descriptor_->file());
137 
138   printer->Print(vars,
139     "extern $dllexport$const ProtobufCEnumDescriptor    $lcclassname$__descriptor;\n");
140 }
141 
142 struct ValueIndex
143 {
144   int value;
145   unsigned index;
146   unsigned final_index;		/* index in uniqified array of values */
147   const char *name;
148 };
GenerateValueInitializer(io::Printer * printer,int index)149 void EnumGenerator::GenerateValueInitializer(io::Printer *printer, int index)
150 {
151   const EnumValueDescriptor *vd = descriptor_->value(index);
152   std::map<std::string, std::string> vars;
153   bool optimize_code_size = descriptor_->file()->options().has_optimize_for() &&
154     descriptor_->file()->options().optimize_for() ==
155     FileOptions_OptimizeMode_CODE_SIZE;
156   vars["enum_value_name"] = vd->name();
157   vars["c_enum_value_name"] = FullNameToUpper(descriptor_->full_name(), descriptor_->file()) + "__" + vd->name();
158   vars["value"] = SimpleItoa(vd->number());
159   if (optimize_code_size)
160     printer->Print(vars, "  { NULL, NULL, $value$ }, /* CODE_SIZE */\n");
161   else
162     printer->Print(vars,
163         "  { \"$enum_value_name$\", \"$c_enum_value_name$\", $value$ },\n");
164 }
165 
compare_value_indices_by_value_then_index(const void * a,const void * b)166 static int compare_value_indices_by_value_then_index(const void *a, const void *b)
167 {
168   const ValueIndex *vi_a = (const ValueIndex *) a;
169   const ValueIndex *vi_b = (const ValueIndex *) b;
170   if (vi_a->value < vi_b->value) return -1;
171   if (vi_a->value > vi_b->value) return +1;
172   if (vi_a->index < vi_b->index) return -1;
173   if (vi_a->index > vi_b->index) return +1;
174   return 0;
175 }
176 
compare_value_indices_by_name(const void * a,const void * b)177 static int compare_value_indices_by_name(const void *a, const void *b)
178 {
179   const ValueIndex *vi_a = (const ValueIndex *) a;
180   const ValueIndex *vi_b = (const ValueIndex *) b;
181   return strcmp (vi_a->name, vi_b->name);
182 }
183 
GenerateEnumDescriptor(io::Printer * printer)184 void EnumGenerator::GenerateEnumDescriptor(io::Printer* printer) {
185   std::map<std::string, std::string> vars;
186   vars["fullname"] = descriptor_->full_name();
187   vars["lcclassname"] = FullNameToLower(descriptor_->full_name(), descriptor_->file());
188   vars["cname"] = FullNameToC(descriptor_->full_name(), descriptor_->file());
189   vars["shortname"] = descriptor_->name();
190   vars["packagename"] = descriptor_->file()->package();
191   vars["value_count"] = SimpleItoa(descriptor_->value_count());
192 
193   bool optimize_code_size = descriptor_->file()->options().has_optimize_for() &&
194     descriptor_->file()->options().optimize_for() ==
195     FileOptions_OptimizeMode_CODE_SIZE;
196 
197   // Sort by name and value, dropping duplicate values if they appear later.
198   // TODO: use a c++ paradigm for this!
199   NameIndex *name_index = new NameIndex[descriptor_->value_count()];
200   ValueIndex *value_index = new ValueIndex[descriptor_->value_count()];
201   for (int j = 0; j < descriptor_->value_count(); j++) {
202     const EnumValueDescriptor *vd = descriptor_->value(j);
203     name_index[j].index = j;
204     name_index[j].name = vd->name().c_str();
205     value_index[j].index = j;
206     value_index[j].value = vd->number();
207     value_index[j].name = vd->name().c_str();
208   }
209   qsort(value_index, descriptor_->value_count(),
210 	sizeof(ValueIndex), compare_value_indices_by_value_then_index);
211 
212   // only record unique values
213   int n_unique_values;
214   if (descriptor_->value_count() == 0) {
215     n_unique_values = 0; // should never happen
216   } else {
217     n_unique_values = 1;
218     value_index[0].final_index = 0;
219     for (int j = 1; j < descriptor_->value_count(); j++) {
220       if (value_index[j-1].value != value_index[j].value)
221 	value_index[j].final_index = n_unique_values++;
222       else
223 	value_index[j].final_index = n_unique_values - 1;
224     }
225   }
226 
227   vars["unique_value_count"] = SimpleItoa(n_unique_values);
228   printer->Print(vars,
229       "static const ProtobufCEnumValue $lcclassname$__enum_values_by_number[$unique_value_count$] =\n"
230       "{\n");
231   if (descriptor_->value_count() > 0) {
232     GenerateValueInitializer(printer, value_index[0].index);
233     for (int j = 1; j < descriptor_->value_count(); j++) {
234       if (value_index[j-1].value != value_index[j].value) {
235         GenerateValueInitializer(printer, value_index[j].index);
236       }
237     }
238   }
239   printer->Print(vars, "};\n");
240   printer->Print(vars, "static const ProtobufCIntRange $lcclassname$__value_ranges[] = {\n");
241   unsigned n_ranges = 0;
242   if (descriptor_->value_count() > 0) {
243     unsigned range_start = 0;
244     unsigned range_len = 1;
245     int range_start_value = value_index[0].value;
246     int last_value = range_start_value;
247     for (int j = 1; j < descriptor_->value_count(); j++) {
248       if (value_index[j-1].value != value_index[j].value) {
249         if (last_value + 1 == value_index[j].value) {
250           range_len++;
251         } else {
252           // output range
253           vars["range_start_value"] = SimpleItoa(range_start_value);
254           vars["orig_index"] = SimpleItoa(range_start);
255           printer->Print (vars, "{$range_start_value$, $orig_index$},");
256           range_start_value = value_index[j].value;
257           range_start += range_len;
258           range_len = 1;
259           n_ranges++;
260         }
261         last_value = value_index[j].value;
262       }
263     }
264     {
265       vars["range_start_value"] = SimpleItoa(range_start_value);
266       vars["orig_index"] = SimpleItoa(range_start);
267       printer->Print (vars, "{$range_start_value$, $orig_index$},");
268       range_start += range_len;
269       n_ranges++;
270     }
271     {
272       vars["range_start_value"] = SimpleItoa(0);
273       vars["orig_index"] = SimpleItoa(range_start);
274       printer->Print (vars, "{$range_start_value$, $orig_index$}\n};\n");
275     }
276   }
277   vars["n_ranges"] = SimpleItoa(n_ranges);
278 
279   if (!optimize_code_size) {
280     qsort(value_index, descriptor_->value_count(),
281         sizeof(ValueIndex), compare_value_indices_by_name);
282     printer->Print(vars,
283         "static const ProtobufCEnumValueIndex $lcclassname$__enum_values_by_name[$value_count$] =\n"
284         "{\n");
285     for (int j = 0; j < descriptor_->value_count(); j++) {
286       vars["index"] = SimpleItoa(value_index[j].final_index);
287       vars["name"] = value_index[j].name;
288       printer->Print (vars, "  { \"$name$\", $index$ },\n");
289     }
290     printer->Print(vars, "};\n");
291   }
292 
293   if (optimize_code_size) {
294     printer->Print(vars,
295         "const ProtobufCEnumDescriptor $lcclassname$__descriptor =\n"
296         "{\n"
297         "  PROTOBUF_C__ENUM_DESCRIPTOR_MAGIC,\n"
298         "  NULL,NULL,NULL,NULL, /* CODE_SIZE */\n"
299         "  $unique_value_count$,\n"
300         "  $lcclassname$__enum_values_by_number,\n"
301         "  0, NULL, /* CODE_SIZE */\n"
302         "  $n_ranges$,\n"
303         "  $lcclassname$__value_ranges,\n"
304         "  NULL,NULL,NULL,NULL   /* reserved[1234] */\n"
305         "};\n");
306   } else {
307     printer->Print(vars,
308         "const ProtobufCEnumDescriptor $lcclassname$__descriptor =\n"
309         "{\n"
310         "  PROTOBUF_C__ENUM_DESCRIPTOR_MAGIC,\n"
311         "  \"$fullname$\",\n"
312         "  \"$shortname$\",\n"
313         "  \"$cname$\",\n"
314         "  \"$packagename$\",\n"
315         "  $unique_value_count$,\n"
316         "  $lcclassname$__enum_values_by_number,\n"
317         "  $value_count$,\n"
318         "  $lcclassname$__enum_values_by_name,\n"
319         "  $n_ranges$,\n"
320         "  $lcclassname$__value_ranges,\n"
321         "  NULL,NULL,NULL,NULL   /* reserved[1234] */\n"
322         "};\n");
323   }
324 
325   delete[] value_index;
326   delete[] name_index;
327 }
328 
329 
330 
331 }  // namespace c
332 }  // namespace compiler
333 }  // namespace protobuf
334 }  // namespace google
335