protobuf-c/protoc-c/c_enum.cc

308 lines
11 KiB
C++
Raw Normal View History

// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// http://code.google.com/p/protobuf/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Author: kenton@google.com (Kenton Varda)
// Based on original Protocol Buffers design by
// Sanjay Ghemawat, Jeff Dean, and others.
// Copyright (c) 2008-2013, Dave Benson. All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
//
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Modified to implement C code by Dave Benson.
#include <set>
#include <map>
2013-11-16 17:11:48 -05:00
#include <protoc-c/c_enum.h>
#include <protoc-c/c_helpers.h>
#include <google/protobuf/io/printer.h>
namespace google {
namespace protobuf {
namespace compiler {
namespace c {
EnumGenerator::EnumGenerator(const EnumDescriptor* descriptor,
const string& dllexport_decl)
: descriptor_(descriptor),
dllexport_decl_(dllexport_decl) {
}
EnumGenerator::~EnumGenerator() {}
void EnumGenerator::GenerateDefinition(io::Printer* printer) {
map<string, string> vars;
vars["classname"] = FullNameToC(descriptor_->full_name());
vars["shortname"] = descriptor_->name();
vars["uc_name"] = FullNameToUpper(descriptor_->full_name());
SourceLocation sourceLoc;
descriptor_->GetSourceLocation(&sourceLoc);
PrintComment (printer, sourceLoc.leading_comments);
printer->Print(vars, "typedef enum _$classname$ {\n");
printer->Indent();
const EnumValueDescriptor* min_value = descriptor_->value(0);
const EnumValueDescriptor* max_value = descriptor_->value(0);
vars["opt_comma"] = ",";
vars["prefix"] = FullNameToUpper(descriptor_->full_name()) + "__";
for (int i = 0; i < descriptor_->value_count(); i++) {
vars["name"] = descriptor_->value(i)->name();
vars["number"] = SimpleItoa(descriptor_->value(i)->number());
if (i + 1 == descriptor_->value_count())
vars["opt_comma"] = "";
SourceLocation valSourceLoc;
descriptor_->value(i)->GetSourceLocation(&valSourceLoc);
PrintComment (printer, valSourceLoc.leading_comments);
PrintComment (printer, valSourceLoc.trailing_comments);
printer->Print(vars, "$prefix$$name$ = $number$$opt_comma$\n");
if (descriptor_->value(i)->number() < min_value->number()) {
min_value = descriptor_->value(i);
}
if (descriptor_->value(i)->number() > max_value->number()) {
max_value = descriptor_->value(i);
}
}
printer->Print(vars, " PROTOBUF_C__FORCE_ENUM_TO_BE_INT_SIZE($uc_name$)\n");
printer->Outdent();
printer->Print(vars, "} $classname$;\n");
}
void EnumGenerator::GenerateDescriptorDeclarations(io::Printer* printer) {
map<string, string> vars;
if (dllexport_decl_.empty()) {
vars["dllexport"] = "";
} else {
vars["dllexport"] = dllexport_decl_ + " ";
}
vars["classname"] = FullNameToC(descriptor_->full_name());
vars["lcclassname"] = FullNameToLower(descriptor_->full_name());
printer->Print(vars,
"extern $dllexport$const ProtobufCEnumDescriptor $lcclassname$__descriptor;\n");
}
struct ValueIndex
{
int value;
unsigned index;
unsigned final_index; /* index in uniqified array of values */
const char *name;
};
void EnumGenerator::GenerateValueInitializer(io::Printer *printer, int index)
{
const EnumValueDescriptor *vd = descriptor_->value(index);
map<string, string> vars;
vars["enum_value_name"] = vd->name();
protoc-c: preserve case in C enum value names (Issue #129) there is some confusion with regard to the use of lower case letters in enum values. take the following message definition: message LowerCase { enum CaseEnum { UPPER = 1; lower = 2; } optional CaseEnum value = 1 [default = lower]; } this generates the following C enum: typedef enum _LowerCase__CaseEnum { LOWER_CASE__CASE_ENUM__UPPER = 1, LOWER_CASE__CASE_ENUM__lower = 2 _PROTOBUF_C_FORCE_ENUM_TO_BE_INT_SIZE(LOWER_CASE__CASE_ENUM) } LowerCase__CaseEnum; note that the case of the enum value 'lower' was preserved in the C symbol name as 'LOWER_CASE__CASE_ENUM__lower', but that the _INIT macro references the same enum value with the (non-existent) C symbol name 'LOWER_CASE__CASE_ENUM__LOWER': #define LOWER_CASE__INIT \ { PROTOBUF_C_MESSAGE_INIT (&lower_case__descriptor) \ , 0,LOWER_CASE__CASE_ENUM__LOWER } additionally, the ProtobufCEnumValue array generated also refers to the same enum value with the (non-existent) upper cased version: const ProtobufCEnumValue lower_case__case_enum__enum_values_by_number[2] = { { "UPPER", "LOWER_CASE__CASE_ENUM__UPPER", 1 }, { "lower", "LOWER_CASE__CASE_ENUM__LOWER", 2 }, }; we should preserve the existing behavior of copying the case from the enum values in the message definition and fix up the places where the (non-existent) upper case version is used, rather than changing the enum definition itself to match the case used in the _INIT macro and enum_values_by_number array, because it's possible that there might be existing working code that uses enum values with lower case letters that would be affected by such a change. incidentally, google's C++ protobuf implementation preserves case in enum values. protoc --cpp_out generates the following enum declaration for the message descriptor above: enum LowerCase_CaseEnum { LowerCase_CaseEnum_UPPER = 1, LowerCase_CaseEnum_lower = 2 };
2014-03-24 15:43:53 -04:00
vars["c_enum_value_name"] = FullNameToUpper(descriptor_->full_name()) + "__" + vd->name();
vars["value"] = SimpleItoa(vd->number());
printer->Print(vars,
" { \"$enum_value_name$\", \"$c_enum_value_name$\", $value$ },\n");
}
static int compare_value_indices_by_value_then_index(const void *a, const void *b)
{
const ValueIndex *vi_a = (const ValueIndex *) a;
const ValueIndex *vi_b = (const ValueIndex *) b;
if (vi_a->value < vi_b->value) return -1;
if (vi_a->value > vi_b->value) return +1;
if (vi_a->index < vi_b->index) return -1;
if (vi_a->index > vi_b->index) return +1;
return 0;
}
static int compare_value_indices_by_name(const void *a, const void *b)
{
const ValueIndex *vi_a = (const ValueIndex *) a;
const ValueIndex *vi_b = (const ValueIndex *) b;
return strcmp (vi_a->name, vi_b->name);
}
void EnumGenerator::GenerateEnumDescriptor(io::Printer* printer) {
map<string, string> vars;
vars["fullname"] = descriptor_->full_name();
vars["lcclassname"] = FullNameToLower(descriptor_->full_name());
vars["cname"] = FullNameToC(descriptor_->full_name());
vars["shortname"] = descriptor_->name();
vars["packagename"] = descriptor_->file()->package();
vars["value_count"] = SimpleItoa(descriptor_->value_count());
// Sort by name and value, dropping duplicate values if they appear later.
// TODO: use a c++ paradigm for this!
NameIndex *name_index = new NameIndex[descriptor_->value_count()];
ValueIndex *value_index = new ValueIndex[descriptor_->value_count()];
for (int j = 0; j < descriptor_->value_count(); j++) {
const EnumValueDescriptor *vd = descriptor_->value(j);
name_index[j].index = j;
name_index[j].name = vd->name().c_str();
value_index[j].index = j;
value_index[j].value = vd->number();
value_index[j].name = vd->name().c_str();
}
qsort(value_index, descriptor_->value_count(),
sizeof(ValueIndex), compare_value_indices_by_value_then_index);
// only record unique values
int n_unique_values;
if (descriptor_->value_count() == 0) {
n_unique_values = 0; // should never happen
} else {
n_unique_values = 1;
value_index[0].final_index = 0;
for (int j = 1; j < descriptor_->value_count(); j++) {
if (value_index[j-1].value != value_index[j].value)
value_index[j].final_index = n_unique_values++;
else
value_index[j].final_index = n_unique_values - 1;
}
}
vars["unique_value_count"] = SimpleItoa(n_unique_values);
printer->Print(vars,
"const ProtobufCEnumValue $lcclassname$__enum_values_by_number[$unique_value_count$] =\n"
"{\n");
if (descriptor_->value_count() > 0) {
GenerateValueInitializer(printer, value_index[0].index);
for (int j = 1; j < descriptor_->value_count(); j++) {
if (value_index[j-1].value != value_index[j].value) {
GenerateValueInitializer(printer, value_index[j].index);
}
}
}
printer->Print(vars, "};\n");
printer->Print(vars, "static const ProtobufCIntRange $lcclassname$__value_ranges[] = {\n");
unsigned n_ranges = 0;
if (descriptor_->value_count() > 0) {
unsigned range_start = 0;
unsigned range_len = 1;
int range_start_value = value_index[0].value;
int last_value = range_start_value;
for (int j = 1; j < descriptor_->value_count(); j++) {
if (value_index[j-1].value != value_index[j].value) {
if (last_value + 1 == value_index[j].value) {
range_len++;
} else {
// output range
vars["range_start_value"] = SimpleItoa(range_start_value);
vars["orig_index"] = SimpleItoa(range_start);
printer->Print (vars, "{$range_start_value$, $orig_index$},");
range_start_value = value_index[j].value;
range_start += range_len;
range_len = 1;
n_ranges++;
}
last_value = value_index[j].value;
}
}
{
vars["range_start_value"] = SimpleItoa(range_start_value);
vars["orig_index"] = SimpleItoa(range_start);
printer->Print (vars, "{$range_start_value$, $orig_index$},");
range_start += range_len;
n_ranges++;
}
{
vars["range_start_value"] = SimpleItoa(0);
vars["orig_index"] = SimpleItoa(range_start);
printer->Print (vars, "{$range_start_value$, $orig_index$}\n};\n");
}
}
vars["n_ranges"] = SimpleItoa(n_ranges);
qsort(value_index, descriptor_->value_count(),
sizeof(ValueIndex), compare_value_indices_by_name);
printer->Print(vars,
"const ProtobufCEnumValueIndex $lcclassname$__enum_values_by_name[$value_count$] =\n"
"{\n");
for (int j = 0; j < descriptor_->value_count(); j++) {
vars["index"] = SimpleItoa(value_index[j].final_index);
vars["name"] = value_index[j].name;
printer->Print (vars, " { \"$name$\", $index$ },\n");
}
printer->Print(vars, "};\n");
printer->Print(vars,
"const ProtobufCEnumDescriptor $lcclassname$__descriptor =\n"
"{\n"
" PROTOBUF_C__ENUM_DESCRIPTOR_MAGIC,\n"
" \"$fullname$\",\n"
" \"$shortname$\",\n"
" \"$cname$\",\n"
" \"$packagename$\",\n"
" $unique_value_count$,\n"
" $lcclassname$__enum_values_by_number,\n"
" $value_count$,\n"
" $lcclassname$__enum_values_by_name,\n"
" $n_ranges$,\n"
" $lcclassname$__value_ranges,\n"
" NULL,NULL,NULL,NULL /* reserved[1234] */\n"
"};\n");
delete[] value_index;
delete[] name_index;
}
} // namespace c
} // namespace compiler
} // namespace protobuf
} // namespace google