From 120d4ff779880ec165a286d87da05f54ea6eaefd Mon Sep 17 00:00:00 2001 From: "(no author)" <(no author)@41a61cd8-c433-0410-bb1c-e256eeef9e11> Date: Fri, 11 Jan 2008 21:01:17 +0000 Subject: [PATCH] r1460@opsdev009 (orig r77478): dreiss | 2008-01-11 12:59:12 -0800 Thrift: C# Bindings. Summary: C# generator, library, and MS Build task contributed by imeem. Reviewed By: mcslee Test Plan: Built the Thrift compiler and generated some C# code. I'd love to say I installed Mono or Portable.NET and built the C# code, but I did not. Revert Plan: ok git-svn-id: http://svn.facebook.com/svnroot/thrift/trunk@749 41a61cd8-c433-0410-bb1c-e256eeef9e11 --- CONTRIBUTORS | 2 + compiler/cpp/Makefile.am | 4 +- compiler/cpp/src/generate/t_csharp_generator.cc | 1526 ++++++++++++++++++++ compiler/cpp/src/generate/t_csharp_generator.h | 94 ++ compiler/cpp/src/main.cc | 14 +- compiler/cpp/src/parse/t_program.h | 11 + compiler/cpp/src/thriftl.ll | 1 + compiler/cpp/src/thrifty.yy | 8 + .../ThriftMSBuildTask/Properties/AssemblyInfo.cs | 36 + lib/csharp/ThriftMSBuildTask/ThriftBuild.cs | 202 +++ .../ThriftMSBuildTask/ThriftMSBuildTask.csproj | 66 + lib/csharp/src/Protocol/TBinaryProtocol.cs | 386 +++++ lib/csharp/src/Protocol/TField.cs | 50 + lib/csharp/src/Protocol/TList.cs | 43 + lib/csharp/src/Protocol/TMap.cs | 50 + lib/csharp/src/Protocol/TMessage.cs | 50 + lib/csharp/src/Protocol/TMessageType.cs | 25 + lib/csharp/src/Protocol/TProtocol.cs | 75 + lib/csharp/src/Protocol/TProtocolException.cs | 45 + lib/csharp/src/Protocol/TProtocolFactory.cs | 23 + lib/csharp/src/Protocol/TProtocolUtil.cs | 88 ++ lib/csharp/src/Protocol/TSet.cs | 43 + lib/csharp/src/Protocol/TStruct.cs | 35 + lib/csharp/src/Protocol/TType.cs | 35 + lib/csharp/src/Server/TServer.cs | 108 ++ lib/csharp/src/Server/TSimpleServer.cs | 108 ++ lib/csharp/src/Server/TThreadPoolServer.cs | 143 ++ lib/csharp/src/TApplicationException.cs | 127 ++ lib/csharp/src/TProcessor.cs | 24 + lib/csharp/src/Thrift.csproj | 79 + lib/csharp/src/Thrift.sln | 51 + lib/csharp/src/Transport/TServerSocket.cs | 131 ++ lib/csharp/src/Transport/TServerTransport.cs | 31 + lib/csharp/src/Transport/TSocket.cs | 122 ++ lib/csharp/src/Transport/TStreamTransport.cs | 87 ++ lib/csharp/src/Transport/TTransport.cs | 60 + lib/csharp/src/Transport/TTransportException.cs | 58 + lib/csharp/src/Transport/TTransportFactory.cs | 32 + test/ThriftTest.thrift | 1 + thrift.el | 2 +- thrift.vim | 3 +- 41 files changed, 4075 insertions(+), 4 deletions(-) create mode 100644 compiler/cpp/src/generate/t_csharp_generator.cc create mode 100644 compiler/cpp/src/generate/t_csharp_generator.h create mode 100644 lib/csharp/ThriftMSBuildTask/Properties/AssemblyInfo.cs create mode 100644 lib/csharp/ThriftMSBuildTask/ThriftBuild.cs create mode 100644 lib/csharp/ThriftMSBuildTask/ThriftMSBuildTask.csproj create mode 100644 lib/csharp/src/Protocol/TBinaryProtocol.cs create mode 100644 lib/csharp/src/Protocol/TField.cs create mode 100644 lib/csharp/src/Protocol/TList.cs create mode 100644 lib/csharp/src/Protocol/TMap.cs create mode 100644 lib/csharp/src/Protocol/TMessage.cs create mode 100644 lib/csharp/src/Protocol/TMessageType.cs create mode 100644 lib/csharp/src/Protocol/TProtocol.cs create mode 100644 lib/csharp/src/Protocol/TProtocolException.cs create mode 100644 lib/csharp/src/Protocol/TProtocolFactory.cs create mode 100644 lib/csharp/src/Protocol/TProtocolUtil.cs create mode 100644 lib/csharp/src/Protocol/TSet.cs create mode 100644 lib/csharp/src/Protocol/TStruct.cs create mode 100644 lib/csharp/src/Protocol/TType.cs create mode 100644 lib/csharp/src/Server/TServer.cs create mode 100644 lib/csharp/src/Server/TSimpleServer.cs create mode 100644 lib/csharp/src/Server/TThreadPoolServer.cs create mode 100644 lib/csharp/src/TApplicationException.cs create mode 100644 lib/csharp/src/TProcessor.cs create mode 100644 lib/csharp/src/Thrift.csproj create mode 100644 lib/csharp/src/Thrift.sln create mode 100644 lib/csharp/src/Transport/TServerSocket.cs create mode 100644 lib/csharp/src/Transport/TServerTransport.cs create mode 100644 lib/csharp/src/Transport/TSocket.cs create mode 100644 lib/csharp/src/Transport/TStreamTransport.cs create mode 100644 lib/csharp/src/Transport/TTransport.cs create mode 100644 lib/csharp/src/Transport/TTransportException.cs create mode 100644 lib/csharp/src/Transport/TTransportFactory.cs diff --git a/CONTRIBUTORS b/CONTRIBUTORS index 5134598..c491c4c 100644 --- a/CONTRIBUTORS +++ b/CONTRIBUTORS @@ -13,6 +13,8 @@ Igor Afanasyev Todd Berman - MinGW port of the compiler. +- C# bindings. +- MS build task. ---------------- Release 20070917 diff --git a/compiler/cpp/Makefile.am b/compiler/cpp/Makefile.am index 2c0368c..3ccef42 100644 --- a/compiler/cpp/Makefile.am +++ b/compiler/cpp/Makefile.am @@ -21,6 +21,7 @@ thrift_SOURCES = src/thrifty.yy \ src/generate/t_hs_generator.cc \ src/generate/t_cocoa_generator.cc \ src/generate/t_st_generator.cc \ + src/generate/t_csharp_generator.cc \ src/globals.h \ src/main.h \ src/platform.h \ @@ -56,7 +57,8 @@ thrift_SOURCES = src/thrifty.yy \ src/generate/t_erl_generator.h \ src/generate/t_hs_generator.h \ src/generate/t_cocoa_generator.h \ - src/generate/t_st_generator.h + src/generate/t_st_generator.h \ + src/generate/t_csharp_generator.h thrift_CXXFLAGS = -Wall -I$(srcdir)/src $(BOOST_CPPFLAGS) thrift_LDFLAGS = -Wall $(BOOST_LDFLAGS) diff --git a/compiler/cpp/src/generate/t_csharp_generator.cc b/compiler/cpp/src/generate/t_csharp_generator.cc new file mode 100644 index 0000000..0ec3e66 --- /dev/null +++ b/compiler/cpp/src/generate/t_csharp_generator.cc @@ -0,0 +1,1526 @@ +// Distributed under the Thrift Software License +// +// See accompanying file LICENSE or visit the Thrift site at: +// http://developers.facebook.com/thrift/ + +#include +#include +#include +#include "t_csharp_generator.h" +#include "../platform.h" + +using namespace std; + +void t_csharp_generator::init_generator() { + MKDIR(get_out_dir().c_str()); + namespace_name_ = program_->get_csharp_namespace(); + + string dir = namespace_name_; + string subdir = get_out_dir().c_str(); + string::size_type loc; + + while ((loc = dir.find(".")) != string::npos) { + subdir = subdir + "/" + dir.substr(0, loc); + MKDIR(subdir.c_str()); + dir = dir.substr(loc + 1); + } + if (dir.size() > 0) { + subdir = subdir + "/" + dir; + MKDIR(subdir.c_str()); + } + + namespace_dir_ = subdir; +} + +void t_csharp_generator::start_csharp_namespace(ofstream& out) { + if (!namespace_name_.empty()) { + out << + "namespace " << namespace_name_ << "\n"; + scope_up(out); + } +} + +void t_csharp_generator::end_csharp_namespace(ofstream& out) { + if (!namespace_name_.empty()) { + scope_down(out); + } +} + +string t_csharp_generator::csharp_type_usings() { + return string() + + "using System;\n" + + "using System.Collections;\n" + + "using System.Collections.Generic;\n" + + "using System.Text;\n" + + "using Thrift;\n"; +} + +string t_csharp_generator::csharp_thrift_usings() { + return string() + + "using Thrift.Protocol;\n" + + "using Thrift.Transport;\n"; +} + +void t_csharp_generator::close_generator() { } +void t_csharp_generator::generate_typedef(t_typedef* ttypedef) {} + +void t_csharp_generator::generate_enum(t_enum* tenum) { + string f_enum_name = namespace_dir_+"/" + (tenum->get_name())+".cs"; + ofstream f_enum; + f_enum.open(f_enum_name.c_str()); + + f_enum << + autogen_comment() << endl; + + start_csharp_namespace(f_enum); + + indent(f_enum) << + "public enum " << tenum->get_name() << "\n"; + scope_up(f_enum); + + vector constants = tenum->get_constants(); + vector::iterator c_iter; + int value = -1; + for (c_iter = constants.begin(); c_iter != constants.end(); ++c_iter) + { + if ((*c_iter)->has_value()) { + value = (*c_iter)->get_value(); + } else { + ++value; + } + + indent(f_enum) << + (*c_iter)->get_name() << + " = " << value << "," << endl; + } + + scope_down(f_enum); + + end_csharp_namespace(f_enum); + + f_enum.close(); +} + +void t_csharp_generator::generate_consts(std::vector consts) { + if (consts.empty()){ + return; + } + string f_consts_name = namespace_dir_ + "/Constants.cs"; + ofstream f_consts; + f_consts.open(f_consts_name.c_str()); + + f_consts << + autogen_comment() << + csharp_type_usings() << endl; + + start_csharp_namespace(f_consts); + + indent(f_consts) << + "public class Constants" << endl; + scope_up(f_consts); + + vector::iterator c_iter; + bool need_static_constructor = false; + for (c_iter = consts.begin(); c_iter != consts.end(); ++c_iter) { + if (print_const_value(f_consts, (*c_iter)->get_name(), (*c_iter)->get_type(), (*c_iter)->get_value(), false)) { + need_static_constructor = true; + } + } + + if (need_static_constructor) { + print_const_constructor(f_consts, consts); + } + + scope_down(f_consts); + end_csharp_namespace(f_consts); + f_consts.close(); +} + +void t_csharp_generator::print_const_def_value(std::ofstream& out, string name, t_type* type, t_const_value* value) +{ + if (type->is_struct() || type->is_xception()) { + const vector& fields = ((t_struct*)type)->get_members(); + vector::const_iterator f_iter; + const map& val = value->get_map(); + map::const_iterator v_iter; + for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) { + t_type* field_type = NULL; + for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { + if ((*f_iter)->get_name() == v_iter->first->get_string()) { + field_type = (*f_iter)->get_type(); + } + } + if (field_type == NULL) { + throw "type error: " + type->get_name() + " has no field " + v_iter->first->get_string(); + } + string val = render_const_value(out, name, field_type, v_iter->second); + indent(out) << name << "." << v_iter->first->get_string() << " = " << val << ";" << endl; + } + } else if (type->is_map()) { + t_type* ktype = ((t_map*)type)->get_key_type(); + t_type* vtype = ((t_map*)type)->get_val_type(); + const map& val = value->get_map(); + map::const_iterator v_iter; + for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) { + string key = render_const_value(out, name, ktype, v_iter->first); + string val = render_const_value(out, name, vtype, v_iter->second); + indent(out) << name << "[" << key << "]" << " = " << val << ";" << endl; + } + } else if (type->is_list() || type->is_set()) { + t_type* etype; + if (type->is_list()) { + etype = ((t_list*)type)->get_elem_type(); + } else { + etype = ((t_set*)type)->get_elem_type(); + } + + const vector& val = value->get_list(); + vector::const_iterator v_iter; + for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) { + string val = render_const_value(out, name, etype, *v_iter); + indent(out) << name << ".Add(" << val << ");" << endl; + } + } +} + +void t_csharp_generator::print_const_constructor(std::ofstream& out, std::vector consts) { + indent(out) << "static Constants()" << endl; + scope_up(out); + vector::iterator c_iter; + for (c_iter = consts.begin(); c_iter != consts.end(); ++c_iter) { + string name = (*c_iter)->get_name(); + t_type* type = (*c_iter)->get_type(); + t_const_value* value = (*c_iter)->get_value(); + + print_const_def_value(out, name, type, value); + } + scope_down(out); +} + + +//it seems like all that methods that call this are using in_static to be the opposite of what it would imply +bool t_csharp_generator::print_const_value(std::ofstream& out, string name, t_type* type, t_const_value* value, bool in_static, bool defval, bool needtype) { + indent(out); + bool need_static_construction = !in_static; + if (!defval || needtype) { + out << + (in_static ? "" : "public static ") << + type_name(type) << " "; + } + if (type->is_base_type()) { + string v2 = render_const_value(out, name, type, value); + out << name << " = " << v2 << ";" << endl; + need_static_construction = false; + } else if (type->is_enum()) { + out << name << " = (" << type_name(type, false, true) << ")" << value->get_integer() << ";" << endl; + need_static_construction = false; + } else if (type->is_struct() || type->is_xception()) { + out << name << " = new " << type_name(type) << "();" << endl; + } else if (type->is_map()) { + out << name << " = new " << type_name(type, true, true) << "();" << endl; + } else if (type->is_list() || type->is_set()) { + out << name << " = new " << type_name(type) << "();" << endl; + } + + if (defval && !type->is_base_type() && !type->is_enum()) { + print_const_def_value(out, name, type, value); + } + + return need_static_construction; +} + +std::string t_csharp_generator::render_const_value(ofstream& out, string name, t_type* type, t_const_value* value) { + std::ostringstream render; + + if (type->is_base_type()) { + t_base_type::t_base tbase = ((t_base_type*)type)->get_base(); + switch (tbase) { + case t_base_type::TYPE_STRING: + render << "\"" + value->get_string() + "\""; + break; + case t_base_type::TYPE_BOOL: + render << ((value->get_integer() > 0) ? "true" : "false"); + break; + case t_base_type::TYPE_BYTE: + case t_base_type::TYPE_I16: + case t_base_type::TYPE_I32: + case t_base_type::TYPE_I64: + render << value->get_integer(); + break; + case t_base_type::TYPE_DOUBLE: + if (value->get_type() == t_const_value::CV_INTEGER) { + render << value->get_integer(); + } else { + render << value->get_double(); + } + break; + default: + throw "compiler error: no const of base type " + tbase; + } + } else if (type->is_enum()) { + render << "(" << type->get_name() << ")" << value->get_integer(); + } else { + string t = tmp("tmp"); + print_const_value(out, t, type, value, true, true, true); + render << t; + } + + return render.str(); +} + +void t_csharp_generator::generate_struct(t_struct* tstruct) { + generate_csharp_struct(tstruct, false); +} + +void t_csharp_generator::generate_xception(t_struct* txception) { + generate_csharp_struct(txception, true); +} + +void t_csharp_generator::generate_csharp_struct(t_struct* tstruct, bool is_exception) { + string f_struct_name = namespace_dir_ + "/" + (tstruct->get_name()) + ".cs"; + ofstream f_struct; + + f_struct.open(f_struct_name.c_str()); + + f_struct << + autogen_comment() << + csharp_type_usings() << + csharp_thrift_usings(); + + generate_csharp_struct_definition(f_struct, tstruct, is_exception); + + f_struct.close(); +} + +void t_csharp_generator::generate_csharp_struct_definition(ofstream &out, t_struct* tstruct, bool is_exception, bool in_class, bool is_result) { + + if (!in_class) + { + start_csharp_namespace(out); + } + + indent(out) << + "public class " << tstruct->get_name() << " "; + + if (is_exception) { + out << ": Exception "; + } + + out << endl; + + scope_up(out); + + const vector& members = tstruct->get_members(); + vector::const_iterator m_iter; + + for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { + indent(out) << + "public " << declare_field(*m_iter, false) << endl; + } + + if (members.size() > 0) { + out << + endl << + indent() << "public Isset __isset = new Isset();" << endl << + indent() << "public sealed class Isset {" << endl; + indent_up(); + for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { + indent(out) << + "public bool " << (*m_iter)->get_name() << " = false;" << endl; + } + + indent_down(); + indent(out) << "}" << endl << endl; + } + + indent(out) << + "public " << tstruct->get_name() << "() {" << endl; + indent_up(); + for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { + t_type* t = (*m_iter)->get_type(); + while (t->is_typedef()) { + t = ((t_typedef*)t)->get_type(); + } + if ((*m_iter)->get_value() != NULL) { + print_const_value(out, "this." + (*m_iter)->get_name(), t, (*m_iter)->get_value(), true, true); + } + } + + indent_down(); + indent(out) << "}" << endl << endl; + + generate_csharp_struct_reader(out, tstruct); + if (is_result) { + generate_csharp_struct_result_writer(out, tstruct); + } else { + generate_csharp_struct_writer(out, tstruct); + } + generate_csharp_struct_tostring(out, tstruct); + scope_down(out); + out << endl; + + if (!in_class) + { + end_csharp_namespace(out); + } +} + +void t_csharp_generator::generate_csharp_struct_reader(ofstream& out, t_struct* tstruct) { + indent(out) << + "public void Read (TProtocol iprot)" << endl; + scope_up(out); + + const vector& fields = tstruct->get_members(); + vector::const_iterator f_iter; + + indent(out) << + "TField field;" << endl << + indent() << "TStruct struc = iprot.ReadStructBegin();" << endl; + + indent(out) << + "while (true)" << endl; + scope_up(out); + + indent(out) << + "field = iprot.ReadFieldBegin();" << endl; + + indent(out) << + "if (field.Type == TType.Stop) { " << endl; + indent_up(); + indent(out) << + "break;" << endl; + indent_down(); + indent(out) << + "}" << endl; + + indent(out) << + "switch (field.ID)" << endl; + + scope_up(out); + + for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { + indent(out) << + "case " << (*f_iter)->get_key() << ":" << endl; + indent_up(); + indent(out) << + "if (field.Type == " << type_to_enum((*f_iter)->get_type()) << ") {" << endl; + indent_up(); + + generate_deserialize_field(out, *f_iter, "this."); + indent(out) << + "this.__isset." << (*f_iter)->get_name() << " = true;" << endl; + indent_down(); + out << + indent() << "} else { " << endl << + indent() << " TProtocolUtil.Skip(iprot, field.Type);" << endl << + indent() << "}" << endl << + indent() << "break;" << endl; + indent_down(); + } + + indent(out) << + "default: " << endl; + indent_up(); + indent(out) << "TProtocolUtil.Skip(iprot, field.Type);" << endl; + indent(out) << "break;" << endl; + indent_down(); + + scope_down(out); + + indent(out) << + "iprot.ReadFieldEnd();" << endl; + + scope_down(out); + + indent(out) << + "iprot.ReadStructEnd();" << endl; + + indent_down(); + + indent(out) << "}" << endl << endl; + +} + +void t_csharp_generator::generate_csharp_struct_writer(ofstream& out, t_struct* tstruct) { + out << + indent() << "public void Write(TProtocol oprot) {" << endl; + indent_up(); + + string name = tstruct->get_name(); + const vector& fields = tstruct->get_members(); + vector::const_iterator f_iter; + + indent(out) << + "TStruct struc = new TStruct(\"" << name << "\");" << endl; + indent(out) << + "TField field = new TField();" << endl; + indent(out) << + "oprot.WriteStructBegin(struc);" << endl; + + for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { + bool null_allowed = type_can_be_null((*f_iter)->get_type()); + if (null_allowed) { + indent(out) << + "if (this." << (*f_iter)->get_name() << " != null) {" << endl; + indent_up(); + } + + indent(out) << + "field.Name = \"" << (*f_iter)->get_name() << "\";" << endl; + indent(out) << + "field.Type = " << type_to_enum((*f_iter)->get_type()) << ";" << endl; + indent(out) << + "field.ID = " << (*f_iter)->get_key() << ";" << endl; + indent(out) << + "oprot.WriteFieldBegin(field);" << endl; + + generate_serialize_field(out, *f_iter, "this."); + + indent(out) << + "oprot.WriteFieldEnd();" << endl; + + if (null_allowed) { + indent_down(); + indent(out) << "}" << endl; + } + } + + indent(out) << + "oprot.WriteFieldStop();" << endl; + indent(out) << + "oprot.WriteStructEnd();" << endl; + + indent_down(); + + indent(out) << + "}" << endl << endl; +} + +void t_csharp_generator::generate_csharp_struct_result_writer(ofstream& out, t_struct* tstruct) { + indent(out) << + "public void Write(TProtocol oprot) {" << endl; + indent_up(); + + string name = tstruct->get_name(); + const vector& fields = tstruct->get_members(); + vector::const_iterator f_iter; + + indent(out) << + "TStruct struc = new TStruct(\"" << name << "\");" << endl; + indent(out) << + "TField field = new TField();" << endl; + indent(out) << + "oprot.WriteStructBegin(struc);" << endl; + + bool first = true; + for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { + if (first) { + first = false; + out << + endl << indent() << "if "; + } else { + out << + " else if "; + } + + out << + "(this.__isset." << (*f_iter)->get_name() << ") {" << endl; + indent_up(); + + bool null_allowed = type_can_be_null((*f_iter)->get_type()); + if (null_allowed) { + indent(out) << + "if (this." << (*f_iter)->get_name() << " != null) {" << endl; + indent_up(); + } + + indent(out) << + "field.Name = \"" << (*f_iter)->get_name() << "\";" << endl; + indent(out) << + "field.Type = " << type_to_enum((*f_iter)->get_type()) << ";" << endl; + indent(out) << + "field.ID = " << (*f_iter)->get_key() << ";" << endl; + indent(out) << + "oprot.WriteFieldBegin(field);" << endl; + + generate_serialize_field(out, *f_iter, "this."); + + indent(out) << + "oprot.WriteFieldEnd();" << endl; + + if (null_allowed) { + indent_down(); + indent(out) << "}" << endl; + } + + indent_down(); + indent(out) << "}"; + } + + out << + endl << + indent() << "oprot.WriteFieldStop();" << endl << + indent() << "oprot.WriteStructEnd();" << endl; + + indent_down(); + + indent(out) << + "}" << endl << endl; +} + +void t_csharp_generator::generate_csharp_struct_tostring(ofstream& out, t_struct* tstruct) { + indent(out) << + "public override string ToString() {" << endl; + indent_up(); + + indent(out) << + "StringBuilder sb = new StringBuilder(\"" << tstruct->get_name() << "(\");" << endl; + + const vector& fields = tstruct->get_members(); + vector::const_iterator f_iter; + + bool first = true; + + for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { + if (first) { + first = false; + indent(out) << + "sb.Append(\"" << (*f_iter)->get_name() << ": \");" << endl; + } else { + indent(out) << + "sb.Append(\"," << (*f_iter)->get_name() << ": \");" << endl; + } + t_type* ttype = (*f_iter)->get_type(); + if (ttype->is_xception() || ttype->is_struct()) { + indent(out) << + "sb.Append(this." << (*f_iter)->get_name() << ".ToString());" << endl; + } else { + indent(out) << + "sb.Append(this." << (*f_iter)->get_name() << ");" << endl; + } + } + + indent(out) << + "sb.Append(\")\");" << endl; + indent(out) << + "return sb.ToString();" << endl; + + indent_down(); + indent(out) << "}" << endl << endl; +} + +void t_csharp_generator::generate_service(t_service* tservice) { + string f_service_name = namespace_dir_ + "/" + service_name_ + ".cs"; + f_service_.open(f_service_name.c_str()); + + f_service_ << + autogen_comment() << + csharp_type_usings() << + csharp_thrift_usings(); + + start_csharp_namespace(f_service_); + + indent(f_service_) << + "public class " << service_name_ << " {" << endl; + indent_up(); + + generate_service_interface(tservice); + generate_service_client(tservice); + generate_service_server(tservice); + generate_service_helpers(tservice); + + indent_down(); + + indent(f_service_) << + "}" << endl; + end_csharp_namespace(f_service_); + f_service_.close(); +} + +void t_csharp_generator::generate_service_interface(t_service* tservice) { + string extends = ""; + string extends_iface = ""; + if (tservice->get_extends() != NULL) { + extends = type_name(tservice->get_extends()); + extends_iface = " : " + extends + ".Iface"; + } + + indent(f_service_) << + "public interface Iface" << extends_iface << " {" << endl; + indent_up(); + vector functions = tservice->get_functions(); + vector::iterator f_iter; + for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) + { + indent(f_service_) << + function_signature(*f_iter) << ";" << endl; + } + indent_down(); + f_service_ << + indent() << "}" << endl << endl; +} + +void t_csharp_generator::generate_service_helpers(t_service* tservice) { + vector functions = tservice->get_functions(); + vector::iterator f_iter; + + for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) { + t_struct* ts = (*f_iter)->get_arglist(); + generate_csharp_struct_definition(f_service_, ts, false, true); + generate_function_helpers(*f_iter); + } +} + +void t_csharp_generator::generate_service_client(t_service* tservice) { + string extends = ""; + string extends_client = ""; + if (tservice->get_extends() != NULL) { + extends = type_name(tservice->get_extends()); + extends_client = extends + ".Client, "; + } + + indent(f_service_) << + "public class Client : " << extends_client << "Iface {" << endl; + indent_up(); + indent(f_service_) << + "public Client(TProtocol prot) : this(prot, prot)" << endl; + scope_up(f_service_); + scope_down(f_service_); + f_service_ << endl; + + indent(f_service_) << + "public Client(TProtocol iprot, TProtocol oprot)" << endl; + scope_up(f_service_); + if (extends.empty()) { + f_service_ << + indent() << "iprot_ = iprot;" << endl << + indent() << "oprot_ = oprot;" << endl; + } else { + f_service_ << + indent() << "base(iprot, oprot);" << endl; + } + + scope_down(f_service_); + + f_service_ << endl; + + if (extends.empty()) { + f_service_ << + indent() << "protected TProtocol iprot_;" << endl << + indent() << "protected TProtocol oprot_;" << endl << + endl << + indent() << "protected int seqid_;" << endl << endl; + } + + vector functions = tservice->get_functions(); + vector::const_iterator f_iter; + for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) { + string funname = (*f_iter)->get_name(); + + indent(f_service_) << + "public " << function_signature(*f_iter) << endl; + scope_up(f_service_); + indent(f_service_) << + "send_" << funname << "("; + + t_struct* arg_struct = (*f_iter)->get_arglist(); + + const vector& fields = arg_struct->get_members(); + vector::const_iterator fld_iter; + bool first = true; + for (fld_iter = fields.begin(); fld_iter != fields.end(); ++fld_iter) { + if (first) { + first = false; + } else { + f_service_ << ", "; + } + f_service_ << (*fld_iter)->get_name(); + } + f_service_ << ");" << endl; + + if (!(*f_iter)->is_async()) { + f_service_ << indent(); + if (!(*f_iter)->get_returntype()->is_void()) { + f_service_ << "return "; + } + f_service_ << + "recv_" << funname << "();" << endl; + } + scope_down(f_service_); + f_service_ << endl; + + t_function send_function(g_type_void, + string("send_") + (*f_iter)->get_name(), + (*f_iter)->get_arglist()); + + string argsname = (*f_iter)->get_name() + "_args"; + + indent(f_service_) << + "public " << function_signature(&send_function) << endl; + scope_up(f_service_); + + f_service_ << + indent() << "oprot_.WriteMessageBegin(new TMessage(\"" << funname << "\", TMessageType.Call, seqid_));" << endl << + indent() << argsname << " args = new " << argsname << "();" << endl; + + for (fld_iter = fields.begin(); fld_iter != fields.end(); ++fld_iter) { + f_service_ << + indent() << "args." << (*fld_iter)->get_name() << " = " << (*fld_iter)->get_name() << ";" << endl; + } + + f_service_ << + indent() << "args.Write(oprot_);" << endl << + indent() << "oprot_.WriteMessageEnd();" << endl << + indent() << "oprot_.Transport.Flush();" << endl; + + scope_down(f_service_); + f_service_ << endl; + + if (!(*f_iter)->is_async()) { + string resultname = (*f_iter)->get_name() + "_result"; + + t_struct noargs(program_); + t_function recv_function((*f_iter)->get_returntype(), + string("recv_") + (*f_iter)->get_name(), + &noargs, + (*f_iter)->get_xceptions()); + indent(f_service_) << + "public " << function_signature(&recv_function) << endl; + scope_up(f_service_); + + f_service_ << + indent() << "TMessage msg = iprot_.ReadMessageBegin();" << endl << + indent() << "if (msg.Type == TMessageType.Exception) {" << endl; + indent_up(); + f_service_ << + indent() << "TApplicationException x = TApplicationException.Read(iprot_);" << endl << + indent() << "iprot_.ReadMessageEnd();" << endl << + indent() << "throw x;" << endl; + indent_down(); + f_service_ << + indent() << "}" << endl << + indent() << resultname << " result = new " << resultname << "();" << endl << + indent() << "result.Read(iprot_);" << endl << + indent() << "iprot_.ReadMessageEnd();" << endl; + + if (!(*f_iter)->get_returntype()->is_void()) { + f_service_ << + indent() << "if (result.__isset.success) {" << endl << + indent() << " return result.success;" << endl << + indent() << "}" << endl; + } + + t_struct *xs = (*f_iter)->get_xceptions(); + + const std::vector& xceptions = xs->get_members(); + vector::const_iterator x_iter; + for (x_iter = xceptions.begin(); x_iter != xceptions.end(); ++x_iter) { + f_service_ << + indent() << "if (result.__isset." << (*x_iter)->get_name() << ") {" << endl << + indent() << " throw result." << (*x_iter)->get_name() << ";" << endl << + indent() << "}" << endl; + } + + if ((*f_iter)->get_returntype()->is_void()) { + indent(f_service_) << + "return;" << endl; + } else { + f_service_ << + indent() << "throw new TApplicationException(TApplicationException.ExceptionType.MissingResult, \"" << (*f_iter)->get_name() << " failed: unknown result\");" << endl; + } + + scope_down(f_service_); + f_service_ << endl; + } + } + + indent_down(); + indent(f_service_) << + "}" << endl; +} + +void t_csharp_generator::generate_service_server(t_service* tservice) { + vector functions = tservice->get_functions(); + vector::iterator f_iter; + + string extends = ""; + string extends_processor = ""; + if (tservice->get_extends() != NULL) { + extends = type_name(tservice->get_extends()); + extends_processor = extends + ".Processor, "; + } + + indent(f_service_) << + "public class Processor : " << extends_processor << "TProcessor {" << endl; + indent_up(); + + indent(f_service_) << + "public Processor(Iface iface)" << endl; + scope_up(f_service_); + if (!extends.empty()) { + f_service_ << + indent() << "base(iface);" << endl; + } + f_service_ << + indent() << "iface_ = iface;" << endl; + + for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) { + f_service_ << + indent() << "processMap_[\"" << (*f_iter)->get_name() << "\"] = new " << (*f_iter)->get_name() << "();" << endl; + } + + scope_down(f_service_); + f_service_ << endl; + + if (extends.empty()) { + f_service_ << + indent() << "protected interface ProcessFunction {" << endl << + indent() << " void Process(int seqid, Iface iface, TProtocol iprot, TProtocol oprot);" << endl << + indent() << "}" << endl << endl; + } + + f_service_ << + indent() << "private Iface iface_;" << endl; + + if (extends.empty()) { + f_service_ << + indent() << "protected Dictionary processMap_ = new Dictionary();" << endl; + } + + f_service_ << endl; + + indent(f_service_) << + "public bool Process(TProtocol iprot, TProtocol oprot)" << endl; + scope_up(f_service_); + + f_service_ << + indent() << "TMessage msg = iprot.ReadMessageBegin();" << endl; + + f_service_ << + indent() << "ProcessFunction fn = processMap_[msg.Name];" << endl << + indent() << "if (fn == null) {" << endl << + indent() << " TProtocolUtil.Skip(iprot, TType.Struct);" << endl << + indent() << " iprot.ReadMessageEnd();" << endl << + indent() << " TApplicationException x = new TApplicationException (TApplicationException.ExceptionType.UnknownMethod, \"Invalid method name: '\" + msg.Name + \"'\");" << endl << + indent() << " oprot.WriteMessageBegin(new TMessage(msg.Name, TMessageType.Exception, msg.SeqID));" << endl << + indent() << " x.Write(oprot);" << endl << + indent() << " oprot.WriteMessageEnd();" << endl << + indent() << " oprot.Transport.Flush();" << endl << + indent() << " return true;" << endl << + indent() << "}" << endl << + indent() << "fn.Process(msg.SeqID, iface_, iprot, oprot);" << endl; + + f_service_ << + indent() << "return true;" << endl; + + scope_down(f_service_); + f_service_ << endl; + + for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) + { + generate_process_function(tservice, *f_iter); + } + + indent_down(); + indent(f_service_) << + "}" << endl << endl; +} + +void t_csharp_generator::generate_function_helpers(t_function* tfunction) { + if (tfunction->is_async()) { + return; + } + + t_struct result(program_, tfunction->get_name() + "_result"); + t_field success(tfunction->get_returntype(), "success", 0); + if (!tfunction->get_returntype()->is_void()) { + result.append(&success); + } + + t_struct *xs = tfunction->get_xceptions(); + const vector& fields = xs->get_members(); + vector::const_iterator f_iter; + for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { + result.append(*f_iter); + } + + generate_csharp_struct_definition(f_service_, &result, false, true, true); +} + +void t_csharp_generator::generate_process_function(t_service* tservice, t_function* tfunction) { + indent(f_service_) << + "private class " << tfunction->get_name() << " : ProcessFunction {" << endl; + indent_up(); + + indent(f_service_) << + "public void Process(int seqid, Iface iface, TProtocol iprot, TProtocol oprot)" << endl; + scope_up(f_service_); + + string argsname = tfunction->get_name() + "_args"; + string resultname = tfunction->get_name() + "_result"; + + f_service_ << + indent() << argsname << " args = new " << argsname << "();" << endl << + indent() << "args.Read(iprot);" << endl << + indent() << "iprot.ReadMessageEnd();" << endl; + + t_struct* xs = tfunction->get_xceptions(); + const std::vector& xceptions = xs->get_members(); + vector::const_iterator x_iter; + + if (!tfunction->is_async()) { + f_service_ << + indent() << resultname << " result = new " << resultname << "();" << endl; + } + + if (xceptions.size() > 0) { + f_service_ << + indent() << "try {" << endl; + indent_up(); + } + + t_struct* arg_struct = tfunction->get_arglist(); + const std::vector& fields = arg_struct->get_members(); + vector::const_iterator f_iter; + + f_service_ << indent(); + if (!tfunction->is_async() && !tfunction->get_returntype()->is_void()) { + f_service_ << "result.success = "; + } + f_service_ << + "iface." << tfunction->get_name() << "("; + bool first = true; + for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { + if (first) { + first = false; + } else { + f_service_ << ", "; + } + f_service_ << "args." << (*f_iter)->get_name(); + } + f_service_ << ");" << endl; + + if (!tfunction->is_async() && !tfunction->get_returntype()->is_void()) { + f_service_ << + indent() << "result.__isset.success = true;" << endl; + } + + if (!tfunction->is_async() && xceptions.size() > 0) { + indent_down(); + f_service_ << indent() << "}"; + for (x_iter = xceptions.begin(); x_iter != xceptions.end(); ++x_iter) { + f_service_ << " catch (" << (*x_iter)->get_type()->get_name() << " " << (*x_iter)->get_name() << ") {" << endl; + if (!tfunction->is_async()) { + indent_up(); + f_service_ << + indent() << "result." << (*x_iter)->get_name() << " = " << (*x_iter)->get_name() << ";" << endl << + indent() << "result.__isset." << (*x_iter)->get_name() << " = true;" << endl; + indent_down(); + f_service_ << indent() << "}"; + } else { + f_service_ << "}"; + } + } + f_service_ << endl; + } + + if (tfunction->is_async()) { + f_service_ << + indent() << "return;" << endl; + scope_down(f_service_); + + indent_down(); + f_service_ << + indent() << "}" << endl << endl; + return; + } + + f_service_ << + indent() << "oprot.WriteMessageBegin(new TMessage(\"" << tfunction->get_name() << "\", TMessageType.Reply, seqid)); " << endl << + indent() << "result.Write(oprot);" << endl << + indent() << "oprot.WriteMessageEnd();" << endl << + indent() << "oprot.Transport.Flush();" << endl; + + scope_down(f_service_); + + f_service_ << endl; + + indent_down(); + + f_service_ << + indent() << "}" << endl << endl; +} + +void t_csharp_generator::generate_deserialize_field(ofstream& out, t_field* tfield, string prefix) { + t_type* type = tfield->get_type(); + while(type->is_typedef()) { + type = ((t_typedef*)type)->get_type(); + } + + if (type->is_void()) { + throw "CANNOT GENERATE DESERIALIZE CODE FOR void TYPE: " + prefix + tfield->get_name(); + } + + string name = prefix + tfield->get_name(); + + if (type->is_struct() || type->is_xception()) { + generate_deserialize_struct(out, (t_struct*)type, name); + } else if (type->is_container()) { + generate_deserialize_container(out, type, name); + } else if (type->is_base_type() || type->is_enum()) { + indent(out) << + name << " = "; + + if (type->is_enum()) + { + out << "(" << type_name(type, false, true) << ")"; + } + + out << "iprot."; + + if (type->is_base_type()) { + t_base_type::t_base tbase = ((t_base_type*)type)->get_base(); + switch (tbase) { + case t_base_type::TYPE_VOID: + throw "compiler error: cannot serialize void field in a struct: " + name; + break; + case t_base_type::TYPE_STRING: + out << "ReadString();"; + break; + case t_base_type::TYPE_BOOL: + out << "ReadBool();"; + break; + case t_base_type::TYPE_BYTE: + out << "ReadByte();"; + break; + case t_base_type::TYPE_I16: + out << "ReadI16();"; + break; + case t_base_type::TYPE_I32: + out << "ReadI32();"; + break; + case t_base_type::TYPE_I64: + out << "ReadI64();"; + break; + case t_base_type::TYPE_DOUBLE: + out << "ReadDouble();"; + break; + default: + throw "compiler error: no C# name for base type " + tbase; + } + } else if (type->is_enum()) { + out << "ReadI32();"; + } + out << endl; + } else { + printf("DO NOT KNOW HOW TO DESERIALIZE FIELD '%s' TYPE '%s'\n", tfield->get_name().c_str(), type_name(type).c_str()); + } +} + +void t_csharp_generator::generate_deserialize_struct(ofstream& out, t_struct* tstruct, string prefix) { + out << + indent() << prefix << " = new " << type_name(tstruct) << "();" << endl << + indent() << prefix << ".Read(iprot);" << endl; +} + +void t_csharp_generator::generate_deserialize_container(ofstream& out, t_type* ttype, string prefix) { + scope_up(out); + + string obj; + + if (ttype->is_map()) { + obj = tmp("_map"); + } else if (ttype->is_set()) { + obj = tmp("_set"); + } else if (ttype->is_list()) { + obj = tmp("_list"); + } + + indent(out) << + prefix << " = new " << type_name(ttype, false, true) << "();" <is_map()) { + out << + indent() << "TMap " << obj << " = iprot.ReadMapBegin();" << endl; + } else if (ttype->is_set()) { + out << + indent() << "TSet " << obj << " = iprot.ReadSetBegin();" << endl; + } else if (ttype->is_list()) { + out << + indent() << "TList " << obj << " = iprot.ReadListBegin();" << endl; + } + + string i = tmp("_i"); + indent(out) << + "for( int " << i << " = 0; " << i << " < " << obj << ".Count" << "; " << "++" << i << ")" << endl; + scope_up(out); + + if (ttype->is_map()) { + generate_deserialize_map_element(out, (t_map*)ttype, prefix); + } else if (ttype->is_set()) { + generate_deserialize_set_element(out, (t_set*)ttype, prefix); + } else if (ttype->is_list()) { + generate_deserialize_list_element(out, (t_list*)ttype, prefix); + } + + scope_down(out); + + if (ttype->is_map()) { + indent(out) << "iprot.ReadMapEnd();" << endl; + } else if (ttype->is_set()) { + indent(out) << "iprot.ReadSetEnd();" << endl; + } else if (ttype->is_list()) { + indent(out) << "iprot.ReadListEnd();" << endl; + } + + scope_down(out); +} + +void t_csharp_generator::generate_deserialize_map_element(ofstream& out, t_map* tmap, string prefix) { + string key = tmp("_key"); + string val = tmp("_val"); + + t_field fkey(tmap->get_key_type(), key); + t_field fval(tmap->get_val_type(), val); + + indent(out) << + declare_field(&fkey) << endl; + indent(out) << + declare_field(&fval) << endl; + + generate_deserialize_field(out, &fkey); + generate_deserialize_field(out, &fval); + + indent(out) << + prefix << "[" << key << "] = " << val << ";" << endl; +} + +void t_csharp_generator::generate_deserialize_set_element(ofstream& out, t_set* tset, string prefix) { + string elem = tmp("_elem"); + t_field felem(tset->get_elem_type(), elem); + + indent(out) << + declare_field(&felem, true) << endl; + + generate_deserialize_field(out, &felem); + + indent(out) << + prefix << ".Add(" << elem << ");" << endl; +} + +void t_csharp_generator::generate_deserialize_list_element(ofstream& out, t_list* tlist, string prefix) { + string elem = tmp("_elem"); + t_field felem(tlist->get_elem_type(), elem); + + indent(out) << + declare_field(&felem, true) << endl; + + generate_deserialize_field(out, &felem); + + indent(out) << + prefix << ".Add(" << elem << ");" << endl; +} + +void t_csharp_generator::generate_serialize_field(ofstream& out, t_field* tfield, string prefix) { + t_type* type = tfield->get_type(); + while (type->is_typedef()) { + type = ((t_typedef*)type)->get_type(); + } + + if (type->is_void()) { + throw "CANNOT GENERATE SERIALIZE CODE FOR void TYPE: " + prefix + tfield->get_name(); + } + + if (type->is_struct() || type->is_xception()) { + generate_serialize_struct(out, (t_struct*)type, prefix + tfield->get_name()); + } else if (type->is_container()) { + generate_serialize_container(out, type, prefix + tfield->get_name()); + } else if (type->is_base_type() || type->is_enum()) { + string name = prefix + tfield->get_name(); + indent(out) << + "oprot."; + + if (type->is_base_type()) { + t_base_type::t_base tbase = ((t_base_type*)type)->get_base(); + + switch(tbase) { + case t_base_type::TYPE_VOID: + throw "compiler error: cannot serialize void field in a struct: " + name; + break; + case t_base_type::TYPE_STRING: + out << "WriteString(" << name << ");"; + break; + case t_base_type::TYPE_BOOL: + out << "WriteBool(" << name << ");"; + break; + case t_base_type::TYPE_BYTE: + out << "WriteByte(" << name << ");"; + break; + case t_base_type::TYPE_I16: + out << "WriteI16(" << name << ");"; + break; + case t_base_type::TYPE_I32: + out << "WriteI32(" << name << ");"; + break; + case t_base_type::TYPE_I64: + out << "WriteI64(" << name << ");"; + break; + case t_base_type::TYPE_DOUBLE: + out << "WriteDouble(" << name << ");"; + break; + default: + throw "compiler error: no C# name for base type " + tbase; + } + } else if (type->is_enum()) { + out << "WriteI32((int)" << name << ");"; + } + out << endl; + } else { + printf("DO NOT KNOW HOW TO SERIALIZE '%s%s' TYPE '%s'\n", + prefix.c_str(), + tfield->get_name().c_str(), + type_name(type).c_str()); + } +} + +void t_csharp_generator::generate_serialize_struct(ofstream& out, t_struct* tstruct, string prefix) { + out << + indent() << prefix << ".Write(oprot);" << endl; +} + +void t_csharp_generator::generate_serialize_container(ofstream& out, t_type* ttype, string prefix) { + scope_up(out); + + if (ttype->is_map()) { + indent(out) << + "oprot.WriteMapBegin(new TMap(" << + type_to_enum(((t_map*)ttype)->get_key_type()) << ", " << + type_to_enum(((t_map*)ttype)->get_val_type()) << ", " << + prefix << ".Count));" << endl; + } else if (ttype->is_set()) { + indent(out) << + "oprot.WriteSetBegin(new TSet(" << + type_to_enum(((t_set*)ttype)->get_elem_type()) << ", " << + prefix << ".Count));" << endl; + } else if (ttype->is_list()) { + indent(out) << + "oprot.WriteListBegin(new TList(" << + type_to_enum(((t_list*)ttype)->get_elem_type()) << ", " << + prefix << ".Count));" << endl; + } + + string iter = tmp("_iter"); + if (ttype->is_map()) { + indent(out) << + "foreach (" << + type_name(((t_map*)ttype)->get_key_type()) << " " << iter << + " in " << + prefix << ".Keys)"; + } else if (ttype->is_set()) { + indent(out) << + "foreach (" << + type_name(((t_set*)ttype)->get_elem_type()) << " " << iter << + " in " << + prefix << ")"; + } else if (ttype->is_list()) { + indent(out) << + "foreach (" << + type_name(((t_list*)ttype)->get_elem_type()) << " " << iter << + " in " << + prefix << ")"; + } + + out << endl; + scope_up(out); + + if (ttype->is_map()) { + generate_serialize_map_element(out, (t_map*)ttype, iter, prefix); + } else if (ttype->is_set()) { + generate_serialize_set_element(out, (t_set*)ttype, iter); + } else if (ttype->is_list()) { + generate_serialize_list_element(out, (t_list*)ttype, iter); + } + + if (ttype->is_map()) { + indent(out) << "oprot.WriteMapEnd();" << endl; + } else if (ttype->is_set()) { + indent(out) << "oprot.WriteSetEnd();" << endl; + } else if (ttype->is_list()) { + indent(out) << "oprot.WriteListEnd();" << endl; + } + + scope_down(out); + scope_down(out); +} + +void t_csharp_generator::generate_serialize_map_element(ofstream& out, t_map* tmap, string iter, string map) { + t_field kfield(tmap->get_key_type(), iter); + generate_serialize_field(out, &kfield, ""); + t_field vfield(tmap->get_val_type(), map + "[" + iter + "]"); + generate_serialize_field(out, &vfield, ""); +} + +void t_csharp_generator::generate_serialize_set_element(ofstream& out, t_set* tset, string iter) { + t_field efield(tset->get_elem_type(), iter); + generate_serialize_field(out, &efield, ""); +} + +void t_csharp_generator::generate_serialize_list_element(ofstream& out, t_list* tlist, string iter) { + t_field efield(tlist->get_elem_type(), iter); + generate_serialize_field(out, &efield, ""); +} + +string t_csharp_generator::type_name(t_type* ttype, bool in_container, bool in_init) { + while (ttype->is_typedef()) { + ttype = ((t_typedef*)ttype)->get_type(); + } + + if (ttype->is_base_type()) { + return base_type_name(((t_base_type*)ttype)->get_base(), in_container); + } else if (ttype->is_map()) { + t_map *tmap = (t_map*) ttype; + return "Dictionary<" + type_name(tmap->get_key_type(), true) + + ", " + type_name(tmap->get_val_type(), true) + ">"; + } else if (ttype->is_set()) { + t_set* tset = (t_set*) ttype; + return "HashSet<" + type_name(tset->get_elem_type(), true) + ">"; + } else if (ttype->is_list()) { + t_list* tlist = (t_list*) ttype; + return "List<" + type_name(tlist->get_elem_type(), true) + ">"; + } + + t_program* program = ttype->get_program(); + if (program != NULL && program != program_) { + string ns = program->get_java_package(); + if (!ns.empty()) { + return ns + "." + ttype->get_name(); + } + } + + return ttype->get_name(); +} + +string t_csharp_generator::base_type_name(t_base_type::t_base tbase, bool in_container) { + switch (tbase) { + case t_base_type::TYPE_VOID: + return "void"; + case t_base_type::TYPE_STRING: + return "string"; + case t_base_type::TYPE_BOOL: + return "bool"; + case t_base_type::TYPE_BYTE: + return "byte"; + case t_base_type::TYPE_I16: + return "short"; + case t_base_type::TYPE_I32: + return "int"; + case t_base_type::TYPE_I64: + return "long"; + case t_base_type::TYPE_DOUBLE: + return "double"; + default: + throw "compiler error: no C# name for base type " + tbase; + } +} + +string t_csharp_generator::declare_field(t_field* tfield, bool init) { + string result = type_name(tfield->get_type()) + " " + tfield->get_name(); + if (init) { + t_type* ttype = tfield->get_type(); + while (ttype->is_typedef()) { + ttype = ((t_typedef*)ttype)->get_type(); + } + if (ttype->is_base_type() && tfield->get_value() != NULL) { + ofstream dummy; + result += " = " + render_const_value(dummy, tfield->get_name(), ttype, tfield->get_value()); + } else if (ttype->is_base_type()) { + t_base_type::t_base tbase = ((t_base_type*)ttype)->get_base(); + switch (tbase) { + case t_base_type::TYPE_VOID: + throw "NO T_VOID CONSTRUCT"; + case t_base_type::TYPE_STRING: + result += " = null"; + break; + case t_base_type::TYPE_BOOL: + result += " = false"; + break; + case t_base_type::TYPE_BYTE: + case t_base_type::TYPE_I16: + case t_base_type::TYPE_I32: + case t_base_type::TYPE_I64: + result += " = 0"; + break; + case t_base_type::TYPE_DOUBLE: + result += " = (double)0"; + break; + } + } else if (ttype->is_enum()) { + result += " = (" + type_name(ttype, false, true) + ")0"; + } else if (ttype->is_container()) { + result += " = new " + type_name(ttype, false, true) + "()"; + } else { + result += " = new " + type_name(ttype, false, true) + "()"; + } + } + return result + ";"; +} + +string t_csharp_generator::function_signature(t_function* tfunction, string prefix) { + t_type* ttype = tfunction->get_returntype(); + return type_name(ttype) + " " + prefix + tfunction->get_name() + "(" + argument_list(tfunction->get_arglist()) + ")"; +} + +string t_csharp_generator::argument_list(t_struct* tstruct) { + string result = ""; + const vector& fields = tstruct->get_members(); + vector::const_iterator f_iter; + bool first = true; + for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { + if (first) { + first = false; + } else { + result += ", "; + } + result += type_name((*f_iter)->get_type()) + " " + (*f_iter)->get_name(); + } + return result; +} + +string t_csharp_generator::type_to_enum(t_type* type) { + while (type->is_typedef()) { + type = ((t_typedef*)type)->get_type(); + } + + if (type->is_base_type()) { + t_base_type::t_base tbase = ((t_base_type*)type)->get_base(); + switch (tbase) { + case t_base_type::TYPE_VOID: + throw "NO T_VOID CONSTRUCT"; + case t_base_type::TYPE_STRING: + return "TType.String"; + case t_base_type::TYPE_BOOL: + return "TType.Bool"; + case t_base_type::TYPE_BYTE: + return "TType.Byte"; + case t_base_type::TYPE_I16: + return "TType.I16"; + case t_base_type::TYPE_I32: + return "TType.I32"; + case t_base_type::TYPE_I64: + return "TType.I64"; + case t_base_type::TYPE_DOUBLE: + return "TType.Double"; + } + } else if (type->is_enum()) { + return "TType.I32"; + } else if (type->is_struct() || type->is_xception()) { + return "TType.Struct"; + } else if (type->is_map()) { + return "TType.Map"; + } else if (type->is_set()) { + return "TType.Set"; + } else if (type->is_list()) { + return "TType.List"; + } + + throw "INVALID TYPE IN type_to_enum: " + type->get_name(); +} diff --git a/compiler/cpp/src/generate/t_csharp_generator.h b/compiler/cpp/src/generate/t_csharp_generator.h new file mode 100644 index 0000000..33d1491 --- /dev/null +++ b/compiler/cpp/src/generate/t_csharp_generator.h @@ -0,0 +1,94 @@ +// Distributed under the Thrift Software License +// +// See accompanying file LICENSE or visit the Thrift site at: +// http://developers.facebook.com/thrift/ + +#ifndef T_CSHARP_GENERATOR_H +#define T_CSHARP_GENERATOR_H + +#include +#include +#include +#include + +#include "t_oop_generator.h" + +class t_csharp_generator : public t_oop_generator +{ + public: + t_csharp_generator(t_program* program) : t_oop_generator(program) { + out_dir_base_ = "gen-csharp"; + } + void init_generator(); + void close_generator(); + + void generate_consts(std::vector consts); + + void generate_typedef (t_typedef* ttypedef); + void generate_enum (t_enum* tenum); + void generate_struct (t_struct* tstruct); + void generate_xception (t_struct* txception); + void generate_service (t_service* tservice); + bool print_const_value (std::ofstream& out, std::string name, t_type* type, t_const_value* value, bool in_static, bool defval=false, bool needtype=false); + std::string render_const_value(std::ofstream& out, std::string name, t_type* type, t_const_value* value); + void print_const_constructor(std::ofstream& out, std::vector consts); + void print_const_def_value(std::ofstream& out, std::string name, t_type* type, t_const_value* value); + + void generate_csharp_struct(t_struct* tstruct, bool is_exception); + void generate_csharp_struct_definition(std::ofstream& out, t_struct* tstruct, bool is_xception=false, bool in_class=false, bool is_result=false); + void generate_csharp_struct_reader(std::ofstream& out, t_struct* tstruct); + void generate_csharp_struct_result_writer(std::ofstream& out, t_struct* tstruct); + void generate_csharp_struct_writer(std::ofstream& out, t_struct* tstruct); + void generate_csharp_struct_tostring(std::ofstream& out, t_struct* tstruct); + + void generate_function_helpers(t_function* tfunction); + void generate_service_interface (t_service* tservice); + void generate_service_helpers (t_service* tservice); + void generate_service_client (t_service* tservice); + void generate_service_server (t_service* tservice); + void generate_process_function (t_service* tservice, t_function* function); + + void generate_deserialize_field (std::ofstream& out, t_field* tfield, std::string prefix=""); + void generate_deserialize_struct (std::ofstream& out, t_struct* tstruct, std::string prefix=""); + void generate_deserialize_container (std::ofstream& out, t_type* ttype, std::string prefix=""); + void generate_deserialize_set_element (std::ofstream& out, t_set* tset, std::string prefix=""); + void generate_deserialize_map_element (std::ofstream& out, t_map* tmap, std::string prefix=""); + void generate_deserialize_list_element (std::ofstream& out, t_list* list, std::string prefix=""); + void generate_serialize_field (std::ofstream& out, t_field* tfield, std::string prefix=""); + void generate_serialize_struct (std::ofstream& out, t_struct* tstruct, std::string prefix=""); + void generate_serialize_container (std::ofstream& out, t_type* ttype, std::string prefix=""); + void generate_serialize_map_element (std::ofstream& out, t_map* tmap, std::string iter, std::string map); + void generate_serialize_set_element (std::ofstream& out, t_set* tmap, std::string iter); + void generate_serialize_list_element (std::ofstream& out, t_list* tlist, std::string iter); + + void start_csharp_namespace (std::ofstream& out); + void end_csharp_namespace (std::ofstream& out); + + std::string csharp_type_usings(); + std::string csharp_thrift_usings(); + + std::string type_name(t_type* ttype, bool in_countainer=false, bool in_init=false); + std::string base_type_name(t_base_type::t_base tbase, bool in_container=false); + std::string declare_field(t_field* tfield, bool init=false); + std::string function_signature(t_function* tfunction, std::string prefix=""); + std::string argument_list(t_struct* tstruct); + std::string type_to_enum(t_type* ttype); + + bool type_can_be_null(t_type* ttype) { + while (ttype->is_typedef()) { + ttype = ((t_typedef*)ttype)->get_type(); + } + + return ttype->is_container() || + ttype->is_struct() || + ttype->is_xception() || + ttype->is_string(); + } + + private: + std::string namespace_name_; + std::ofstream f_service_; + std::string namespace_dir_; +}; + +#endif diff --git a/compiler/cpp/src/main.cc b/compiler/cpp/src/main.cc index 4877431..131820a 100644 --- a/compiler/cpp/src/main.cc +++ b/compiler/cpp/src/main.cc @@ -46,6 +46,7 @@ #include "generate/t_hs_generator.h" #include "generate/t_cocoa_generator.h" #include "generate/t_st_generator.h" +#include "generate/t_csharp_generator.h" using namespace std; @@ -156,6 +157,7 @@ bool gen_erl = false; bool gen_ocaml = false; bool gen_hs = false; bool gen_cocoa = false; +bool gen_csharp = false; bool gen_st = false; bool gen_recurse = false; @@ -605,6 +607,7 @@ void usage() { fprintf(stderr, " -ocaml Generate OCaml output files\n"); fprintf(stderr, " -hs Generate Haskell output files\n"); fprintf(stderr, " -cocoa Generate Cocoa/Objective-C output files\n"); + fprintf(stderr, " -csharp Generate C# output files\n"); fprintf(stderr, " -st Generate Squeak/Smalltalk output files\n"); fprintf(stderr, " -o dir Set the output directory for gen-* packages\n"); fprintf(stderr, " (default: current directory)\n"); @@ -924,6 +927,13 @@ void generate(t_program* program) { delete st; } + if (gen_csharp) { + pverbose("Generating C#\n"); + t_csharp_generator* csharp = new t_csharp_generator(program); + csharp->generate_program(); + delete csharp; + } + if (dump_docs) { dump_docstrings(program); } @@ -1022,6 +1032,8 @@ int main(int argc, char** argv) { gen_cocoa = true; } else if (strcmp(arg, "-st") == 0) { gen_st = true; + } else if (strcmp(arg, "-csharp") == 0) { + gen_csharp = true; } else if (strcmp(arg, "-I") == 0) { // An argument of "-I\ asdf" is invalid and has unknown results arg = argv[++i]; @@ -1068,7 +1080,7 @@ int main(int argc, char** argv) { } // You gotta generate something! - if (!gen_cpp && !gen_java && !gen_javabean && !gen_php && !gen_phpi && !gen_py && !gen_rb && !gen_xsd && !gen_perl && !gen_erl && !gen_ocaml && !gen_hs && !gen_cocoa && !gen_st) { + if (!gen_cpp && !gen_java && !gen_javabean && !gen_php && !gen_phpi && !gen_py && !gen_rb && !gen_xsd && !gen_perl && !gen_erl && !gen_ocaml && !gen_hs && !gen_cocoa && !gen_st && !gen_csharp) { fprintf(stderr, "!!! No output language(s) specified\n\n"); usage(); } diff --git a/compiler/cpp/src/parse/t_program.h b/compiler/cpp/src/parse/t_program.h index dc8c80c..66f0620 100644 --- a/compiler/cpp/src/parse/t_program.h +++ b/compiler/cpp/src/parse/t_program.h @@ -152,6 +152,14 @@ class t_program : public t_doc { return java_package_; } + void set_csharp_namespace(std::string csharp_namespace) { + csharp_namespace_ = csharp_namespace; + } + + const std::string& get_csharp_namespace() const { + return csharp_namespace_; + } + void set_xsd_namespace(std::string xsd_namespace) { xsd_namespace_ = xsd_namespace; } @@ -267,6 +275,9 @@ class t_program : public t_doc { std::string smalltalk_category_; // Smalltalk prefix std::string smalltalk_prefix_; + + // C# namespace + std::string csharp_namespace_; }; #endif diff --git a/compiler/cpp/src/thriftl.ll b/compiler/cpp/src/thriftl.ll index 0889a12..21a0dd7 100644 --- a/compiler/cpp/src/thriftl.ll +++ b/compiler/cpp/src/thriftl.ll @@ -73,6 +73,7 @@ st_identifier ([a-zA-Z-][\.a-zA-Z_0-9-]*) "cpp_type" { return tok_cpp_type; } "java_package" { return tok_java_package; } "cocoa_prefix" { return tok_cocoa_prefix; } +"csharp_namespace" { return tok_csharp_namespace; } "php_namespace" { return tok_php_namespace; } "py_module" { return tok_py_module; } "perl_package" { return tok_perl_package; } diff --git a/compiler/cpp/src/thrifty.yy b/compiler/cpp/src/thrifty.yy index 9475adf..5be19b3 100644 --- a/compiler/cpp/src/thrifty.yy +++ b/compiler/cpp/src/thrifty.yy @@ -89,6 +89,7 @@ int g_arglist = 0; %token tok_smalltalk_category %token tok_smalltalk_prefix %token tok_cocoa_prefix +%token tok_csharp_namespace /** * Base datatype keywords @@ -331,6 +332,13 @@ Header: g_program->set_xsd_namespace($2); } } +| tok_csharp_namespace tok_identifier + { + pdebug("Header -> tok_csharp_package tok_identifier"); + if (g_parse_mode == PROGRAM) { + g_program->set_csharp_namespace($2); + } + } Include: tok_include tok_literal diff --git a/lib/csharp/ThriftMSBuildTask/Properties/AssemblyInfo.cs b/lib/csharp/ThriftMSBuildTask/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..4375a52 --- /dev/null +++ b/lib/csharp/ThriftMSBuildTask/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("ThriftMSBuildTask")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("ThriftMSBuildTask")] +[assembly: AssemblyCopyright("Copyright © 2007")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("5095e09d-7b95-4be1-b250-e1c1db1c485e")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/lib/csharp/ThriftMSBuildTask/ThriftBuild.cs b/lib/csharp/ThriftMSBuildTask/ThriftBuild.cs new file mode 100644 index 0000000..9cf5f41 --- /dev/null +++ b/lib/csharp/ThriftMSBuildTask/ThriftBuild.cs @@ -0,0 +1,202 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Microsoft.Build.Framework; +using Microsoft.Build.Utilities; +using Microsoft.Build.Tasks; +using System.IO; +using System.Diagnostics; + +namespace ThriftMSBuildTask +{ + /// + /// MSBuild Task to generate csharp from .thrift files, and compile the code into a library: ThriftImpl.dll + /// + public class ThriftBuild : Task + { + /// + /// The full path to the thrift.exe compiler + /// + [Required] + public ITaskItem ThriftExecutable + { + get; + set; + } + + /// + /// The full path to a thrift.dll C# library + /// + [Required] + public ITaskItem ThriftLibrary + { + get; + set; + } + + /// + /// A direcotry containing .thrift files + /// + [Required] + public ITaskItem ThriftDefinitionDir + { + get; + set; + } + + /// + /// The name of the auto-gen and compiled thrift library. It will placed in + /// the same directory as ThriftLibrary + /// + [Required] + public ITaskItem OutputName + { + get; + set; + } + + /// + /// The full path to the compiled ThriftLibrary. This allows msbuild tasks to use this + /// output as a variable for use elsewhere. + /// + [Output] + public ITaskItem ThriftImplementation + { + get { return thriftImpl; } + } + + private ITaskItem thriftImpl; + private const string lastCompilationName = "LAST_COMP_TIMESTAMP"; + + //use the Message Build Task to write something to build log + private void LogMessage(string text, MessageImportance importance) + { + Message m = new Message(); + m.Text = text; + m.Importance = importance.ToString(); + m.BuildEngine = this.BuildEngine; + m.Execute(); + } + + //recursively find .cs files in srcDir, paths should initially be non-null and empty + private void FindSourcesHelper(string srcDir, List paths) + { + string[] files = Directory.GetFiles(srcDir, "*.cs"); + foreach (string f in files) + { + paths.Add(f); + } + string[] dirs = Directory.GetDirectories(srcDir); + foreach (string dir in dirs) + { + FindSourcesHelper(dir, paths); + } + } + + /// + /// Quote paths with spaces + /// + private string SafePath(string path) + { + if (path.Contains(' ') && !path.StartsWith("\"")) + { + return "\"" + path + "\""; + } + return path; + } + + private ITaskItem[] FindSources(string srcDir) + { + List files = new List(); + FindSourcesHelper(srcDir, files); + ITaskItem[] items = new ITaskItem[files.Count]; + for (int i = 0; i < items.Length; i++) + { + items[i] = new TaskItem(files[i]); + } + return items; + } + + private string LastWriteTime(string defDir) + { + string[] files = Directory.GetFiles(defDir, "*.thrift"); + DateTime d = (new DirectoryInfo(defDir)).LastWriteTime; + foreach(string file in files) + { + FileInfo f = new FileInfo(file); + DateTime curr = f.LastWriteTime; + if (DateTime.Compare(curr, d) > 0) + { + d = curr; + } + } + return d.ToFileTimeUtc().ToString(); + } + + public override bool Execute() + { + string defDir = SafePath(ThriftDefinitionDir.ItemSpec); + //look for last compilation timestamp + string lastBuildPath = Path.Combine(defDir, lastCompilationName); + DirectoryInfo defDirInfo = new DirectoryInfo(defDir); + string lastWrite = LastWriteTime(defDir); + if (File.Exists(lastBuildPath)) + { + string lastComp = File.ReadAllText(lastBuildPath); + + if (lastComp == lastWrite) + { + //the .thrift dir hasn't been written to since last compilation, don't need to do anything + LogMessage("ThriftImpl up-to-date", MessageImportance.Normal); + return true; + } + } + + //find the directory of the thriftlibrary (that's where output will go) + FileInfo thriftLibInfo = new FileInfo(SafePath(ThriftLibrary.ItemSpec)); + string thriftDir = thriftLibInfo.Directory.FullName; + + string genDir = Path.Combine(thriftDir, "gen-csharp"); + if (Directory.Exists(genDir)) + { + Directory.Delete(genDir, true); + } + + //run the thrift executable to generate C# + foreach (string thriftFile in Directory.GetFiles(defDir, "*.thrift")) + { + LogMessage("Generating code for: " + thriftFile, MessageImportance.Normal); + Process p = new Process(); + p.StartInfo.FileName = SafePath(ThriftExecutable.ItemSpec); + p.StartInfo.Arguments = "-csharp -o " + SafePath(thriftDir) + " -r " + thriftFile; + p.StartInfo.UseShellExecute = false; + p.StartInfo.CreateNoWindow = true; + p.StartInfo.RedirectStandardOutput = false; + p.Start(); + p.WaitForExit(); + } + + Csc csc = new Csc(); + csc.TargetType = "library"; + csc.References = new ITaskItem[] { new TaskItem(ThriftLibrary.ItemSpec) }; + csc.EmitDebugInformation = true; + string outputPath = Path.Combine(thriftDir, OutputName.ItemSpec); + csc.OutputAssembly = new TaskItem(outputPath); + csc.Sources = FindSources(Path.Combine(thriftDir, "gen-csharp")); + csc.BuildEngine = this.BuildEngine; + LogMessage("Compiling generated cs...", MessageImportance.Normal); + if (!csc.Execute()) + { + return false; + } + + //write file to defDir to indicate a build was successfully completed + File.WriteAllText(lastBuildPath, lastWrite); + + thriftImpl = new TaskItem(outputPath); + + return true; + } + } +} diff --git a/lib/csharp/ThriftMSBuildTask/ThriftMSBuildTask.csproj b/lib/csharp/ThriftMSBuildTask/ThriftMSBuildTask.csproj new file mode 100644 index 0000000..68c6bd1 --- /dev/null +++ b/lib/csharp/ThriftMSBuildTask/ThriftMSBuildTask.csproj @@ -0,0 +1,66 @@ + + + + Debug + AnyCPU + 9.0.21022 + 2.0 + {EC0A0231-66EA-4593-A792-C6CA3BB8668E} + Library + Properties + ThriftMSBuildTask + ThriftMSBuildTask + v3.5 + 512 + SAK + SAK + SAK + SAK + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + + + + + 3.5 + + + 3.5 + + + 3.5 + + + + + + + + + + + \ No newline at end of file diff --git a/lib/csharp/src/Protocol/TBinaryProtocol.cs b/lib/csharp/src/Protocol/TBinaryProtocol.cs new file mode 100644 index 0000000..36f03d0 --- /dev/null +++ b/lib/csharp/src/Protocol/TBinaryProtocol.cs @@ -0,0 +1,386 @@ +// +// TBinaryProtocol.cs +// +// Begin: Aug 19, 2007 +// Authors: +// Todd Berman +// Will Palmeri +// +// Distributed under the Thrift Software License +// +// See accompanying file LICENSE or visit the Thrift site at: +// http://developers.facebook.com/thrift/using + +using System; +using System.Collections.Generic; +using System.Text; +using Thrift.Transport; + +namespace Thrift.Protocol +{ + public class TBinaryProtocol : TProtocol + { + protected const uint VERSION_MASK = 0xffff0000; + protected const uint VERSION_1 = 0x80010000; + + protected bool strictRead_ = false; + protected bool strictWrite_ = true; + + protected int readLength_; + protected bool checkReadLength_ = false; + + + #region BinaryProtocol Factory + /** + * Factory + */ + public class Factory : TProtocolFactory { + + protected bool strictRead_ = false; + protected bool strictWrite_ = true; + + public Factory() + :this(false, true) + { + } + + public Factory(bool strictRead, bool strictWrite) + { + strictRead_ = strictRead; + strictWrite_ = strictWrite; + } + + public TProtocol GetProtocol(TTransport trans) { + return new TBinaryProtocol(trans, strictRead_, strictWrite_); + } + } + + #endregion + + public TBinaryProtocol(TTransport trans) + : this(trans, false, true) + { + } + + public TBinaryProtocol(TTransport trans, bool strictRead, bool strictWrite) + :base(trans) + { + strictRead_ = strictRead; + strictWrite_ = strictWrite; + } + + #region Write Methods + + public override void WriteMessageBegin(TMessage message) + { + if (strictWrite_) + { + uint version = VERSION_1 | (uint)(message.Type); + WriteI32((int)version); + WriteString(message.Name); + WriteI32(message.SeqID); + } + else + { + WriteString(message.Name); + WriteByte((byte)message.Type); + WriteI32(message.SeqID); + } + } + + public override void WriteMessageEnd() + { + } + + public override void WriteStructBegin(TStruct struc) + { + } + + public override void WriteStructEnd() + { + } + + public override void WriteFieldBegin(TField field) + { + WriteByte((byte)field.Type); + WriteI16(field.ID); + } + + public override void WriteFieldEnd() + { + } + + public override void WriteFieldStop() + { + WriteByte((byte)TType.Stop); + } + + public override void WriteMapBegin(TMap map) + { + WriteByte((byte)map.KeyType); + WriteByte((byte)map.ValueType); + WriteI32(map.Count); + } + + public override void WriteMapEnd() + { + } + + public override void WriteListBegin(TList list) + { + WriteByte((byte)list.ElementType); + WriteI32(list.Count); + } + + public override void WriteListEnd() + { + } + + public override void WriteSetBegin(TSet set) + { + WriteByte((byte)set.ElementType); + WriteI32(set.Count); + } + + public override void WriteSetEnd() + { + } + + public override void WriteBool(bool b) + { + WriteByte(b ? (byte)1 : (byte)0); + } + + private byte[] bout = new byte[1]; + public override void WriteByte(byte b) + { + bout[0] = b; + trans.Write(bout, 0, 1); + } + + private byte[] i16out = new byte[2]; + public override void WriteI16(short s) + { + i16out[0] = (byte)(0xff & (s >> 8)); + i16out[1] = (byte)(0xff & s); + trans.Write(i16out, 0, 2); + } + + private byte[] i32out = new byte[4]; + public override void WriteI32(int i32) + { + i32out[0] = (byte)(0xff & (i32 >> 24)); + i32out[1] = (byte)(0xff & (i32 >> 16)); + i32out[2] = (byte)(0xff & (i32 >> 8)); + i32out[3] = (byte)(0xff & i32); + trans.Write(i32out, 0, 4); + } + + private byte[] i64out = new byte[8]; + public override void WriteI64(long i64) + { + i64out[0] = (byte)(0xff & (i64 >> 56)); + i64out[1] = (byte)(0xff & (i64 >> 48)); + i64out[2] = (byte)(0xff & (i64 >> 40)); + i64out[3] = (byte)(0xff & (i64 >> 32)); + i64out[4] = (byte)(0xff & (i64 >> 24)); + i64out[5] = (byte)(0xff & (i64 >> 16)); + i64out[6] = (byte)(0xff & (i64 >> 8)); + i64out[7] = (byte)(0xff & i64); + trans.Write(i64out, 0, 8); + } + + public override void WriteDouble(double d) + { + WriteI64(BitConverter.DoubleToInt64Bits(d)); + } + + public override void WriteString(string s) + { + byte[] b = Encoding.UTF8.GetBytes(s); + WriteI32(b.Length); + trans.Write(b, 0, b.Length); + } + + #endregion + + #region ReadMethods + + public override TMessage ReadMessageBegin() + { + TMessage message = new TMessage(); + int size = ReadI32(); + if (size < 0) + { + uint version = (uint)size & VERSION_MASK; + if (version != VERSION_1) + { + throw new TProtocolException(TProtocolException.BAD_VERSION, "Bad version in ReadMessageBegin: " + version); + } + message.Type = (TMessageType)(size & 0x000000ff); + message.Name = ReadString(); + message.SeqID = ReadI32(); + } + else + { + if (strictRead_) + { + throw new TProtocolException(TProtocolException.BAD_VERSION, "Missing version in readMessageBegin, old client?"); + } + message.Name = ReadStringBody(size); + message.Type = (TMessageType)ReadByte(); + message.SeqID = ReadI32(); + } + return message; + } + + public override void ReadMessageEnd() + { + } + + public override TStruct ReadStructBegin() + { + return new TStruct(); + } + + public override void ReadStructEnd() + { + } + + public override TField ReadFieldBegin() + { + TField field = new TField(); + field.Type = (TType)ReadByte(); + + if (field.Type != TType.Stop) + { + field.ID = ReadI16(); + } + + return field; + } + + public override void ReadFieldEnd() + { + } + + public override TMap ReadMapBegin() + { + TMap map = new TMap(); + map.KeyType = (TType)ReadByte(); + map.ValueType = (TType)ReadByte(); + map.Count = ReadI32(); + + return map; + } + + public override void ReadMapEnd() + { + } + + public override TList ReadListBegin() + { + TList list = new TList(); + list.ElementType = (TType)ReadByte(); + list.Count = ReadI32(); + + return list; + } + + public override void ReadListEnd() + { + } + + public override TSet ReadSetBegin() + { + TSet set = new TSet(); + set.ElementType = (TType)ReadByte(); + set.Count = ReadI32(); + + return set; + } + + public override void ReadSetEnd() + { + } + + public override bool ReadBool() + { + return ReadByte() == 1; + } + + private byte[] bin = new byte[1]; + public override byte ReadByte() + { + ReadAll(bin, 0, 1); + return bin[0]; + } + + private byte[] i16in = new byte[2]; + public override short ReadI16() + { + ReadAll(i16in, 0, 2); + return (short)(((i16in[0] & 0xff) << 8) | ((i16in[1] & 0xff))); + } + + private byte[] i32in = new byte[4]; + public override int ReadI32() + { + ReadAll(i32in, 0, 4); + return (int)(((i32in[0] & 0xff) << 24) | ((i32in[1] & 0xff) << 16) | ((i32in[2] & 0xff) << 8) | ((i32in[3] & 0xff))); + } + + private byte[] i64in = new byte[8]; + public override long ReadI64() + { + ReadAll(i64in, 0, 8); + return (long)(((long)(i64in[0] & 0xff) << 56) | ((long)(i64in[1] & 0xff) << 48) | ((long)(i64in[2] & 0xff) << 40) | ((long)(i64in[3] & 0xff) << 32) | + ((long)(i64in[4] & 0xff) << 24) | ((long)(i64in[5] & 0xff) << 16) | ((long)(i64in[6] & 0xff) << 8) | ((long)(i64in[7] & 0xff))); + } + + public override double ReadDouble() + { + return BitConverter.Int64BitsToDouble(ReadI64()); + } + + public void SetReadLength(int readLength) + { + readLength_ = readLength; + checkReadLength_ = true; + } + + protected void CheckReadLength(int length) + { + if (checkReadLength_) + { + readLength_ -= length; + if (readLength_ < 0) + { + throw new Exception("Message length exceeded: " + length); + } + } + } + + public override string ReadString() + { + int size = ReadI32(); + return ReadStringBody(size); + } + + public string ReadStringBody(int size) + { + CheckReadLength(size); + byte[] buf = new byte[size]; + trans.ReadAll(buf, 0, size); + return Encoding.UTF8.GetString(buf); + } + + private int ReadAll(byte[] buf, int off, int len) + { + CheckReadLength(len); + return trans.ReadAll(buf, off, len); + } + + #endregion + } +} diff --git a/lib/csharp/src/Protocol/TField.cs b/lib/csharp/src/Protocol/TField.cs new file mode 100644 index 0000000..f18381f --- /dev/null +++ b/lib/csharp/src/Protocol/TField.cs @@ -0,0 +1,50 @@ +// +// TField.cs +// +// Begin: Aug 19, 2007 +// Authors: +// Todd Berman +// +// Distributed under the Thrift Software License +// +// See accompanying file LICENSE or visit the Thrift site at: +// http://developers.facebook.com/thrift/using + +using System; +using System.Collections.Generic; +using System.Text; + +namespace Thrift.Protocol +{ + public class TField + { + public TField() + { + } + + public TField(string name, TType type, short id) + { + Name = name; + Type = type; + ID = id; + } + + public string Name + { + get; + set; + } + + public TType Type + { + get; + set; + } + + public short ID + { + get; + set; + } + } +} diff --git a/lib/csharp/src/Protocol/TList.cs b/lib/csharp/src/Protocol/TList.cs new file mode 100644 index 0000000..ff6aa53 --- /dev/null +++ b/lib/csharp/src/Protocol/TList.cs @@ -0,0 +1,43 @@ +// +// TList.cs +// +// Begin: Aug 19, 2007 +// Authors: +// Todd Berman +// +// Distributed under the Thrift Software License +// +// See accompanying file LICENSE or visit the Thrift site at: +// http://developers.facebook.com/thrift/using + +using System; +using System.Collections.Generic; +using System.Text; + +namespace Thrift.Protocol +{ + public class TList + { + public TList() + { + } + + public TList(TType elementType, int count) + { + ElementType = elementType; + Count = count; + } + + public TType ElementType + { + get; + set; + } + + public int Count + { + get; + set; + } + } +} diff --git a/lib/csharp/src/Protocol/TMap.cs b/lib/csharp/src/Protocol/TMap.cs new file mode 100644 index 0000000..495bf7c --- /dev/null +++ b/lib/csharp/src/Protocol/TMap.cs @@ -0,0 +1,50 @@ +// +// TMap.cs +// +// Begin: Aug 19, 2007 +// Authors: +// Todd Berman +// +// Distributed under the Thrift Software License +// +// See accompanying file LICENSE or visit the Thrift site at: +// http://developers.facebook.com/thrift/using + +using System; +using System.Collections.Generic; +using System.Text; + +namespace Thrift.Protocol +{ + public class TMap + { + public TMap() + { + } + + public TMap(TType keyType, TType valueType, int count) + { + KeyType = keyType; + ValueType = valueType; + Count = count; + } + + public TType KeyType + { + get; + set; + } + + public TType ValueType + { + get; + set; + } + + public int Count + { + get; + set; + } + } +} diff --git a/lib/csharp/src/Protocol/TMessage.cs b/lib/csharp/src/Protocol/TMessage.cs new file mode 100644 index 0000000..60df660 --- /dev/null +++ b/lib/csharp/src/Protocol/TMessage.cs @@ -0,0 +1,50 @@ +// +// TMessage.cs +// +// Begin: Aug 19, 2007 +// Authors: +// Todd Berman +// +// Distributed under the Thrift Software License +// +// See accompanying file LICENSE or visit the Thrift site at: +// http://developers.facebook.com/thrift/using + +using System; +using System.Collections.Generic; +using System.Text; + +namespace Thrift.Protocol +{ + public class TMessage + { + public TMessage() + { + } + + public TMessage(string name, TMessageType type, int seqid) + { + Name = name; + Type = type; + SeqID = seqid; + } + + public string Name + { + get; + set; + } + + public TMessageType Type + { + get; + set; + } + + public int SeqID + { + get; + set; + } + } +} diff --git a/lib/csharp/src/Protocol/TMessageType.cs b/lib/csharp/src/Protocol/TMessageType.cs new file mode 100644 index 0000000..1cadf31 --- /dev/null +++ b/lib/csharp/src/Protocol/TMessageType.cs @@ -0,0 +1,25 @@ +// +// TMessageType.cs +// +// Begin: Aug 19, 2007 +// Authors: +// Todd Berman +// +// Distributed under the Thrift Software License +// +// See accompanying file LICENSE or visit the Thrift site at: +// http://developers.facebook.com/thrift/using + +using System; +using System.Collections.Generic; +using System.Text; + +namespace Thrift.Protocol +{ + public enum TMessageType + { + Call = 1, + Reply = 2, + Exception = 3 + } +} diff --git a/lib/csharp/src/Protocol/TProtocol.cs b/lib/csharp/src/Protocol/TProtocol.cs new file mode 100644 index 0000000..f46b6d1 --- /dev/null +++ b/lib/csharp/src/Protocol/TProtocol.cs @@ -0,0 +1,75 @@ +// +// TProtocol.cs +// +// Begin: Aug 19, 2007 +// Authors: +// Todd Berman +// +// Distributed under the Thrift Software License +// +// See accompanying file LICENSE or visit the Thrift site at: +// http://developers.facebook.com/thrift/using + +using System; +using System.Collections.Generic; +using System.Text; +using Thrift.Transport; + +namespace Thrift.Protocol +{ + public abstract class TProtocol + { + protected TTransport trans; + + protected TProtocol(TTransport trans) + { + this.trans = trans; + } + + public TTransport Transport + { + get { return trans; } + } + + public abstract void WriteMessageBegin(TMessage message); + public abstract void WriteMessageEnd(); + public abstract void WriteStructBegin(TStruct struc); + public abstract void WriteStructEnd(); + public abstract void WriteFieldBegin(TField field); + public abstract void WriteFieldEnd(); + public abstract void WriteFieldStop(); + public abstract void WriteMapBegin(TMap map); + public abstract void WriteMapEnd(); + public abstract void WriteListBegin(TList list); + public abstract void WriteListEnd(); + public abstract void WriteSetBegin(TSet set); + public abstract void WriteSetEnd(); + public abstract void WriteBool(bool b); + public abstract void WriteByte(byte b); + public abstract void WriteI16(short i16); + public abstract void WriteI32(int i32); + public abstract void WriteI64(long i64); + public abstract void WriteDouble(double d); + public abstract void WriteString(string s); + + public abstract TMessage ReadMessageBegin(); + public abstract void ReadMessageEnd(); + public abstract TStruct ReadStructBegin(); + public abstract void ReadStructEnd(); + public abstract TField ReadFieldBegin(); + public abstract void ReadFieldEnd(); + public abstract TMap ReadMapBegin(); + public abstract void ReadMapEnd(); + public abstract TList ReadListBegin(); + public abstract void ReadListEnd(); + public abstract TSet ReadSetBegin(); + public abstract void ReadSetEnd(); + public abstract bool ReadBool(); + public abstract byte ReadByte(); + public abstract short ReadI16(); + public abstract int ReadI32(); + public abstract long ReadI64(); + public abstract double ReadDouble(); + public abstract string ReadString(); + } +} diff --git a/lib/csharp/src/Protocol/TProtocolException.cs b/lib/csharp/src/Protocol/TProtocolException.cs new file mode 100644 index 0000000..0941a81 --- /dev/null +++ b/lib/csharp/src/Protocol/TProtocolException.cs @@ -0,0 +1,45 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Thrift.Protocol +{ + class TProtocolException : Exception + { + public const int UNKNOWN = 0; + public const int INVALID_DATA = 1; + public const int NEGATIVE_SIZE = 2; + public const int SIZE_LIMIT = 3; + public const int BAD_VERSION = 4; + + protected int type_ = UNKNOWN; + + public TProtocolException() + : base() + { + } + + public TProtocolException(int type) + : base() + { + type_ = type; + } + + public TProtocolException(int type, String message) + : base(message) + { + type_ = type; + } + + public TProtocolException(String message) + : base(message) + { + } + + public int getType() + { + return type_; + } + } +} diff --git a/lib/csharp/src/Protocol/TProtocolFactory.cs b/lib/csharp/src/Protocol/TProtocolFactory.cs new file mode 100644 index 0000000..5434da9 --- /dev/null +++ b/lib/csharp/src/Protocol/TProtocolFactory.cs @@ -0,0 +1,23 @@ +// +// TProtocolFactory.cs +// +// Begin: Dec 3, 2007 +// Authors: +// Will Palmeri +// +// Distributed under the Thrift Software License +// +// See accompanying file LICENSE or visit the Thrift site at: +// http://developers.facebook.com/thrift/using +using System; +using System.Collections.Generic; +using System.Text; +using Thrift.Transport; + +namespace Thrift.Protocol +{ + public interface TProtocolFactory + { + TProtocol GetProtocol(TTransport trans); + } +} diff --git a/lib/csharp/src/Protocol/TProtocolUtil.cs b/lib/csharp/src/Protocol/TProtocolUtil.cs new file mode 100644 index 0000000..67f0ce6 --- /dev/null +++ b/lib/csharp/src/Protocol/TProtocolUtil.cs @@ -0,0 +1,88 @@ +// +// TProtocolUtil.cs +// +// Begin: Aug 19, 2007 +// Authors: +// Todd Berman +// +// Distributed under the Thrift Software License +// +// See accompanying file LICENSE or visit the Thrift site at: +// http://developers.facebook.com/thrift/using + +using System; +using System.Collections.Generic; +using System.Text; + +namespace Thrift.Protocol +{ + public static class TProtocolUtil + { + public static void Skip(TProtocol prot, TType type) + { + switch (type) + { + case TType.Bool: + prot.ReadBool(); + break; + case TType.Byte: + prot.ReadByte(); + break; + case TType.I16: + prot.ReadI16(); + break; + case TType.I32: + prot.ReadI32(); + break; + case TType.I64: + prot.ReadI64(); + break; + case TType.Double: + prot.ReadDouble(); + break; + case TType.String: + prot.ReadString(); + break; + case TType.Struct: + prot.ReadStructBegin(); + while (true) + { + TField field = prot.ReadFieldBegin(); + if (field.Type == TType.Stop) + { + break; + } + Skip(prot, field.Type); + prot.ReadFieldEnd(); + } + prot.ReadStructEnd(); + break; + case TType.Map: + TMap map = prot.ReadMapBegin(); + for (int i = 0; i < map.Count; i++) + { + Skip(prot, map.KeyType); + Skip(prot, map.ValueType); + } + prot.ReadMapEnd(); + break; + case TType.Set: + TSet set = prot.ReadSetBegin(); + for (int i = 0; i < set.Count; i++) + { + Skip(prot, set.ElementType); + } + prot.ReadSetEnd(); + break; + case TType.List: + TList list = prot.ReadListBegin(); + for (int i = 0; i < list.Count; i++) + { + Skip(prot, list.ElementType); + } + prot.ReadListEnd(); + break; + } + } + } +} diff --git a/lib/csharp/src/Protocol/TSet.cs b/lib/csharp/src/Protocol/TSet.cs new file mode 100644 index 0000000..c4c6fc3 --- /dev/null +++ b/lib/csharp/src/Protocol/TSet.cs @@ -0,0 +1,43 @@ +// +// TSet.cs +// +// Begin: Aug 19, 2007 +// Authors: +// Todd Berman +// +// Distributed under the Thrift Software License +// +// See accompanying file LICENSE or visit the Thrift site at: +// http://developers.facebook.com/thrift/using + +using System; +using System.Collections.Generic; +using System.Text; + +namespace Thrift.Protocol +{ + public class TSet + { + public TSet() + { + } + + public TSet(TType elementType, int count) + { + ElementType = elementType; + Count = count; + } + + public TType ElementType + { + get; + set; + } + + public int Count + { + get; + set; + } + } +} diff --git a/lib/csharp/src/Protocol/TStruct.cs b/lib/csharp/src/Protocol/TStruct.cs new file mode 100644 index 0000000..88f12df --- /dev/null +++ b/lib/csharp/src/Protocol/TStruct.cs @@ -0,0 +1,35 @@ +// +// TStruct.cs +// +// Begin: Aug 19, 2007 +// Authors: +// Todd Berman +// +// Distributed under the Thrift Software License +// +// See accompanying file LICENSE or visit the Thrift site at: +// http://developers.facebook.com/thrift/using +using System; +using System.Collections.Generic; +using System.Text; + +namespace Thrift.Protocol +{ + public class TStruct + { + public TStruct() + { + } + + public TStruct(string name) + { + Name = name; + } + + public string Name + { + get; + set; + } + } +} diff --git a/lib/csharp/src/Protocol/TType.cs b/lib/csharp/src/Protocol/TType.cs new file mode 100644 index 0000000..0badb33 --- /dev/null +++ b/lib/csharp/src/Protocol/TType.cs @@ -0,0 +1,35 @@ +// +// TType.cs +// +// Begin: Aug 19, 2007 +// Authors: +// Todd Berman +// +// Distributed under the Thrift Software License +// +// See accompanying file LICENSE or visit the Thrift site at: +// http://developers.facebook.com/thrift/using + +using System; +using System.Collections.Generic; +using System.Text; + +namespace Thrift.Protocol +{ + public enum TType : byte + { + Stop = 0, + Void = 1, + Bool = 2, + Byte = 3, + Double = 4, + I16 = 6, + I32 = 8, + I64 = 10, + String = 11, + Struct = 12, + Map = 13, + Set = 14, + List = 15 + } +} diff --git a/lib/csharp/src/Server/TServer.cs b/lib/csharp/src/Server/TServer.cs new file mode 100644 index 0000000..3e70b11 --- /dev/null +++ b/lib/csharp/src/Server/TServer.cs @@ -0,0 +1,108 @@ +// +// TServer.cs +// +// Begin: Dec 3, 2007 +// Authors: +// Will Palmeri +// +// Distributed under the Thrift Software License +// +// See accompanying file LICENSE or visit the Thrift site at: +// http://developers.facebook.com/thrift/using + +using System; +using System.Collections.Generic; +using Thrift.Protocol; +using Thrift.Transport; + +namespace Thrift.Server +{ + public abstract class TServer + { + /** + * Core processor + */ + protected TProcessor processor; + + /** + * Server transport + */ + protected TServerTransport serverTransport; + + /** + * Input Transport Factory + */ + protected TTransportFactory inputTransportFactory; + + /** + * Output Transport Factory + */ + protected TTransportFactory outputTransportFactory; + + /** + * Input Protocol Factory + */ + protected TProtocolFactory inputProtocolFactory; + + /** + * Output Protocol Factory + */ + protected TProtocolFactory outputProtocolFactory; + + /** + * Default constructors. + */ + + public TServer(TProcessor processor, + TServerTransport serverTransport) + :this(processor, serverTransport, new TTransportFactory(), new TTransportFactory(), new TBinaryProtocol.Factory(), new TBinaryProtocol.Factory()) + { + } + + public TServer(TProcessor processor, + TServerTransport serverTransport, + TTransportFactory transportFactory) + :this(processor, + serverTransport, + transportFactory, + transportFactory, + new TBinaryProtocol.Factory(), + new TBinaryProtocol.Factory()) + { + } + + public TServer(TProcessor processor, + TServerTransport serverTransport, + TTransportFactory transportFactory, + TProtocolFactory protocolFactory) + :this(processor, + serverTransport, + transportFactory, + transportFactory, + protocolFactory, + protocolFactory) + { + } + + public TServer(TProcessor processor, + TServerTransport serverTransport, + TTransportFactory inputTransportFactory, + TTransportFactory outputTransportFactory, + TProtocolFactory inputProtocolFactory, + TProtocolFactory outputProtocolFactory) + { + this.processor = processor; + this.serverTransport = serverTransport; + this.inputTransportFactory = inputTransportFactory; + this.outputTransportFactory = outputTransportFactory; + this.inputProtocolFactory = inputProtocolFactory; + this.outputProtocolFactory = outputProtocolFactory; + } + + /** + * The run method fires up the server and gets things going. + */ + public abstract void Serve(); + } +} + diff --git a/lib/csharp/src/Server/TSimpleServer.cs b/lib/csharp/src/Server/TSimpleServer.cs new file mode 100644 index 0000000..c3461ef --- /dev/null +++ b/lib/csharp/src/Server/TSimpleServer.cs @@ -0,0 +1,108 @@ +// +// TSimpleServer.cs +// +// Begin: Dec 3, 2007 +// Authors: +// Will Palmeri +// +// Distributed under the Thrift Software License +// +// See accompanying file LICENSE or visit the Thrift site at: +// http://developers.facebook.com/thrift/using +using System; +using System.Collections.Generic; +using System.Text; +using Thrift.Transport; +using Thrift.Protocol; + +namespace Thrift.Server +{ + /// + /// Simple single-threaded server for testing + /// + class TSimpleServer : TServer + { + public TSimpleServer(TProcessor processor, + TServerTransport serverTransport) + :base(processor, serverTransport, new TTransportFactory(), new TTransportFactory(), new TBinaryProtocol.Factory(), new TBinaryProtocol.Factory()) + { + } + + public TSimpleServer(TProcessor processor, + TServerTransport serverTransport, + TTransportFactory transportFactory) + :base(processor, + serverTransport, + transportFactory, + transportFactory, + new TBinaryProtocol.Factory(), + new TBinaryProtocol.Factory()) + { + } + + public TSimpleServer(TProcessor processor, + TServerTransport serverTransport, + TTransportFactory transportFactory, + TProtocolFactory protocolFactory) + :base(processor, + serverTransport, + transportFactory, + transportFactory, + protocolFactory, + protocolFactory) + { + } + + public override void Serve() + { + try + { + serverTransport.Listen(); + } + catch (TTransportException ttx) + { + Console.Error.WriteLine(ttx); + return; + } + + while (true) + { + TTransport client = null; + TTransport inputTransport = null; + TTransport outputTransport = null; + TProtocol inputProtocol = null; + TProtocol outputProtocol = null; + try + { + client = serverTransport.Accept(); + if (client != null) + { + inputTransport = inputTransportFactory.GetTransport(client); + outputTransport = outputTransportFactory.GetTransport(client); + inputProtocol = inputProtocolFactory.GetProtocol(inputTransport); + outputProtocol = outputProtocolFactory.GetProtocol(outputTransport); + while (processor.Process(inputProtocol, outputProtocol)) { } + } + } + catch (TTransportException ttx) + { + // Client died, just move on + } + catch (Exception x) + { + Console.Error.WriteLine(x); + } + + if (inputTransport != null) + { + inputTransport.Close(); + } + + if (outputTransport != null) + { + outputTransport.Close(); + } + } + } + } +} diff --git a/lib/csharp/src/Server/TThreadPoolServer.cs b/lib/csharp/src/Server/TThreadPoolServer.cs new file mode 100644 index 0000000..c42d348 --- /dev/null +++ b/lib/csharp/src/Server/TThreadPoolServer.cs @@ -0,0 +1,143 @@ +// +// TThreadPoolServer.cs +// +// Begin: Dec 3, 2007 +// Authors: +// Will Palmeri +// +// Distributed under the Thrift Software License +// +// See accompanying file LICENSE or visit the Thrift site at: +// http://developers.facebook.com/thrift/using +using System; +using System.Collections.Generic; +using System.Text; +using System.Threading; +using Thrift.Protocol; +using Thrift.Transport; + +namespace Thrift.Server +{ + /// + /// Server that uses C# built-in ThreadPool to spawn threads when handling requests + /// + public class TThreadPoolServer : TServer + { + private const int DEFAULT_MIN_THREADS = 10; + private const int DEFAULT_MAX_THREADS = 100; + + public TThreadPoolServer(TProcessor processor, TServerTransport serverTransport) + :this(processor, serverTransport, + new TTransportFactory(), new TTransportFactory(), + new TBinaryProtocol.Factory(), new TBinaryProtocol.Factory(), + DEFAULT_MIN_THREADS, DEFAULT_MAX_THREADS) + { + } + + public TThreadPoolServer(TProcessor processor, + TServerTransport serverTransport, + TTransportFactory transportFactory, + TProtocolFactory protocolFactory) + :this(processor, serverTransport, + transportFactory, transportFactory, + protocolFactory, protocolFactory, + DEFAULT_MIN_THREADS, DEFAULT_MAX_THREADS) + { + } + + public TThreadPoolServer(TProcessor processor, + TServerTransport serverTransport, + TTransportFactory inputTransportFactory, + TTransportFactory outputTransportFactory, + TProtocolFactory inputProtocolFactory, + TProtocolFactory outputProtocolFactory, + int minThreadPoolThreads, int maxThreadPoolThreads) + :base(processor, serverTransport, inputTransportFactory, outputTransportFactory, + inputProtocolFactory, outputProtocolFactory) + { + if (!ThreadPool.SetMinThreads(minThreadPoolThreads, minThreadPoolThreads)) + { + throw new Exception("Error: could not SetMinThreads in ThreadPool"); + } + if (!ThreadPool.SetMaxThreads(maxThreadPoolThreads, maxThreadPoolThreads)) + { + throw new Exception("Error: could not SetMaxThreads in ThreadPool"); + } + + } + + /// + /// Use new ThreadPool thread for each new client connection + /// + public override void Serve() + { + try + { + serverTransport.Listen(); + } + catch (TTransportException ttx) + { + Console.Error.WriteLine("Error, could not listen on ServerTransport: " + ttx); + return; + } + + while (true) + { + int failureCount = 0; + try + { + TTransport client = serverTransport.Accept(); + ThreadPool.QueueUserWorkItem(this.Execute, client); + } + catch (TTransportException ttx) + { + ++failureCount; + Console.Error.WriteLine(ttx); + } + } + } + + /// + /// Loops on processing a client forever + /// threadContext will be a TTransport instance + /// + /// + private void Execute(Object threadContext) + { + TTransport client = (TTransport)threadContext; + TTransport inputTransport = null; + TTransport outputTransport = null; + TProtocol inputProtocol = null; + TProtocol outputProtocol = null; + try + { + inputTransport = inputTransportFactory.GetTransport(client); + outputTransport = outputTransportFactory.GetTransport(client); + inputProtocol = inputProtocolFactory.GetProtocol(inputTransport); + outputProtocol = outputProtocolFactory.GetProtocol(outputTransport); + while (processor.Process(inputProtocol, outputProtocol)) + { + //keep processing requests until client disconnects + } + } + catch (TTransportException ttx) + { + // Assume the client died and continue silently + } + + catch (Exception x) + { + Console.Error.WriteLine("Error: " + x); + } + + if (inputTransport != null) + { + inputTransport.Close(); + } + if (outputTransport != null) + { + outputTransport.Close(); + } + } + } +} diff --git a/lib/csharp/src/TApplicationException.cs b/lib/csharp/src/TApplicationException.cs new file mode 100644 index 0000000..e4f3acf --- /dev/null +++ b/lib/csharp/src/TApplicationException.cs @@ -0,0 +1,127 @@ +// +// TApplicationException.cs +// +// Begin: Aug 19, 2007 +// Authors: +// Todd Berman +// +// Distributed under the Thrift Software License +// +// See accompanying file LICENSE or visit the Thrift site at: +// http://developers.facebook.com/thrift/using + +using System; +using System.Collections.Generic; +using System.Text; +using Thrift.Protocol; + +namespace Thrift +{ + public class TApplicationException : Exception + { + protected ExceptionType type; + + public TApplicationException() + { + } + + public TApplicationException(ExceptionType type) + { + this.type = type; + } + + public TApplicationException(ExceptionType type, string message) + : base(message) + { + this.type = type; + } + + public static TApplicationException Read(TProtocol iprot) + { + TField field; + TStruct struc = iprot.ReadStructBegin(); + + string message = null; + ExceptionType type = ExceptionType.Unknown; + + while (true) + { + field = iprot.ReadFieldBegin(); + if (field.Type == TType.Stop) + { + break; + } + + switch (field.ID) + { + case 1: + if (field.Type == TType.String) + { + message = iprot.ReadString(); + } + else + { + TProtocolUtil.Skip(iprot, field.Type); + } + break; + case 2: + if (field.Type == TType.I32) + { + type = (ExceptionType)iprot.ReadI32(); + } + else + { + TProtocolUtil.Skip(iprot, field.Type); + } + break; + default: + TProtocolUtil.Skip(iprot, field.Type); + break; + } + + iprot.ReadFieldEnd(); + } + + iprot.ReadStructEnd(); + + return new TApplicationException(type, message); + } + + public void Write(TProtocol oprot) + { + TStruct struc = new TStruct("TApplicationException"); + TField field = new TField(); + + oprot.WriteStructBegin(struc); + + if (!String.IsNullOrEmpty(Message)) + { + field.Name = "message"; + field.Type = TType.String; + field.ID = 1; + oprot.WriteFieldBegin(field); + oprot.WriteString(Message); + oprot.WriteFieldEnd(); + } + + field.Name = "type"; + field.Type = TType.I32; + field.ID = 2; + oprot.WriteFieldBegin(field); + oprot.WriteI32((int)type); + oprot.WriteFieldEnd(); + oprot.WriteFieldStop(); + oprot.WriteStructEnd(); + } + + public enum ExceptionType + { + Unknown, + UnknownMethod, + InvalidMessageType, + WrongMethodName, + BadSequenceID, + MissingResult + } + } +} diff --git a/lib/csharp/src/TProcessor.cs b/lib/csharp/src/TProcessor.cs new file mode 100644 index 0000000..7567820 --- /dev/null +++ b/lib/csharp/src/TProcessor.cs @@ -0,0 +1,24 @@ +// +// TProcessor.cs +// +// Begin: Aug 19, 2007 +// Authors: +// Todd Berman +// +// Distributed under the Thrift Software License +// +// See accompanying file LICENSE or visit the Thrift site at: +// http://developers.facebook.com/thrift/using + +using System; +using System.Collections.Generic; +using System.Text; +using Thrift.Protocol; + +namespace Thrift +{ + public interface TProcessor + { + bool Process(TProtocol iprot, TProtocol oprot); + } +} diff --git a/lib/csharp/src/Thrift.csproj b/lib/csharp/src/Thrift.csproj new file mode 100644 index 0000000..4b407b1 --- /dev/null +++ b/lib/csharp/src/Thrift.csproj @@ -0,0 +1,79 @@ + + + Debug + AnyCPU + {499EB63C-D74C-47E8-AE48-A2FC94538E9D} + 9.0.21022 + 2.0 + Library + false + Thrift + v3.5 + 512 + Thrift + SAK + SAK + SAK + SAK + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + + 3.5 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/lib/csharp/src/Thrift.sln b/lib/csharp/src/Thrift.sln new file mode 100644 index 0000000..cb7342c --- /dev/null +++ b/lib/csharp/src/Thrift.sln @@ -0,0 +1,51 @@ + +Microsoft Visual Studio Solution File, Format Version 10.00 +# Visual Studio 2008 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Thrift", "Thrift.csproj", "{499EB63C-D74C-47E8-AE48-A2FC94538E9D}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ThriftTest", "..\..\..\test\csharp\ThriftTest\ThriftTest.csproj", "{48DD757F-CA95-4DD7-BDA4-58DB6F108C2C}" + ProjectSection(ProjectDependencies) = postProject + {499EB63C-D74C-47E8-AE48-A2FC94538E9D} = {499EB63C-D74C-47E8-AE48-A2FC94538E9D} + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ThriftMSBuildTask", "..\ThriftMSBuildTask\ThriftMSBuildTask.csproj", "{EC0A0231-66EA-4593-A792-C6CA3BB8668E}" +EndProject +Global + GlobalSection(SourceCodeControl) = preSolution + SccNumberOfProjects = 4 + SccProjectName0 = Perforce\u0020Project + SccLocalPath0 = ..\\..\\.. + SccProvider0 = MSSCCI:Perforce\u0020SCM + SccProjectFilePathRelativizedFromConnection0 = lib\\csharp\\src\\ + SccProjectUniqueName1 = Thrift.csproj + SccLocalPath1 = ..\\..\\.. + SccProjectFilePathRelativizedFromConnection1 = lib\\csharp\\src\\ + SccProjectUniqueName2 = ..\\..\\..\\test\\csharp\\ThriftTest\\ThriftTest.csproj + SccLocalPath2 = ..\\..\\.. + SccProjectFilePathRelativizedFromConnection2 = test\\csharp\\ThriftTest\\ + SccProjectUniqueName3 = ..\\ThriftMSBuildTask\\ThriftMSBuildTask.csproj + SccLocalPath3 = ..\\..\\.. + SccProjectFilePathRelativizedFromConnection3 = lib\\csharp\\ThriftMSBuildTask\\ + EndGlobalSection + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {499EB63C-D74C-47E8-AE48-A2FC94538E9D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {499EB63C-D74C-47E8-AE48-A2FC94538E9D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {499EB63C-D74C-47E8-AE48-A2FC94538E9D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {499EB63C-D74C-47E8-AE48-A2FC94538E9D}.Release|Any CPU.Build.0 = Release|Any CPU + {48DD757F-CA95-4DD7-BDA4-58DB6F108C2C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {48DD757F-CA95-4DD7-BDA4-58DB6F108C2C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {48DD757F-CA95-4DD7-BDA4-58DB6F108C2C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {48DD757F-CA95-4DD7-BDA4-58DB6F108C2C}.Release|Any CPU.Build.0 = Release|Any CPU + {EC0A0231-66EA-4593-A792-C6CA3BB8668E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {EC0A0231-66EA-4593-A792-C6CA3BB8668E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {EC0A0231-66EA-4593-A792-C6CA3BB8668E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {EC0A0231-66EA-4593-A792-C6CA3BB8668E}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/lib/csharp/src/Transport/TServerSocket.cs b/lib/csharp/src/Transport/TServerSocket.cs new file mode 100644 index 0000000..cb4a84f --- /dev/null +++ b/lib/csharp/src/Transport/TServerSocket.cs @@ -0,0 +1,131 @@ +// +// TServerTransport.cs +// +// Begin: Dec 3, 2007 +// Authors: +// Will Palmeri +// +// Copyright (C) 2007 imeem, inc. +// All rights reserved. + +using System; +using System.Collections.Generic; +using System.Text; +using System.Net.Sockets; + + +namespace Thrift.Transport +{ + public class TServerSocket : TServerTransport + { + /** + * Underlying server with socket + */ + private TcpListener server = null; + + /** + * Port to listen on + */ + private int port = 0; + + /** + * Timeout for client sockets from accept + */ + private int clientTimeout = 0; + + /** + * Creates a server socket from underlying socket object + */ + public TServerSocket(TcpListener listener) + :this(listener, 0) + { + } + + /** + * Creates a server socket from underlying socket object + */ + public TServerSocket(TcpListener listener, int clientTimeout) + { + this.server = listener; + this.clientTimeout = clientTimeout; + } + + /** + * Creates just a port listening server socket + */ + public TServerSocket(int port) + : this(port, 0) + { + } + + /** + * Creates just a port listening server socket + */ + public TServerSocket(int port, int clientTimeout) + { + this.port = port; + this.clientTimeout = clientTimeout; + try + { + // Make server socket + server = new TcpListener(System.Net.IPAddress.Any, this.port); + } + catch (Exception e) + { + server = null; + throw new TTransportException("Could not create ServerSocket on port " + port + "."); + } + } + + public override void Listen() + { + // Make sure not to block on accept + if (server != null) + { + try + { + server.Start(); + } + catch (SocketException sx) + { + Console.Error.WriteLine(sx); + } + } + } + + protected override TTransport AcceptImpl() + { + if (server == null) + { + throw new TTransportException(TTransportException.ExceptionType.NotOpen, "No underlying server socket."); + } + try + { + TcpClient result = server.AcceptTcpClient(); + TSocket result2 = new TSocket(result); + result2.Timeout = clientTimeout; + return result2; + } + catch (Exception ex) + { + throw new TTransportException(ex.ToString()); + } + } + + public override void Close() + { + if (server != null) + { + try + { + server.Stop(); + } + catch (Exception ex) + { + Console.Error.WriteLine("WARNING: Could not close server socket: " + ex); + } + server = null; + } + } + } +} diff --git a/lib/csharp/src/Transport/TServerTransport.cs b/lib/csharp/src/Transport/TServerTransport.cs new file mode 100644 index 0000000..b4f5f07 --- /dev/null +++ b/lib/csharp/src/Transport/TServerTransport.cs @@ -0,0 +1,31 @@ +// +// TServerTransport.cs +// +// Begin: Dec 3, 2007 +// Authors: +// Will Palmeri +// +// Copyright (C) 2007 imeem, inc. +// All rights reserved. +using System; +using System.Collections.Generic; +using System.Text; + +namespace Thrift.Transport +{ + public abstract class TServerTransport + { + public abstract void Listen(); + public abstract void Close(); + protected abstract TTransport AcceptImpl(); + + public TTransport Accept() + { + TTransport transport = AcceptImpl(); + if (transport == null) { + throw new TTransportException("accept() may not return NULL"); + } + return transport; + } + } +} diff --git a/lib/csharp/src/Transport/TSocket.cs b/lib/csharp/src/Transport/TSocket.cs new file mode 100644 index 0000000..c05d587 --- /dev/null +++ b/lib/csharp/src/Transport/TSocket.cs @@ -0,0 +1,122 @@ +// +// TSocket.cs +// +// Begin: Aug 19, 2007 +// Authors: +// Todd Berman +// +// Copyright (C) 2007 imeem, inc. +// All rights reserved. +// + +using System; +using System.Collections.Generic; +using System.Text; +using System.Net.Sockets; + +namespace Thrift.Transport +{ + public class TSocket : TStreamTransport + { + private TcpClient client = null; + private string host = null; + private int port = 0; + private int timeout = 0; + + public TSocket(TcpClient client) + { + this.client = client; + + if (IsOpen) + { + inputStream = client.GetStream(); + outputStream = client.GetStream(); + } + } + + public TSocket(string host, int port) : this(host, port, 0) + { + } + + public TSocket(string host, int port, int timeout) + { + this.host = host; + this.port = port; + this.timeout = timeout; + + InitSocket(); + } + + private void InitSocket() + { + client = new TcpClient(); + client.ReceiveTimeout = client.SendTimeout = timeout; + } + + public int Timeout + { + set + { + client.ReceiveTimeout = client.SendTimeout = timeout = value; + } + } + + public TcpClient TcpClient + { + get + { + return client; + } + } + + public override bool IsOpen + { + get + { + if (client == null) + { + return false; + } + + return client.Connected; + } + } + + public override void Open() + { + if (IsOpen) + { + throw new TTransportException(TTransportException.ExceptionType.AlreadyOpen, "Socket already connected"); + } + + if (String.IsNullOrEmpty(host)) + { + throw new TTransportException(TTransportException.ExceptionType.NotOpen, "Cannot open null host"); + } + + if (port <= 0) + { + throw new TTransportException(TTransportException.ExceptionType.NotOpen, "Cannot open without port"); + } + + if (client == null) + { + InitSocket(); + } + + client.Connect(host, port); + inputStream = client.GetStream(); + outputStream = client.GetStream(); + } + + public override void Close() + { + base.Close(); + if (client != null) + { + client.Close(); + client = null; + } + } + } +} diff --git a/lib/csharp/src/Transport/TStreamTransport.cs b/lib/csharp/src/Transport/TStreamTransport.cs new file mode 100644 index 0000000..1715b6a --- /dev/null +++ b/lib/csharp/src/Transport/TStreamTransport.cs @@ -0,0 +1,87 @@ +// +// TStreamTransport.cs +// +// Begin: Aug 19, 2007 +// Authors: +// Todd Berman +// +// Copyright (C) 2007 imeem, inc. +// All rights reserved. +// + +using System; +using System.Collections.Generic; +using System.Text; +using System.IO; + +namespace Thrift.Transport +{ + public class TStreamTransport : TTransport + { + protected Stream inputStream; + protected Stream outputStream; + + public TStreamTransport() + { + } + + public TStreamTransport(Stream inputStream, Stream outputStream) + { + this.inputStream = inputStream; + this.outputStream = outputStream; + } + + public override bool IsOpen + { + get { return true; } + } + + public override void Open() + { + } + + public override void Close() + { + if (inputStream != null) + { + inputStream.Close(); + inputStream = null; + } + if (outputStream != null) + { + outputStream.Close(); + outputStream = null; + } + } + + public override int Read(byte[] buf, int off, int len) + { + if (inputStream == null) + { + throw new TTransportException(TTransportException.ExceptionType.NotOpen, "Cannot read from null inputstream"); + } + + return inputStream.Read(buf, off, len); + } + + public override void Write(byte[] buf, int off, int len) + { + if (outputStream == null) + { + throw new TTransportException(TTransportException.ExceptionType.NotOpen, "Cannot write to null outputstream"); + } + + outputStream.Write(buf, off, len); + } + + public override void Flush() + { + if (outputStream == null) + { + throw new TTransportException(TTransportException.ExceptionType.NotOpen, "Cannot flush null outputstream"); + } + + outputStream.Flush(); + } + } +} diff --git a/lib/csharp/src/Transport/TTransport.cs b/lib/csharp/src/Transport/TTransport.cs new file mode 100644 index 0000000..90f7e74 --- /dev/null +++ b/lib/csharp/src/Transport/TTransport.cs @@ -0,0 +1,60 @@ +// +// TTransport.cs +// +// Begin: Aug 19, 2007 +// Authors: +// Todd Berman +// +// Copyright (C) 2007 imeem, inc. +// All rights reserved. +// + +using System; +using System.Collections.Generic; +using System.Text; + +namespace Thrift.Transport +{ + public abstract class TTransport + { + public abstract bool IsOpen + { + get; + } + + public bool Peek() + { + return IsOpen; + } + + public abstract void Open(); + + public abstract void Close(); + + public abstract int Read(byte[] buf, int off, int len); + + public int ReadAll(byte[] buf, int off, int len) + { + int got = 0; + int ret = 0; + + while (got < len) + { + ret = Read(buf, off + got, len - got); + if (ret <= 0) + { + throw new TTransportException("Cannot read, Remote side has closed"); + } + got += ret; + } + + return got; + } + + public abstract void Write(byte[] buf, int off, int len); + + public virtual void Flush() + { + } + } +} diff --git a/lib/csharp/src/Transport/TTransportException.cs b/lib/csharp/src/Transport/TTransportException.cs new file mode 100644 index 0000000..d59c46e --- /dev/null +++ b/lib/csharp/src/Transport/TTransportException.cs @@ -0,0 +1,58 @@ +// +// TTransportException.cs +// +// Begin: Aug 19, 2007 +// Authors: +// Todd Berman +// +// Copyright (C) 2007 imeem, inc. +// All rights reserved. +// + +using System; +using System.Collections.Generic; +using System.Text; + +namespace Thrift.Transport +{ + public class TTransportException : Exception + { + protected ExceptionType type; + + public TTransportException() + : base() + { + } + + public TTransportException(ExceptionType type) + : this() + { + this.type = type; + } + + public TTransportException(ExceptionType type, string message) + : base(message) + { + this.type = type; + } + + public TTransportException(string message) + : base(message) + { + } + + public ExceptionType Type + { + get { return type; } + } + + public enum ExceptionType + { + Unknown, + NotOpen, + AlreadyOpen, + TimedOut, + EndOfFile + } + } +} diff --git a/lib/csharp/src/Transport/TTransportFactory.cs b/lib/csharp/src/Transport/TTransportFactory.cs new file mode 100644 index 0000000..456140d --- /dev/null +++ b/lib/csharp/src/Transport/TTransportFactory.cs @@ -0,0 +1,32 @@ +// +// TTransportFactory.cs +// +// Begin: Dec 3, 2007 +// Authors: +// Will Palmeri +// +// Copyright (C) 2007 imeem, inc. +// All rights reserved. +// +using System; +using System.Collections.Generic; +using System.Text; + + +namespace Thrift.Transport +{ + /// + /// From Mark Slee & Aditya Agarwal of Facebook: + /// Factory class used to create wrapped instance of Transports. + /// This is used primarily in servers, which get Transports from + /// a ServerTransport and then may want to mutate them (i.e. create + /// a BufferedTransport from the underlying base transport) + /// + public class TTransportFactory + { + public TTransport GetTransport(TTransport trans) + { + return trans; + } + } +} diff --git a/test/ThriftTest.thrift b/test/ThriftTest.thrift index fd0ac97..8c91cb7 100644 --- a/test/ThriftTest.thrift +++ b/test/ThriftTest.thrift @@ -2,6 +2,7 @@ java_package thrift.test cpp_namespace thrift.test ruby_namespace Thrift.Test perl_package ThriftTest +csharp_namespace Thrift.Test enum Numberz { diff --git a/thrift.el b/thrift.el index 2e79051..cff0589 100644 --- a/thrift.el +++ b/thrift.el @@ -10,7 +10,7 @@ (defconst thrift-font-lock-keywords (list '("#.*$" . font-lock-comment-face) ;; perl style comments - '("\\<\\(include\\|struct\\|exception\\|typedef\\|cpp_namespace\\|java_package\\|cocoa_prefix\\|php_namespace\\|ruby_namespace\\|py_module\\|perl_package\\|const\\|enum\\|service\\|extends\\|void\\|async\\|throws\\|optional\\|required\\)\\>" . font-lock-keyword-face) ;; keywords + '("\\<\\(include\\|struct\\|exception\\|typedef\\|cpp_namespace\\|java_package\\|cocoa_prefix\\|csharp_namespace\\|php_namespace\\|ruby_namespace\\|py_module\\|perl_package\\|smalltalk_category\\|smalltalk_prefix\\|const\\|enum\\|service\\|extends\\|void\\|async\\|throws\\|optional\\|required\\)\\>" . font-lock-keyword-face) ;; keywords '("\\<\\(bool\\|byte\\|i16\\|i32\\|i64\\|double\\|string\\|binary\\|map\\|list\\|set\\)\\>" . font-lock-type-face) ;; built-in types '("\\<\\([0-9]+\\)\\>" . font-lock-variable-name-face) ;; ordinals '("\\<\\(\\w+\\)\\s-*(" (1 font-lock-function-name-face)) ;; functions diff --git a/thrift.vim b/thrift.vim index d6727fb..83e7403 100644 --- a/thrift.vim +++ b/thrift.vim @@ -31,7 +31,8 @@ syn match thriftNumber "-\=\<\d\+\>" contained " Keywords syn keyword thriftKeyword namespace cpp_namespace java_package cocoa_prefix -syn keyword php_namespace ruby_namespace py_module perl_package +syn keyword thriftKeyword csharp_namespace smalltalk_category smalltalk_prefix +syn keyword thriftKeyword php_namespace ruby_namespace py_module perl_package syn keyword thriftKeyword xsd_all xsd_optional xsd_nillable xsd_namespace xsd_attrs syn keyword thriftKeyword include cpp_include cpp_type const optional required syn keyword thriftBasicTypes void bool byte i16 i32 i64 double string binary -- 2.11.4.GIT