blob: d4e1b7aede92f071003fab4f886c188f7257b60e [file] [log] [blame]
Yi Jin0473f88b2017-10-09 11:21:40 -07001#include "Errors.h"
2#include "string_utils.h"
3
4#include "google/protobuf/compiler/plugin.pb.h"
5#include "google/protobuf/io/zero_copy_stream_impl.h"
6#include "google/protobuf/text_format.h"
7
8#include <iomanip>
9#include <iostream>
10#include <sstream>
11
12using namespace android::stream_proto;
13using namespace google::protobuf;
14using namespace google::protobuf::compiler;
15using namespace google::protobuf::io;
16using namespace std;
17
18/**
19 * Position of the field type in a (long long) fieldId.
20 */
21const uint64_t FIELD_TYPE_SHIFT = 32;
22
23//
24// FieldId flags for whether the field is single, repeated or packed.
25// TODO: packed is not supported yet.
26//
27const uint64_t FIELD_COUNT_SHIFT = 40;
28const uint64_t FIELD_COUNT_MASK = 0x0fULL << FIELD_COUNT_SHIFT;
29const uint64_t FIELD_COUNT_UNKNOWN = 0;
30const uint64_t FIELD_COUNT_SINGLE = 1ULL << FIELD_COUNT_SHIFT;
31const uint64_t FIELD_COUNT_REPEATED = 2ULL << FIELD_COUNT_SHIFT;
32const uint64_t FIELD_COUNT_PACKED = 4ULL << FIELD_COUNT_SHIFT;
33
34// Indent
35const string INDENT = " ";
36
37/**
38 * See if this is the file for this request, and not one of the imported ones.
39 */
40static bool
41should_generate_for_file(const CodeGeneratorRequest& request, const string& file)
42{
43 const int N = request.file_to_generate_size();
44 for (int i=0; i<N; i++) {
45 if (request.file_to_generate(i) == file) {
46 return true;
47 }
48 }
49 return false;
50}
51
52static string
53make_filename(const FileDescriptorProto& file_descriptor)
54{
55 return file_descriptor.name() + ".h";
56}
57
58static string
59get_proto_type(const FieldDescriptorProto& field)
60{
61 switch (field.type()) {
62 case FieldDescriptorProto::TYPE_DOUBLE:
63 return "double";
64 case FieldDescriptorProto::TYPE_FLOAT:
65 return "float";
66 case FieldDescriptorProto::TYPE_INT64:
67 return "int64";
68 case FieldDescriptorProto::TYPE_UINT64:
69 return "uint64";
70 case FieldDescriptorProto::TYPE_INT32:
71 return "int32";
72 case FieldDescriptorProto::TYPE_FIXED64:
73 return "fixed64";
74 case FieldDescriptorProto::TYPE_FIXED32:
75 return "fixed32";
76 case FieldDescriptorProto::TYPE_BOOL:
77 return "bool";
78 case FieldDescriptorProto::TYPE_STRING:
79 return "string";
80 case FieldDescriptorProto::TYPE_GROUP:
81 return "group<unsupported!>";
82 case FieldDescriptorProto::TYPE_MESSAGE:
83 return field.type_name();
84 case FieldDescriptorProto::TYPE_BYTES:
85 return "bytes";
86 case FieldDescriptorProto::TYPE_UINT32:
87 return "uint32";
88 case FieldDescriptorProto::TYPE_ENUM:
89 return field.type_name();
90 case FieldDescriptorProto::TYPE_SFIXED32:
91 return "sfixed32";
92 case FieldDescriptorProto::TYPE_SFIXED64:
93 return "sfixed64";
94 case FieldDescriptorProto::TYPE_SINT32:
95 return "sint32";
96 case FieldDescriptorProto::TYPE_SINT64:
97 return "sint64";
98 default:
99 // won't happen
100 return "void";
101 }
102}
103
104static void
105write_enum(stringstream& text, const EnumDescriptorProto& enu, const string& indent)
106{
107 const int N = enu.value_size();
108 text << indent << "// enum " << enu.name() << endl;
109 for (int i=0; i<N; i++) {
110 const EnumValueDescriptorProto& value = enu.value(i);
111 text << indent << "const uint32_t "
112 << make_constant_name(value.name())
113 << " = " << value.number() << ";" << endl;
114 }
115 text << endl;
116}
117
118static uint64_t
119get_field_id(const FieldDescriptorProto& field)
120{
121 // Number
122 uint64_t result = (uint64_t)field.number();
123
124 // Type
125 result |= (uint64_t)field.type() << FIELD_TYPE_SHIFT;
126
127 // Count
128 if (field.options().packed()) {
129 result |= FIELD_COUNT_PACKED;
130 } else if (field.label() == FieldDescriptorProto::LABEL_REPEATED) {
131 result |= FIELD_COUNT_REPEATED;
132 } else {
133 result |= FIELD_COUNT_SINGLE;
134 }
135
136 return result;
137}
138
139static void
140write_field(stringstream& text, const FieldDescriptorProto& field, const string& indent)
141{
142 string optional_comment = field.label() == FieldDescriptorProto::LABEL_OPTIONAL
143 ? "optional " : "";
144 string repeated_comment = field.label() == FieldDescriptorProto::LABEL_REPEATED
145 ? "repeated " : "";
146 string proto_type = get_proto_type(field);
147 string packed_comment = field.options().packed()
148 ? " [packed=true]" : "";
149 text << indent << "// " << optional_comment << repeated_comment << proto_type << ' '
150 << field.name() << " = " << field.number() << packed_comment << ';' << endl;
151
152 text << indent << "const uint64_t " << make_constant_name(field.name()) << " = 0x";
153
154 ios::fmtflags fmt(text.flags());
155 text << setfill('0') << setw(16) << hex << get_field_id(field);
156 text.flags(fmt);
157
158 text << "LL;" << endl;
159
160 text << endl;
161}
162
163static void
164write_message(stringstream& text, const DescriptorProto& message, const string& indent)
165{
166 int N;
167 const string indented = indent + INDENT;
168
169 text << indent << "// message " << message.name() << endl;
170 text << indent << "class " << message.name() << " {" << endl;
171 text << indent << "public:" << endl;
172
173 // Enums
174 N = message.enum_type_size();
175 for (int i=0; i<N; i++) {
176 write_enum(text, message.enum_type(i), indented);
177 }
178
179 // Nested classes
180 N = message.nested_type_size();
181 for (int i=0; i<N; i++) {
182 write_message(text, message.nested_type(i), indented);
183 }
184
185 // Fields
186 N = message.field_size();
187 for (int i=0; i<N; i++) {
188 write_field(text, message.field(i), indented);
189 }
190
191 text << indent << "};" << endl;
192 text << endl;
193}
194
195static void
196write_cpp_file(CodeGeneratorResponse* response, const FileDescriptorProto& file_descriptor)
197{
198 stringstream text;
199
200 text << "// Generated by protoc-gen-cppstream. DO NOT MODIFY." << endl;
201 text << "// source: " << file_descriptor.name() << endl << endl;
202
203 string header = "ANDROID_" + replace_string(file_descriptor.name(), '/', '_');
204 header = replace_string(header, '.', '_') + "_stream_h";
205 header = make_constant_name(header);
206
207 text << "#ifndef " << header << endl;
208 text << "#define " << header << endl;
209 text << endl;
210
211 vector<string> namespaces = split(file_descriptor.package(), '.');
212 for (vector<string>::iterator it = namespaces.begin(); it != namespaces.end(); it++) {
213 text << "namespace " << *it << " {" << endl;
214 }
215 text << endl;
216
217 size_t N;
218 N = file_descriptor.enum_type_size();
219 for (size_t i=0; i<N; i++) {
220 write_enum(text, file_descriptor.enum_type(i), "");
221 }
222
223 N = file_descriptor.message_type_size();
224 for (size_t i=0; i<N; i++) {
225 write_message(text, file_descriptor.message_type(i), "");
226 }
227
228 for (vector<string>::iterator it = namespaces.begin(); it != namespaces.end(); it++) {
229 text << "} // " << *it << endl;
230 }
231
232 text << endl;
233 text << "#endif // " << header << endl;
234
235 CodeGeneratorResponse::File* file_response = response->add_file();
236 file_response->set_name(make_filename(file_descriptor));
237 file_response->set_content(text.str());
238}
239
240int main(int argc, char const *argv[])
241{
242 (void)argc;
243 (void)argv;
244
245 GOOGLE_PROTOBUF_VERIFY_VERSION;
246
247 CodeGeneratorRequest request;
248 CodeGeneratorResponse response;
249
250 // Read the request
251 request.ParseFromIstream(&cin);
252
253 // Build the files we need.
254 const int N = request.proto_file_size();
255 for (int i=0; i<N; i++) {
256 const FileDescriptorProto& file_descriptor = request.proto_file(i);
257 if (should_generate_for_file(request, file_descriptor.name())) {
258 write_cpp_file(&response, file_descriptor);
259 }
260 }
261
262 // If we had errors, don't write the response. Print the errors and exit.
263 if (ERRORS.HasErrors()) {
264 ERRORS.Print();
265 return 1;
266 }
267
268 // If we didn't have errors, write the response and exit happily.
269 response.SerializeToOstream(&cout);
270
271 /* code */
272 return 0;
273}