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