2 * Copyright 2014 Google Inc. All rights reserved.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
17 // independent from idl_parser, since this code is not needed for most clients
19 #include "flatbuffers/code_generators.h"
20 #include "flatbuffers/flatbuffers.h"
21 #include "flatbuffers/idl.h"
22 #include "flatbuffers/util.h"
24 #if defined(FLATBUFFERS_CPP98_STL)
26 #endif // defined(FLATBUFFERS_CPP98_STL)
28 namespace flatbuffers {
30 static TypedFloatConstantGenerator CSharpFloatGen("Double.", "Single.", "NaN",
33 static CommentConfig comment_config = {
40 class CSharpGenerator : public BaseGenerator {
42 CSharpGenerator(const Parser &parser, const std::string &path,
43 const std::string &file_name)
44 : BaseGenerator(parser, path, file_name, "", "."),
45 cur_name_space_(nullptr) {}
47 CSharpGenerator &operator=(const CSharpGenerator &);
50 std::string one_file_code;
51 cur_name_space_ = parser_.current_namespace_;
53 for (auto it = parser_.enums_.vec.begin(); it != parser_.enums_.vec.end();
56 auto &enum_def = **it;
57 if (!parser_.opts.one_file) cur_name_space_ = enum_def.defined_namespace;
58 GenEnum(enum_def, &enumcode);
59 if (parser_.opts.one_file) {
60 one_file_code += enumcode;
62 if (!SaveType(enum_def.name, *enum_def.defined_namespace, enumcode,
68 for (auto it = parser_.structs_.vec.begin();
69 it != parser_.structs_.vec.end(); ++it) {
71 auto &struct_def = **it;
72 if (!parser_.opts.one_file)
73 cur_name_space_ = struct_def.defined_namespace;
74 GenStruct(struct_def, &declcode);
75 if (parser_.opts.one_file) {
76 one_file_code += declcode;
78 if (!SaveType(struct_def.name, *struct_def.defined_namespace, declcode,
84 if (parser_.opts.one_file) {
85 return SaveType(file_name_, *parser_.current_namespace_, one_file_code,
91 // Save out the generated code for a single class while adding
92 // declaration boilerplate.
93 bool SaveType(const std::string &defname, const Namespace &ns,
94 const std::string &classcode, bool needs_includes) const {
95 if (!classcode.length()) return true;
98 "// <auto-generated>\n"
100 std::string(FlatBuffersGeneratedWarning()) +
102 "// </auto-generated>\n\n";
104 std::string namespace_name = FullNamespace(".", ns);
105 if (!namespace_name.empty()) {
106 code += "namespace " + namespace_name + "\n{\n\n";
108 if (needs_includes) {
109 code += "using global::System;\nusing global::FlatBuffers;\n\n";
112 if (!namespace_name.empty()) { code += "\n}\n"; }
113 auto filename = NamespaceDir(ns) + defname + ".cs";
114 return SaveFile(filename.c_str(), code, false);
117 const Namespace *CurrentNameSpace() const { return cur_name_space_; }
119 std::string GenTypeBasic(const Type &type, bool enableLangOverrides) const {
121 static const char * const csharp_typename[] = {
122 #define FLATBUFFERS_TD(ENUM, IDLTYPE, \
123 CTYPE, JTYPE, GTYPE, NTYPE, PTYPE, RTYPE, KTYPE) \
125 FLATBUFFERS_GEN_TYPES(FLATBUFFERS_TD)
126 #undef FLATBUFFERS_TD
130 if (enableLangOverrides) {
131 if (IsEnum(type)) return WrapInNameSpace(*type.enum_def);
132 if (type.base_type == BASE_TYPE_STRUCT) {
133 return "Offset<" + WrapInNameSpace(*type.struct_def) + ">";
137 return csharp_typename[type.base_type];
140 inline std::string GenTypeBasic(const Type &type) const {
141 return GenTypeBasic(type, true);
144 std::string GenTypePointer(const Type &type) const {
145 switch (type.base_type) {
146 case BASE_TYPE_STRING: return "string";
147 case BASE_TYPE_VECTOR: return GenTypeGet(type.VectorType());
148 case BASE_TYPE_STRUCT: return WrapInNameSpace(*type.struct_def);
149 case BASE_TYPE_UNION: return "TTable";
150 default: return "Table";
154 std::string GenTypeGet(const Type &type) const {
155 return IsScalar(type.base_type)
157 : (IsArray(type) ? GenTypeGet(type.VectorType())
158 : GenTypePointer(type));
161 std::string GenOffsetType(const StructDef &struct_def) const {
162 return "Offset<" + WrapInNameSpace(struct_def) + ">";
165 std::string GenOffsetConstruct(const StructDef &struct_def,
166 const std::string &variable_name) const {
167 return "new Offset<" + WrapInNameSpace(struct_def) + ">(" + variable_name +
171 // Casts necessary to correctly read serialized data
172 std::string DestinationCast(const Type &type) const {
173 if (IsSeries(type)) {
174 return DestinationCast(type.VectorType());
176 if (IsEnum(type)) return "(" + WrapInNameSpace(*type.enum_def) + ")";
181 // Cast statements for mutator method parameters.
182 // In Java, parameters representing unsigned numbers need to be cast down to
183 // their respective type. For example, a long holding an unsigned int value
184 // would be cast down to int before being put onto the buffer. In C#, one cast
185 // directly cast an Enum to its underlying type, which is essential before
186 // putting it onto the buffer.
187 std::string SourceCast(const Type &type) const {
188 if (IsSeries(type)) {
189 return SourceCast(type.VectorType());
191 if (IsEnum(type)) return "(" + GenTypeBasic(type, false) + ")";
196 std::string SourceCastBasic(const Type &type) const {
197 return IsScalar(type.base_type) ? SourceCast(type) : "";
200 std::string GenEnumDefaultValue(const FieldDef &field) const {
201 auto &value = field.value;
202 FLATBUFFERS_ASSERT(value.type.enum_def);
203 auto &enum_def = *value.type.enum_def;
204 auto enum_val = enum_def.FindByValue(value.constant);
205 return enum_val ? (WrapInNameSpace(enum_def) + "." + enum_val->name)
209 std::string GenDefaultValue(const FieldDef &field,
210 bool enableLangOverrides) const {
211 auto &value = field.value;
212 if (enableLangOverrides) {
213 // handles both enum case and vector of enum case
214 if (value.type.enum_def != nullptr &&
215 value.type.base_type != BASE_TYPE_UNION) {
216 return GenEnumDefaultValue(field);
220 auto longSuffix = "";
221 switch (value.type.base_type) {
222 case BASE_TYPE_BOOL: return value.constant == "0" ? "false" : "true";
223 case BASE_TYPE_ULONG: return value.constant;
225 case BASE_TYPE_LONG: return value.constant + longSuffix;
227 if (IsFloat(value.type.base_type))
228 return CSharpFloatGen.GenFloatConstant(field);
230 return value.constant;
234 std::string GenDefaultValue(const FieldDef &field) const {
235 return GenDefaultValue(field, true);
238 std::string GenDefaultValueBasic(const FieldDef &field,
239 bool enableLangOverrides) const {
240 auto &value = field.value;
241 if (!IsScalar(value.type.base_type)) {
242 if (enableLangOverrides) {
243 switch (value.type.base_type) {
244 case BASE_TYPE_STRING: return "default(StringOffset)";
245 case BASE_TYPE_STRUCT:
246 return "default(Offset<" + WrapInNameSpace(*value.type.struct_def) +
248 case BASE_TYPE_VECTOR: return "default(VectorOffset)";
254 return GenDefaultValue(field, enableLangOverrides);
257 std::string GenDefaultValueBasic(const FieldDef &field) const {
258 return GenDefaultValueBasic(field, true);
261 void GenEnum(EnumDef &enum_def, std::string *code_ptr) const {
262 std::string &code = *code_ptr;
263 if (enum_def.generated) return;
265 // Generate enum definitions of the form:
266 // public static (final) int name = value;
267 // In Java, we use ints rather than the Enum feature, because we want them
268 // to map directly to how they're used in C/C++ and file formats.
269 // That, and Java Enums are expensive, and not universally liked.
270 GenComment(enum_def.doc_comment, code_ptr, &comment_config);
272 // In C# this indicates enumeration values can be treated as bit flags.
273 if (enum_def.attributes.Lookup("bit_flags")) {
274 code += "[System.FlagsAttribute]\n";
276 if (enum_def.attributes.Lookup("private")) {
281 code += "enum " + enum_def.name;
282 code += " : " + GenTypeBasic(enum_def.underlying_type, false);
284 for (auto it = enum_def.Vals().begin(); it != enum_def.Vals().end(); ++it) {
286 GenComment(ev.doc_comment, code_ptr, &comment_config, " ");
288 code += ev.name + " = ";
289 code += enum_def.ToString(ev);
296 // Returns the function name that is able to read a value of the given type.
297 std::string GenGetter(const Type &type) const {
298 switch (type.base_type) {
299 case BASE_TYPE_STRING: return "__p.__string";
300 case BASE_TYPE_STRUCT: return "__p.__struct";
301 case BASE_TYPE_UNION: return "__p.__union";
302 case BASE_TYPE_VECTOR: return GenGetter(type.VectorType());
303 case BASE_TYPE_ARRAY: return GenGetter(type.VectorType());
305 std::string getter = "__p.bb.Get";
306 if (type.base_type == BASE_TYPE_BOOL) {
307 getter = "0!=" + getter;
308 } else if (GenTypeBasic(type, false) != "byte") {
309 getter += MakeCamel(GenTypeBasic(type, false));
316 // Returns the function name that is able to read a value of the given type.
317 std::string GenGetterForLookupByKey(flatbuffers::FieldDef *key_field,
318 const std::string &data_buffer,
319 const char *num = nullptr) const {
320 auto type = key_field->value.type;
322 auto dest_cast = DestinationCast(type);
323 auto getter = data_buffer + ".Get";
324 if (GenTypeBasic(type, false) != "byte") {
325 getter += MakeCamel(GenTypeBasic(type, false));
327 getter = dest_cast + getter + "(" + GenOffsetGetter(key_field, num) + ")" +
332 // Direct mutation is only allowed for scalar fields.
333 // Hence a setter method will only be generated for such fields.
334 std::string GenSetter(const Type &type) const {
335 if (IsScalar(type.base_type)) {
336 std::string setter = "__p.bb.Put";
337 if (GenTypeBasic(type, false) != "byte" &&
338 type.base_type != BASE_TYPE_BOOL) {
339 setter += MakeCamel(GenTypeBasic(type, false));
347 // Returns the method name for use with add/put calls.
348 std::string GenMethod(const Type &type) const {
349 return IsScalar(type.base_type) ? MakeCamel(GenTypeBasic(type, false))
350 : (IsStruct(type) ? "Struct" : "Offset");
353 // Recursively generate arguments for a constructor, to deal with nested
355 void GenStructArgs(const StructDef &struct_def, std::string *code_ptr,
356 const char *nameprefix, size_t array_count = 0) const {
357 std::string &code = *code_ptr;
358 for (auto it = struct_def.fields.vec.begin();
359 it != struct_def.fields.vec.end(); ++it) {
361 const auto &field_type = field.value.type;
362 const auto array_field = IsArray(field_type);
363 const auto &type = array_field ? field_type.VectorType() : field_type;
364 const auto array_cnt = array_field ? (array_count + 1) : array_count;
365 if (IsStruct(type)) {
366 // Generate arguments for a struct inside a struct. To ensure names
367 // don't clash, and to make it obvious these arguments are constructing
368 // a nested struct, prefix the name with the field name.
369 GenStructArgs(*field_type.struct_def, code_ptr,
370 (nameprefix + (field.name + "_")).c_str(), array_cnt);
373 code += GenTypeBasic(type);
376 for (size_t i = 1; i < array_cnt; i++) code += ",";
381 code += MakeCamel(field.name, true);
386 // Recusively generate struct construction statements of the form:
387 // builder.putType(name);
388 // and insert manual padding.
389 void GenStructBody(const StructDef &struct_def, std::string *code_ptr,
390 const char *nameprefix, size_t index = 0,
391 bool in_array = false) const {
392 std::string &code = *code_ptr;
393 std::string indent((index + 1) * 2, ' ');
394 code += indent + " builder.Prep(";
395 code += NumToString(struct_def.minalign) + ", ";
396 code += NumToString(struct_def.bytesize) + ");\n";
397 for (auto it = struct_def.fields.vec.rbegin();
398 it != struct_def.fields.vec.rend(); ++it) {
400 const auto &field_type = field.value.type;
402 code += indent + " builder.Pad(";
403 code += NumToString(field.padding) + ");\n";
405 if (IsStruct(field_type)) {
406 GenStructBody(*field_type.struct_def, code_ptr,
407 (nameprefix + (field.name + "_")).c_str(), index,
411 IsArray(field_type) ? field_type.VectorType() : field_type;
412 const auto index_var = "_idx" + NumToString(index);
413 if (IsArray(field_type)) {
414 code += indent + " for (int " + index_var + " = ";
415 code += NumToString(field_type.fixed_length);
416 code += "; " + index_var + " > 0; " + index_var + "--) {\n";
419 if (IsStruct(type)) {
420 GenStructBody(*field_type.struct_def, code_ptr,
421 (nameprefix + (field.name + "_")).c_str(), index + 1,
424 code += IsArray(field_type) ? " " : "";
425 code += indent + " builder.Put";
426 code += GenMethod(type) + "(";
427 code += SourceCast(type);
428 auto argname = nameprefix + MakeCamel(field.name, true);
430 size_t array_cnt = index + (IsArray(field_type) ? 1 : 0);
433 for (size_t i = 0; in_array && i < array_cnt; i++) {
434 code += "_idx" + NumToString(i) + "-1";
435 if (i != (array_cnt - 1)) code += ",";
441 if (IsArray(field_type)) { code += indent + " }\n"; }
445 std::string GenOffsetGetter(flatbuffers::FieldDef *key_field,
446 const char *num = nullptr) const {
447 std::string key_offset =
448 "Table.__offset(" + NumToString(key_field->value.offset) + ", ";
451 key_offset += ".Value, builder.DataBuffer)";
453 key_offset += "bb.Length";
454 key_offset += " - tableOffset, bb)";
459 std::string GenLookupKeyGetter(flatbuffers::FieldDef *key_field) const {
460 std::string key_getter = " ";
461 key_getter += "int tableOffset = Table.";
462 key_getter += "__indirect(vectorLocation + 4 * (start + middle)";
463 key_getter += ", bb);\n ";
464 if (key_field->value.type.base_type == BASE_TYPE_STRING) {
465 key_getter += "int comp = Table.";
466 key_getter += "CompareStrings(";
467 key_getter += GenOffsetGetter(key_field);
468 key_getter += ", byteKey, bb);\n";
470 auto get_val = GenGetterForLookupByKey(key_field, "bb");
471 key_getter += "int comp = " + get_val + ".CompareTo(key);\n";
476 std::string GenKeyGetter(flatbuffers::FieldDef *key_field) const {
477 std::string key_getter = "";
478 auto data_buffer = "builder.DataBuffer";
479 if (key_field->value.type.base_type == BASE_TYPE_STRING) {
480 key_getter += "Table.CompareStrings(";
481 key_getter += GenOffsetGetter(key_field, "o1") + ", ";
482 key_getter += GenOffsetGetter(key_field, "o2") + ", " + data_buffer + ")";
484 auto field_getter = GenGetterForLookupByKey(key_field, data_buffer, "o1");
485 key_getter += field_getter;
486 field_getter = GenGetterForLookupByKey(key_field, data_buffer, "o2");
487 key_getter += ".CompareTo(" + field_getter + ")";
492 void GenStruct(StructDef &struct_def, std::string *code_ptr) const {
493 if (struct_def.generated) return;
494 std::string &code = *code_ptr;
496 // Generate a struct accessor class, with methods of the form:
497 // public type name() { return bb.getType(i + offset); }
498 // or for tables of the form:
499 // public type name() {
500 // int o = __offset(offset); return o != 0 ? bb.getType(o + i) : default;
502 GenComment(struct_def.doc_comment, code_ptr, &comment_config);
503 if (struct_def.attributes.Lookup("private")) {
508 if (struct_def.attributes.Lookup("csharp_partial")) {
509 // generate a partial class for this C# struct/table
512 code += "struct " + struct_def.name;
513 code += " : IFlatbufferObject";
516 code += struct_def.fixed ? "Struct" : "Table";
519 code += " public ByteBuffer ByteBuffer { get { return __p.bb; } }\n";
521 if (!struct_def.fixed) {
522 // Generate verson check method.
523 // Force compile time error if not using the same version runtime.
524 code += " public static void ValidateVersion() {";
525 code += " FlatBufferConstants.";
526 code += "FLATBUFFERS_1_11_1(); ";
529 // Generate a special accessor for the table that when used as the root
531 std::string method_name = "GetRootAs" + struct_def.name;
532 std::string method_signature =
533 " public static " + struct_def.name + " " + method_name;
535 // create convenience method that doesn't require an existing object
536 code += method_signature + "(ByteBuffer _bb) ";
537 code += "{ return " + method_name + "(_bb, new " + struct_def.name +
540 // create method that allows object reuse
542 method_signature + "(ByteBuffer _bb, " + struct_def.name + " obj) { ";
543 code += "return (obj.__assign(_bb.GetInt(_bb.Position";
544 code += ") + _bb.Position";
545 code += ", _bb)); }\n";
546 if (parser_.root_struct_def_ == &struct_def) {
547 if (parser_.file_identifier_.length()) {
548 // Check if a buffer has the identifier.
549 code += " public static ";
550 code += "bool " + struct_def.name;
551 code += "BufferHasIdentifier(ByteBuffer _bb) { return ";
552 code += "Table.__has_identifier(_bb, \"";
553 code += parser_.file_identifier_;
558 // Generate the __init method that sets the field in a pre-existing
559 // accessor object. This is to allow object reuse.
560 code += " public void __init(int _i, ByteBuffer _bb) ";
562 code += "__p = new ";
563 code += struct_def.fixed ? "Struct" : "Table";
564 code += "(_i, _bb); ";
567 " public " + struct_def.name + " __assign(int _i, ByteBuffer _bb) ";
568 code += "{ __init(_i, _bb); return this; }\n\n";
569 for (auto it = struct_def.fields.vec.begin();
570 it != struct_def.fields.vec.end(); ++it) {
572 if (field.deprecated) continue;
573 GenComment(field.doc_comment, code_ptr, &comment_config, " ");
574 std::string type_name = GenTypeGet(field.value.type);
575 std::string type_name_dest = GenTypeGet(field.value.type);
576 std::string conditional_cast = "";
577 std::string optional = "";
578 if (!struct_def.fixed &&
579 (field.value.type.base_type == BASE_TYPE_STRUCT ||
580 field.value.type.base_type == BASE_TYPE_UNION ||
581 (field.value.type.base_type == BASE_TYPE_VECTOR &&
582 (field.value.type.element == BASE_TYPE_STRUCT ||
583 field.value.type.element == BASE_TYPE_UNION)))) {
585 conditional_cast = "(" + type_name_dest + optional + ")";
587 std::string dest_mask = "";
588 std::string dest_cast = DestinationCast(field.value.type);
589 std::string src_cast = SourceCast(field.value.type);
590 std::string method_start = " public " + type_name_dest + optional + " " +
591 MakeCamel(field.name, true);
592 std::string obj = "(new " + type_name + "())";
594 // Most field accessors need to retrieve and test the field offset first,
595 // this is the prefix code for that:
597 IsArray(field.value.type)
599 : (" { int o = __p.__offset(" + NumToString(field.value.offset) +
600 "); return o != 0 ? ");
601 // Generate the accessors that don't do object reuse.
602 if (field.value.type.base_type == BASE_TYPE_STRUCT) {
603 } else if (field.value.type.base_type == BASE_TYPE_VECTOR &&
604 field.value.type.element == BASE_TYPE_STRUCT) {
605 } else if (field.value.type.base_type == BASE_TYPE_UNION ||
606 (field.value.type.base_type == BASE_TYPE_VECTOR &&
607 field.value.type.VectorType().base_type == BASE_TYPE_UNION)) {
608 method_start += "<TTable>";
609 type_name = type_name_dest;
611 std::string getter = dest_cast + GenGetter(field.value.type);
612 code += method_start;
613 std::string default_cast = "";
614 // only create default casts for c# scalars or vectors of scalars
615 if ((IsScalar(field.value.type.base_type) ||
616 (field.value.type.base_type == BASE_TYPE_VECTOR &&
617 IsScalar(field.value.type.element)))) {
618 // For scalars, default value will be returned by GetDefaultValue().
619 // If the scalar is an enum, GetDefaultValue() returns an actual c# enum
620 // that doesn't need to be casted. However, default values for enum
621 // elements of vectors are integer literals ("0") and are still casted
623 if (field.value.type.enum_def == nullptr ||
624 field.value.type.base_type == BASE_TYPE_VECTOR) {
625 default_cast = "(" + type_name_dest + ")";
628 std::string member_suffix = "; ";
629 if (IsScalar(field.value.type.base_type)) {
631 member_suffix += "} ";
632 if (struct_def.fixed) {
633 code += " { return " + getter;
634 code += "(__p.bb_pos + ";
635 code += NumToString(field.value.offset) + ")";
638 code += offset_prefix + getter;
639 code += "(o + __p.bb_pos)" + dest_mask;
640 code += " : " + default_cast;
641 code += GenDefaultValue(field);
644 switch (field.value.type.base_type) {
645 case BASE_TYPE_STRUCT:
647 member_suffix += "} ";
648 if (struct_def.fixed) {
649 code += " { return " + obj + ".__assign(" + "__p.";
650 code += "bb_pos + " + NumToString(field.value.offset) + ", ";
653 code += offset_prefix + conditional_cast;
654 code += obj + ".__assign(";
655 code += field.value.type.struct_def->fixed
657 : "__p.__indirect(o + __p.bb_pos)";
658 code += ", __p.bb) : null";
661 case BASE_TYPE_STRING:
663 member_suffix += "} ";
664 code += offset_prefix + getter + "(o + " + "__p.";
665 code += "bb_pos) : null";
667 case BASE_TYPE_ARRAY: FLATBUFFERS_FALLTHROUGH(); // fall thru
668 case BASE_TYPE_VECTOR: {
669 auto vectortype = field.value.type.VectorType();
670 if (vectortype.base_type == BASE_TYPE_UNION) {
671 conditional_cast = "(TTable?)";
672 getter += "<TTable>";
675 if (vectortype.base_type == BASE_TYPE_STRUCT) {
676 getter = obj + ".__assign";
677 } else if (vectortype.base_type == BASE_TYPE_UNION) {
680 const auto body = offset_prefix + conditional_cast + getter + "(";
681 if (vectortype.base_type == BASE_TYPE_UNION) {
682 code += " where TTable : struct, IFlatbufferObject" + body;
686 std::string index = "__p.";
687 if (IsArray(field.value.type)) {
688 index += "bb_pos + " + NumToString(field.value.offset) + " + ";
690 index += "__vector(o) + ";
692 index += "j * " + NumToString(InlineSize(vectortype));
693 if (vectortype.base_type == BASE_TYPE_STRUCT) {
694 code += vectortype.struct_def->fixed
696 : "__p.__indirect(" + index + ")";
701 code += ")" + dest_mask;
702 if (!IsArray(field.value.type)) {
705 field.value.type.element == BASE_TYPE_BOOL
707 : (IsScalar(field.value.type.element) ? default_cast + "0"
713 case BASE_TYPE_UNION:
714 code += "() where TTable : struct, IFlatbufferObject";
715 code += offset_prefix + "(TTable?)" + getter;
716 code += "<TTable>(o + __p.bb_pos) : null";
718 default: FLATBUFFERS_ASSERT(0);
721 code += member_suffix;
723 if (field.value.type.base_type == BASE_TYPE_VECTOR) {
724 code += " public int " + MakeCamel(field.name, true);
727 code += offset_prefix;
728 code += "__p.__vector_len(o) : 0; ";
731 // See if we should generate a by-key accessor.
732 if (field.value.type.element == BASE_TYPE_STRUCT &&
733 !field.value.type.struct_def->fixed) {
734 auto &sd = *field.value.type.struct_def;
735 auto &fields = sd.fields.vec;
736 for (auto kit = fields.begin(); kit != fields.end(); ++kit) {
737 auto &key_field = **kit;
739 auto qualified_name = WrapInNameSpace(sd);
740 code += " public " + qualified_name + "? ";
741 code += MakeCamel(field.name, true) + "ByKey(";
742 code += GenTypeGet(key_field.value.type) + " key)";
743 code += offset_prefix;
744 code += qualified_name + ".__lookup_by_key(";
745 code += "__p.__vector(o), key, ";
746 code += "__p.bb) : null; ";
753 // Generate a ByteBuffer accessor for strings & vectors of scalars.
754 if ((field.value.type.base_type == BASE_TYPE_VECTOR &&
755 IsScalar(field.value.type.VectorType().base_type)) ||
756 field.value.type.base_type == BASE_TYPE_STRING) {
757 code += "#if ENABLE_SPAN_T\n";
758 code += " public Span<" + GenTypeBasic(field.value.type.VectorType()) +
760 code += MakeCamel(field.name, true);
761 code += "Bytes() { return ";
762 code += "__p.__vector_as_span<" +
763 GenTypeBasic(field.value.type.VectorType()) + ">(";
764 code += NumToString(field.value.offset);
766 ", " + NumToString(SizeOf(field.value.type.VectorType().base_type));
769 code += " public ArraySegment<byte>? Get";
770 code += MakeCamel(field.name, true);
771 code += "Bytes() { return ";
772 code += "__p.__vector_as_arraysegment(";
773 code += NumToString(field.value.offset);
777 // For direct blockcopying the data into a typed array
779 code += GenTypeBasic(field.value.type.VectorType());
781 code += MakeCamel(field.name, true);
782 code += "Array() { ";
783 if (IsEnum(field.value.type.VectorType())) {
784 // Since __vector_as_array does not work for enum types,
785 // fill array using an explicit loop.
786 code += "int o = __p.__offset(";
787 code += NumToString(field.value.offset);
788 code += "); if (o == 0) return null; int p = ";
789 code += "__p.__vector(o); int l = ";
790 code += "__p.__vector_len(o); ";
791 code += GenTypeBasic(field.value.type.VectorType());
792 code += "[] a = new ";
793 code += GenTypeBasic(field.value.type.VectorType());
794 code += "[l]; for (int i = 0; i < l; i++) { a[i] = " + getter;
796 code += NumToString(InlineSize(field.value.type.VectorType()));
797 code += "); } return a;";
800 code += "__p.__vector_as_array<";
801 code += GenTypeBasic(field.value.type.VectorType());
803 code += NumToString(field.value.offset);
808 // generate object accessors if is nested_flatbuffer
809 if (field.nested_flatbuffer) {
810 auto nested_type_name = WrapInNameSpace(*field.nested_flatbuffer);
811 auto nested_method_name =
812 MakeCamel(field.name, true) + "As" + field.nested_flatbuffer->name;
813 auto get_nested_method_name = nested_method_name;
814 get_nested_method_name = "Get" + nested_method_name;
815 conditional_cast = "(" + nested_type_name + "?)";
816 obj = "(new " + nested_type_name + "())";
817 code += " public " + nested_type_name + "? ";
818 code += get_nested_method_name + "(";
819 code += ") { int o = __p.__offset(";
820 code += NumToString(field.value.offset) + "); ";
821 code += "return o != 0 ? " + conditional_cast + obj + ".__assign(";
823 code += "__indirect(__p.__vector(o)), ";
824 code += "__p.bb) : null; }\n";
826 // Generate mutators for scalar fields or vectors of scalars.
827 if (parser_.opts.mutable_buffer) {
828 auto is_series = (IsSeries(field.value.type));
829 const auto &underlying_type =
830 is_series ? field.value.type.VectorType() : field.value.type;
831 // Boolean parameters have to be explicitly converted to byte
833 auto setter_parameter = underlying_type.base_type == BASE_TYPE_BOOL
834 ? "(byte)(" + field.name + " ? 1 : 0)"
836 auto mutator_prefix = MakeCamel("mutate", true);
837 // A vector mutator also needs the index of the vector element it should
839 auto mutator_params = (is_series ? "(int j, " : "(") +
840 GenTypeGet(underlying_type) + " " + field.name +
845 (IsArray(field.value.type)
846 ? "bb_pos + " + NumToString(field.value.offset)
848 +" + j * " + NumToString(InlineSize(underlying_type))
850 ? "__p.bb_pos + " + NumToString(field.value.offset)
852 if (IsScalar(underlying_type.base_type) && !IsUnion(field.value.type)) {
854 code += struct_def.fixed ? "void " : "bool ";
855 code += mutator_prefix + MakeCamel(field.name, true);
856 code += mutator_params;
857 if (struct_def.fixed) {
858 code += GenSetter(underlying_type) + "(" + setter_index + ", ";
859 code += src_cast + setter_parameter + "); }\n";
861 code += "int o = __p.__offset(";
862 code += NumToString(field.value.offset) + ");";
863 code += " if (o != 0) { " + GenSetter(underlying_type);
864 code += "(" + setter_index + ", " + src_cast + setter_parameter +
865 "); return true; } else { return false; } }\n";
869 if (parser_.opts.java_primitive_has_method &&
870 IsScalar(field.value.type.base_type) && !struct_def.fixed) {
871 auto vt_offset_constant = " public static final int VT_" +
872 MakeScreamingCamel(field.name) + " = " +
873 NumToString(field.value.offset) + ";";
875 code += vt_offset_constant;
880 flatbuffers::FieldDef *key_field = nullptr;
881 if (struct_def.fixed) {
882 // create a struct constructor function
883 code += " public static " + GenOffsetType(struct_def) + " ";
885 code += struct_def.name + "(FlatBufferBuilder builder";
886 GenStructArgs(struct_def, code_ptr, "");
888 GenStructBody(struct_def, code_ptr, "");
890 code += GenOffsetConstruct(struct_def, "builder.Offset");
893 // Generate a method that creates a table in one go. This is only possible
894 // when the table has no struct fields, since those have to be created
895 // inline, and there's no way to do so in Java.
896 bool has_no_struct_fields = true;
898 for (auto it = struct_def.fields.vec.begin();
899 it != struct_def.fields.vec.end(); ++it) {
901 if (field.deprecated) continue;
902 if (IsStruct(field.value.type)) {
903 has_no_struct_fields = false;
908 // JVM specifications restrict default constructor params to be < 255.
909 // Longs and doubles take up 2 units, so we set the limit to be < 127.
910 if (has_no_struct_fields && num_fields && num_fields < 127) {
911 // Generate a table constructor of the form:
912 // public static int createName(FlatBufferBuilder builder, args...)
913 code += " public static " + GenOffsetType(struct_def) + " ";
914 code += "Create" + struct_def.name;
915 code += "(FlatBufferBuilder builder";
916 for (auto it = struct_def.fields.vec.begin();
917 it != struct_def.fields.vec.end(); ++it) {
919 if (field.deprecated) continue;
921 code += GenTypeBasic(field.value.type);
924 if (!IsScalar(field.value.type.base_type)) code += "Offset";
927 code += GenDefaultValueBasic(field);
929 code += ") {\n builder.";
930 code += "StartTable(";
931 code += NumToString(struct_def.fields.vec.size()) + ");\n";
932 for (size_t size = struct_def.sortbysize ? sizeof(largest_scalar_t) : 1;
934 for (auto it = struct_def.fields.vec.rbegin();
935 it != struct_def.fields.vec.rend(); ++it) {
937 if (!field.deprecated &&
938 (!struct_def.sortbysize ||
939 size == SizeOf(field.value.type.base_type))) {
940 code += " " + struct_def.name + ".";
942 code += MakeCamel(field.name) + "(builder, " + field.name;
943 if (!IsScalar(field.value.type.base_type)) code += "Offset";
948 code += " return " + struct_def.name + ".";
949 code += "End" + struct_def.name;
950 code += "(builder);\n }\n\n";
952 // Generate a set of static methods that allow table construction,
954 // public static void addName(FlatBufferBuilder builder, short name)
955 // { builder.addShort(id, name, default); }
956 // Unlike the Create function, these always work.
957 code += " public static void Start";
958 code += struct_def.name;
959 code += "(FlatBufferBuilder builder) { builder.";
960 code += "StartTable(";
961 code += NumToString(struct_def.fields.vec.size()) + "); }\n";
962 for (auto it = struct_def.fields.vec.begin();
963 it != struct_def.fields.vec.end(); ++it) {
965 if (field.deprecated) continue;
966 if (field.key) key_field = &field;
967 code += " public static void Add";
968 code += MakeCamel(field.name);
969 code += "(FlatBufferBuilder builder, ";
970 code += GenTypeBasic(field.value.type);
971 auto argname = MakeCamel(field.name, false);
972 if (!IsScalar(field.value.type.base_type)) argname += "Offset";
973 code += " " + argname + ") { builder.Add";
974 code += GenMethod(field.value.type) + "(";
975 code += NumToString(it - struct_def.fields.vec.begin()) + ", ";
976 code += SourceCastBasic(field.value.type);
978 if (!IsScalar(field.value.type.base_type) &&
979 field.value.type.base_type != BASE_TYPE_UNION) {
983 code += GenDefaultValue(field, false);
985 if (field.value.type.base_type == BASE_TYPE_VECTOR) {
986 auto vector_type = field.value.type.VectorType();
987 auto alignment = InlineAlignment(vector_type);
988 auto elem_size = InlineSize(vector_type);
989 if (!IsStruct(vector_type)) {
990 code += " public static VectorOffset ";
992 code += MakeCamel(field.name);
993 code += "Vector(FlatBufferBuilder builder, ";
994 code += GenTypeBasic(vector_type) + "[] data) ";
995 code += "{ builder.StartVector(";
996 code += NumToString(elem_size);
997 code += ", data.Length, ";
998 code += NumToString(alignment);
999 code += "); for (int i = data.";
1000 code += "Length - 1; i >= 0; i--) builder.";
1002 code += GenMethod(vector_type);
1004 code += SourceCastBasic(vector_type);
1006 if ((vector_type.base_type == BASE_TYPE_STRUCT ||
1007 vector_type.base_type == BASE_TYPE_STRING))
1009 code += "); return ";
1010 code += "builder.EndVector(); }\n";
1012 code += " public static VectorOffset ";
1014 code += MakeCamel(field.name);
1015 code += "VectorBlock(FlatBufferBuilder builder, ";
1016 code += GenTypeBasic(vector_type) + "[] data) ";
1017 code += "{ builder.StartVector(";
1018 code += NumToString(elem_size);
1019 code += ", data.Length, ";
1020 code += NumToString(alignment);
1021 code += "); builder.Add(data); return builder.EndVector(); }\n";
1023 // Generate a method to start a vector, data to be added manually
1025 code += " public static void Start";
1026 code += MakeCamel(field.name);
1027 code += "Vector(FlatBufferBuilder builder, int numElems) ";
1028 code += "{ builder.StartVector(";
1029 code += NumToString(elem_size);
1030 code += ", numElems, " + NumToString(alignment);
1034 code += " public static " + GenOffsetType(struct_def) + " ";
1035 code += "End" + struct_def.name;
1036 code += "(FlatBufferBuilder builder) {\n int o = builder.";
1037 code += "EndTable();\n";
1038 for (auto it = struct_def.fields.vec.begin();
1039 it != struct_def.fields.vec.end(); ++it) {
1041 if (!field.deprecated && field.required) {
1042 code += " builder.Required(o, ";
1043 code += NumToString(field.value.offset);
1044 code += "); // " + field.name + "\n";
1047 code += " return " + GenOffsetConstruct(struct_def, "o") + ";\n }\n";
1048 if (parser_.root_struct_def_ == &struct_def) {
1049 std::string size_prefix[] = { "", "SizePrefixed" };
1050 for (int i = 0; i < 2; ++i) {
1051 code += " public static void ";
1052 code += "Finish" + size_prefix[i] + struct_def.name;
1054 "Buffer(FlatBufferBuilder builder, " + GenOffsetType(struct_def);
1055 code += " offset) {";
1056 code += " builder.Finish" + size_prefix[i] + "(offset";
1059 if (parser_.file_identifier_.length())
1060 code += ", \"" + parser_.file_identifier_ + "\"";
1065 // Only generate key compare function for table,
1066 // because `key_field` is not set for struct
1067 if (struct_def.has_key && !struct_def.fixed) {
1068 FLATBUFFERS_ASSERT(key_field);
1069 code += "\n public static VectorOffset ";
1070 code += "CreateSortedVectorOf" + struct_def.name;
1071 code += "(FlatBufferBuilder builder, ";
1072 code += "Offset<" + struct_def.name + ">";
1073 code += "[] offsets) {\n";
1074 code += " Array.Sort(offsets, (Offset<" + struct_def.name +
1075 "> o1, Offset<" + struct_def.name + "> o2) => " +
1076 GenKeyGetter(key_field);
1078 code += " return builder.CreateVectorOfTables(offsets);\n }\n";
1080 code += "\n public static " + struct_def.name + "?";
1081 code += " __lookup_by_key(";
1082 code += "int vectorLocation, ";
1083 code += GenTypeGet(key_field->value.type);
1084 code += " key, ByteBuffer bb) {\n";
1085 if (key_field->value.type.base_type == BASE_TYPE_STRING) {
1086 code += " byte[] byteKey = ";
1087 code += "System.Text.Encoding.UTF8.GetBytes(key);\n";
1089 code += " int span = ";
1090 code += "bb.GetInt(vectorLocation - 4);\n";
1091 code += " int start = 0;\n";
1092 code += " while (span != 0) {\n";
1093 code += " int middle = span / 2;\n";
1094 code += GenLookupKeyGetter(key_field);
1095 code += " if (comp > 0) {\n";
1096 code += " span = middle;\n";
1097 code += " } else if (comp < 0) {\n";
1098 code += " middle++;\n";
1099 code += " start += middle;\n";
1100 code += " span -= middle;\n";
1101 code += " } else {\n";
1103 code += "new " + struct_def.name + "()";
1104 code += ".__assign(tableOffset, bb);\n";
1106 code += " return null;\n";
1112 void GenVectorAccessObject(StructDef &struct_def,
1113 std::string *code_ptr) const {
1114 auto &code = *code_ptr;
1115 // Generate a vector of structs accessor class.
1118 if (!struct_def.attributes.Lookup("private")) code += "public ";
1119 code += "static struct Vector : BaseVector\n{\n";
1121 // Generate the __assign method that sets the field in a pre-existing
1122 // accessor object. This is to allow object reuse.
1123 std::string method_indent = " ";
1124 code += method_indent + "public Vector ";
1125 code += "__assign(int _vector, int _element_size, ByteBuffer _bb) { ";
1126 code += "__reset(_vector, _element_size, _bb); return this; }\n\n";
1128 auto type_name = struct_def.name;
1129 auto method_start = method_indent + "public " + type_name + " Get";
1130 // Generate the accessors that don't do object reuse.
1131 code += method_start + "(int j) { return Get";
1132 code += "(new " + type_name + "(), j); }\n";
1133 code += method_start + "(" + type_name + " obj, int j) { ";
1134 code += " return obj.__assign(";
1135 code += struct_def.fixed ? "__p.__element(j)"
1136 : "__p.__indirect(__p.__element(j), bb)";
1137 code += ", __p.bb); }\n";
1138 // See if we should generate a by-key accessor.
1139 if (!struct_def.fixed) {
1140 auto &fields = struct_def.fields.vec;
1141 for (auto kit = fields.begin(); kit != fields.end(); ++kit) {
1142 auto &key_field = **kit;
1143 if (key_field.key) {
1144 auto nullable_annotation =
1145 parser_.opts.gen_nullable ? "@Nullable " : "";
1146 code += method_indent + nullable_annotation;
1147 code += "public " + type_name + "? ";
1148 code += "GetByKey(";
1149 code += GenTypeGet(key_field.value.type) + " key) { ";
1150 code += " return __lookup_by_key(null, ";
1151 code += "__p.__vector(), key, ";
1152 code += "__p.bb); ";
1154 code += method_indent + nullable_annotation;
1155 code += "public " + type_name + "?" + " ";
1156 code += "GetByKey(";
1157 code += type_name + "? obj, ";
1158 code += GenTypeGet(key_field.value.type) + " key) { ";
1159 code += " return __lookup_by_key(obj, ";
1160 code += "__p.__vector(), key, ";
1161 code += "__p.bb); ";
1170 // This tracks the current namespace used to determine if a type need to be
1171 // prefixed by its namespace
1172 const Namespace *cur_name_space_;
1174 } // namespace csharp
1176 bool GenerateCSharp(const Parser &parser, const std::string &path,
1177 const std::string &file_name) {
1178 csharp::CSharpGenerator generator(parser, path, file_name);
1179 return generator.generate();
1182 } // namespace flatbuffers