From 213a66479872e0ccf828f8f2675d1546cdff0a18 Mon Sep 17 00:00:00 2001 From: Roger Meier Date: Wed, 27 Oct 2010 12:30:11 +0000 Subject: [PATCH] THRIFT-582 C(c_glib) implementation of Thrift Patch: Anatol Pomozov and Michael Lum git-svn-id: https://svn.apache.org/repos/asf/incubator/thrift/trunk@1027933 13f79535-47bb-0310-9956-ffa450edef68 --- .gitignore | 19 + compiler/cpp/Makefile.am | 3 + .../cpp/src/generate/t_c_glib_generator.cc | 2986 +++++++++++++++++ compiler/cpp/src/parse/t_program.h | 11 + configure.ac | 44 + lib/Makefile.am | 4 + lib/c_glib/Makefile.am | 93 + lib/c_glib/src/processor/thrift_processor.c | 50 + lib/c_glib/src/processor/thrift_processor.h | 59 + .../src/protocol/thrift_binary_protocol.c | 983 ++++++ .../src/protocol/thrift_binary_protocol.h | 55 + .../protocol/thrift_binary_protocol_factory.c | 58 + .../protocol/thrift_binary_protocol_factory.h | 46 + lib/c_glib/src/protocol/thrift_protocol.c | 604 ++++ lib/c_glib/src/protocol/thrift_protocol.h | 324 ++ .../src/protocol/thrift_protocol_factory.c | 50 + .../src/protocol/thrift_protocol_factory.h | 57 + lib/c_glib/src/server/thrift_server.c | 192 ++ lib/c_glib/src/server/thrift_server.h | 75 + lib/c_glib/src/server/thrift_simple_server.c | 133 + lib/c_glib/src/server/thrift_simple_server.h | 53 + lib/c_glib/src/thrift.c | 13 + lib/c_glib/src/thrift.h | 18 + lib/c_glib/src/thrift_application_exception.c | 192 ++ lib/c_glib/src/thrift_application_exception.h | 67 + lib/c_glib/src/thrift_struct.c | 55 + lib/c_glib/src/thrift_struct.h | 50 + .../src/transport/thrift_buffered_transport.c | 406 +++ .../src/transport/thrift_buffered_transport.h | 61 + .../src/transport/thrift_framed_transport.c | 397 +++ .../src/transport/thrift_framed_transport.h | 61 + .../src/transport/thrift_memory_buffer.c | 260 ++ .../src/transport/thrift_memory_buffer.h | 55 + .../src/transport/thrift_server_socket.c | 272 ++ .../src/transport/thrift_server_socket.h | 73 + .../src/transport/thrift_server_transport.c | 70 + .../src/transport/thrift_server_transport.h | 72 + lib/c_glib/src/transport/thrift_socket.c | 334 ++ lib/c_glib/src/transport/thrift_socket.h | 56 + lib/c_glib/src/transport/thrift_transport.c | 114 + lib/c_glib/src/transport/thrift_transport.h | 135 + .../src/transport/thrift_transport_factory.c | 51 + .../src/transport/thrift_transport_factory.h | 55 + lib/c_glib/test/Makefile.am | 229 ++ lib/c_glib/test/glib.suppress | 64 + lib/c_glib/test/test-wrapper.sh.in | 58 + lib/c_glib/test/testbufferedtransport.c | 188 ++ lib/c_glib/test/testdebugproto.c | 64 + lib/c_glib/test/testframedtransport.c | 176 + lib/c_glib/test/testmemorybuffer.c | 75 + lib/c_glib/test/testoptionalrequired.c | 182 + lib/c_glib/test/testprotocolbinary.c | 652 ++++ lib/c_glib/test/testsimpleserver.c | 113 + lib/c_glib/test/teststruct.c | 121 + lib/c_glib/test/testthrifttest.c | 28 + lib/c_glib/test/testthrifttestclient.cpp | 551 +++ lib/c_glib/test/testtransportsocket.c | 200 ++ lib/c_glib/thrift_c_glib.pc.in | 29 + test/DebugProtoTest.thrift | 1 + test/OptionalRequiredTest.thrift | 1 + test/ThriftTest.thrift | 1 + 61 files changed, 11499 insertions(+) create mode 100644 compiler/cpp/src/generate/t_c_glib_generator.cc create mode 100644 lib/c_glib/Makefile.am create mode 100644 lib/c_glib/src/processor/thrift_processor.c create mode 100644 lib/c_glib/src/processor/thrift_processor.h create mode 100644 lib/c_glib/src/protocol/thrift_binary_protocol.c create mode 100644 lib/c_glib/src/protocol/thrift_binary_protocol.h create mode 100644 lib/c_glib/src/protocol/thrift_binary_protocol_factory.c create mode 100644 lib/c_glib/src/protocol/thrift_binary_protocol_factory.h create mode 100644 lib/c_glib/src/protocol/thrift_protocol.c create mode 100644 lib/c_glib/src/protocol/thrift_protocol.h create mode 100644 lib/c_glib/src/protocol/thrift_protocol_factory.c create mode 100644 lib/c_glib/src/protocol/thrift_protocol_factory.h create mode 100644 lib/c_glib/src/server/thrift_server.c create mode 100644 lib/c_glib/src/server/thrift_server.h create mode 100644 lib/c_glib/src/server/thrift_simple_server.c create mode 100644 lib/c_glib/src/server/thrift_simple_server.h create mode 100644 lib/c_glib/src/thrift.c create mode 100644 lib/c_glib/src/thrift.h create mode 100644 lib/c_glib/src/thrift_application_exception.c create mode 100644 lib/c_glib/src/thrift_application_exception.h create mode 100644 lib/c_glib/src/thrift_struct.c create mode 100644 lib/c_glib/src/thrift_struct.h create mode 100644 lib/c_glib/src/transport/thrift_buffered_transport.c create mode 100644 lib/c_glib/src/transport/thrift_buffered_transport.h create mode 100644 lib/c_glib/src/transport/thrift_framed_transport.c create mode 100644 lib/c_glib/src/transport/thrift_framed_transport.h create mode 100644 lib/c_glib/src/transport/thrift_memory_buffer.c create mode 100644 lib/c_glib/src/transport/thrift_memory_buffer.h create mode 100644 lib/c_glib/src/transport/thrift_server_socket.c create mode 100644 lib/c_glib/src/transport/thrift_server_socket.h create mode 100644 lib/c_glib/src/transport/thrift_server_transport.c create mode 100644 lib/c_glib/src/transport/thrift_server_transport.h create mode 100644 lib/c_glib/src/transport/thrift_socket.c create mode 100644 lib/c_glib/src/transport/thrift_socket.h create mode 100644 lib/c_glib/src/transport/thrift_transport.c create mode 100644 lib/c_glib/src/transport/thrift_transport.h create mode 100644 lib/c_glib/src/transport/thrift_transport_factory.c create mode 100644 lib/c_glib/src/transport/thrift_transport_factory.h create mode 100644 lib/c_glib/test/Makefile.am create mode 100644 lib/c_glib/test/glib.suppress create mode 100644 lib/c_glib/test/test-wrapper.sh.in create mode 100644 lib/c_glib/test/testbufferedtransport.c create mode 100644 lib/c_glib/test/testdebugproto.c create mode 100644 lib/c_glib/test/testframedtransport.c create mode 100644 lib/c_glib/test/testmemorybuffer.c create mode 100644 lib/c_glib/test/testoptionalrequired.c create mode 100644 lib/c_glib/test/testprotocolbinary.c create mode 100644 lib/c_glib/test/testsimpleserver.c create mode 100644 lib/c_glib/test/teststruct.c create mode 100644 lib/c_glib/test/testthrifttest.c create mode 100644 lib/c_glib/test/testthrifttestclient.cpp create mode 100644 lib/c_glib/test/testtransportsocket.c create mode 100644 lib/c_glib/thrift_c_glib.pc.in diff --git a/.gitignore b/.gitignore index 5aed523a..7a39712a 100644 --- a/.gitignore +++ b/.gitignore @@ -53,6 +53,25 @@ /lib/cpp/*.la /lib/cpp/*.lo /lib/cpp/*.pc +/lib/c_glib/*.gcda +/lib/c_glib/*.gcno +/lib/c_glib/*.la +/lib/c_glib/*.lo +/lib/c_glib/*.loT +/lib/c_glib/*.o +/lib/c_glib/.deps +/lib/c_glib/.libs +/lib/c_glib/Makefile.in +/lib/c_glib/Makefile +/lib/c_glib/thriftc.pc +/lib/c_glib/test/*.o +/lib/c_glib/test/*.lo +/lib/c_glib/test/testwrapper.sh +/lib/c_glib/test/testwrapper-test* +/lib/c_glib/test/.deps +/lib/c_glib/test/.libs +/lib/c_glib/test/gen-cpp/ +/lib/c_glib/test/gen-c_glib /lib/csharp/Makefile /lib/csharp/Makefile.in /lib/hs/dist diff --git a/compiler/cpp/Makefile.am b/compiler/cpp/Makefile.am index 6902ba47..ac69b344 100644 --- a/compiler/cpp/Makefile.am +++ b/compiler/cpp/Makefile.am @@ -60,6 +60,9 @@ thrift_SOURCES = src/thrifty.yy \ src/generate/t_generator.h \ src/generate/t_oop_generator.h +if THRIFT_GEN_c_glib +thrift_SOURCES += src/generate/t_c_glib_generator.cc +endif if THRIFT_GEN_cpp thrift_SOURCES += src/generate/t_cpp_generator.cc endif diff --git a/compiler/cpp/src/generate/t_c_glib_generator.cc b/compiler/cpp/src/generate/t_c_glib_generator.cc new file mode 100644 index 00000000..223d4896 --- /dev/null +++ b/compiler/cpp/src/generate/t_c_glib_generator.cc @@ -0,0 +1,2986 @@ + +#include +#include +#include +#include + +#include + +#include "platform.h" +#include "t_oop_generator.h" + +using namespace std; + +/* forward declarations */ +string initial_caps_to_underscores (string name); +string to_upper_case (string name); +string to_lower_case (string name); + +/** + * C code generator, using glib for C typing. + */ +class t_c_glib_generator : public t_oop_generator +{ + public: + + /* constructor */ + t_c_glib_generator (t_program *program, + const map &parsed_options, + const string &option_string) : t_oop_generator (program) + { + /* set the output directory */ + this->out_dir_base_ = "gen-c_glib"; + + /* set the namespace */ + this->nspace = program_->get_namespace ("c_glib"); + + if (this->nspace.empty()) + { + this->nspace = ""; + this->nspace_u = ""; + this->nspace_uc = ""; + this->nspace_lc = ""; + } else { + /* replace dots with underscores */ + char *tmp = strdup (this->nspace.c_str ()); + for (unsigned int i = 0; i < strlen (tmp); i++) + { + if (tmp[i] == '.') + { + tmp[i] = '_'; + } + } + this->nspace = string (tmp, strlen (tmp)); + free (tmp); + + /* clean up the namespace for C. + * An input of 'namespace foo' should result in: + * - nspace = foo - for thrift objects and typedefs + * - nspace_u = Foo - for internal GObject prefixes + * - nspace_uc = FOO_ - for macro prefixes + * - nspace_lc = foo_ - for filename and method prefixes + * The underscores are there since uc and lc strings are used as file and + * variable prefixes. + */ + this->nspace_u = initial_caps_to_underscores (this->nspace); + this->nspace_uc = to_upper_case (this->nspace_u) + "_"; + this->nspace_lc = to_lower_case (this->nspace_u) + "_"; + } + } + + /* initialization and destruction */ + void init_generator (); + void close_generator (); + + /* generation functions */ + void generate_typedef (t_typedef *ttypedef); + void generate_enum (t_enum *tenum); + void generate_consts (vector consts); + void generate_struct (t_struct *tstruct); + void generate_service (t_service *tservice); + void generate_xception (t_struct *tstruct); + + private: + + /* file streams */ + ofstream f_types_; + ofstream f_types_impl_; + ofstream f_header_; + ofstream f_service_; + + /* namespace variables */ + string nspace; + string nspace_u; + string nspace_uc; + string nspace_lc; + + /* helper functions */ + bool is_complex_type (t_type *ttype); + string type_name (t_type* ttype, bool in_typedef=false, + bool is_const=false); + string base_type_name (t_base_type *type); + string type_to_enum (t_type *type); + string constant_value (string name, t_type *type, t_const_value *value); + string function_signature (t_function *tfunction); + string argument_list (t_struct *tstruct); + string xception_list (t_struct *tstruct); + string declare_field (t_field *tfield, bool init=false, bool pointer=false, + bool constant=false, bool reference=false); + + /* generation functions */ + void generate_const_initializer (string name, t_type *type, + t_const_value *value); + void generate_service_client (t_service *tservice); + void generate_service_server (t_service *tservice); + void generate_object (t_struct *tstruct); + void generate_struct_writer (ofstream &out, t_struct *tstruct, + string this_name, string this_get="", + bool is_function=true); + void generate_struct_reader (ofstream &out, t_struct *tstruct, + string this_name, string this_get="", + bool is_function=true); + + void generate_serialize_field (ofstream &out, t_field *tfield, + string prefix, string suffix, + int error_ret); + void generate_serialize_struct (ofstream &out, t_struct *tstruct, + string prefix, int error_ret); + void generate_serialize_container (ofstream &out, t_type *ttype, + string prefix, int error_ret); + void generate_serialize_map_element (ofstream &out, t_map *tmap, string key, + string value, int error_ret); + void generate_serialize_set_element (ofstream &out, t_set *tset, + string element, int error_ret); + void generate_serialize_list_element (ofstream &out, t_list *tlist, + string list, string index, + int error_ret); + + void generate_deserialize_field (ofstream &out, t_field *tfield, + string prefix, string suffix, + int error_ret); + void generate_deserialize_struct (ofstream &out, t_struct *tstruct, + string prefix, int error_ret); + void generate_deserialize_container (ofstream &out, t_type *ttype, + string prefix, int error_ret); + void generate_deserialize_map_element (ofstream &out, t_map *tmap, + string prefix, int error_ret); + void generate_deserialize_set_element (ofstream &out, t_set *tset, + string prefix, int error_ret); + void generate_deserialize_list_element (ofstream &out, t_list *tlist, + string prefix, string index, + int error_ret); + + string generate_new_hash_from_type (t_type * ttype); + string generate_new_array_from_type(t_type * ttype); +}; + +/** + * Prepare for file generation by opening up the necessary file + * output streams. + */ +void +t_c_glib_generator::init_generator () +{ + /* create output directory */ + MKDIR (get_out_dir ().c_str ()); + + string program_name_u = initial_caps_to_underscores (program_name_); + string program_name_uc = to_upper_case (program_name_u); + string program_name_lc = to_lower_case (program_name_u); + + /* create output files */ + string f_types_name = get_out_dir () + this->nspace_lc + + program_name_lc + "_types.h"; + f_types_.open (f_types_name.c_str ()); + string f_types_impl_name = get_out_dir () + this->nspace_lc + + program_name_lc + "_types.c"; + f_types_impl_.open (f_types_impl_name.c_str ()); + + /* add thrift boilerplate headers */ + f_types_ << autogen_comment (); + f_types_impl_ << autogen_comment (); + + /* include inclusion guard */ + f_types_ << + "#ifndef " << this->nspace_uc << program_name_uc << "_TYPES_H" << endl << + "#define " << this->nspace_uc << program_name_uc << "_TYPES_H" << endl << + endl; + + /* include base types */ + f_types_ << + "/* base includes */" << endl << + "#include " << endl << + "#include " << endl << + "#include " << endl; + + /* include other thrift includes */ + const vector &includes = program_->get_includes (); + for (size_t i = 0; i < includes.size (); ++i) + { + f_types_ << + "/* other thrift includes */" << endl << + "#include \"" << this->nspace_lc << includes[i]->get_name () << + "_types.h\"" << endl; + } + f_types_ << endl; + + /* include custom headers */ + const vector &c_includes = program_->get_c_includes (); + f_types_ << "/* custom thrift includes */" << endl; + for (size_t i = 0; i < c_includes.size (); ++i) + { + if (c_includes[i][0] == '<') + { + f_types_ << + "#include " << c_includes[i] << endl; + } else { + f_types_ << + "#include \"" << c_includes[i] << "\"" << endl; + } + } + f_types_ << endl; + + // include the types file + f_types_impl_ << + endl << + "#include \"" << this->nspace_lc << program_name_u << + "_types.h\"" << endl << + "#include " << endl << + endl; + + f_types_ << + "/* begin types */" << endl << endl; +} + +/** + * Finish up generation and close all file streams. + */ +void +t_c_glib_generator::close_generator () +{ + string program_name_uc = to_upper_case + (initial_caps_to_underscores (program_name_)); + + /* end the header inclusion guard */ + f_types_ << + "#endif /* " << this->nspace_uc << program_name_uc << "_TYPES_H */" << endl; + + /* close output file */ + f_types_.close (); + f_types_impl_.close (); +} + +/** + * Generates a Thrift typedef in C code. For example: + * + * Thrift: + * typedef map SomeMap + * + * C: + * typedef GHashTable * ThriftSomeMap; + */ +void +t_c_glib_generator::generate_typedef (t_typedef* ttypedef) +{ + f_types_ << + indent() << "typedef " << type_name (ttypedef->get_type (), true) << + " " << this->nspace << ttypedef->get_symbolic () << ";" << endl << + endl; +} + +/** + * Generates a C enumeration. For example: + * + * Thrift: + * enum MyEnum { + * ONE = 1, + * TWO + * } + * + * C: + * enum _ThriftMyEnum { + * THRIFT_MY_ENUM_ONE = 1, + * THRIFT_MY_ENUM_TWO + * }; + * typedef enum _ThriftMyEnum ThriftMyEnum; + */ +void +t_c_glib_generator::generate_enum (t_enum *tenum) +{ + string name = tenum->get_name (); + string name_uc = to_upper_case (initial_caps_to_underscores (name)); + + f_types_ << + indent () << "enum _" << this->nspace << name << " {" << endl; + + indent_up (); + + vector constants = tenum->get_constants (); + vector::iterator c_iter; + bool first = true; + + /* output each of the enumeration elements */ + for (c_iter = constants.begin (); c_iter != constants.end (); ++c_iter) + { + if (first) + { + first = false; + } else { + f_types_ << "," << endl; + } + + f_types_ << + indent() << this->nspace_uc << name_uc << "_" << (*c_iter)->get_name (); + if ((*c_iter)->has_value ()) + { + f_types_ << + " = " << (*c_iter)->get_value (); + } + } + + indent_down (); + f_types_ << + endl << + "};" << endl << + "typedef enum _" << this->nspace << name << " " << this->nspace << name << ";" << endl << + endl; +} + +/** + * Generates Thrift constants in C code. + */ +void +t_c_glib_generator::generate_consts (vector consts) +{ + f_types_ << "/* constants */" << endl; + f_types_impl_ << "/* constants */" << endl; + + vector::iterator c_iter; + for (c_iter = consts.begin (); c_iter != consts.end (); ++c_iter) + { + string name = (*c_iter)->get_name (); + string name_uc = to_upper_case (name); + string name_lc = to_lower_case (name); + t_type *type = (*c_iter)->get_type (); + t_const_value *value = (*c_iter)->get_value (); + + f_types_ << + indent () << "#define " << this->nspace_uc << name_uc << " " << + constant_value (name_lc, type, value) << endl; + + generate_const_initializer (name_lc, type, value); + } + + f_types_ << endl; + f_types_impl_ << endl; +} + +/** + * Generate Thrift structs in C code, as GObjects. Example: + * + * Thrift: + * struct Bonk + * { + * 1: string message, + * 2: i32 type + * } + * + * C GObject instance header: + * struct _ThriftBonk + * { + * GObject parent; + * + * gchar * message; + * gint32 type; + * }; + * typedef struct _ThriftBonk ThriftBonk + * // ... additional GObject boilerplate ... + */ +void +t_c_glib_generator::generate_struct (t_struct *tstruct) +{ + f_types_ << "/* struct " << tstruct->get_name () << " */" << endl; + generate_object (tstruct); +} + +/** + * Generate C code to represent Thrift services. Creates a new GObject + * which can be used to access the service. + */ +void +t_c_glib_generator::generate_service (t_service *tservice) +{ + string svcname_u = initial_caps_to_underscores (tservice->get_name ()); + string svcname_uc = this->nspace_uc + to_upper_case (svcname_u); + string filename = this->nspace_lc + to_lower_case (svcname_u); + + // make output files + string f_header_name = get_out_dir () + filename + ".h"; + f_header_.open (f_header_name.c_str ()); + + string program_name_u = initial_caps_to_underscores (program_name_); + string program_name_lc = to_lower_case (program_name_u); + + // add header file boilerplate + f_header_ << + autogen_comment (); + + // add an inclusion guard + f_header_ << + "#ifndef " << svcname_uc << "_H" << endl << + "#define " << svcname_uc << "_H" << endl << + endl; + + // add standard includes + f_header_ << + "#include \"" << this->nspace_lc << program_name_lc << "_types.h\"" << endl; + + // if we are inheriting from another service, include its header + t_service *extends_service = tservice->get_extends (); + if (extends_service != NULL) + { + f_header_ << + "#include \"" << this->nspace_lc << to_lower_case (initial_caps_to_underscores (extends_service->get_name ())) << ".h\"" << endl; + } + f_header_ << endl; + + // create the service implementation + string f_service_name = get_out_dir () + filename + ".c"; + f_service_.open (f_service_name.c_str ()); + + // add the boilerplace header + f_service_ << + autogen_comment(); + + // include the headers + f_service_ << + "#include " << endl << + "#include " << endl << + "#include " << endl << + "#include \"" << filename << ".h\"" << endl << + endl; + + // generate the client objects + generate_service_client (tservice); + + // generate the server objects + generate_service_server (tservice); + + // end the header inclusion guard + f_header_ << + "#endif /* " << svcname_uc << "_H */" << endl; + + // close the files + f_service_.close (); + f_header_.close (); +} + +/** + * + */ +void +t_c_glib_generator::generate_xception (t_struct *tstruct) +{ + string name = tstruct->get_name (); + string name_u = initial_caps_to_underscores (name); + string name_lc = to_lower_case (name_u); + string name_uc = to_upper_case (name_u); + + generate_object (tstruct); + + f_types_ << "/* exception */" << endl << + "typedef enum" << endl << + "{" << endl << + " " << this->nspace_uc << name_uc << "_ERROR_CODE," << endl << + "} " << this->nspace << name << "Error;" << endl << + endl << + "GQuark " << this->nspace_lc << name_lc << "_error_quark (void);" << endl << + "#define " << this->nspace_uc << name_uc << "_ERROR (" << + this->nspace_lc << name_lc << "_error_quark ())" << endl << + endl << + endl; + + f_types_impl_ << + "/* define the GError domain for exceptions */" << endl << + "#define " << this->nspace_uc << name_uc << "_ERROR_DOMAIN \"" << + this->nspace_lc << name_lc << "_error_quark\"" << endl << + "GQuark" << endl << + this->nspace_lc << name_lc << "_error_quark (void)" << endl << + "{" << endl << + " return g_quark_from_static_string (" << this->nspace_uc << name_uc << + "_ERROR_DOMAIN);" << endl << + "}" << endl << + endl; +} + +/******************** + * HELPER FUNCTIONS * + ********************/ + +/** + * Returns true if ttype is not a primitive. + */ +bool +t_c_glib_generator::is_complex_type (t_type *ttype) +{ + ttype = get_true_type (ttype); + + return ttype->is_container () + || ttype->is_struct () + || ttype->is_xception () + || (ttype->is_base_type () + && (((t_base_type *) ttype)->get_base() + == t_base_type::TYPE_STRING)); +} + + +/** + * Maps a Thrift t_type to a C type. + */ +string +t_c_glib_generator::type_name (t_type* ttype, bool in_typedef, bool is_const) +{ + if (ttype->is_base_type ()) + { + string bname = base_type_name ((t_base_type *) ttype); + + if (is_const) + { + return "const " + bname; + } else { + return bname; + } + } + + if (ttype->is_container ()) + { + string cname; + + t_container *tcontainer = (t_container *) ttype; + if (tcontainer->has_cpp_name ()) + { + cname = tcontainer->get_cpp_name (); + } else if (ttype->is_map ()) { + cname = "GHashTable *"; + } else if (ttype->is_set ()) { + // since a set requires unique elements, use a GHashTable, and + // populate the keys and values with the same data, using keys for + // the actual writes and reads. + // TODO: discuss whether or not to implement TSet, THashSet or GHashSet + cname = "GHashTable *"; + } else if (ttype->is_list ()) { + // TODO: investigate other implementations besides GPtrArray + cname = "GPtrArray *"; + t_type *etype = ((t_list *) ttype)->get_elem_type (); + if (etype->is_base_type ()) + { + t_base_type::t_base tbase = ((t_base_type *) etype)->get_base (); + switch (tbase) + { + case t_base_type::TYPE_VOID: + throw "compiler error: cannot determine array type"; + case t_base_type::TYPE_BOOL: + case t_base_type::TYPE_BYTE: + case t_base_type::TYPE_I16: + case t_base_type::TYPE_I32: + case t_base_type::TYPE_I64: + case t_base_type::TYPE_DOUBLE: + cname = "GArray *"; + break; + case t_base_type::TYPE_STRING: + break; + default: + throw "compiler error: no array info for type"; + } + } + } + + if (is_const) + { + return "const " + cname; + } else { + return cname; + } + } + + // check for a namespace + string pname = this->nspace + ttype->get_name (); + + if (is_complex_type (ttype)) + { + pname += " *"; + } + + if (is_const) + { + return "const " + pname; + } else { + return pname; + } +} + +/** + * Maps a Thrift primitive to a C primitive. + */ +string +t_c_glib_generator::base_type_name (t_base_type *type) +{ + t_base_type::t_base tbase = type->get_base (); + + switch (tbase) + { + case t_base_type::TYPE_VOID: + return "void"; + case t_base_type::TYPE_STRING: + if (type->is_binary ()) + { + return "GByteArray *"; + } else { + return "gchar *"; + } + case t_base_type::TYPE_BOOL: + return "gboolean"; + case t_base_type::TYPE_BYTE: + return "gint8"; + case t_base_type::TYPE_I16: + return "gint16"; + case t_base_type::TYPE_I32: + return "gint32"; + case t_base_type::TYPE_I64: + return "gint64"; + case t_base_type::TYPE_DOUBLE: + return "gdouble"; + default: + throw "compiler error: no C base type name for base type " + + t_base_type::t_base_name (tbase); + } +} + +/** + * Returns a member of the ThriftType C enumeration in thrift_protocol.h + * for a Thrift type. + */ +string +t_c_glib_generator::type_to_enum (t_type *type) +{ + type = get_true_type (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 "T_STRING"; + case t_base_type::TYPE_BOOL: + return "T_BOOL"; + case t_base_type::TYPE_BYTE: + return "T_BYTE"; + case t_base_type::TYPE_I16: + return "T_I16"; + case t_base_type::TYPE_I32: + return "T_I32"; + case t_base_type::TYPE_I64: + return "T_I64"; + case t_base_type::TYPE_DOUBLE: + return "T_DOUBLE"; + } + } else if (type->is_enum ()) { + return "T_I32"; + } else if (type->is_struct ()) { + return "T_STRUCT"; + } else if (type->is_xception ()) { + return "T_STRUCT"; + } else if (type->is_map ()) { + return "T_MAP"; + } else if (type->is_set ()) { + return "T_SET"; + } else if (type->is_list ()) { + return "T_LIST"; + } + + throw "INVALID TYPE IN type_to_enum: " + type->get_name (); +} + + +/** + * Returns C code that represents a Thrift constant. + */ +string +t_c_glib_generator::constant_value (string name, t_type *type, t_const_value *value) +{ + ostringstream render; + + if (type->is_base_type ()) + { + /* primitives */ + 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) ? 1 : 0); + 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 " + + t_base_type::t_base_name (tbase); + } + } else if (type->is_enum ()) { + render << "(" << type_name (type) << ")" << value->get_integer (); + } else if (type->is_struct () || type->is_xception () || type->is_list () + || type->is_set () || type->is_map ()) { + render << "(" << this->nspace_lc << + to_lower_case (name) << "_constant ())"; + } else { + render << "NULL /* not supported */"; + } + + return render.str (); +} + +/** + * Renders a function signature of the form 'type name(args)' + * + * @param tfunction Function definition + * @return String of rendered function definition + */ +string +t_c_glib_generator::function_signature (t_function* tfunction) +{ + t_type* ttype = tfunction->get_returntype(); + t_struct* arglist = tfunction->get_arglist(); + t_struct* xlist = tfunction->get_xceptions(); + string fname = initial_caps_to_underscores(tfunction->get_name()); + + bool has_return = !ttype->is_void(); + bool has_args = arglist->get_members().size() == 0; + bool has_xceptions = xlist->get_members().size() == 0; + return + "gboolean " + this->nspace_lc + fname + " (" + this->nspace + + service_name_ + "If * iface" + + (has_return ? ", " + type_name(ttype) + "* _return" : "") + + (has_args ? "" : (", " + argument_list (arglist))) + + (has_xceptions ? "" : (", " + xception_list (xlist))) + + ", GError ** error)"; +} + +/** + * Renders a field list + * + * @param tstruct The struct definition + * @return Comma sepearated list of all field names in that struct + */ +string +t_c_glib_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(), false, true) + " " + + (*f_iter)->get_name (); + } + return result; +} + +/** + * Renders mutable exception lists + * + * @param tstruct The struct definition + * @return Comma sepearated list of all field names in that struct + */ +string +t_c_glib_generator::xception_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(), false, false) + "* " + + (*f_iter)->get_name (); + } + return result; +} + + +/** + * Declares a field, including any necessary initialization. + */ +string +t_c_glib_generator::declare_field (t_field *tfield, bool init, bool pointer, + bool constant, bool reference) +{ + string result = ""; + if (constant) + { + result += "const "; + } + result += type_name(tfield->get_type()); + if (pointer) + { + result += "*"; + } + if (reference) + { + result += "*"; + } + result += " " + tfield->get_name(); + if (init) + { + t_type* type = get_true_type(tfield->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: + break; + case t_base_type::TYPE_BOOL: + 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 += " = (gdouble) 0"; + break; + case t_base_type::TYPE_STRING: + result += " = NULL"; + break; + default: + throw "compiler error: no C intializer for base type " + + t_base_type::t_base_name (tbase); + } + } else if (type->is_enum ()) { + result += " = (" + type_name (type) + ") 0"; + } + } + + if (!reference) + { + result += ";"; + } + + return result; +} + +/** + * Generates C code that initializes complex constants. + */ +void +t_c_glib_generator::generate_const_initializer (string name, t_type *type, + t_const_value *value) +{ + string name_u = initial_caps_to_underscores (name); + string name_lc = to_lower_case (name_u); + string type_u = initial_caps_to_underscores (type->get_name ()); + string type_uc = to_upper_case (type_u); + + 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; + ostringstream initializers; + + // initialize any constants that may be referenced by this initializer + for (v_iter = val.begin (); v_iter != val.end (); ++v_iter) + { + t_type *field_type = NULL; + string field_name = ""; + + 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 (); + field_name = (*f_iter)->get_name (); + } + } + if (field_type == NULL) + { + throw "type error: " + type->get_name () + " has no field " + + v_iter->first->get_string(); + } + field_name = tmp (field_name); + + generate_const_initializer (name + "_constant_" + field_name, + field_type, v_iter->second); + initializers << + " constant->" << v_iter->first->get_string () << " = " << + constant_value (name + "_constant_" + field_name, + field_type, v_iter->second) << ";" << endl << + " constant->__isset_" << v_iter->first->get_string () << + " = TRUE;" << endl; + } + + // implement the initializer + f_types_impl_ << + "static " << this->nspace << type->get_name () << " *" << endl << + this->nspace_lc << name_lc << "_constant (void)" << endl << + "{" << endl << + " static " << this->nspace << type->get_name () << + " *constant = NULL;" << endl << + " if (constant == NULL)" << endl << + " {" << endl << + " constant = g_object_new (" << this->nspace_uc << "TYPE_" << + type_uc << ", NULL);" << endl << + initializers.str () << endl << + " }" << endl << + " return constant;" << endl << + "}" << endl << + endl; + } else if (type->is_list ()) { + string list_type = "GPtrArray *"; + string list_initializer = "g_ptr_array_new ()"; + string list_appender = "g_ptr_array_add"; + bool list_variable = false; + + t_type* etype = ((t_list*)type)->get_elem_type(); + const vector& val = value->get_list(); + vector::const_iterator v_iter; + ostringstream initializers; + + if (etype->is_base_type ()) + { + t_base_type::t_base tbase = ((t_base_type *) etype)->get_base (); + switch (tbase) + { + case t_base_type::TYPE_VOID: + throw "compiler error: cannot determine array type"; + case t_base_type::TYPE_BOOL: + case t_base_type::TYPE_BYTE: + case t_base_type::TYPE_I16: + case t_base_type::TYPE_I32: + case t_base_type::TYPE_I64: + case t_base_type::TYPE_DOUBLE: + list_type = "GArray *"; + list_initializer = generate_new_array_from_type (etype); + list_appender = "g_array_append_val"; + list_variable = true; + break; + case t_base_type::TYPE_STRING: + break; + default: + throw "compiler error: no array info for type"; + } + } + + for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) + { + string fname = tmp (name); + + generate_const_initializer (fname, etype, (*v_iter)); + if (list_variable) + { + initializers << + " " << type_name (etype) << " " << fname << " = " << + constant_value (fname, (t_type *) etype, (*v_iter)) << ";" << + endl << + " " << list_appender << "(constant, " << fname << ");" << endl; + } else { + initializers << + " " << list_appender << "(constant, " << + constant_value (fname, (t_type *) etype, (*v_iter)) << ");" << endl; + } + } + + f_types_impl_ << + "static " << list_type << endl << + this->nspace_lc << name_lc << "_constant (void)" << endl << + "{" << endl << + " static " << list_type << " constant = NULL;" << endl << + " if (constant == NULL)" << endl << + " {" << endl << + " constant = " << list_initializer << ";" << endl << + initializers.str () << endl << + " }" << endl << + " return constant;" << endl << + "}" << endl << + endl; + } else if (type->is_set ()) { + t_type *etype = ((t_set *) type)->get_elem_type (); + const vector& val = value->get_list (); + vector::const_iterator v_iter; + ostringstream initializers; + + for (v_iter = val.begin (); v_iter != val.end (); ++v_iter) + { + string fname = tmp (name); + generate_const_initializer (fname, etype, (*v_iter)); + initializers << + " " << type_name (etype) << " " << fname << " = " << + constant_value (fname, (t_type *) etype, (*v_iter)) << ";" << endl << + " g_hash_table_insert (constant, &" << fname << ", &" << fname << + ");" << endl; + } + + f_types_impl_ << + "static GHashTable *" << endl << + this->nspace_lc << name_lc << "_constant (void)" << endl << + "{" << endl << + " static GHashTable *constant = NULL;" << endl << + " if (constant == NULL)" << endl << + " {" << endl << + " constant = g_hash_table_new (NULL, NULL);" << endl << + initializers.str () << endl << + " }" << endl << + " return constant;" << endl << + "}" << endl << + 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 vector& val = value->get_list (); + vector::const_iterator v_iter; + ostringstream initializers; + + for (v_iter = val.begin (); v_iter != val.end (); ++v_iter) + { + string fname = tmp (name); + string kname = fname + "key"; + string vname = fname + "val"; + generate_const_initializer (kname, ktype, (*v_iter)); + generate_const_initializer (vname, vtype, (*v_iter)); + + initializers << + " " << type_name (ktype) << " " << kname << " = " << + constant_value (kname, (t_type *) ktype, (*v_iter)) << ";" << endl << + " " << type_name (vtype) << " " << vname << " = " << + constant_value (vname, (t_type *) vtype, (*v_iter)) << ";" << endl << + " g_hash_table_insert (constant, &" << fname << ", &" << fname << + ");" << endl; + } + + f_types_impl_ << + "static GHashTable *" << endl << + this->nspace_lc << name_lc << "_constant (void)" << endl << + "{" << endl << + " static GHashTable *constant = NULL;" << endl << + " if (constant == NULL)" << endl << + " {" << endl << + " constant = g_hash_table_new (NULL, NULL);" << endl << + initializers.str () << endl << + " }" << endl << + " return constant;" << endl << + "}" << endl << + endl; + } +} + +/** + * Generates C code that represents a Thrift service client. + */ +void +t_c_glib_generator::generate_service_client (t_service *tservice) +{ + /* get some C friendly service names */ + string service_name_lc = to_lower_case (initial_caps_to_underscores (service_name_)); + string service_name_uc = to_upper_case (service_name_lc); + + // Generate the client interface dummy object in the header. + f_header_ << + "/* " << service_name_ << " service interface */" << endl << + "typedef struct _" << this->nspace << service_name_ << "If " << + this->nspace << service_name_ << "If; " << + " /* dummy object */" << endl << + endl; + + // Generate the client interface object in the header. + f_header_ << + "struct _" << this->nspace << service_name_ << "IfInterface" << endl << + "{" << endl << + " GTypeInterface parent;" << endl << + endl; + + /* write out the functions for this interface */ + indent_up (); + vector functions = tservice->get_functions (); + vector::const_iterator f_iter; + for (f_iter = functions.begin (); f_iter != functions.end (); ++f_iter) { + /* make the function name C friendly */ + string funname = initial_caps_to_underscores ((*f_iter)->get_name ()); + t_type *ttype = (*f_iter)->get_returntype (); + t_struct *arglist = (*f_iter)->get_arglist (); + t_struct *xlist = (*f_iter)->get_xceptions(); + bool has_return = !ttype->is_void(); + bool has_args = arglist->get_members().size() == 0; + bool has_xceptions = xlist->get_members().size() == 0; + + string params = "(" + this->nspace + service_name_ + "If *iface" + + (has_return ? ", " + type_name (ttype) + "* _return" : "") + + (has_args ? "" : (", " + argument_list (arglist))) + + (has_xceptions ? "" : (", " + xception_list (xlist))) + + ", GError **error)"; + + indent (f_header_) << "gboolean (*" << funname << ") " << params << ";" << + endl; + } + indent_down (); + + f_header_ << + "};" << endl << + "typedef struct _" << this->nspace << service_name_ << "IfInterface " << + this->nspace << service_name_ << "IfInterface;" << endl << + endl; + + // generate all the interface boilerplate + f_header_ << + "GType " << this->nspace_lc << service_name_lc << + "_if_get_type (void);" << endl << + "#define " << this->nspace_uc << "TYPE_" << service_name_uc << "_IF " << + "(" << this->nspace_lc << service_name_lc << "_if_get_type ())" << + endl << + "#define " << this->nspace_uc << service_name_uc << "_IF(obj) " << + "(G_TYPE_CHECK_INSTANCE_CAST ((obj), " << + this->nspace_uc << "TYPE_" << service_name_uc << "_IF, " << + this->nspace << service_name_ << "If))" << endl << + "#define " << this->nspace_uc << "IS_" << service_name_uc << "_IF(obj) " << + "(G_TYPE_CHECK_INSTANCE_TYPE ((obj, " << + this->nspace_uc << "TYPE_" << service_name_uc << "_IF))" << endl << + "#define " << this->nspace_uc << service_name_uc << + "_IF_GET_INTERFACE(inst) (G_TYPE_INSTANCE_GET_INTERFACE ((inst), " << + this->nspace_uc << "TYPE_" << service_name_uc << "_IF, " << + this->nspace << service_name_ << "IfInterface))" << endl << + endl; + + // write out all the interface function prototypes + for (f_iter = functions.begin (); f_iter != functions.end (); ++f_iter) { + /* make the function name C friendly */ + string funname = initial_caps_to_underscores ((*f_iter)->get_name ()); + t_type *ttype = (*f_iter)->get_returntype (); + t_struct *arglist = (*f_iter)->get_arglist (); + t_struct *xlist = (*f_iter)->get_xceptions (); + bool has_return = !ttype->is_void(); + bool has_args = arglist->get_members().size() == 0; + bool has_xceptions = xlist->get_members().size() == 0; + + string params = "(" + this->nspace + service_name_ + "If *iface" + + (has_return ? ", " + type_name (ttype) + "* _return" : "") + + (has_args ? "" : (", " + argument_list (arglist))) + + (has_xceptions ? "" : (", " + xception_list (xlist))) + + ", GError **error)"; + + f_header_ << "gboolean " << this->nspace_lc << service_name_lc << + "_if_" << funname << " " << params << ";" << endl; + } + f_header_ << endl; + + // Generate the client object instance definition in the header. + f_header_ << + "/* " << service_name_ << " service client */" << endl << + "struct _" << this->nspace << service_name_ << "Client" << endl << + "{" << endl << + " GObject parent;" << endl << + endl << + " ThriftProtocol *input_protocol;" << endl << + " ThriftProtocol *output_protocol;" << endl << + "};" << endl << + "typedef struct _" << this->nspace << service_name_ << "Client " << + this->nspace << service_name_ << "Client;" << endl << + endl; + + // Generate the class definition in the header. + f_header_ << + "struct _" << this->nspace << service_name_ << "ClientClass" << endl << + "{" << endl << + " GObjectClass parent;" << endl << + "};" << endl << + "typedef struct _" << this->nspace << service_name_ << "ClientClass " << + this->nspace << service_name_ << "ClientClass;" << endl << + endl; + + // Create all the GObject boilerplate + f_header_ << + "GType " << this->nspace_lc << service_name_lc << + "_client_get_type (void);" << endl << + "#define " << this->nspace_uc << "TYPE_" << service_name_uc << "_CLIENT " << + "(" << this->nspace_lc << service_name_lc << "_client_get_type ())" << + endl << + "#define " << this->nspace_uc << service_name_uc << "_CLIENT(obj) " << + "(G_TYPE_CHECK_INSTANCE_CAST ((obj), " << + this->nspace_uc << "TYPE_" << service_name_uc << "_CLIENT, " << + this->nspace << service_name_ << "Client))" << endl << + "#define " << this->nspace_uc << service_name_uc << "_CLIENT_CLASS(c) " << + "(G_TYPE_CHECK_CLASS_CAST ((c), " << + this->nspace_uc << "TYPE_" << service_name_uc << "_CLIENT, " << + this->nspace << service_name_ << "ClientClass))" << endl << + "#define " << this->nspace_uc << service_name_uc << "_IS_CLIENT(obj) " << + "(G_TYPE_CHECK_INSTANCE_TYPE ((obj), " << + this->nspace_uc << "TYPE_" << service_name_uc << "_CLIENT))" << endl << + "#define " << this->nspace_uc << service_name_uc << + "_IS_CLIENT_CLASS(c) " << "(G_TYPE_CHECK_CLASS_TYPE ((c), " << + this->nspace_uc << "TYPE_" << service_name_uc << "_CLIENT))" << endl << + "#define " << this->nspace_uc << service_name_uc << + "_CLIENT_GET_CLASS(obj) " << "(G_TYPE_INSTANCE_GET_CLASS ((obj), " << + this->nspace_uc << "TYPE_" << service_name_uc << "_CLIENT, " << + this->nspace << service_name_ << "ClientClass))" << endl << + endl; + + /* write out the function prototypes */ + for (f_iter = functions.begin (); f_iter != functions.end (); ++f_iter) { + /* make the function name C friendly */ + string funname = to_lower_case (initial_caps_to_underscores ((*f_iter)->get_name ())); + + t_function service_function ((*f_iter)->get_returntype (), + service_name_lc + string ("_client_") + + funname, + (*f_iter)->get_arglist (), + (*f_iter)->get_xceptions ()); + indent (f_header_) << function_signature (&service_function) << ";" << endl; + + t_function send_function (g_type_void, + service_name_lc + string ("_client_send_") + + funname, + (*f_iter)->get_arglist ()); + indent (f_header_) << function_signature (&send_function) << ";" << endl; + + // implement recv if not a oneway service + if (!(*f_iter)->is_oneway ()) { + t_struct noargs (program_); + t_function recv_function ((*f_iter)->get_returntype (), + service_name_lc + string ("_client_recv_") + + funname, + &noargs, + (*f_iter)->get_xceptions ()); + indent (f_header_) << function_signature (&recv_function) << ";" << endl; + } + } + + f_header_ << endl; + // end of header code + + // Generate interface method implementations + for (f_iter = functions.begin (); f_iter != functions.end (); ++f_iter) { + /* make the function name C friendly */ + string funname = initial_caps_to_underscores ((*f_iter)->get_name ()); + t_type *ttype = (*f_iter)->get_returntype (); + t_struct *arglist = (*f_iter)->get_arglist (); + t_struct *xlist = (*f_iter)->get_xceptions (); + bool has_return = !ttype->is_void(); + bool has_args = arglist->get_members().size() == 0; + bool has_xceptions = xlist->get_members().size() == 0; + + string params = "(" + this->nspace + service_name_ + "If *iface" + + (has_return ? ", " + type_name (ttype) + "* _return" : "") + + (has_args ? "" : (", " + argument_list (arglist))) + + (has_xceptions ? "" : (", " + xception_list (xlist))) + + ", GError **error)"; + + string params_without_type = string("iface, ") + + (has_return ? "_return, " : ""); + + const vector& fields = arglist->get_members (); + vector::const_iterator f_iter_field; + for (f_iter_field = fields.begin(); f_iter_field != fields.end(); + ++f_iter_field) + { + params_without_type += (*f_iter_field)->get_name (); + params_without_type += ", "; + } + + const vector& xceptions = xlist->get_members (); + vector::const_iterator x_iter; + for (x_iter = xceptions.begin(); x_iter != xceptions.end(); ++x_iter) + { + params_without_type += (*x_iter)->get_name (); + params_without_type += ", "; + } + + f_service_ << + "gboolean" << endl << + this->nspace_lc << service_name_lc << "_if_" << funname << " " << + params << endl << + "{" << endl << + " return " << this->nspace_uc << service_name_uc << + "_IF_GET_INTERFACE (iface)->" << funname << " (" << + params_without_type << "error);" << endl << + "}" << endl << + endl; + } + + // Generate interface boilerplate + f_service_ << + "GType" << endl << + this->nspace_lc << service_name_lc << "_if_get_type (void)" << endl << + "{" << endl << + " static GType type = 0;" << endl << + " if (type == 0)" << endl << + " {" << endl << + " static const GTypeInfo type_info =" << endl << + " {" << endl << + " sizeof (" << this->nspace << service_name_ << "IfInterface)," << + endl << + " NULL, /* base_init */" << endl << + " NULL /* base_finalize */" << endl << + " };" << endl << + " type = g_type_register_static (G_TYPE_INTERFACE," << endl << + " \"" << this->nspace << service_name_ << + "If\"," << endl << + " &type_info, 0);" << endl << + " }" << endl << + " return type;" << endl << + "}" << endl << + endl; + + // Generate client boilerplate + f_service_ << + "static void " << endl << + this->nspace_lc << service_name_lc << + "_if_interface_init (" << this->nspace << service_name_ << + "IfInterface *iface);" << endl << + endl << + "G_DEFINE_TYPE_WITH_CODE (" << this->nspace << service_name_ << + "Client, " << this->nspace_lc << service_name_lc << "_client," << endl << + " G_TYPE_OBJECT, " << endl << + " G_IMPLEMENT_INTERFACE (" << + this->nspace_uc << "TYPE_" << service_name_uc << "_IF," << endl << + " " << + this->nspace_lc << service_name_lc << "_if_interface_init));" << endl << + endl; + + // Generate client properties + f_service_ << + "enum _" << this->nspace << service_name_ << "ClientProperties" << endl << + "{" << endl << + " PROP_0," << endl << + " PROP_" << this->nspace_uc << service_name_uc << + "_CLIENT_INPUT_PROTOCOL," << + endl << + " PROP_" << this->nspace_uc << service_name_uc << + "_CLIENT_OUTPUT_PROTOCOL" << + endl << + "};" << endl << + endl; + + // generate property setter + f_service_ << + "void" << endl << + this->nspace_lc << service_name_lc << "_client_set_property (" << + "GObject *object, guint property_id, const GValue *value, " << + "GParamSpec *pspec)" << endl << + "{" << endl << + " " << this->nspace << service_name_ << "Client *client = " << + this->nspace_uc << service_name_uc << "_CLIENT (object);" << endl << + endl << + " THRIFT_UNUSED_VAR (pspec);" << endl << + endl << + " switch (property_id)" << endl << + " {" << endl << + " case PROP_" << this->nspace_uc << service_name_uc << + "_CLIENT_INPUT_PROTOCOL:" << endl << + " client->input_protocol = g_value_get_object (value);" << endl << + " break;" << endl << + " case PROP_" << this->nspace_uc << service_name_uc << + "_CLIENT_OUTPUT_PROTOCOL:" << endl << + " client->output_protocol = g_value_get_object (value);" << endl << + " break;" << endl << + " }" << endl << + "}" << endl << + endl; + + // generate property getter + f_service_ << + "void" << endl << + this->nspace_lc << service_name_lc << "_client_get_property (" << + "GObject *object, guint property_id, GValue *value, " << + "GParamSpec *pspec)" << endl << + "{" << endl << + " " << this->nspace << service_name_ << "Client *client = " << + this->nspace_uc << service_name_uc << "_CLIENT (object);" << endl << + endl << + " THRIFT_UNUSED_VAR (pspec);" << endl << + endl << + " switch (property_id)" << endl << + " {" << endl << + " case PROP_" << this->nspace_uc << service_name_uc << + "_CLIENT_INPUT_PROTOCOL:" << endl << + " g_value_set_object (value, client->input_protocol);" << endl << + " break;" << endl << + " case PROP_" << this->nspace_uc << service_name_uc << + "_CLIENT_OUTPUT_PROTOCOL:" << endl << + " g_value_set_object (value, client->output_protocol);" << endl << + " break;" << endl << + " }" << endl << + "}" << endl << + endl; + + + // Generate client method implementations + for (f_iter = functions.begin (); f_iter != functions.end (); ++f_iter) { + string name = (*f_iter)->get_name (); + string funname = initial_caps_to_underscores (name); + + // Get the struct of function call params and exceptions + t_struct* arg_struct = (*f_iter)->get_arglist (); + + // Function for sending + t_function send_function (g_type_void, + service_name_lc + string ("_client_send_") + + funname, + (*f_iter)->get_arglist ()); + + // Open the send function + indent (f_service_) << + function_signature (&send_function) << endl; + scope_up(f_service_); + + // Serialize the request + f_service_ << + indent () << "gint32 cseqid = 0;" << endl << + indent () << "ThriftProtocol * protocol = " << + this->nspace_uc << service_name_uc << + "_CLIENT (iface)->output_protocol;" << endl << + endl << + indent () << "if (thrift_protocol_write_message_begin (protocol, \"" << + name << "\", T_CALL, cseqid, error) < 0)" << endl << + indent () << " return FALSE;" << endl << + endl; + + generate_struct_writer (f_service_, arg_struct, "", "", false); + + f_service_ << + indent () << + "if (thrift_protocol_write_message_end (protocol, error) < 0)" << + endl << + indent () << + " return FALSE;" << endl << + indent () << + "if (!thrift_transport_flush (protocol->transport, error))" << endl << + indent () << + " return FALSE;" << endl << + indent () << + "if (!thrift_transport_write_end (protocol->transport, error))" << + endl << + indent () << + " return FALSE;" << endl << + endl << + indent () << + "return TRUE;" << endl; + + scope_down (f_service_); + f_service_ << endl; + + // Generate recv function only if not an async function + if (!(*f_iter)->is_oneway ()) { + t_struct noargs (program_); + t_function recv_function ((*f_iter)->get_returntype (), + service_name_lc + + string ("_client_recv_") + funname, &noargs, + (*f_iter)->get_xceptions ()); + // Open function + indent (f_service_) << + function_signature (&recv_function) << endl; + scope_up (f_service_); + + f_service_ << endl << + indent() << "gint32 rseqid;" << endl << + indent() << "gchar * fname;" << endl << + indent() << "ThriftMessageType mtype;" << endl << + indent() << "ThriftProtocol * protocol = " << + this->nspace_uc << service_name_uc << + "_CLIENT (iface)->input_protocol;" << endl << + endl << + indent() << "if (thrift_protocol_read_message_begin " << + "(protocol, &fname, &mtype, &rseqid, error) < 0)" << + endl << + indent() << "{" << endl << + indent() << " if (fname) g_free (fname);" << endl << + indent() << " return FALSE;" << endl << + indent() << "}" << endl << + endl << + indent() << "if (mtype == T_EXCEPTION) {" << endl << + indent() << " if (fname) g_free (fname);" << endl << + indent() << " ThriftApplicationException *xception = g_object_new (THRIFT_TYPE_APPLICATION_EXCEPTION, NULL);" << endl << + + indent() << " thrift_struct_read (THRIFT_STRUCT (xception), protocol, NULL);" << endl << + indent() << " thrift_protocol_read_message_end (protocol, NULL);" << endl << + indent() << " thrift_transport_read_end (protocol->transport, NULL);" << endl << + indent() << " g_set_error (error, THRIFT_APPLICATION_EXCEPTION_ERROR, xception->type, \"application error: %s\", xception->message);" << endl << + indent() << " g_object_unref (xception);" << endl << + indent() << " return FALSE;" << endl << + indent() << "} else if (mtype != T_REPLY) {" << endl << + indent() << " if (fname) g_free (fname);" << endl << + indent() << " thrift_protocol_skip (protocol, T_STRUCT, NULL);" << endl << + indent() << " thrift_protocol_read_message_end (protocol, NULL);" << endl << + indent() << " thrift_transport_read_end (protocol->transport, NULL);" << endl << + indent() << " g_set_error (error, THRIFT_APPLICATION_EXCEPTION_ERROR, THRIFT_APPLICATION_EXCEPTION_ERROR_INVALID_MESSAGE_TYPE, \"invalid message type %d, expected T_REPLY\", mtype);" << endl << + indent() << " return FALSE;" << endl << + indent() << "} else if (strncmp (fname, \"" << name << "\", " << name.length () << ") != 0) {" << endl << + indent() << " thrift_protocol_skip (protocol, T_STRUCT, NULL);" << endl << + indent() << " thrift_protocol_read_message_end (protocol, error);" << endl << + indent() << " thrift_transport_read_end (protocol->transport, error);" << endl << + indent() << " g_set_error (error, THRIFT_APPLICATION_EXCEPTION_ERROR, THRIFT_APPLICATION_EXCEPTION_ERROR_WRONG_METHOD_NAME, \"wrong method name %s, expected " << name << "\", fname);" << endl << + indent() << " if (fname) g_free (fname);" << endl << + indent() << " return FALSE;" << endl << + indent() << "}" << endl << + indent() << "if (fname) g_free (fname);" << endl << + endl; + + t_struct* xs = (*f_iter)->get_xceptions(); + const std::vector& xceptions = xs->get_members(); + vector::const_iterator x_iter; + + { + t_struct result(program_, tservice->get_name() + "_" + + (*f_iter)->get_name() + "_result"); + t_field success((*f_iter)->get_returntype(), "*_return", 0); + if (!(*f_iter)->get_returntype()->is_void()) { + result.append(&success); + } + + // add readers for exceptions, dereferencing the pointer. + for (x_iter = xceptions.begin(); x_iter != xceptions.end(); x_iter++) { + t_field *xception = new t_field((*x_iter)->get_type (), + "*" + (*x_iter)->get_name (), + (*x_iter)->get_key ()); + result.append (xception); + } + + generate_struct_reader (f_service_, &result, "", "", false); + } + + f_service_ << + indent() << "if (thrift_protocol_read_message_end (protocol, error) < 0)" << endl << + indent() << " return FALSE;" << endl << + endl << + indent() << "if (!thrift_transport_read_end (protocol->transport, error))" << endl << + indent() << " return FALSE;" << endl << + endl; + + // copy over any throw exceptions and return failure + for (x_iter = xceptions.begin(); x_iter != xceptions.end(); x_iter++) { + f_service_ << + indent() << "if (*" << (*x_iter)->get_name() << " != NULL)" << endl << + indent() << "{" << endl << + indent() << " g_set_error (error, " << this->nspace_uc << + to_upper_case (initial_caps_to_underscores ( + (*x_iter)->get_type ()->get_name ())) << + "_ERROR, " << + this->nspace_uc << + to_upper_case (initial_caps_to_underscores ( + (*x_iter)->get_type ()->get_name ())) << + "_ERROR_CODE, \"" << (*x_iter)->get_type ()->get_name () << + "\");" << endl << + indent() << " return FALSE;" << endl << + indent() << "}" << endl; + } + // Close function + indent(f_service_) << "return TRUE;" << endl; + scope_down(f_service_); + f_service_ << endl; + } + + // Open function + t_function service_function ((*f_iter)->get_returntype (), + service_name_lc + + string ("_client_") + funname, + (*f_iter)->get_arglist (), + (*f_iter)->get_xceptions ()); + indent (f_service_) << + function_signature (&service_function) << endl; + scope_up (f_service_); + + // wrap each function + f_service_ << + indent () << "if (!" << this->nspace_lc << service_name_lc << + "_client_send_" << funname << + " (iface"; + + // Declare the function arguments + const vector &fields = arg_struct->get_members (); + vector::const_iterator fld_iter; + for (fld_iter = fields.begin (); fld_iter != fields.end (); ++fld_iter) { + f_service_ << ", " << (*fld_iter)->get_name (); + } + f_service_ << ", error))" << endl << + indent () << " return FALSE;" << endl; + + // if not oneway, implement recv + if (!(*f_iter)->is_oneway ()) { + string ret = (*f_iter)->get_returntype ()->is_void () ? "" : "_return, "; + + const vector& xceptions = + (*f_iter)->get_xceptions()->get_members (); + vector::const_iterator x_iter; + for (x_iter = xceptions.begin(); x_iter != xceptions.end(); ++x_iter) + { + ret += (*x_iter)->get_name (); + ret += ", "; + } + + f_service_ << + indent () << "if (!" << this->nspace_lc << service_name_lc << + "_client_recv_" << funname << + " (iface, " << ret << "error))" << endl << + indent () << " return FALSE;" << endl; + } + + // return TRUE which means all functions were called OK + indent (f_service_) << "return TRUE;" << endl; + scope_down (f_service_); + f_service_ << endl; + } + + // create the interface initializer + f_service_ << + "static void" << endl << + this->nspace_lc << service_name_lc << "_if_interface_init (" << + this->nspace << service_name_ << "IfInterface *iface)" << endl << + "{" << endl; + for (f_iter = functions.begin (); f_iter != functions.end (); ++f_iter) { + /* make the function name C friendly */ + string funname = initial_caps_to_underscores ((*f_iter)->get_name ()); + + f_service_ << + " iface->" << funname << " = " << this->nspace_lc << service_name_lc << + "_client_" << funname << ";" << endl; + } + f_service_ << + "}" << endl << + endl; + + // create the client instance initializer + f_service_ << + "static void" << endl << + this->nspace_lc << service_name_lc << "_client_init (" << + this->nspace << service_name_ << "Client *client)" << endl << + "{" << endl << + " client->input_protocol = NULL;" << endl << + " client->output_protocol = NULL;" << endl << + "}" << endl << + endl; + + // create the client class initializer + f_service_ << + "static void" << endl << + this->nspace_lc << service_name_lc << "_client_class_init (" << + this->nspace << service_name_ << "ClientClass *cls)" << endl << + "{" << endl << + " GObjectClass *gobject_class = G_OBJECT_CLASS (cls);" << endl << + " GParamSpec *param_spec;" << endl << + endl << + " gobject_class->set_property = " << this->nspace_lc << + service_name_lc << "_client_set_property;" << endl << + " gobject_class->get_property = " << this->nspace_lc << + service_name_lc << "_client_get_property;" << endl << + endl << + " param_spec = g_param_spec_object (\"input_protocol\"," << endl << + " \"input protocol (construct)\"," << + endl << + " \"Set the client input protocol\"," << + endl << + " THRIFT_TYPE_PROTOCOL," << endl << + " G_PARAM_READWRITE);" << endl << + " g_object_class_install_property (gobject_class," << endl << + " PROP_" << this->nspace_uc << + service_name_uc << "_CLIENT_INPUT_PROTOCOL, param_spec);" << endl << + endl << + " param_spec = g_param_spec_object (\"output_protocol\"," << endl << + " \"output protocol (construct)\"," << + endl << + " \"Set the client output protocol\"," << + endl << + " THRIFT_TYPE_PROTOCOL," << endl << + " G_PARAM_READWRITE);" << endl << + " g_object_class_install_property (gobject_class," << endl << + " PROP_" << this->nspace_uc << + service_name_uc << "_CLIENT_OUTPUT_PROTOCOL, param_spec);" << endl << + "}" << endl << + endl; +} + +/** + * Generates C code that represents a Thrift service server. + */ +void +t_c_glib_generator::generate_service_server (t_service *tservice) +{ + /* get some C friendly service names */ + string service_name_u = initial_caps_to_underscores (service_name_); + string service_name_uc = to_upper_case (service_name_u); + + // write the server object instance definition in the header. + // TODO: implement after implement TServer and TProcessor +} + +/** + * Generates C code to represent a THrift structure as a GObject. + */ +void +t_c_glib_generator::generate_object (t_struct *tstruct) +{ + string name = tstruct->get_name (); + string name_u = initial_caps_to_underscores (name); + string name_uc = to_upper_case (name_u); + + // write the instance definition + f_types_ << + "struct _" << this->nspace << name << endl << + "{ " << endl << + " ThriftStruct parent; " << endl << + endl << + " /* public */" << endl; + + // for each field, add a member variable + bool has_nonrequired_fields = false; + vector::const_iterator m_iter; + const vector &members = tstruct->get_members (); + for (m_iter = members.begin (); m_iter != members.end (); ++m_iter) + { + t_type *t = get_true_type ((*m_iter)->get_type ()); + f_types_ << + " " << type_name (t) << " " << (*m_iter)->get_name () << ";" << endl; + if ((*m_iter)->get_req () != t_field::T_REQUIRED) + { + has_nonrequired_fields = true; + f_types_ << + " gboolean __isset_" << (*m_iter)->get_name () << ";" << endl; + } + } + + // close the structure definition and create a typedef + f_types_ << + "};" << endl << + "typedef struct _" << this->nspace << name << " " << + this->nspace << name << ";" << endl << + endl; + + // write the class definition + f_types_ << + "struct _" << this->nspace << name << "Class" << endl << + "{" << endl << + " ThriftStructClass parent;" << endl << + "};" << endl << + "typedef struct _" << this->nspace << name << "Class " << this->nspace << name << "Class;" << endl << + endl; + + // write the standard GObject boilerplate + f_types_ << + "GType " << this->nspace_lc << name_u << "_get_type (void);" << endl << + "#define " << this->nspace_uc << "TYPE_" << name_uc << " (" << this->nspace_lc << name_u << "_get_type ())" << endl << + "#define " << this->nspace_uc << name_uc << "(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), " << this->nspace_uc << "TYPE_" << name_uc << ", " << this->nspace << name << "))" << endl << + "#define " << this->nspace_uc << name_uc << "_CLASS(c) (G_TYPE_CHECK_CLASS_CAST ((c), " << this->nspace_uc << "_TYPE_" << name_uc << ", " << this->nspace << name << "Class))" << endl << + "#define " << this->nspace_uc << "IS_" << name_uc << "(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), " << this->nspace_uc << "TYPE_" << name_uc << "))" << endl << + "#define " << this->nspace_uc << "IS_" << name_uc << "_CLASS(c) (G_TYPE_CHECK_CLASS_TYPE ((c), " << this->nspace_uc << "TYPE_" << name_uc << "))" << endl << + "#define " << this->nspace_uc << name_uc << "_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), " << this->nspace_uc << "TYPE_" << name_uc << ", " << this->nspace << name << "Class))" << endl << + endl; + + // start writing the object implementation .c file + // generate struct I/O methods + string this_get = this->nspace + name + " * this_object = " + + this->nspace_uc + name_uc + "(object);"; + generate_struct_reader (f_types_impl_, tstruct, "this_object->", this_get); + generate_struct_writer (f_types_impl_, tstruct, "this_object->", this_get); + + // generate the instance init function + f_types_impl_ << + "void " << endl << + this->nspace_lc << name_u << "_instance_init (" << this->nspace << name << " * object)" << endl << + "{" << endl; + + // satisfy compilers with -Wall turned on + indent_up (); + indent (f_types_impl_) << "/* satisfy -Wall */" << endl << + indent () << "THRIFT_UNUSED_VAR (object);" << endl; + + for (m_iter = members.begin (); m_iter != members.end (); ++m_iter) + { + t_type* t = get_true_type ((*m_iter)->get_type ()); + if (t->is_base_type ()) { + // only have init's for base types + string dval = " = "; + if (t->is_enum ()) { + dval += "(" + type_name (t) + ")"; + } + t_const_value* cv = (*m_iter)->get_value (); + if (cv != NULL) { + dval += constant_value ("", t, cv); + } else { + dval += t->is_string () ? "NULL" : "0"; + } + indent(f_types_impl_) << "object->" << (*m_iter)->get_name () << dval << ";" << endl; + } else if (t->is_struct ()) { + string name = (*m_iter)->get_name (); + string type_name_uc = to_upper_case + (initial_caps_to_underscores ((*m_iter)->get_type ()->get_name ())); + indent (f_types_impl_) << "object->" << name << " = g_object_new (" << this->nspace_uc << "TYPE_" << type_name_uc << ", NULL);" << endl; + } else if (t->is_xception()) { + string name = (*m_iter)->get_name(); + indent (f_types_impl_) << "object->" << name << " = NULL;" << endl; + } else if (t->is_container()) { + string name = (*m_iter)->get_name(); + + if (t->is_map () || t->is_set ()) + { + indent (f_types_impl_) << "object->" << name << " = g_hash_table_new (NULL, NULL);" << endl; + } else if (t->is_list ()) { + t_type *etype = ((t_list *) t)->get_elem_type (); + string init_function = "g_ptr_array_new ()"; + + if (etype->is_base_type ()) + { + t_base_type::t_base tbase = ((t_base_type *) etype)->get_base (); + switch (tbase) + { + case t_base_type::TYPE_VOID: + throw "compiler error: cannot determine array type"; + case t_base_type::TYPE_BOOL: + case t_base_type::TYPE_BYTE: + case t_base_type::TYPE_I16: + case t_base_type::TYPE_I32: + case t_base_type::TYPE_I64: + case t_base_type::TYPE_DOUBLE: + init_function = generate_new_array_from_type (etype); + break; + case t_base_type::TYPE_STRING: + break; + default: + throw "compiler error: no array info for type"; + } + } + + indent (f_types_impl_) << "object->" << name << " = " << + init_function << ";" << endl; + } + + } + + /* if not required, initialize the __isset variable */ + if ((*m_iter)->get_req () != t_field::T_REQUIRED) + { + indent (f_types_impl_) << "object->__isset_" << (*m_iter)->get_name () << " = FALSE;" << endl; + } + } + + indent_down (); + f_types_impl_ << "}" << endl << + endl; + + /* create the destructor */ + f_types_impl_ << + "void " << endl << + this->nspace_lc << name_u << "_finalize (GObject *object)" << endl << + "{" << endl; + indent_up (); + + f_types_impl_ << + indent () << + this->nspace << name << " *tobject = " << this->nspace_uc << + name_uc << " (object);" << endl << endl; + + f_types_impl_ << + indent () << "/* satisfy -Wall in case we don't use tobject */" << endl << + indent () << "THRIFT_UNUSED_VAR (tobject);" << endl; + + for (m_iter = members.begin (); m_iter != members.end (); ++m_iter) + { + t_type* t = get_true_type ((*m_iter)->get_type ()); + if (t->is_container()) + { + string name = (*m_iter)->get_name(); + if (t->is_map () || t->is_set ()) + { + f_types_impl_ << + indent () << "g_hash_table_unref (tobject->" << name << ");" << endl; + } else if (t->is_list ()) { + t_type *etype = ((t_list *) t)->get_elem_type (); + string destructor_function = "g_ptr_array_free"; + + if (etype->is_base_type ()) + { + t_base_type::t_base tbase = ((t_base_type *) etype)->get_base (); + switch (tbase) + { + case t_base_type::TYPE_VOID: + throw "compiler error: cannot determine array type"; + case t_base_type::TYPE_BOOL: + case t_base_type::TYPE_BYTE: + case t_base_type::TYPE_I16: + case t_base_type::TYPE_I32: + case t_base_type::TYPE_I64: + case t_base_type::TYPE_DOUBLE: + destructor_function = "g_array_free"; + break; + case t_base_type::TYPE_STRING: + break; + default: + throw "compiler error: no array info for type"; + } + } + + f_types_impl_ << + indent () << destructor_function << " (tobject->" << name << + ", FALSE);" << endl; + } + } + } + + indent_down (); + f_types_impl_ << + "}" << endl << + endl; + + + f_types_impl_ << + "void " << endl << + this->nspace_lc << name_u << "_class_init (ThriftStructClass * cls)" << endl << + "{" << endl; + indent_up (); + + f_types_impl_ << + indent () << "GObjectClass *gobject_class = G_OBJECT_CLASS (cls);" << endl << + endl << + indent () << "gobject_class->finalize = " << this->nspace_lc << name_u << "_finalize;" << endl << + indent () << "cls->read = " << this->nspace_lc << name_u << "_read;" << endl << + indent () << "cls->write = " << this->nspace_lc << name_u << "_write;" << endl; + + indent_down (); + f_types_impl_ << + "}" << endl << + endl; + + + f_types_impl_ << + "GType" << endl << + this->nspace_lc << name_u << "_get_type (void)" << endl << + "{" << endl << + " static GType type = 0;" << endl << + endl << + " if (type == 0) " << endl << + " {" << endl << + " static const GTypeInfo type_info = " << endl << + " {" << endl << + " sizeof (" << this->nspace << name << "Class)," << endl << + " NULL, /* base_init */" << endl << + " NULL, /* base_finalize */" << endl << + " (GClassInitFunc) " << this->nspace_lc << name_u << "_class_init," << endl << + " NULL, /* class_finalize */" << endl << + " NULL, /* class_data */" << endl << + " sizeof (" << this->nspace << name << ")," << endl << + " 0, /* n_preallocs */" << endl << + " (GInstanceInitFunc) " << this->nspace_lc << name_u << "_instance_init," << endl << + " NULL, /* value_table */" << endl << + " };" << endl << + endl << + " type = g_type_register_static (THRIFT_TYPE_STRUCT, " << endl << + " \"" << this->nspace << name << "Type\"," << endl << + " &type_info, 0);" << endl << + " }" << endl << + endl << + " return type;" << endl << + "}" << endl << + endl; +} + +/** + * Generates functions to write Thrift structures to a stream. + */ +void +t_c_glib_generator::generate_struct_writer (ofstream &out, t_struct *tstruct, + string this_name, string this_get, + bool is_function) +{ + string name = tstruct->get_name (); + string name_u = initial_caps_to_underscores (name); + string name_uc = to_upper_case (name_u); + + const vector &fields = tstruct->get_members (); + vector ::const_iterator f_iter; + int error_ret = 0; + + if (is_function) + { + error_ret = -1; + indent(out) << + "gint32" << endl << + this->nspace_lc << name_u << + "_write (ThriftStruct *object, ThriftProtocol *protocol, GError **error)" << endl; + } + indent (out) << "{" << endl; + indent_up (); + + out << + indent () << "gint32 ret;" << endl << + indent () << "gint32 xfer = 0;" << endl << + endl; + + indent (out) << this_get << endl; + // satisfy -Wall in the case of an empty struct + if (!this_get.empty()) + { + indent (out) << "THRIFT_UNUSED_VAR (this_object);" << endl; + } + + out << + indent () << "if ((ret = thrift_protocol_write_struct_begin (protocol, \"" << name << "\", error)) < 0)" << endl << + indent () << " return " << error_ret << ";" << endl << + indent () << "xfer += ret;" << endl; + + for (f_iter = fields.begin (); f_iter != fields.end (); ++f_iter) + { + if ((*f_iter)->get_req () == t_field::T_OPTIONAL) + { + indent (out) << "if (this_object->__isset_" << (*f_iter)->get_name () << " == TRUE) {" << endl; + indent_up (); + } + + out << + indent () << "if ((ret = thrift_protocol_write_field_begin (protocol, " << + "\"" << (*f_iter)->get_name () << "\", " << + type_to_enum ((*f_iter)->get_type ()) << ", " << + (*f_iter)->get_key () << ", error)) < 0)" << endl << + indent () << " return " << error_ret << ";" << endl << + indent () << "xfer += ret;" << endl; + generate_serialize_field (out, *f_iter, this_name, "", error_ret); + out << + indent () << "if ((ret = thrift_protocol_write_field_end (protocol, error)) < 0)" << endl << + indent () << " return " << error_ret << ";" << endl << + indent () << "xfer += ret;" << endl; + + if ((*f_iter)->get_req () == t_field::T_OPTIONAL) + { + indent_down (); + indent (out) << "}" << endl; + } + } + + // write the struct map + out << + indent () << "if ((ret = thrift_protocol_write_field_stop (protocol, error)) < 0)" << endl << + indent () << " return " << error_ret << ";" << endl << + indent () << "xfer += ret;" << endl << + indent () << "if ((ret = thrift_protocol_write_struct_end (protocol, error)) < 0)" << endl << + indent () << " return " << error_ret << ";" << endl << + indent () << "xfer += ret;" << endl << + endl; + + if (is_function) + { + indent (out) << "return xfer;" << endl; + } + + indent_down (); + indent (out) << + "}" << endl << + endl; +} + +/** + * Generates code to read Thrift structures from a stream. + */ +void +t_c_glib_generator::generate_struct_reader (ofstream &out, t_struct *tstruct, + string this_name, string this_get, + bool is_function) +{ + string name = tstruct->get_name (); + string name_u = initial_caps_to_underscores (name); + string name_uc = to_upper_case (name_u); + int error_ret = 0; + const vector &fields = tstruct->get_members (); + vector ::const_iterator f_iter; + + if (is_function) + { + error_ret = -1; + indent (out) << + "/* reads a " << name_u << " object */" << endl << + "gint32" << endl << + this->nspace_lc << name_u << + "_read (ThriftStruct *object, ThriftProtocol *protocol, GError **error)" << endl; + } + + indent (out) << "{" << endl; + indent_up (); + + // declare stack temp variables + out << + indent () << "gint32 ret;" << endl << + indent () << "gint32 xfer = 0;" << endl << + indent () << "gchar *name;" << endl << + indent () << "ThriftType ftype;" << endl << + indent () << "gint16 fid;" << endl << + indent () << "guint32 len = 0;" << endl << + indent () << "gpointer data = NULL;" << endl << + indent () << this_get << endl; + + for (f_iter = fields.begin (); f_iter != fields.end (); ++f_iter) + { + if ((*f_iter)->get_req () == t_field::T_REQUIRED) + { + indent (out) << "gboolean isset_" << (*f_iter)->get_name () << " = FALSE;" << endl; + } + } + + out << endl; + + // satisfy -Wall in case we don't use some variables + out << + indent () << "/* satisfy -Wall in case these aren't used */" << endl << + indent () << "THRIFT_UNUSED_VAR (len);" << endl << + indent () << "THRIFT_UNUSED_VAR (data);" << endl; + + if (!this_get.empty()) + { + out << indent () << "THRIFT_UNUSED_VAR (this_object);" << endl; + } + out << endl; + + // read the beginning of the structure marker + out << + indent () << "/* read the struct begin marker */" << endl << + indent () << "if ((ret = thrift_protocol_read_struct_begin (protocol, &name, error)) < 0)" << endl << + indent () << "{" << endl << + indent () << " if (name) g_free (name);" << endl << + indent () << " return " << error_ret << ";" << endl << + indent () << "}" << endl << + indent () << "xfer += ret;" << endl << + indent () << "if (name) g_free (name);" << endl << + endl; + + // read the struct fields + out << + indent () << "/* read the struct fields */" << endl << + indent () << "while (1)" << endl; + scope_up (out); + + // read beginning field marker + out << + indent () << "/* read the beginning of a field */" << endl << + indent () << "if ((ret = thrift_protocol_read_field_begin (protocol, &name, &ftype, &fid, error)) < 0)" << endl << + indent () << "{" << endl << + indent () << " if (name) g_free (name);" << endl << + indent () << " return " << error_ret << ";" << endl << + indent () << "}" << endl << + indent () << "xfer += ret;" << endl << + indent () << "if (name) g_free (name);" << endl << + endl; + + // check for field STOP marker + out << + indent () << "/* break if we get a STOP field */" << endl << + indent () << "if (ftype == T_STOP)" << endl << + indent () << "{" << endl << + indent () << " break;" << endl << + indent () << "}" << endl << + endl; + + // switch depending on the field type + indent (out) << + "switch (fid)" << endl; + + // start switch + scope_up (out); + + // generate deserialization code for known types + for (f_iter = fields.begin (); f_iter != fields.end (); ++f_iter) + { + indent (out) << + "case " << (*f_iter)->get_key () << ":" << endl; + indent_up (); + indent (out) << + "if (ftype == " << type_to_enum ((*f_iter)->get_type ()) << ")" << endl; + indent (out) << + "{" << endl; + + + indent_up (); + // generate deserialize field + generate_deserialize_field (out, *f_iter, this_name, "", error_ret); + indent_down (); + + out << + indent () << "} else {" << endl << + indent () << " if ((ret = thrift_protocol_skip (protocol, ftype, error)) < 0)" << endl << + indent () << " return " << error_ret << ";" << endl << + indent () << " xfer += ret;" << endl << + indent () << "}" << endl << + indent () << "break;" << endl; + indent_down(); + } + + // create the default case + out << + indent () << "default:" << endl << + indent () << " if ((ret = thrift_protocol_skip (protocol, ftype, error)) < 0)" << endl << + indent () << " return " << error_ret << ";" << endl << + indent () << " xfer += ret;" << endl << + indent () << " break;" << endl; + + // end switch + scope_down (out); + + // read field end marker + out << + indent () << "if ((ret = thrift_protocol_read_field_end (protocol, error)) < 0)" << endl << + indent () << " return " << error_ret << ";" << endl << + indent () << "xfer += ret;" << endl; + + // end while loop + scope_down (out); + out << endl; + + // read the end of the structure + out << + indent () << "if ((ret = thrift_protocol_read_struct_end (protocol, error)) < 0)" << endl << + indent () << " return " << error_ret << ";" << endl << + indent () << "xfer += ret;" << endl << + endl; + + // if a required field is missing, throw an error + for (f_iter = fields.begin (); f_iter != fields.end (); ++f_iter) + { + if ((*f_iter)->get_req () == t_field::T_REQUIRED) + { + out << + indent () << "if (!isset_" << (*f_iter)->get_name () << ")" << endl << + indent () << "{" << endl << + indent () << " g_set_error (error, THRIFT_PROTOCOL_ERROR," << endl << + indent () << " THRIFT_PROTOCOL_ERROR_INVALID_DATA," << endl << + indent () << " \"missing field\");" << endl << + indent () << " return -1;" << endl << + indent () << "}" << endl << + endl; + } + } + + if (is_function) + { + indent (out) << + "return xfer;" << endl; + } + + // end the function/structure + indent_down (); + indent (out) << + "}" << endl << + endl; +} + +void +t_c_glib_generator::generate_serialize_field (ofstream &out, t_field *tfield, + string prefix, string suffix, + int error_ret) +{ + t_type *type = get_true_type (tfield->get_type ()); + string name = prefix + tfield->get_name () + suffix; + + if (type->is_void ()) + { + throw "CANNOT GENERATE SERIALIZE CODE FOR void TYPE: " + name; + } + + if (type->is_struct () || type->is_xception ()) + { + generate_serialize_struct (out, (t_struct *) type, name, error_ret); + } else if (type->is_container ()) { + generate_serialize_container (out, type, name, error_ret); + } else if (type->is_base_type () || type->is_enum ()) { + indent (out) << + "if ((ret = thrift_protocol_write_"; + + 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_BOOL: + out << "bool (protocol, " << name; + break; + case t_base_type::TYPE_BYTE: + out << "byte (protocol, " << name; + break; + case t_base_type::TYPE_I16: + out << "i16 (protocol, " << name; + break; + case t_base_type::TYPE_I32: + out << "i32 (protocol, " << name; + break; + case t_base_type::TYPE_I64: + out << "i64 (protocol, " << name; + break; + case t_base_type::TYPE_DOUBLE: + out << "double (protocol, " << name; + break; + case t_base_type::TYPE_STRING: + if (((t_base_type *) type)->is_binary ()) + { + out << "binary (protocol, ((GByteArray *) " << name << + ")->data, ((GByteArray *) " << name << + ")->len"; + } else { + out << "string (protocol, " << name; + } + break; + default: + throw "compiler error: no C writer for base type " + + t_base_type::t_base_name (tbase) + name; + } + } else if (type->is_enum ()) { + out << "i32 (protocol, (gint32) " << name; + } + out << ", error)) < 0)" << endl << + indent () << " return " << error_ret << ";" << endl; + } else { + printf ("DO NOT KNOW HOW TO SERIALIZE FIELD '%s' TYPE '%s'\n", + name.c_str(), type_name (type).c_str()); + } +} + +void +t_c_glib_generator::generate_serialize_struct (ofstream &out, t_struct *tstruct, + string prefix, int error_ret) +{ + out << + indent () << "if ((ret = thrift_struct_write (THRIFT_STRUCT (" << prefix << "), protocol, error)) < 0)" << endl << + indent () << " return " << error_ret << ";" << endl << + indent () << "xfer += ret;" << endl << + endl; +} + +void +t_c_glib_generator::generate_serialize_container (ofstream &out, t_type *ttype, + string prefix, int error_ret) +{ + scope_up (out); + + if (ttype->is_map ()) + { + string length = "g_hash_table_size ((GHashTable *) " + prefix + ")"; + t_type *tkey = ((t_map *) ttype)->get_key_type (); + t_type *tval = ((t_map *) ttype)->get_val_type (); + string tkey_name = type_name (tkey); + string tval_name = type_name (tval); + string tkey_ptr = tkey->is_string () || !tkey->is_base_type () ? "" : "*"; + string tval_ptr = tval->is_string () || !tval->is_base_type () ? "" : "*"; + + /* + * Some ugliness here. To maximize backwards compatibility, we + * avoid using GHashTableIter and instead get a GList of all keys, + * then copy it into a array on the stack, and free it. + * This is because we may exit early before we get a chance to free the + * GList. + */ + out << + indent () << "if ((ret = thrift_protocol_write_map_begin (protocol, " << + type_to_enum (tkey) << ", " << type_to_enum (tval) << + ", (gint32) " << length << ", error)) < 0)" << endl << + indent () << " return " << error_ret << ";" << endl << + indent () << "xfer += ret;" << endl << + endl << + indent () << "GList *key_list = NULL, *iter = NULL;" << endl << + indent () << tkey_name << tkey_ptr << " key;" << endl << + indent () << tval_name << tval_ptr << " value;" << endl << + indent () << "g_hash_table_foreach ((GHashTable *) " << prefix << + ", thrift_hash_table_get_keys, &key_list);" << endl << + indent () << tkey_name << tkey_ptr << + " keys[g_list_length (key_list)];" << endl << + indent () << "int i=0, key_count = g_list_length (key_list);" << endl << + indent () << + "for (iter = g_list_first (key_list); iter; iter = iter->next)" << + endl << + indent () << "{" << endl << + indent () << " keys[i++] = (" << tkey_name << tkey_ptr << + ") iter->data;" << endl << + indent () << "}" << endl << + indent () << "g_list_free (key_list);" << endl << + endl << + indent () << "for (i = 0; i < key_count; ++i)" << endl; + + scope_up (out); + out << + indent () << "key = keys[i];" << endl << + indent () << "value = (" << tval_name << tval_ptr << + ") g_hash_table_lookup (((GHashTable *) " << prefix << + "), (gpointer) key);" << endl << + endl; + generate_serialize_map_element (out, (t_map *) ttype, tkey_ptr + " key", + tval_ptr + " value", error_ret); + scope_down (out); + + out << + indent () << "if ((ret = thrift_protocol_write_map_end (protocol, error)) < 0)" << endl << + indent () << " return " << error_ret << ";" << endl << + indent () << "xfer += ret;" << endl; + } else if (ttype->is_set ()) { + string length = "g_hash_table_size ((GHashTable *) " + prefix + ")"; + t_type *telem = ((t_set *) ttype)->get_elem_type (); + string telem_name = type_name (telem); + string telem_ptr = telem->is_string () || !telem->is_base_type () ? "" : "*"; + out << + indent () << "if ((ret = thrift_protocol_write_set_begin (protocol, " << + type_to_enum (telem) << ", (gint32) " << length << + ", error)) < 0)" << endl << + indent () << " return " << error_ret << ";" << endl << + indent () << "xfer += ret;" << endl << + indent () << "GList *key_list = NULL, *iter = NULL;" << endl << + indent () << telem_name << telem_ptr << " elem;" << endl << + indent () << "gpointer value;" << endl << + indent () << "g_hash_table_foreach ((GHashTable *) " << prefix << + ", thrift_hash_table_get_keys, &key_list);" << endl << + indent () << telem_name << telem_ptr << "keys[g_list_length (key_list)];" << endl << + indent () << "int i=0, key_count = g_list_length (key_list);" << endl << + indent () << "for (iter = g_list_first (key_list); iter; iter = iter->next)" << endl << + indent () << "{" << endl << + indent () << " keys[i++] = (" << telem_name << telem_ptr << ") iter->data;" << endl << + indent () << "}" << endl << + indent () << "g_list_free (key_list);" << endl << + endl << + indent () << "for (i=0; iis_list ()) { + string length = prefix + "->len"; + out << + indent () << "if ((ret = thrift_protocol_write_list_begin (protocol, " << + type_to_enum (((t_list *) ttype)->get_elem_type ()) << + ", (gint32) " << length << ", error)) < 0)" << endl << + indent () << " return " << error_ret << ";" << endl << + indent () << "xfer += ret;" << endl << + indent () << "guint i;" << endl << + indent () << "for (i=0; i<" << length << "; i++)" << endl; + + scope_up (out); + generate_serialize_list_element (out, (t_list *) ttype, prefix, "i", error_ret); + scope_down (out); + + out << + indent () << "if ((ret = thrift_protocol_write_list_end (protocol, error)) < 0)" << endl << + indent () << " return " << error_ret << ";" << endl << + indent () << "xfer += ret;" << endl; + } + + scope_down (out); +} + +void +t_c_glib_generator::generate_serialize_map_element (ofstream &out, t_map *tmap, + string key, string value, + int error_ret) +{ + t_field kfield (tmap->get_key_type (), key); + generate_serialize_field (out, &kfield, "", "", error_ret); + + t_field vfield (tmap->get_val_type (), value); + generate_serialize_field (out, &vfield, "", "", error_ret); +} + +void +t_c_glib_generator::generate_serialize_set_element (ofstream &out, t_set *tset, + string element, int error_ret) +{ + t_field efield (tset->get_elem_type (), element); + generate_serialize_field (out, &efield, "", "", error_ret); +} + +void +t_c_glib_generator::generate_serialize_list_element (ofstream &out, t_list *tlist, + string list, string index, + int error_ret) +{ + t_type *ttype = tlist->get_elem_type (); + + // cast to non-const + string name = "g_ptr_array_index ((GPtrArray *) " + list + ", " + + index + ")"; + + 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 "compiler error: cannot determine array type"; + break; + case t_base_type::TYPE_BOOL: + name = "g_array_index (" + list + ", gboolean, " + index + ")"; + break; + case t_base_type::TYPE_BYTE: + name = "g_array_index (" + list + ", gint8, " + index + ")"; + break; + case t_base_type::TYPE_I16: + name = "g_array_index (" + list + ", gint16, " + index + ")"; + break; + case t_base_type::TYPE_I32: + name = "g_array_index (" + list + ", gint32, " + index + ")"; + break; + case t_base_type::TYPE_I64: + name = "g_array_index (" + list + ", gint64, " + index + ")"; + break; + case t_base_type::TYPE_DOUBLE: + name = "g_array_index (" + list + ", gdouble, " + index + ")"; + break; + case t_base_type::TYPE_STRING: + break; + default: + throw "compiler error: no array info for type"; + } + } + + t_field efield (ttype, name); + generate_serialize_field (out, &efield, "", "", error_ret); +} + +/* deserializes a field of any type. */ +void +t_c_glib_generator::generate_deserialize_field (ofstream &out, t_field *tfield, + string prefix, string suffix, + int error_ret) +{ + t_type *type = get_true_type (tfield->get_type ()); + + if (type->is_void ()) + { + throw "CANNOT GENERATE DESERIALIZE CODE FOR void TYPE: " + + prefix + tfield->get_name (); + } + + string name = prefix + tfield->get_name () + suffix; + + if (type->is_struct () || type->is_xception ()) + { + generate_deserialize_struct (out, (t_struct *) type, name, error_ret); + } else if (type->is_container ()) { + generate_deserialize_container (out, type, name, error_ret); + } else if (type->is_base_type ()) { + t_base_type::t_base tbase = ((t_base_type *) type)->get_base (); + + indent (out) << "if ((ret = thrift_protocol_read_"; + + 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: + if (((t_base_type *) type)->is_binary ()) + { + out << "binary (protocol, &data, &len"; + } else { + out << "string (protocol, &" << name; + } + break; + case t_base_type::TYPE_BOOL: + out << "bool (protocol, &" << name; + break; + case t_base_type::TYPE_BYTE: + out << "byte (protocol, &" << name; + break; + case t_base_type::TYPE_I16: + out << "i16 (protocol, &" << name; + break; + case t_base_type::TYPE_I32: + out << "i32 (protocol, &" << name; + break; + case t_base_type::TYPE_I64: + out << "i64 (protocol, &" << name; + break; + case t_base_type::TYPE_DOUBLE: + out << "double (protocol, &" << name; + break; + default: + throw "compiler error: no C reader for base type " + + t_base_type::t_base_name (tbase) + name; + } + out << ", error)) < 0)" << endl; + out << indent () << " return " << error_ret << ";" << endl << + indent () << "xfer += ret;" << endl; + + // load the byte array with the data + if (tbase == t_base_type::TYPE_STRING + && ((t_base_type *) type)->is_binary ()) + { + indent (out) << name << " = g_byte_array_new ();" << endl; + indent (out) << "g_byte_array_append (" << name << ", (guint8 *) data, (guint) len);" << endl; + indent (out) << "g_free (data);" << endl; + } + } else if (type->is_enum ()) { + string t = tmp ("ecast"); + out << + indent () << "gint32 " << t << ";" << endl << + indent () << "if ((ret = thrift_protocol_read_i32 (protocol, &" << t << ", error)) < 0)" << endl << + indent () << " return " << error_ret << ";" << endl << + indent () << "xfer += ret;" << endl << + indent () << name << " = (" << type_name (type) << ")" << t << ";" << endl; + } else { + printf ("DO NOT KNOW HOW TO DESERIALIZE FIELD '%s' TYPE '%s'\n", + tfield->get_name ().c_str (), type_name (type).c_str ()); + } + + // if the type is not required and this is a thrift struct (no prefix), + // set the isset variable. if the type is required, then set the + // local variable indicating the value was set, so that we can do // validation later. + if (tfield->get_req () != t_field::T_REQUIRED && prefix != "") + { + indent (out) << prefix << "__isset_" << tfield->get_name () << suffix << " = TRUE;" << endl; + } else if (tfield->get_req () == t_field::T_REQUIRED && prefix != "") { + indent (out) << "isset_" << tfield->get_name () << " = TRUE;" << endl; + } +} + +void +t_c_glib_generator::generate_deserialize_struct (ofstream &out, t_struct *tstruct, + string prefix, int error_ret) +{ + string name_uc = to_upper_case (initial_caps_to_underscores (tstruct->get_name ())); + out << + indent () << prefix << " = g_object_new (" << this->nspace_uc << "TYPE_" << name_uc << ", NULL);" << endl << + indent () << "if ((ret = thrift_struct_read (THRIFT_STRUCT (" << prefix << "), protocol, error)) < 0)" << endl << + indent () << " return " << error_ret << ";" << endl << + indent () << "xfer += ret;" << endl; +} + +void +t_c_glib_generator::generate_deserialize_container (ofstream &out, t_type *ttype, + string prefix, int error_ret) +{ + scope_up (out); + + if (ttype->is_map ()) + { + out << + indent () << "guint32 size;" << endl << + indent () << "ThriftType key_type;" << endl << + indent () << "ThriftType value_type;" << endl << + endl << + indent () << "/* read the map begin marker */" << endl << + indent () << "if ((ret = thrift_protocol_read_map_begin (protocol, &key_type, &value_type, &size, error)) < 0)" << endl << + indent () << " return " << error_ret << ";" << endl << + indent () << "xfer += ret;" << endl << + endl; + + // iterate over map elements + out << + indent () << "/* iterate through each of the map's fields */" << endl << + indent () << "guint32 i;" << endl << + indent () << "for (i = 0; i < size; i++)" << endl; + scope_up (out); + generate_deserialize_map_element (out, (t_map *) ttype, prefix, error_ret); + scope_down (out); + out << endl; + + // read map end + out << + indent () << "/* read the map end marker */" << endl << + indent () << "if ((ret = thrift_protocol_read_map_end (protocol, error)) < 0)" << endl << + indent () << " return " << error_ret << ";" << endl << + indent () << "xfer += ret;" << endl; + } else if (ttype->is_set ()) { + out << + indent () << "guint32 size;" << endl << + indent () << "ThriftType element_type;" << endl << + indent () << "if ((ret = thrift_protocol_read_set_begin (protocol, &element_type, &size, error)) < 0)" << endl << + indent () << " return " << error_ret << ";" << endl << + indent () << "xfer += ret;" << endl << + endl; + + // iterate over the elements + out << + indent () << "/* iterate through the set elements */" << endl << + indent () << "guint32 i;" << endl << + indent () << "for (i = 0; i < size; ++i)" << endl; + + scope_up (out); + generate_deserialize_set_element (out, (t_set *) ttype, prefix, error_ret); + scope_down (out); + + // read set end + out << + indent () << "if ((ret = thrift_protocol_read_set_end (protocol, error)) < 0)" << endl << + indent () << " return " << error_ret << ";" << endl << + indent () << "xfer += ret;" << endl << + endl; + } else if (ttype->is_list ()) { + out << + indent () << "guint32 size;" << endl << + indent () << "ThriftType element_type;" << endl << + indent () << "if ((ret = thrift_protocol_read_list_begin (protocol, &element_type, &size, error)) < 0)" << endl << + indent () << " return " << error_ret << ";" << endl << + indent () << "xfer += ret;" << endl << + endl; + + out << + indent () << "/* iterate through list elements */" << endl << + indent () << "guint32 i;" << endl << + indent () << "for (i = 0; i < size; i++)" << endl; + + scope_up (out); + generate_deserialize_list_element (out, (t_list *) ttype, prefix, "i", + error_ret); + scope_down (out); + + out << + indent () << "if ((ret = thrift_protocol_read_list_end (protocol, error)) < 0)" << endl << + indent () << " return " << error_ret << ";" << endl << + indent () << "xfer += ret;" << endl << + endl; + } + + scope_down (out); +} + +void +t_c_glib_generator::generate_deserialize_map_element (ofstream &out, t_map *tmap, + string prefix, int error_ret) +{ + t_type *tkey = tmap->get_key_type (); + t_type *tval = tmap->get_val_type (); + string tkey_name = type_name (tkey); + string tval_name = type_name (tval); + string tkey_ptr = tkey->is_string () || !tkey->is_base_type () ? "" : "*"; + string tval_ptr = tval->is_string () || !tval->is_base_type () ? "" : "*"; + + string keyname = tmp("key"); + string valname = tmp("val"); + + if (tkey->is_map ()) + { + out << + indent () << tkey_name << tkey_ptr << " " << keyname << " = g_hash_table_new (NULL, NULL);" << endl; + } else { + out << + indent () << tkey_name << tkey_ptr << " " << keyname << (tkey_ptr != "" ? " = g_new (" + tkey_name + ", 1)" : "") << ";" << endl; + } + + if (tval->is_map ()) + { + out << + indent () << tval_name << tval_ptr << " " << valname << " = g_hash_table_new (NULL, NULL);" << endl; + } else { + out << + indent () << tval_name << tval_ptr << " " << valname << (tval_ptr != "" ? " = g_new (" + tval_name + ", 1)" : "") << ";" << endl; + } + + // deserialize the fields of the map element + t_field fkey (tkey, tkey_ptr + keyname); + generate_deserialize_field (out, &fkey, "", "", error_ret); + t_field fval (tval, tval_ptr + valname); + generate_deserialize_field (out, &fval, "", "", error_ret); + + // insert into the hashtable. if the field is not a pointer, then use + // the address of the object. + indent (out) << + "g_hash_table_insert ((GHashTable *)" << prefix << ", (gpointer) " << (tkey_ptr != "" ? "" : "&") << keyname << ", (gpointer) " << (tval_ptr != "" ? "" : "&") << valname << ");" << endl; +} + +void +t_c_glib_generator::generate_deserialize_set_element (ofstream &out, t_set *tset, + string prefix, int error_ret) +{ + t_type *telem = tset->get_elem_type (); + string telem_name = type_name (telem); + string telem_ptr = telem->is_string () || !telem->is_base_type () ? "" : "*"; + + if (telem->is_map()) + { + out << + indent () << telem_name << telem_ptr << " elem = g_hash_table_new (NULL, NULL);" << endl; + } else { + out << + indent () << telem_name << telem_ptr << " elem" << (telem_ptr != "" ? " = g_new (" + telem_name + ", 1)" : "") << ";" << endl; + } + + t_field felem (telem, telem_ptr + "elem"); + generate_deserialize_field (out, &felem, "", "", error_ret); + + indent (out) << + "g_hash_table_insert ((GHashTable *) " << prefix << ", (gpointer) elem, (gpointer) 1);" << endl; +} + +void +t_c_glib_generator::generate_deserialize_list_element (ofstream &out, t_list *tlist, + string prefix, string index, + int error_ret) +{ + string elem = tmp ("_elem"); + t_field felem (tlist->get_elem_type (), elem); + + indent (out) << declare_field (&felem, true) << endl; + generate_deserialize_field (out, &felem, "", "", error_ret); + + indent (out); + + t_type *ttype = tlist->get_elem_type (); + 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 "compiler error: cannot determine array type"; + case t_base_type::TYPE_STRING: + out << "g_ptr_array_add (" << prefix << ", " << elem << ");" << endl; + return; + case t_base_type::TYPE_BOOL: + case t_base_type::TYPE_BYTE: + case t_base_type::TYPE_I16: + case t_base_type::TYPE_I32: + case t_base_type::TYPE_I64: + case t_base_type::TYPE_DOUBLE: + out << "g_array_append_val (" << prefix << ", " << elem << ");" << endl; + return; + default: + throw "compiler error: no array info for type"; + } + } + out << "g_ptr_array_add (" << prefix << ", " << elem << ");" << endl; +} + +string +t_c_glib_generator::generate_new_hash_from_type (t_type * ttype) +{ + 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 "compiler error: cannot determine hash type"; + break; + case t_base_type::TYPE_BOOL: + return "g_hash_table_new (thrift_gboolean_hash, thrift_gboolean_equal);"; + case t_base_type::TYPE_BYTE: + return "g_hash_table_new (thrift_gint8_hash, thrift_gint8_equal);"; + case t_base_type::TYPE_I16: + return "g_hash_table_new (thrift_gint16_hash, thrift_gint16_equal);"; + case t_base_type::TYPE_I32: + return "g_hash_table_new (thrift_gint32_hash, thrift_gint32_equal);"; + case t_base_type::TYPE_I64: + return "g_hash_table_new (thrift_gint64_hash, thrift_gint64_equal);"; + case t_base_type::TYPE_DOUBLE: + return "g_hash_table_new (thrift_gdouble_hash, thrift_gdouble_equal);"; + case t_base_type::TYPE_STRING: + return "g_hash_table_new (g_str_hash, g_str_equal);"; + default: + throw "compiler error: no hash table info for type"; + } + } + return "g_hash_table_new (NULL, NULL);"; +} + +string +t_c_glib_generator::generate_new_array_from_type(t_type * ttype) +{ + 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 "compiler error: cannot determine array type"; + break; + case t_base_type::TYPE_BOOL: + return "g_array_new (0, 1, sizeof (gboolean));"; + case t_base_type::TYPE_BYTE: + return "g_array_new (0, 1, sizeof (gint8));"; + case t_base_type::TYPE_I16: + return "g_array_new (0, 1, sizeof (gint16));"; + case t_base_type::TYPE_I32: + return "g_array_new (0, 1, sizeof (gint32));"; + case t_base_type::TYPE_I64: + return "g_array_new (0, 1, sizeof (gint64));"; + case t_base_type::TYPE_DOUBLE: + return "g_array_new (0, 1, sizeof (gdouble));"; + case t_base_type::TYPE_STRING: + return "g_ptr_array_new ();"; + default: + throw "compiler error: no array info for type"; + } + } + return "g_ptr_array_new ();"; +} + + +/*************************************** + * UTILITY FUNCTIONS * + ***************************************/ + +/** + * Upper case a string. Wraps boost's string utility. + */ +string +to_upper_case (string name) +{ + string s (name); + std::transform (s.begin(), s.end(), s.begin(), ::toupper); + return s; +// return boost::to_upper_copy (name); +} + +/** + * Lower case a string. Wraps boost's string utility. + */ +string +to_lower_case (string name) +{ + string s (name); + std::transform (s.begin(), s.end(), s.begin(), ::tolower); + return s; +// return boost::to_lower_copy (name); +} + +/** + * Makes a string friendly to C code standards by lowercasing and adding + * underscores, with the exception of the first character. For example: + * + * Input: "ZomgCamelCase" + * Output: "zomg_camel_case" + */ +string +initial_caps_to_underscores (string name) +{ + string ret; + const char *tmp = name.c_str (); + int pos = 0; + + /* the first character isn't underscored if uppercase, just lowercased */ + ret += tolower (tmp[pos]); + pos++; + for (unsigned int i = pos; i < name.length (); i++) + { + char lc = tolower (tmp[i]); + if (lc != tmp[i]) + { + ret += '_'; + } + ret += lc; + } + + return ret; +} + +/* register this generator with the main program */ +THRIFT_REGISTER_GENERATOR(c_glib, "C, using GLib", ""); + + diff --git a/compiler/cpp/src/parse/t_program.h b/compiler/cpp/src/parse/t_program.h index 320d1f09..440d151e 100644 --- a/compiler/cpp/src/parse/t_program.h +++ b/compiler/cpp/src/parse/t_program.h @@ -209,6 +209,14 @@ class t_program : public t_doc { return cpp_includes_; } + void add_c_include(std::string path) { + c_includes_.push_back(path); + } + + const std::vector& get_c_includes() { + return c_includes_; + } + private: // File path @@ -247,6 +255,9 @@ class t_program : public t_doc { // C++ extra includes std::vector cpp_includes_; + // C extra includes + std::vector c_includes_; + }; #endif diff --git a/configure.ac b/configure.ac index d843efb2..82896ba2 100644 --- a/configure.ac +++ b/configure.ac @@ -99,6 +99,16 @@ AM_CONDITIONAL([WITH_CPP], [test "$have_cpp" = "yes"]) AM_CONDITIONAL([AMX_HAVE_LIBEVENT], [test "$have_libevent" = "yes"]) AM_CONDITIONAL([AMX_HAVE_ZLIB], [test "$have_zlib" = "yes"]) +AX_THRIFT_LIB(c_glib, [C (GLib)], no) +if test "$with_c_glib" = "yes"; then + PKG_CHECK_MODULES([GLIB], [glib-2.0 >= 2.0], have_glib2=yes, have_glib2=no) + PKG_CHECK_MODULES([GOBJECT], [gobject-2.0 >= 2.0], have_gobject2=yes, have_gobject2=no) + if test "$have_glib2" = "yes" -a "$have_gobject2" = "yes" ; then + have_c_glib="yes" + fi +fi +AM_CONDITIONAL(WITH_C_GLIB, [test "$have_glib2" = "yes" -a "$have_gobject2" = "yes"]) + AX_THRIFT_LIB(csharp, [C#], yes) if test "$with_csharp" = "yes"; then PKG_CHECK_MODULES(MONO, mono >= 2.0.0, net_3_5=yes, net_3_5=no) @@ -279,6 +289,7 @@ AC_CHECK_DECL([AI_ADDRCONFIG], [], ]) AC_FUNC_ALLOCA +AC_FUNC_FORK AC_FUNC_MALLOC AC_FUNC_MEMCMP AC_FUNC_REALLOC @@ -305,6 +316,7 @@ AC_CHECK_FUNCS([strstr]) AC_CHECK_FUNCS([strtol]) AC_CHECK_FUNCS([sqrt]) dnl The following functions are optional. +AC_CHECK_FUNCS([alarm]) AC_CHECK_FUNCS([clock_gettime]) AC_CHECK_FUNCS([sched_get_priority_min]) AC_CHECK_FUNCS([sched_get_priority_max]) @@ -323,6 +335,8 @@ fi AX_THRIFT_GEN(cpp, [C++], yes) AM_CONDITIONAL([THRIFT_GEN_cpp], [test "$ax_thrift_gen_cpp" = "yes"]) +AX_THRIFT_GEN(c_glib, [C (GLib)], yes) +AM_CONDITIONAL([THRIFT_GEN_c_glib], [test "$ax_thrift_gen_c_glib" = "yes"]) AX_THRIFT_GEN(java, [Java], yes) AM_CONDITIONAL([THRIFT_GEN_java], [test "$ax_thrift_gen_java" = "yes"]) AX_THRIFT_GEN(as3, [AS3], yes) @@ -356,6 +370,29 @@ AM_CONDITIONAL([THRIFT_GEN_js], [test "$ax_thrift_gen_js" = "yes"]) AX_THRIFT_GEN(javame, [JavaME], yes) AM_CONDITIONAL([THRIFT_GEN_javame], [test "$ax_thrift_gen_javame" = "yes"]) +# --- Coverage hooks --- + +AC_ARG_ENABLE(coverage, + [ --enable-coverage turn on -fprofile-arcs -ftest-coverage], + [case "${enableval}" in + yes) ENABLE_COVERAGE=1 ;; + no) ENABLE_COVERAGE=0 ;; + *) AC_MSG_ERROR(bad value ${enableval} for --enable-cov) ;; + esac], + [ENABLE_COVERAGE=2]) + +if test "x[$]ENABLE_COVERAGE" = "x1"; then + AC_MSG_WARN(enable coverage) + GCOV_CFLAGS="`echo \"[$]CFLAGS\" | perl -pe 's/-O\d+//g;'` -fprofile-arcs -ftest-coverage" + GCOV_CXXFLAGS="`echo \"[$]CXXFLAGS\" | perl -pe 's/-O\d+//g;'` -fprofile-arcs -ftest-coverage" + GCOV_LDFLAGS="-XCClinker -fprofile-arcs -XCClinker -ftest-coverage" +fi + +AC_SUBST(ENABLE_COVERAGE) +AC_SUBST(GCOV_CFLAGS) +AC_SUBST(GCOV_CXXFLAGS) +AC_SUBST(GCOV_LDFLAGS) + AC_CONFIG_HEADERS(config.h:config.hin) AC_CONFIG_FILES([ @@ -368,6 +405,9 @@ AC_CONFIG_FILES([ lib/cpp/thrift-nb.pc lib/cpp/thrift-z.pc lib/cpp/thrift.pc + lib/c_glib/Makefile + lib/c_glib/thrift_c_glib.pc + lib/c_glib/test/Makefile lib/csharp/Makefile lib/erl/Makefile lib/erl/src/Makefile @@ -385,6 +425,9 @@ AC_CONFIG_FILES([ test/rb/Makefile ]) +AC_CONFIG_FILES([lib/c_glib/test/test-wrapper.sh], + [chmod +x lib/c_glib/test/test-wrapper.sh]) + AC_OUTPUT @@ -394,6 +437,7 @@ echo echo "Building code generators ..... :$thrift_generators" echo echo "Building C++ Library ......... : $have_cpp" +echo "Building C (GLib) Library .... : $have_c_glib" echo "Building Java Library ........ : $have_java" echo "Building C# Library .......... : $have_csharp" echo "Building Python Library ...... : $have_python" diff --git a/lib/Makefile.am b/lib/Makefile.am index 367621b4..9dbc1c1b 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -23,6 +23,10 @@ if WITH_CPP SUBDIRS += cpp endif +if WITH_C_GLIB +SUBDIRS += c_glib +endif + if WITH_MONO SUBDIRS += csharp endif diff --git a/lib/c_glib/Makefile.am b/lib/c_glib/Makefile.am new file mode 100644 index 00000000..5b66818d --- /dev/null +++ b/lib/c_glib/Makefile.am @@ -0,0 +1,93 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +SUBDIRS = . test + +pkgconfigdir = $(libdir)/pkgconfig + +lib_LTLIBRARIES = libthrift_c_glib.la +pkgconfig_DATA = thrift_c_glib.pc + +common_cflags = -g -Wall -W -Werror -Isrc $(GLIB_CFLAGS) +common_ldflags = -g -Wall -W $(GLIB_LDFLAGS) @GCOV_LDFLAGS@ + +# this removes optimizations and adds coverage flags +CFLAGS = @GCOV_CFLAGS@ + +# Define the source files for the module + +libthrift_c_glib_la_SOURCES = src/thrift.c \ + src/thrift_struct.c \ + src/thrift_application_exception.c \ + src/processor/thrift_processor.c \ + src/protocol/thrift_protocol.c \ + src/protocol/thrift_protocol_factory.c \ + src/protocol/thrift_binary_protocol.c \ + src/protocol/thrift_binary_protocol_factory.c \ + src/transport/thrift_transport.c \ + src/transport/thrift_transport_factory.c \ + src/transport/thrift_socket.c \ + src/transport/thrift_server_transport.c \ + src/transport/thrift_server_socket.c \ + src/transport/thrift_buffered_transport.c \ + src/transport/thrift_framed_transport.c \ + src/transport/thrift_memory_buffer.c \ + src/server/thrift_server.c \ + src/server/thrift_simple_server.c + +libthrift_c_glib_la_CFLAGS = $(common_cflags) + +include_thriftdir = $(includedir)/thrift +include_thrift_HEADERS = \ + $(top_builddir)/config.h \ + src/thrift.h \ + src/thrift_application_exception.h \ + src/thrift_struct.h + +include_protocoldir = $(include_thriftdir)/protocol +include_protocol_HEADERS = src/protocol/thrift_protocol.h \ + src/protocol/thrift_protocol_factory.h \ + src/protocol/thrift_binary_protocol.h \ + src/protocol/thrift_binary_protocol_factory.h + +include_transportdir = $(include_thriftdir)/transport +include_transport_HEADERS = src/transport/thrift_buffered_transport.h \ + src/transport/thrift_framed_transport.h \ + src/transport/thrift_memory_buffer.h \ + src/transport/thrift_server_socket.h \ + src/transport/thrift_server_transport.h \ + src/transport/thrift_socket.h \ + src/transport/thrift_transport.h \ + src/transport/thrift_transport_factory.h + +include_serverdir = $(include_thriftdir)/server +include_server_HEADERS = src/server/thrift_server.h \ + src/server/thrift_simple_server.h + +include_processordir = $(include_thriftdir)/processor +include_processor_HEADERS = src/processor/thrift_processor.h + + +EXTRA_DIST = \ + README \ + thrift_c_glib.pc.in + +CLEANFILES = \ + *.gcno \ + *.gcda diff --git a/lib/c_glib/src/processor/thrift_processor.c b/lib/c_glib/src/processor/thrift_processor.c new file mode 100644 index 00000000..c5d4034a --- /dev/null +++ b/lib/c_glib/src/processor/thrift_processor.c @@ -0,0 +1,50 @@ +#include "thrift.h" +#include "processor/thrift_processor.h" + +/* forward declarations */ +static void thrift_processor_class_init (ThriftProcessorClass *cls); + +/* define ThriftProcessorClass's type */ +GType +thrift_processor_get_type (void) +{ + static GType type = 0; + + if (type == 0) + { + static const GTypeInfo info = + { + sizeof (ThriftProcessorClass), + NULL, /* base_init */ + NULL, /* base_finalize */ + (GClassInitFunc) thrift_processor_class_init, + NULL, /* class_finalize */ + NULL, /* class_data */ + sizeof (ThriftProcessor), + 0, /* n_preallocs */ + NULL, /* instance_init */ + NULL, /* value_table */ + }; + + type = g_type_register_static (G_TYPE_OBJECT, "ThriftProcessor", + &info, G_TYPE_FLAG_ABSTRACT); + } + + return type; +} + +/* class initializer for ThriftProcessor */ +static void +thrift_processor_class_init (ThriftProcessorClass *cls) +{ + /* set these as virtual methods to be implemented by a subclass */ + cls->process = thrift_processor_process; +} + +gboolean +thrift_processor_process (ThriftProcessor *processor, ThriftProtocol *in, + ThriftProtocol *out) +{ + return THRIFT_PROCESSOR_GET_CLASS (processor)->process (processor, in, out); +} + diff --git a/lib/c_glib/src/processor/thrift_processor.h b/lib/c_glib/src/processor/thrift_processor.h new file mode 100644 index 00000000..cc0b0060 --- /dev/null +++ b/lib/c_glib/src/processor/thrift_processor.h @@ -0,0 +1,59 @@ +#ifndef _THRIFT_PROCESSOR_H +#define _THRIFT_PROCESSOR_H + +#include + +#include "protocol/thrift_protocol.h" + +/*! \file thrift_processor.h + * \brief Abstract class for Thrift processors. + */ + +/* type macros */ +#define THRIFT_TYPE_PROCESSOR (thrift_processor_get_type ()) +#define THRIFT_PROCESSOR(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), \ + THRIFT_TYPE_PROCESSOR, ThriftProcessor)) +#define THRIFT_IS_PROCESSOR(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \ + THRIFT_TYPE_PROCESSOR)) +#define THRIFT_PROCESSOR_CLASS(c) (G_TYPE_CHECK_CLASS_CAST ((c), \ + THRIFT_TYPE_PROCESSOR, \ + ThriftProcessorClass)) +#define THRIFT_IS_PROCESSOR_CLASS(c) (G_TYPE_CHECK_CLASS_TYPE ((c), \ + THRIFT_TYPE_PROCESSOR)) +#define THRIFT_PROCESSOR_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), \ + THRIFT_TYPE_PROCESSOR, \ + ThriftProcessorClass)) + +/*! + * Thrift Processorobject + */ +struct _ThriftProcessor +{ + GObject parent; +}; +typedef struct _ThriftProcessor ThriftProcessor; + +/*! + * Thrift Processor class + */ +struct _ThriftProcessorClass +{ + GObjectClass parent; + + /* vtable */ + gboolean (*process) (ThriftProcessor *processor, ThriftProtocol *in, + ThriftProtocol *out); +}; +typedef struct _ThriftProcessorClass ThriftProcessorClass; + +/* used by THRIFT_TYPE_PROCESSOR */ +GType thrift_processor_get_type (void); + +/*! + * Processes the request. + * \public \memberof ThriftProcessorClass + */ +gboolean thrift_processor_process (ThriftProcessor *processor, + ThriftProtocol *in, ThriftProtocol *out); + +#endif /* _THRIFT_PROCESSOR_H */ diff --git a/lib/c_glib/src/protocol/thrift_binary_protocol.c b/lib/c_glib/src/protocol/thrift_binary_protocol.c new file mode 100644 index 00000000..afedc9ec --- /dev/null +++ b/lib/c_glib/src/protocol/thrift_binary_protocol.c @@ -0,0 +1,983 @@ +#include +#include + +#include "thrift.h" +#include "protocol/thrift_protocol.h" +#include "protocol/thrift_binary_protocol.h" + +/* forward declarations */ +static guint64 thrift_bitwise_cast_guint64 (gdouble v); +static gdouble thrift_bitwise_cast_gdouble (guint64 v); +static void thrift_binary_protocol_class_init (ThriftProtocolClass *cls); + +gint32 thrift_binary_protocol_write_message_begin (ThriftProtocol *protocol, + const gchar *name, const ThriftMessageType message_type, + const gint32 seqid, GError **error); +gint32 thrift_binary_protocol_write_message_end (ThriftProtocol *protocol, + GError **error); +gint32 thrift_binary_protocol_write_struct_begin (ThriftProtocol *protocol, + const gchar *name, + GError **error); +gint32 thrift_binary_protocol_write_struct_end (ThriftProtocol *protocol, + GError **error); +gint32 thrift_binary_protocol_write_field_begin (ThriftProtocol *protocol, + const gchar *name, + const ThriftType field_type, + const gint16 field_id, + GError **error); +gint32 thrift_binary_protocol_write_field_end (ThriftProtocol *protocol, + GError **error); +gint32 thrift_binary_protocol_write_field_stop (ThriftProtocol *protocol, + GError **error); +gint32 thrift_binary_protocol_write_map_begin (ThriftProtocol *protocol, + const ThriftType key_type, + const ThriftType value_type, + const guint32 size, + GError **error); +gint32 thrift_binary_protocol_write_map_end (ThriftProtocol *protocol, + GError **error); +gint32 thrift_binary_protocol_write_list_begin (ThriftProtocol *protocol, + const ThriftType element_type, + const guint32 size, + GError **error); +gint32 thrift_binary_protocol_write_list_end (ThriftProtocol *protocol, + GError **error); +gint32 thrift_binary_protocol_write_set_begin (ThriftProtocol *protocol, + const ThriftType element_type, + const guint32 size, + GError **error); +gint32 thrift_binary_protocol_write_set_end (ThriftProtocol *protocol, + GError **error); +gint32 thrift_binary_protocol_write_bool (ThriftProtocol *protocol, + const gboolean value, GError **error); +gint32 thrift_binary_protocol_write_byte (ThriftProtocol *protocol, + const gint8 value, GError **error); +gint32 thrift_binary_protocol_write_i16 (ThriftProtocol *protocol, + const gint16 value, GError **error); +gint32 thrift_binary_protocol_write_i32 (ThriftProtocol *protocol, + const gint32 value, GError **error); +gint32 thrift_binary_protocol_write_i64 (ThriftProtocol *protocol, + const gint64 value, GError **error); +gint32 thrift_binary_protocol_write_double (ThriftProtocol *protocol, + const gdouble value, + GError **error); +gint32 thrift_binary_protocol_write_string (ThriftProtocol *protocol, + const gchar *str, GError **error); +gint32 thrift_binary_protocol_write_binary (ThriftProtocol *protocol, + const gpointer buf, + const guint32 len, GError **error); +gint32 thrift_binary_protocol_read_message_begin ( + ThriftProtocol *protocol, gchar **name, + ThriftMessageType *message_type, gint32 *seqid, GError **error); +gint32 thrift_binary_protocol_read_message_end (ThriftProtocol *protocol, + GError **error); +gint32 thrift_binary_protocol_read_struct_begin (ThriftProtocol *protocol, + gchar **name, + GError **error); +gint32 thrift_binary_protocol_read_struct_end (ThriftProtocol *protocol, + GError **error); +gint32 thrift_binary_protocol_read_field_begin (ThriftProtocol *protocol, + gchar **name, + ThriftType *field_type, + gint16 *field_id, + GError **error); +gint32 thrift_binary_protocol_read_field_end (ThriftProtocol *protocol, + GError **error); +gint32 thrift_binary_protocol_read_map_begin (ThriftProtocol *protocol, + ThriftType *key_type, + ThriftType *value_type, + guint32 *size, + GError **error); +gint32 thrift_binary_protocol_read_map_end (ThriftProtocol *protocol, + GError **error); +gint32 thrift_binary_protocol_read_list_begin (ThriftProtocol *protocol, + ThriftType *element_type, + guint32 *size, GError **error); +gint32 thrift_binary_protocol_read_list_end (ThriftProtocol *protocol, + GError **error); +gint32 thrift_binary_protocol_read_set_begin (ThriftProtocol *protocol, + ThriftType *element_type, + guint32 *size, GError **error); +gint32 thrift_binary_protocol_read_set_end (ThriftProtocol *protocol, + GError **error); +gint32 thrift_binary_protocol_read_bool (ThriftProtocol *protocol, + gboolean *value, GError **error); +gint32 thrift_binary_protocol_read_byte (ThriftProtocol *protocol, gint8 *value, + GError **error); +gint32 thrift_binary_protocol_read_i16 (ThriftProtocol *protocol, gint16 *value, + GError **error); +gint32 thrift_binary_protocol_read_i32 (ThriftProtocol *protocol, gint32 *value, + GError **error); +gint32 thrift_binary_protocol_read_i64 (ThriftProtocol *protocol, gint64 *value, + GError **error); +gint32 thrift_binary_protocol_read_double (ThriftProtocol *protocol, + gdouble *value, GError **error); +gint32 thrift_binary_protocol_read_string (ThriftProtocol *protocol, + gchar **str, GError **error); +gint32 thrift_binary_protocol_read_binary (ThriftProtocol *protocol, + gpointer *buf, guint32 *len, + GError **error); + +static guint64 +thrift_bitwise_cast_guint64 (gdouble v) +{ + union { + gdouble from; + guint64 to; + } u; + u.from = v; + return u.to; +} + +static gdouble +thrift_bitwise_cast_gdouble (guint64 v) +{ + union { + guint64 from; + gdouble to; + } u; + u.from = v; + return u.to; +} + +GType +thrift_binary_protocol_get_type (void) +{ + static GType type = 0; + + if (type == 0) + { + static const GTypeInfo info = + { + sizeof (ThriftBinaryProtocolClass), + NULL, /* base_init */ + NULL, /* base_finalize */ + (GClassInitFunc) thrift_binary_protocol_class_init, + NULL, /* class_finalize */ + NULL, /* class_data */ + sizeof (ThriftBinaryProtocol), + 0, /* n_preallocs */ + NULL, /* instance_init */ + NULL, /* value_table */ + }; + + type = g_type_register_static (THRIFT_TYPE_PROTOCOL, + "ThriftBinaryProtocolType", + &info, 0); + } + + return type; +} + +/* initialize the class */ +static void +thrift_binary_protocol_class_init (ThriftProtocolClass *cls) +{ + cls->write_message_begin = thrift_binary_protocol_write_message_begin; + cls->write_message_end = thrift_binary_protocol_write_message_end; + cls->write_struct_begin = thrift_binary_protocol_write_struct_begin; + cls->write_struct_end = thrift_binary_protocol_write_struct_end; + cls->write_field_begin = thrift_binary_protocol_write_field_begin; + cls->write_field_end = thrift_binary_protocol_write_field_end; + cls->write_field_stop = thrift_binary_protocol_write_field_stop; + cls->write_map_begin = thrift_binary_protocol_write_map_begin; + cls->write_map_end = thrift_binary_protocol_write_map_end; + cls->write_list_begin = thrift_binary_protocol_write_list_begin; + cls->write_list_end = thrift_binary_protocol_write_list_end; + cls->write_set_begin = thrift_binary_protocol_write_set_begin; + cls->write_set_end = thrift_binary_protocol_write_set_end; + cls->write_bool = thrift_binary_protocol_write_bool; + cls->write_byte = thrift_binary_protocol_write_byte; + cls->write_i16 = thrift_binary_protocol_write_i16; + cls->write_i32 = thrift_binary_protocol_write_i32; + cls->write_i64 = thrift_binary_protocol_write_i64; + cls->write_double = thrift_binary_protocol_write_double; + cls->write_string = thrift_binary_protocol_write_string; + cls->write_binary = thrift_binary_protocol_write_binary; + cls->read_message_begin = thrift_binary_protocol_read_message_begin; + cls->read_message_end = thrift_binary_protocol_read_message_end; + cls->read_struct_begin = thrift_binary_protocol_read_struct_begin; + cls->read_struct_end = thrift_binary_protocol_read_struct_end; + cls->read_field_begin = thrift_binary_protocol_read_field_begin; + cls->read_field_end = thrift_binary_protocol_read_field_end; + cls->read_map_begin = thrift_binary_protocol_read_map_begin; + cls->read_map_end = thrift_binary_protocol_read_map_end; + cls->read_list_begin = thrift_binary_protocol_read_list_begin; + cls->read_list_end = thrift_binary_protocol_read_list_end; + cls->read_set_begin = thrift_binary_protocol_read_set_begin; + cls->read_set_end = thrift_binary_protocol_read_set_end; + cls->read_bool = thrift_binary_protocol_read_bool; + cls->read_byte = thrift_binary_protocol_read_byte; + cls->read_i16 = thrift_binary_protocol_read_i16; + cls->read_i32 = thrift_binary_protocol_read_i32; + cls->read_i64 = thrift_binary_protocol_read_i64; + cls->read_double = thrift_binary_protocol_read_double; + cls->read_string = thrift_binary_protocol_read_string; + cls->read_binary = thrift_binary_protocol_read_binary; +} + +gint32 +thrift_binary_protocol_write_message_begin (ThriftProtocol *protocol, + const gchar *name, const ThriftMessageType message_type, + const gint32 seqid, GError **error) +{ + g_return_val_if_fail (THRIFT_IS_BINARY_PROTOCOL (protocol), -1); + + gint32 version = (THRIFT_BINARY_PROTOCOL_VERSION_1) + | ((gint32) message_type); + gint32 ret; + gint32 xfer = 0; + + if ((ret = thrift_protocol_write_i32 (protocol, version, error)) < 0) + { + return -1; + } + xfer += ret; + if ((ret = thrift_protocol_write_string (protocol, name, error)) < 0) + { + return -1; + } + xfer += ret; + if ((ret = thrift_protocol_write_i32 (protocol, seqid, error)) < 0) + { + return -1; + } + xfer += ret; + return xfer; +} + +gint32 +thrift_binary_protocol_write_message_end (ThriftProtocol *protocol, + GError **error) +{ + /* satisfy -Wall */ + THRIFT_UNUSED_VAR (protocol); + THRIFT_UNUSED_VAR (error); + return 0; +} + +gint32 +thrift_binary_protocol_write_struct_begin (ThriftProtocol *protocol, + const gchar *name, + GError **error) +{ + /* satisfy -Wall */ + THRIFT_UNUSED_VAR (protocol); + THRIFT_UNUSED_VAR (name); + THRIFT_UNUSED_VAR (error); + return 0; +} + +gint32 +thrift_binary_protocol_write_struct_end (ThriftProtocol *protocol, + GError **error) +{ + /* satisfy -Wall */ + THRIFT_UNUSED_VAR (protocol); + THRIFT_UNUSED_VAR (error); + return 0; +} + +gint32 +thrift_binary_protocol_write_field_begin (ThriftProtocol *protocol, + const gchar *name, + const ThriftType field_type, + const gint16 field_id, + GError **error) +{ + THRIFT_UNUSED_VAR (name); + g_return_val_if_fail (THRIFT_IS_BINARY_PROTOCOL (protocol), -1); + + gint32 ret; + gint32 xfer = 0; + + if ((ret = thrift_protocol_write_byte (protocol, (gint8) field_type, + error)) < 0) + { + return -1; + } + xfer += ret; + if ((ret = thrift_protocol_write_i16 (protocol, field_id, error)) < 0) + { + return -1; + } + xfer += ret; + return xfer; +} + +gint32 +thrift_binary_protocol_write_field_end (ThriftProtocol *protocol, + GError **error) +{ + /* satisfy -Wall */ + THRIFT_UNUSED_VAR (protocol); + THRIFT_UNUSED_VAR (error); + return 0; +} + +gint32 +thrift_binary_protocol_write_field_stop (ThriftProtocol *protocol, + GError **error) +{ + g_return_val_if_fail (THRIFT_IS_BINARY_PROTOCOL (protocol), -1); + return thrift_protocol_write_byte (protocol, (gint8) T_STOP, error); +} + +gint32 +thrift_binary_protocol_write_map_begin (ThriftProtocol *protocol, + const ThriftType key_type, + const ThriftType value_type, + const guint32 size, + GError **error) +{ + g_return_val_if_fail (THRIFT_IS_BINARY_PROTOCOL (protocol), -1); + + gint32 ret; + gint32 xfer = 0; + + if ((ret = thrift_protocol_write_byte (protocol, (gint8) key_type, + error)) < 0) + { + return -1; + } + xfer += ret; + if ((ret = thrift_protocol_write_byte (protocol, (gint8) value_type, + error)) < 0) + { + return -1; + } + xfer += ret; + if ((ret = thrift_protocol_write_i32 (protocol, (gint32) size, error)) < 0) + { + return -1; + } + xfer += ret; + return xfer; +} + +gint32 +thrift_binary_protocol_write_map_end (ThriftProtocol *protocol, + GError **error) +{ + THRIFT_UNUSED_VAR (protocol); + THRIFT_UNUSED_VAR (error); + return 0; +} + +gint32 +thrift_binary_protocol_write_list_begin (ThriftProtocol *protocol, + const ThriftType element_type, + const guint32 size, + GError **error) +{ + g_return_val_if_fail (THRIFT_IS_BINARY_PROTOCOL (protocol), -1); + + gint32 ret; + gint32 xfer = 0; + + if ((ret = thrift_protocol_write_byte (protocol, (gint8) element_type, + error)) < 0) + { + return -1; + } + xfer += ret; + + if ((ret = thrift_protocol_write_i32 (protocol, (gint32) size, error)) < 0) + { + return -1; + } + xfer += ret; + + return xfer; +} + +gint32 +thrift_binary_protocol_write_list_end (ThriftProtocol *protocol, + GError **error) +{ + THRIFT_UNUSED_VAR (protocol); + THRIFT_UNUSED_VAR (error); + return 0; +} + +gint32 +thrift_binary_protocol_write_set_begin (ThriftProtocol *protocol, + const ThriftType element_type, + const guint32 size, + GError **error) +{ + g_return_val_if_fail (THRIFT_IS_BINARY_PROTOCOL (protocol), -1); + + return thrift_protocol_write_list_begin (protocol, element_type, + size, error); +} + +gint32 +thrift_binary_protocol_write_set_end (ThriftProtocol *protocol, GError **error) +{ + THRIFT_UNUSED_VAR (protocol); + THRIFT_UNUSED_VAR (error); + return 0; +} + +gint32 +thrift_binary_protocol_write_bool (ThriftProtocol *protocol, + const gboolean value, GError **error) +{ + g_return_val_if_fail (THRIFT_IS_BINARY_PROTOCOL (protocol), -1); + guint8 tmp = value ? 1 : 0; + return thrift_protocol_write_byte (protocol, tmp, error); +} + +gint32 +thrift_binary_protocol_write_byte (ThriftProtocol *protocol, const gint8 value, + GError **error) +{ + g_return_val_if_fail (THRIFT_IS_BINARY_PROTOCOL (protocol), -1); + + if (thrift_transport_write (protocol->transport, + (const gpointer) &value, 1, error)) + { + return 1; + } else { + return -1; + } +} + +gint32 +thrift_binary_protocol_write_i16 (ThriftProtocol *protocol, const gint16 value, + GError **error) +{ + g_return_val_if_fail (THRIFT_IS_BINARY_PROTOCOL (protocol), -1); + + gint16 net = g_htons (value); + if (thrift_transport_write (protocol->transport, + (const gpointer) &net, 2, error)) + { + return 2; + } else { + return -1; + } +} + +gint32 +thrift_binary_protocol_write_i32 (ThriftProtocol *protocol, const gint32 value, + GError **error) +{ + g_return_val_if_fail (THRIFT_IS_BINARY_PROTOCOL (protocol), -1); + + gint32 net = g_htonl (value); + if (thrift_transport_write (protocol->transport, + (const gpointer) &net, 4, error)) + { + return 4; + } else { + return -1; + } +} + +gint32 +thrift_binary_protocol_write_i64 (ThriftProtocol *protocol, const gint64 value, + GError **error) +{ + g_return_val_if_fail (THRIFT_IS_BINARY_PROTOCOL (protocol), -1); + + gint64 net = GUINT64_TO_BE (value); + if (thrift_transport_write (protocol->transport, + (const gpointer) &net, 8, error)) + { + return 8; + } else { + return -1; + } +} + +gint32 +thrift_binary_protocol_write_double (ThriftProtocol *protocol, + const gdouble value, GError **error) +{ + g_return_val_if_fail (THRIFT_IS_BINARY_PROTOCOL (protocol), -1); + + guint64 bits = GUINT64_FROM_BE (thrift_bitwise_cast_guint64 (value)); + if (thrift_transport_write (protocol->transport, + (const gpointer) &bits, 8, error)) + { + return 8; + } else { + return -1; + } +} + +gint32 +thrift_binary_protocol_write_string (ThriftProtocol *protocol, + const gchar *str, GError **error) +{ + g_return_val_if_fail (THRIFT_IS_BINARY_PROTOCOL (protocol), -1); + + guint32 len = str != NULL ? strlen (str) : 0; + /* write the string length + 1 which includes the null terminator */ + return thrift_protocol_write_binary (protocol, (const gpointer) str, + len, error); +} + +gint32 +thrift_binary_protocol_write_binary (ThriftProtocol *protocol, + const gpointer buf, + const guint32 len, GError **error) +{ + g_return_val_if_fail (THRIFT_IS_BINARY_PROTOCOL (protocol), -1); + gint32 ret; + gint32 xfer = 0; + + if ((ret = thrift_protocol_write_i32 (protocol, len, error)) < 0) + { + return -1; + } + xfer += ret; + + if (len > 0) + { + if (thrift_transport_write (protocol->transport, + (const gpointer) buf, len, error) == FALSE) + { + return -1; + } + xfer += len; + } + + return xfer; +} + +gint32 +thrift_binary_protocol_read_message_begin (ThriftProtocol *protocol, + gchar **name, + ThriftMessageType *message_type, + gint32 *seqid, GError **error) +{ + g_return_val_if_fail (THRIFT_IS_BINARY_PROTOCOL (protocol), -1); + + gint32 ret; + gint32 xfer = 0; + gint32 sz; + + if ((ret = thrift_protocol_read_i32 (protocol, &sz, error)) < 0) + { + return -1; + } + xfer += ret; + + if (sz < 0) + { + /* check for version */ + guint32 version = sz & THRIFT_BINARY_PROTOCOL_VERSION_MASK; + if (version != THRIFT_BINARY_PROTOCOL_VERSION_1) + { + g_set_error (error, THRIFT_PROTOCOL_ERROR, + THRIFT_PROTOCOL_ERROR_BAD_VERSION, + "expected version %d, got %d", + THRIFT_BINARY_PROTOCOL_VERSION_1, version); + return -1; + } + + *message_type = (ThriftMessageType) (sz & 0x000000ff); + + if ((ret = thrift_protocol_read_string (protocol, name, error)) < 0) + { + return -1; + } + xfer += ret; + + if ((ret = thrift_protocol_read_i32 (protocol, seqid, error)) < 0) + { + return -1; + } + xfer += ret; + } + return xfer; +} + +gint32 +thrift_binary_protocol_read_message_end (ThriftProtocol *protocol, + GError **error) +{ + THRIFT_UNUSED_VAR (protocol); + THRIFT_UNUSED_VAR (error); + return 0; +} + +gint32 +thrift_binary_protocol_read_struct_begin (ThriftProtocol *protocol, + gchar **name, + GError **error) +{ + THRIFT_UNUSED_VAR (protocol); + THRIFT_UNUSED_VAR (error); + *name = NULL; + return 0; +} + +gint32 +thrift_binary_protocol_read_struct_end (ThriftProtocol *protocol, + GError **error) +{ + THRIFT_UNUSED_VAR (protocol); + THRIFT_UNUSED_VAR (error); + return 0; +} + +gint32 +thrift_binary_protocol_read_field_begin (ThriftProtocol *protocol, + gchar **name, + ThriftType *field_type, + gint16 *field_id, + GError **error) +{ + THRIFT_UNUSED_VAR (name); + g_return_val_if_fail (THRIFT_IS_BINARY_PROTOCOL (protocol), -1); + + gint32 ret; + gint32 xfer = 0; + gint8 type; + + if ((ret = thrift_protocol_read_byte (protocol, &type, error)) < 0) + { + return -1; + } + xfer += ret; + *field_type = (ThriftType) type; + if (*field_type == T_STOP) + { + *field_id = 0; + return xfer; + } + if ((ret = thrift_protocol_read_i16 (protocol, field_id, error)) < 0) + { + return -1; + } + xfer += ret; + return xfer; +} + +gint32 +thrift_binary_protocol_read_field_end (ThriftProtocol *protocol, + GError **error) +{ + THRIFT_UNUSED_VAR (protocol); + THRIFT_UNUSED_VAR (error); + return 0; +} + +gint32 +thrift_binary_protocol_read_map_begin (ThriftProtocol *protocol, + ThriftType *key_type, + ThriftType *value_type, + guint32 *size, + GError **error) +{ + g_return_val_if_fail (THRIFT_IS_BINARY_PROTOCOL (protocol), -1); + + gint32 ret; + gint32 xfer = 0; + gint8 k, v; + gint32 sizei; + + if ((ret = thrift_protocol_read_byte (protocol, &k, error)) < 0) + { + return -1; + } + xfer += ret; + *key_type = (ThriftType) k; + + if ((ret = thrift_protocol_read_byte (protocol, &v, error)) < 0) + { + return -1; + } + xfer += ret; + *value_type = (ThriftType) v; + + if ((ret = thrift_protocol_read_i32 (protocol, &sizei, error)) <0) + { + return -1; + } + xfer += ret; + + if (sizei < 0) + { + g_set_error (error, THRIFT_PROTOCOL_ERROR, + THRIFT_PROTOCOL_ERROR_NEGATIVE_SIZE, + "got negative size of %d", sizei); + return -1; + } + + *size = (guint32) sizei; + return xfer; +} + +gint32 +thrift_binary_protocol_read_map_end (ThriftProtocol *protocol, + GError **error) +{ + THRIFT_UNUSED_VAR (protocol); + THRIFT_UNUSED_VAR (error); + return 0; +} + +gint32 +thrift_binary_protocol_read_list_begin (ThriftProtocol *protocol, + ThriftType *element_type, + guint32 *size, GError **error) +{ + g_return_val_if_fail (THRIFT_IS_BINARY_PROTOCOL (protocol), -1); + + gint32 ret; + gint32 xfer = 0; + gint8 e; + gint32 sizei; + + if ((ret = thrift_protocol_read_byte (protocol, &e, error)) < 0) + { + return -1; + } + xfer += ret; + *element_type = (ThriftType) e; + + if ((ret = thrift_protocol_read_i32 (protocol, &sizei, error)) < 0) + { + return -1; + } + xfer += ret; + + if (sizei < 0) + { + g_set_error (error, THRIFT_PROTOCOL_ERROR, + THRIFT_PROTOCOL_ERROR_NEGATIVE_SIZE, + "got negative size of %d", sizei); + return -1; + } + + *size = (guint32) sizei; + return xfer; +} + +gint32 +thrift_binary_protocol_read_list_end (ThriftProtocol *protocol, + GError **error) +{ + THRIFT_UNUSED_VAR (protocol); + THRIFT_UNUSED_VAR (error); + return 0; +} + +gint32 +thrift_binary_protocol_read_set_begin (ThriftProtocol *protocol, + ThriftType *element_type, + guint32 *size, GError **error) +{ + g_return_val_if_fail (THRIFT_IS_BINARY_PROTOCOL (protocol), -1); + + return thrift_protocol_read_list_begin (protocol, element_type, size, error); +} + +gint32 +thrift_binary_protocol_read_set_end (ThriftProtocol *protocol, + GError **error) +{ + THRIFT_UNUSED_VAR (protocol); + THRIFT_UNUSED_VAR (error); + return 0; +} + +gint32 +thrift_binary_protocol_read_bool (ThriftProtocol *protocol, gboolean *value, + GError **error) +{ + g_return_val_if_fail (THRIFT_IS_BINARY_PROTOCOL (protocol), -1); + gint32 ret; + gpointer b[1]; + + if ((ret = + thrift_transport_read (protocol->transport, + b, 1, error)) < 0) + { + return -1; + } + *value = *(gint8 *) b != 0; + return ret; +} + +gint32 +thrift_binary_protocol_read_byte (ThriftProtocol *protocol, gint8 *value, + GError **error) +{ + g_return_val_if_fail (THRIFT_IS_BINARY_PROTOCOL (protocol), -1); + gint32 ret; + gpointer b[1]; + + if ((ret = + thrift_transport_read (protocol->transport, + b, 1, error)) < 0) + { + return -1; + } + *value = *(gint8 *) b; + return ret; +} + +gint32 +thrift_binary_protocol_read_i16 (ThriftProtocol *protocol, gint16 *value, + GError **error) +{ + g_return_val_if_fail (THRIFT_IS_BINARY_PROTOCOL (protocol), -1); + gint32 ret; + gpointer b[2]; + + if ((ret = + thrift_transport_read (protocol->transport, + b, 2, error)) < 0) + { + return -1; + } + *value = *(gint16 *) b; + *value = g_ntohs (*value); + return ret; +} + +gint32 +thrift_binary_protocol_read_i32 (ThriftProtocol *protocol, gint32 *value, + GError **error) +{ + g_return_val_if_fail (THRIFT_IS_BINARY_PROTOCOL (protocol), -1); + gint32 ret; + gpointer b[4]; + + if ((ret = + thrift_transport_read (protocol->transport, + b, 4, error)) < 0) + { + return -1; + } + *value = *(gint32 *) b; + *value = g_ntohl (*value); + return ret; +} + +gint32 +thrift_binary_protocol_read_i64 (ThriftProtocol *protocol, gint64 *value, + GError **error) +{ + g_return_val_if_fail (THRIFT_IS_BINARY_PROTOCOL (protocol), -1); + gint32 ret; + gpointer b[8]; + + if ((ret = + thrift_transport_read (protocol->transport, + b, 8, error)) < 0) + { + return -1; + } + *value = *(gint64 *) b; + *value = GUINT64_FROM_BE (*value); + return ret; +} + +gint32 +thrift_binary_protocol_read_double (ThriftProtocol *protocol, + gdouble *value, GError **error) +{ + g_return_val_if_fail (THRIFT_IS_BINARY_PROTOCOL (protocol), -1); + gint32 ret; + gpointer b[8]; + + if ((ret = + thrift_transport_read (protocol->transport, + b, 8, error)) < 0) + { + return -1; + } + guint64 bits = *(guint64 *) b; + bits = GUINT64_FROM_BE (bits); + *value = thrift_bitwise_cast_gdouble (bits); + return ret; +} + +gint32 +thrift_binary_protocol_read_string (ThriftProtocol *protocol, + gchar **str, GError **error) +{ + g_return_val_if_fail (THRIFT_IS_BINARY_PROTOCOL (protocol), -1); + guint32 len; + gint32 ret; + gint32 xfer = 0; + gint32 read_len = 0; + + /* read the length into read_len */ + if ((ret = + thrift_protocol_read_i32 (protocol, &read_len, error)) < 0) + { + return -1; + } + xfer += ret; + + if (read_len > 0) + { + /* allocate the memory for the string */ + len = (guint32) read_len + 1; // space for null terminator + *str = g_new0 (gchar, len); + if ((ret = + thrift_transport_read (protocol->transport, + *str, read_len, error)) < 0) + { + g_free (*str); + *str = NULL; + len = 0; + return -1; + } + xfer += ret; + } else { + *str = NULL; + } + + return xfer; +} + +gint32 +thrift_binary_protocol_read_binary (ThriftProtocol *protocol, + gpointer *buf, guint32 *len, + GError **error) +{ + g_return_val_if_fail (THRIFT_IS_BINARY_PROTOCOL (protocol), -1); + gint32 ret; + gint32 xfer = 0; + gint32 read_len = 0; + + /* read the length into read_len */ + if ((ret = + thrift_protocol_read_i32 (protocol, &read_len, error)) < 0) + { + return -1; + } + xfer += ret; + + if (read_len > 0) + { + /* allocate the memory as an array of unsigned char for binary data */ + *len = (guint32) read_len; + *buf = g_new (guchar, *len); + if ((ret = + thrift_transport_read (protocol->transport, + *buf, *len, error)) < 0) + { + g_free (*buf); + *buf = NULL; + *len = 0; + return -1; + } + xfer += ret; + } else { + *buf = NULL; + } + + return xfer; +} + + diff --git a/lib/c_glib/src/protocol/thrift_binary_protocol.h b/lib/c_glib/src/protocol/thrift_binary_protocol.h new file mode 100644 index 00000000..461c5247 --- /dev/null +++ b/lib/c_glib/src/protocol/thrift_binary_protocol.h @@ -0,0 +1,55 @@ +#ifndef _THRIFT_BINARY_PROTOCOL_H +#define _THRIFT_BINARY_PROTOCOL_H + +#include + +#include "protocol/thrift_protocol.h" +#include "transport/thrift_transport.h" + +/*! \file thrift_binary_protocol.h + * \brief Binary protocol implementation of a Thrift protocol. Implements the + * ThriftProtocol interface. + */ + +/* type macros */ +#define THRIFT_TYPE_BINARY_PROTOCOL (thrift_binary_protocol_get_type ()) +#define THRIFT_BINARY_PROTOCOL(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), \ + THRIFT_TYPE_BINARY_PROTOCOL, \ + ThriftBinaryProtocol)) +#define THRIFT_IS_BINARY_PROTOCOL(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \ + THRIFT_TYPE_BINARY_PROTOCOL)) +#define THRIFT_BINARY_PROTOCOL_CLASS(c) (G_TYPE_CHECK_CLASS_CAST ((c), \ + THRIFT_TYPE_BINARY_PROTOCOL, \ + ThriftBinaryProtocolClass)) +#define THRIFT_IS_BINARY_PROTOCOL_CLASS(c) (G_TYPE_CHECK_CLASS_TYPE ((c), \ + THRIFT_TYPE_BINARY_PROTOCOL)) +#define THRIFT_BINARY_PROTOCOL_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS ((obj), THRIFT_TYPE_BINARY_PROTOCOL, \ + ThriftBinaryProtocolClass)) + +/* version numbers */ +#define THRIFT_BINARY_PROTOCOL_VERSION_1 0x80010000 +#define THRIFT_BINARY_PROTOCOL_VERSION_MASK 0xffff0000 + +/*! + * Thrift Binary Protocol instance. + */ +struct _ThriftBinaryProtocol +{ + ThriftProtocol parent; +}; +typedef struct _ThriftBinaryProtocol ThriftBinaryProtocol; + +/*! + * Thrift Binary Protocol class. + */ +struct _ThriftBinaryProtocolClass +{ + ThriftProtocolClass parent; +}; +typedef struct _ThriftBinaryProtocolClass ThriftBinaryProtocolClass; + +/* used by THRIFT_TYPE_BINARY_PROTOCOL */ +GType thrift_binary_protocol_get_type (void); + +#endif /* _THRIFT_BINARY_PROTOCOL_H */ diff --git a/lib/c_glib/src/protocol/thrift_binary_protocol_factory.c b/lib/c_glib/src/protocol/thrift_binary_protocol_factory.c new file mode 100644 index 00000000..a3b4373c --- /dev/null +++ b/lib/c_glib/src/protocol/thrift_binary_protocol_factory.c @@ -0,0 +1,58 @@ + +#include "thrift.h" +#include "protocol/thrift_binary_protocol.h" +#include "protocol/thrift_binary_protocol_factory.h" + +/* forward declarations */ +static void thrift_binary_protocol_factory_class_init (ThriftProtocolFactoryClass *cls); + +ThriftProtocol *thrift_binary_protocol_factory_get_protocol (ThriftProtocolFactory *factory, ThriftTransport *transport); + +GType +thrift_binary_protocol_factory_get_type (void) +{ + static GType type = 0; + + if (type == 0) + { + static const GTypeInfo info = + { + sizeof (ThriftBinaryProtocolFactoryClass), + NULL, /* base_init */ + NULL, /* base_finalize */ + (GClassInitFunc) thrift_binary_protocol_factory_class_init, + NULL, /* class_finalize */ + NULL, /* class_data */ + sizeof (ThriftBinaryProtocolFactory), + 0, /* n_preallocs */ + NULL, /* instance_init */ + NULL, /* value_table */ + }; + + type = g_type_register_static (THRIFT_TYPE_PROTOCOL_FACTORY, + "ThriftBinaryProtocolFactoryType", + &info, 0); + } + + return type; +} + +static void +thrift_binary_protocol_factory_class_init (ThriftProtocolFactoryClass *cls) +{ + cls->get_protocol = thrift_binary_protocol_factory_get_protocol; +} + +ThriftProtocol * +thrift_binary_protocol_factory_get_protocol (ThriftProtocolFactory *factory, + ThriftTransport *transport) +{ + THRIFT_UNUSED_VAR (factory); + + ThriftBinaryProtocol *tb = g_object_new (THRIFT_TYPE_BINARY_PROTOCOL, + "transport", transport, NULL); + + return THRIFT_PROTOCOL (tb); +} + + diff --git a/lib/c_glib/src/protocol/thrift_binary_protocol_factory.h b/lib/c_glib/src/protocol/thrift_binary_protocol_factory.h new file mode 100644 index 00000000..ae4ae406 --- /dev/null +++ b/lib/c_glib/src/protocol/thrift_binary_protocol_factory.h @@ -0,0 +1,46 @@ +#ifndef _THRIFT_BINARY_PROTOCOL_FACTORY_H +#define _THRIFT_BINARY_PROTOCOL_FACTORY_H + +#include + +#include "protocol/thrift_protocol_factory.h" + +/* type macros */ +#define THRIFT_TYPE_BINARY_PROTOCOL_FACTORY \ + (thrift_binary_protocol_factory_get_type ()) +#define THRIFT_BINARY_PROTOCOL_FACTORY(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST ((obj), \ + THRIFT_TYPE_BINARY_PROTOCOL_FACTORY, \ + ThriftBinaryProtocolFactory)) +#define THRIFT_IS_BINARY_PROTOCOL_FACTORY(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \ + THRIFT_TYPE_BINARY_PROTOCOL_FACTORY)) +#define THRIFT_BINARY_PROTOCOL_FACTORY_CLASS(c) \ + (G_TYPE_CHECK_CLASS_CAST ((c), \ + THRIFT_TYPE_BINARY_PROTOCOL_FACTORY, \ + ThriftBinaryProtocolFactoryClass)) +#define THRIFT_IS_BINARY_PROTOCOL_FACTORY_CLASS(c) \ + (G_TYPE_CHECK_CLASS_TYPE ((c), \ + THRIFT_TYPE_BINARY_PROTOCOL_FACTORY)) +#define THRIFT_BINARY_PROTOCOL_FACTORY_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS ((obj), \ + THRIFT_TYPE_BINARY_PROTOCOL_FACTORY, \ + ThriftBinaryProtocolFactoryClass)) + +struct _ThriftBinaryProtocolFactory +{ + ThriftProtocolFactory parent; +}; +typedef struct _ThriftBinaryProtocolFactory ThriftBinaryProtocolFactory; + +struct _ThriftBinaryProtocolFactoryClass +{ + ThriftProtocolFactoryClass parent; +}; +typedef struct _ThriftBinaryProtocolFactoryClass + ThriftBinaryProtocolFactoryClass; + +/* used by THRIFT_TYPE_BINARY_PROTOCOL_FACTORY */ +GType thrift_binary_protocol_factory_get_type (void); + +#endif /* _THRIFT_BINARY_PROTOCOL_FACTORY_H */ diff --git a/lib/c_glib/src/protocol/thrift_protocol.c b/lib/c_glib/src/protocol/thrift_protocol.c new file mode 100644 index 00000000..8ea54b68 --- /dev/null +++ b/lib/c_glib/src/protocol/thrift_protocol.c @@ -0,0 +1,604 @@ +#include "thrift.h" +#include "protocol/thrift_protocol.h" +#include "transport/thrift_transport.h" + +/* define the GError domain string */ +#define THRIFT_PROTOCOL_ERROR_DOMAIN "thrift-protocol-error-quark" + +/* object properties */ +enum _ThriftProtocolProperties +{ + PROP_0, + PROP_THRIFT_PROTOCOL_TRANSPORT +}; + +/* forward declarations */ +static void thrift_protocol_instance_init (ThriftProtocol *protocol); +static void thrift_protocol_class_init (ThriftProtocolClass *cls); +void thrift_protocol_get_property (GObject *object, guint property_id, + GValue *value, GParamSpec *pspec); +void thrift_protocol_set_property (GObject *object, guint property_id, + const GValue *value, GParamSpec *pspec); + +/* define ThriftProtocolInterface's type */ +GType +thrift_protocol_get_type (void) +{ + static GType type = 0; + + if (type == 0) + { + static const GTypeInfo info = { + sizeof (ThriftProtocolClass), + NULL, /* base_init */ + NULL, /* base_finalize */ + (GClassInitFunc) thrift_protocol_class_init, + NULL, /* class_finalize */ + NULL, /* class_data */ + sizeof (ThriftProtocol), + 0, /* n_preallocs */ + (GInstanceInitFunc) thrift_protocol_instance_init, + NULL, /* value_table */ + }; + + type = g_type_register_static (G_TYPE_OBJECT, "ThriftProtocol", + &info, G_TYPE_FLAG_ABSTRACT); + } + + return type; +} + +static void +thrift_protocol_instance_init (ThriftProtocol *protocol) +{ + protocol->transport = NULL; +} + +static void +thrift_protocol_class_init (ThriftProtocolClass *cls) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (cls); + + gobject_class->get_property = thrift_protocol_get_property; + gobject_class->set_property = thrift_protocol_set_property; + + g_object_class_install_property (gobject_class, + PROP_THRIFT_PROTOCOL_TRANSPORT, + g_param_spec_object ("transport", "Transport", "Thrift Transport", + THRIFT_TYPE_TRANSPORT, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); + + cls->write_message_begin = thrift_protocol_write_message_begin; + cls->write_message_end = thrift_protocol_write_message_end; + cls->write_struct_begin = thrift_protocol_write_struct_begin; + cls->write_struct_end = thrift_protocol_write_struct_end; + cls->write_field_begin = thrift_protocol_write_field_begin; + cls->write_field_end = thrift_protocol_write_field_end; + cls->write_field_stop = thrift_protocol_write_field_stop; + cls->write_map_begin = thrift_protocol_write_map_begin; + cls->write_map_end = thrift_protocol_write_map_end; + cls->write_list_begin = thrift_protocol_write_list_begin; + cls->write_list_end = thrift_protocol_write_list_end; + cls->write_set_begin = thrift_protocol_write_set_begin; + cls->write_set_end = thrift_protocol_write_set_end; + cls->write_bool = thrift_protocol_write_bool; + cls->write_byte = thrift_protocol_write_byte; + cls->write_i16 = thrift_protocol_write_i16; + cls->write_i32 = thrift_protocol_write_i32; + cls->write_i64 = thrift_protocol_write_i64; + cls->write_double = thrift_protocol_write_double; + cls->write_string = thrift_protocol_write_string; + cls->write_binary = thrift_protocol_write_binary; + cls->read_message_begin = thrift_protocol_read_message_begin; + cls->read_message_end = thrift_protocol_read_message_end; + cls->read_struct_begin = thrift_protocol_read_struct_begin; + cls->read_struct_end = thrift_protocol_read_struct_end; + cls->read_field_begin = thrift_protocol_read_field_begin; + cls->read_field_end = thrift_protocol_read_field_end; + cls->read_map_begin = thrift_protocol_read_map_begin; + cls->read_map_end = thrift_protocol_read_map_end; + cls->read_list_begin = thrift_protocol_read_list_begin; + cls->read_set_begin = thrift_protocol_read_set_begin; + cls->read_set_end = thrift_protocol_read_set_end; + cls->read_bool = thrift_protocol_read_bool; + cls->read_byte = thrift_protocol_read_byte; + cls->read_i16 = thrift_protocol_read_i16; + cls->read_i32 = thrift_protocol_read_i32; + cls->read_i64 = thrift_protocol_read_i64; + cls->read_double = thrift_protocol_read_double; + cls->read_string = thrift_protocol_read_string; + cls->read_binary = thrift_protocol_read_binary; +} + +void +thrift_protocol_get_property (GObject *object, guint property_id, + GValue *value, GParamSpec *pspec) +{ + ThriftProtocol *protocol = THRIFT_PROTOCOL (object); + + THRIFT_UNUSED_VAR (pspec); + + switch (property_id) + { + case PROP_THRIFT_PROTOCOL_TRANSPORT: + g_value_set_object (value, protocol->transport); + break; + } +} + +void +thrift_protocol_set_property (GObject *object, guint property_id, + const GValue *value, GParamSpec *pspec) +{ + + ThriftProtocol *protocol = THRIFT_PROTOCOL (object); + + THRIFT_UNUSED_VAR (pspec); + + switch (property_id) + { + case PROP_THRIFT_PROTOCOL_TRANSPORT: + protocol->transport = g_value_get_object (value); + break; + } +} + + +gint32 +thrift_protocol_write_message_begin (ThriftProtocol *protocol, + const gchar *name, + const ThriftMessageType message_type, + const gint32 seqid, GError **error) +{ + return THRIFT_PROTOCOL_GET_CLASS (protocol)->write_message_begin + (protocol, name, + message_type, seqid, + error); +} + +gint32 +thrift_protocol_write_message_end (ThriftProtocol *protocol, GError **error) +{ + return THRIFT_PROTOCOL_GET_CLASS (protocol)->write_message_end (protocol, + error); +} + +gint32 +thrift_protocol_write_struct_begin (ThriftProtocol *protocol, const gchar *name, + GError **error) +{ + return THRIFT_PROTOCOL_GET_CLASS (protocol)->write_struct_begin (protocol, + name, error); +} + +gint32 +thrift_protocol_write_struct_end (ThriftProtocol *protocol, GError **error) +{ + return THRIFT_PROTOCOL_GET_CLASS (protocol)->write_struct_end (protocol, + error); +} + +gint32 +thrift_protocol_write_field_begin (ThriftProtocol *protocol, + const gchar *name, + const ThriftType field_type, + const gint16 field_id, + GError **error) +{ + return THRIFT_PROTOCOL_GET_CLASS (protocol)->write_field_begin (protocol, + name, field_type, + field_id, error); +} + +gint32 +thrift_protocol_write_field_end (ThriftProtocol *protocol, GError **error) +{ + return THRIFT_PROTOCOL_GET_CLASS (protocol)->write_field_end (protocol, + error); +} + +gint32 +thrift_protocol_write_field_stop (ThriftProtocol *protocol, GError **error) +{ + return THRIFT_PROTOCOL_GET_CLASS (protocol)->write_field_stop (protocol, + error); +} + +gint32 +thrift_protocol_write_map_begin (ThriftProtocol *protocol, + const ThriftType key_type, + const ThriftType value_type, + const guint32 size, GError **error) +{ + return THRIFT_PROTOCOL_GET_CLASS (protocol)->write_map_begin (protocol, + key_type, value_type, + size, error); +} + +gint32 +thrift_protocol_write_map_end (ThriftProtocol *protocol, GError **error) +{ + return THRIFT_PROTOCOL_GET_CLASS (protocol)->write_map_end (protocol, + error); +} + +gint32 +thrift_protocol_write_list_begin (ThriftProtocol *protocol, + const ThriftType element_type, + const guint32 size, GError **error) +{ + return THRIFT_PROTOCOL_GET_CLASS (protocol)->write_list_begin (protocol, + element_type, size, + error); +} + +gint32 +thrift_protocol_write_list_end (ThriftProtocol *protocol, GError **error) +{ + return THRIFT_PROTOCOL_GET_CLASS (protocol)->write_list_end (protocol, + error); +} + +gint32 +thrift_protocol_write_set_begin (ThriftProtocol *protocol, + const ThriftType element_type, + const guint32 size, GError **error) +{ + return THRIFT_PROTOCOL_GET_CLASS (protocol)->write_set_begin (protocol, + element_type, size, + error); +} + +gint32 +thrift_protocol_write_set_end (ThriftProtocol *protocol, GError **error) +{ + return THRIFT_PROTOCOL_GET_CLASS (protocol)->write_set_end (protocol, + error); +} + +gint32 +thrift_protocol_write_bool (ThriftProtocol *protocol, + const gboolean value, GError **error) +{ + return THRIFT_PROTOCOL_GET_CLASS (protocol)->write_bool (protocol, value, + error); +} + +gint32 +thrift_protocol_write_byte (ThriftProtocol *protocol, const gint8 value, + GError **error) +{ + return THRIFT_PROTOCOL_GET_CLASS (protocol)->write_byte (protocol, value, + error); +} + +gint32 +thrift_protocol_write_i16 (ThriftProtocol *protocol, const gint16 value, + GError **error) +{ + return THRIFT_PROTOCOL_GET_CLASS (protocol)->write_i16 (protocol, value, + error); +} + +gint32 +thrift_protocol_write_i32 (ThriftProtocol *protocol, const gint32 value, + GError **error) +{ + return THRIFT_PROTOCOL_GET_CLASS (protocol)->write_i32 (protocol, value, + error); +} + +gint32 +thrift_protocol_write_i64 (ThriftProtocol *protocol, const gint64 value, + GError **error) +{ + return THRIFT_PROTOCOL_GET_CLASS (protocol)->write_i64 (protocol, value, + error); +} + +gint32 +thrift_protocol_write_double (ThriftProtocol *protocol, + const gdouble value, GError **error) +{ + return THRIFT_PROTOCOL_GET_CLASS (protocol)->write_double (protocol, + value, error); +} + +gint32 +thrift_protocol_write_string (ThriftProtocol *protocol, + const gchar *str, GError **error) +{ + return THRIFT_PROTOCOL_GET_CLASS (protocol)->write_string (protocol, str, + error); +} + +gint32 +thrift_protocol_write_binary (ThriftProtocol *protocol, const gpointer buf, + const guint32 len, GError **error) +{ + return THRIFT_PROTOCOL_GET_CLASS (protocol)->write_binary (protocol, buf, + len, error); +} + +gint32 +thrift_protocol_read_message_begin (ThriftProtocol *protocol, + gchar **name, + ThriftMessageType *message_type, + gint32 *seqid, GError **error) +{ + return THRIFT_PROTOCOL_GET_CLASS (protocol)->read_message_begin (protocol, + name, message_type, + seqid, error); +} + +gint32 +thrift_protocol_read_message_end (ThriftProtocol *protocol, + GError **error) +{ + return THRIFT_PROTOCOL_GET_CLASS (protocol)->read_message_end (protocol, + error); +} + +gint32 +thrift_protocol_read_struct_begin (ThriftProtocol *protocol, + gchar **name, + GError **error) +{ + return THRIFT_PROTOCOL_GET_CLASS (protocol)->read_struct_begin (protocol, + name, + error); +} + +gint32 +thrift_protocol_read_struct_end (ThriftProtocol *protocol, GError **error) +{ + return THRIFT_PROTOCOL_GET_CLASS (protocol)->read_struct_end (protocol, + error); +} + +gint32 +thrift_protocol_read_field_begin (ThriftProtocol *protocol, + gchar **name, + ThriftType *field_type, + gint16 *field_id, + GError **error) +{ + return THRIFT_PROTOCOL_GET_CLASS (protocol)->read_field_begin (protocol, + name, + field_type, + field_id, + error); +} + +gint32 +thrift_protocol_read_field_end (ThriftProtocol *protocol, + GError **error) +{ + return THRIFT_PROTOCOL_GET_CLASS (protocol)->read_field_end (protocol, + error); +} + +gint32 +thrift_protocol_read_map_begin (ThriftProtocol *protocol, + ThriftType *key_type, + ThriftType *value_type, guint32 *size, + GError **error) +{ + return THRIFT_PROTOCOL_GET_CLASS (protocol)->read_map_begin (protocol, + key_type, + value_type, + size, + error); +} + +gint32 +thrift_protocol_read_map_end (ThriftProtocol *protocol, GError **error) +{ + return THRIFT_PROTOCOL_GET_CLASS (protocol)->read_map_end (protocol, + error); +} + +gint32 +thrift_protocol_read_list_begin (ThriftProtocol *protocol, + ThriftType *element_type, + guint32 *size, GError **error) +{ + return THRIFT_PROTOCOL_GET_CLASS (protocol)->read_list_begin (protocol, + element_type, + size, error); +} + +gint32 +thrift_protocol_read_list_end (ThriftProtocol *protocol, GError **error) +{ + return THRIFT_PROTOCOL_GET_CLASS (protocol)->read_list_end (protocol, + error); +} + +gint32 +thrift_protocol_read_set_begin (ThriftProtocol *protocol, + ThriftType *element_type, + guint32 *size, GError **error) +{ + return THRIFT_PROTOCOL_GET_CLASS (protocol)->read_set_begin (protocol, + element_type, + size, error); +} + +gint32 +thrift_protocol_read_set_end (ThriftProtocol *protocol, GError **error) +{ + return THRIFT_PROTOCOL_GET_CLASS (protocol)->read_set_end (protocol, + error); +} + +gint32 +thrift_protocol_read_bool (ThriftProtocol *protocol, gboolean *value, + GError **error) +{ + return THRIFT_PROTOCOL_GET_CLASS (protocol)->read_bool (protocol, value, + error); +} + +gint32 +thrift_protocol_read_byte (ThriftProtocol *protocol, gint8 *value, + GError **error) +{ + return THRIFT_PROTOCOL_GET_CLASS (protocol)->read_byte (protocol, value, + error); +} + +gint32 +thrift_protocol_read_i16 (ThriftProtocol *protocol, gint16 *value, + GError **error) +{ + return THRIFT_PROTOCOL_GET_CLASS (protocol)->read_i16 (protocol, value, + error); +} + +gint32 +thrift_protocol_read_i32 (ThriftProtocol *protocol, gint32 *value, + GError **error) +{ + return THRIFT_PROTOCOL_GET_CLASS (protocol)->read_i32 (protocol, value, + error); +} + +gint32 +thrift_protocol_read_i64 (ThriftProtocol *protocol, gint64 *value, + GError **error) +{ + return THRIFT_PROTOCOL_GET_CLASS (protocol)->read_i64 (protocol, value, + error); +} + +gint32 +thrift_protocol_read_double (ThriftProtocol *protocol, + gdouble *value, GError **error) +{ + return THRIFT_PROTOCOL_GET_CLASS (protocol)->read_double (protocol, value, + error); +} + +gint32 +thrift_protocol_read_string (ThriftProtocol *protocol, + gchar **str, GError **error) +{ + return THRIFT_PROTOCOL_GET_CLASS (protocol)->read_string (protocol, str, + error); +} + +gint32 +thrift_protocol_read_binary (ThriftProtocol *protocol, gpointer *buf, + guint32 *len, GError **error) +{ + return THRIFT_PROTOCOL_GET_CLASS (protocol)->read_binary (protocol, buf, + len, error); +} + +gint32 +thrift_protocol_skip (ThriftProtocol *protocol, ThriftType type, GError **error) +{ + switch (type) + { + case T_BOOL: + { + gboolean boolv; + return thrift_protocol_read_bool (protocol, &boolv, error); + } + case T_BYTE: + { + gint8 bytev; + return thrift_protocol_read_byte (protocol, &bytev, error); + } + + case T_I16: + { + gint16 i16; + return thrift_protocol_read_i16 (protocol, &i16, error); + } + case T_I32: + { + gint32 i32; + return thrift_protocol_read_i32 (protocol, &i32, error); + } + case T_I64: + { + gint64 i64; + return thrift_protocol_read_i64 (protocol, &i64, error); + } + case T_DOUBLE: + { + gdouble dub; + return thrift_protocol_read_double (protocol, &dub, error); + } + case T_STRING: + { + gpointer data; + guint32 len; + gint32 ret = thrift_protocol_read_binary (protocol, &data, &len, error); + g_free (data); + return ret; + } + case T_STRUCT: + { + guint32 result = 0; + gchar *name; + gint16 fid; + ThriftType ftype; + result += thrift_protocol_read_struct_begin (protocol, &name, error); + + while (1) + { + result += thrift_protocol_read_field_begin (protocol, &name, &ftype, + &fid, error); + if (ftype == T_STOP) + { + break; + } + result += thrift_protocol_skip (protocol, ftype, error); + result += thrift_protocol_read_field_end (protocol, error); + } + result += thrift_protocol_read_struct_end (protocol, error); + return result; + } + case T_MAP: + { + guint32 result = 0; + ThriftType elem_type; + guint32 i, size; + result += thrift_protocol_read_set_begin (protocol, &elem_type, &size, + error); + for (i = 0; i < size; i++) + { + result += thrift_protocol_skip (protocol, elem_type, error); + } + result += thrift_protocol_read_set_end (protocol, error); + return result; + } + case T_LIST: + { + guint32 result = 0; + ThriftType elem_type; + guint32 i, size; + result += thrift_protocol_read_list_begin (protocol, &elem_type, &size, + error); + for (i = 0; i < size; i++) + { + result += thrift_protocol_skip (protocol, elem_type, error); + } + result += thrift_protocol_read_list_end (protocol, error); + return result; + } + default: + return 0; + } +} + +/* define the GError domain for Thrift protocols */ +GQuark +thrift_protocol_error_quark (void) +{ + return g_quark_from_static_string (THRIFT_PROTOCOL_ERROR_DOMAIN); +} + diff --git a/lib/c_glib/src/protocol/thrift_protocol.h b/lib/c_glib/src/protocol/thrift_protocol.h new file mode 100644 index 00000000..0340a60c --- /dev/null +++ b/lib/c_glib/src/protocol/thrift_protocol.h @@ -0,0 +1,324 @@ +#ifndef _THRIFT_PROTOCOL_H +#define _THRIFT_PROTOCOL_H + +#include + +#include "transport/thrift_transport.h" + +/** + * Enumerated definition of the types that the Thrift protocol supports. + * Take special note of the T_END type which is used specifically to mark + * the end of a sequence of fields. + */ +enum _ThriftType { + T_STOP = 0, + T_VOID = 1, + T_BOOL = 2, + T_BYTE = 3, + T_I08 = 3, + T_I16 = 6, + T_I32 = 8, + T_U64 = 9, + T_I64 = 10, + T_DOUBLE = 4, + T_STRING = 11, + T_UTF7 = 11, + T_STRUCT = 12, + T_MAP = 13, + T_SET = 14, + T_LIST = 15, + T_UTF8 = 16, + T_UTF16 = 17 +}; +typedef enum _ThriftType ThriftType; + +/** + * Enumerated definition of the message types that the Thrift protocol + * supports. + */ +enum _ThriftMessageType { + T_CALL = 1, + T_REPLY = 2, + T_EXCEPTION = 3, + T_ONEWAY = 4 +}; +typedef enum _ThriftMessageType ThriftMessageType; + +/*! \file thrift_protocol.h + * \brief Abstract class for Thrift protocol implementations. + */ + +/* type macros */ +#define THRIFT_TYPE_PROTOCOL (thrift_protocol_get_type ()) +#define THRIFT_PROTOCOL(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), \ + THRIFT_TYPE_PROTOCOL, ThriftProtocol)) +#define THRIFT_IS_PROTOCOL(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \ + THRIFT_TYPE_PROTOCOL)) +#define THRIFT_PROTOCOL_CLASS(c) (G_TYPE_CHECK_CLASS_CAST ((c), \ + THRIFT_TYPE_PROTOCOL, \ + ThriftProtocolClass)) +#define THRIFT_IS_PROTOCOL_CLASS(c) (G_TYPE_CHECK_CLASS_TYPE ((c), \ + THRIFT_TYPE_PROTOCOL)) +#define THRIFT_PROTOCOL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), \ + THRIFT_TYPE_PROTOCOL, \ + ThriftProtocolClass)) + +/*! + * Thrift Protocol object + */ +struct _ThriftProtocol +{ + GObject parent; + + /* protected */ + ThriftTransport *transport; +}; +typedef struct _ThriftProtocol ThriftProtocol; + +/*! + * Thrift Protocol class + */ +struct _ThriftProtocolClass +{ + GObjectClass parent; + + gint32 (*write_message_begin) (ThriftProtocol *protocol, const gchar *name, + const ThriftMessageType message_type, + const gint32 seqid, GError **error); + gint32 (*write_message_end) (ThriftProtocol *protocol, GError **error); + gint32 (*write_struct_begin) (ThriftProtocol *protocol, const gchar *name, + GError **error); + gint32 (*write_struct_end) (ThriftProtocol *protocol, GError **error); + gint32 (*write_field_begin) (ThriftProtocol *protocol, const gchar *name, + const ThriftType field_type, + const gint16 field_id, GError **error); + gint32 (*write_field_end) (ThriftProtocol *protocol, GError **error); + gint32 (*write_field_stop) (ThriftProtocol *protocol, GError **error); + gint32 (*write_map_begin) (ThriftProtocol *protocol, + const ThriftType key_type, + const ThriftType value_type, + const guint32 size, GError **error); + gint32 (*write_map_end) (ThriftProtocol *protocol, GError **error); + gint32 (*write_list_begin) (ThriftProtocol *protocol, + const ThriftType element_type, + const guint32 size, GError **error); + gint32 (*write_list_end) (ThriftProtocol *protocol, GError **error); + gint32 (*write_set_begin) (ThriftProtocol *protocol, + const ThriftType element_type, + const guint32 size, GError **error); + gint32 (*write_set_end) (ThriftProtocol *protocol, GError **error); + gint32 (*write_bool) (ThriftProtocol *protocol, const gboolean value, + GError **error); + gint32 (*write_byte) (ThriftProtocol *protocol, const gint8 value, + GError **error); + gint32 (*write_i16) (ThriftProtocol *protocol, const gint16 value, + GError **error); + gint32 (*write_i32) (ThriftProtocol *protocol, const gint32 value, + GError **error); + gint32 (*write_i64) (ThriftProtocol *protocol, const gint64 value, + GError **error); + gint32 (*write_double) (ThriftProtocol *protocol, const gdouble value, + GError **error); + gint32 (*write_string) (ThriftProtocol *protocol, const gchar *str, + GError **error); + gint32 (*write_binary) (ThriftProtocol *protocol, const gpointer buf, + const guint32 len, GError **error); + + gint32 (*read_message_begin) (ThriftProtocol *thrift_protocol, gchar **name, + ThriftMessageType *message_type, + gint32 *seqid, GError **error); + gint32 (*read_message_end) (ThriftProtocol *protocol, GError **error); + gint32 (*read_struct_begin) (ThriftProtocol *protocol, gchar **name, + GError **error); + gint32 (*read_struct_end) (ThriftProtocol *protocol, GError **error); + gint32 (*read_field_begin) (ThriftProtocol *protocol, gchar **name, + ThriftType *field_type, gint16 *field_id, + GError **error); + gint32 (*read_field_end) (ThriftProtocol *protocol, GError **error); + gint32 (*read_map_begin) (ThriftProtocol *protocol, ThriftType *key_type, + ThriftType *value_type, guint32 *size, + GError **error); + gint32 (*read_map_end) (ThriftProtocol *protocol, GError **error); + gint32 (*read_list_begin) (ThriftProtocol *protocol, ThriftType *element_type, + guint32 *size, GError **error); + gint32 (*read_list_end) (ThriftProtocol *protocol, GError **error); + gint32 (*read_set_begin) (ThriftProtocol *protocol, ThriftType *element_type, + guint32 *size, GError **error); + gint32 (*read_set_end) (ThriftProtocol *protocol, GError **error); + gint32 (*read_bool) (ThriftProtocol *protocol, gboolean *value, + GError **error); + gint32 (*read_byte) (ThriftProtocol *protocol, gint8 *value, GError **error); + gint32 (*read_i16) (ThriftProtocol *protocol, gint16 *value, GError **error); + gint32 (*read_i32) (ThriftProtocol *protocol, gint32 *value, GError **error); + gint32 (*read_i64) (ThriftProtocol *protocol, gint64 *value, GError **error); + gint32 (*read_double) (ThriftProtocol *protocol, gdouble *value, + GError **error); + gint32 (*read_string) (ThriftProtocol *protocol, gchar **str, GError **error); + gint32 (*read_binary) (ThriftProtocol *protocol, gpointer *buf, + guint32 *len, GError **error); +}; +typedef struct _ThriftProtocolClass ThriftProtocolClass; + +/* used by THRIFT_TYPE_PROTOCOL */ +GType thrift_protocol_get_type (void); + +/* virtual public methods */ +gint32 thrift_protocol_write_message_begin (ThriftProtocol *protocol, + const gchar *name, const ThriftMessageType message_type, + const gint32 seqid, GError **error); + +gint32 thrift_protocol_write_message_end (ThriftProtocol *protocol, + GError **error); + +gint32 thrift_protocol_write_struct_begin (ThriftProtocol *protocol, + const gchar *name, + GError **error); + +gint32 thrift_protocol_write_struct_end (ThriftProtocol *protocol, + GError **error); + +gint32 thrift_protocol_write_field_begin (ThriftProtocol *protocol, + const gchar *name, + const ThriftType field_type, + const gint16 field_id, + GError **error); + +gint32 thrift_protocol_write_field_end (ThriftProtocol *protocol, + GError **error); + +gint32 thrift_protocol_write_field_stop (ThriftProtocol *protocol, + GError **error); + +gint32 thrift_protocol_write_map_begin (ThriftProtocol *protocol, + const ThriftType key_type, + const ThriftType value_type, + const guint32 size, GError **error); + +gint32 thrift_protocol_write_map_end (ThriftProtocol *protocol, GError **error); + +gint32 thrift_protocol_write_list_begin (ThriftProtocol *protocol, + const ThriftType element_type, + const guint32 size, GError **error); + +gint32 thrift_protocol_write_list_end (ThriftProtocol *protocol, + GError **error); + +gint32 thrift_protocol_write_set_begin (ThriftProtocol *protocol, + const ThriftType element_type, + const guint32 size, GError **error); + +gint32 thrift_protocol_write_set_end (ThriftProtocol *protocol, + GError **error); + +gint32 thrift_protocol_write_bool (ThriftProtocol *protocol, + const gboolean value, GError **error); + +gint32 thrift_protocol_write_byte (ThriftProtocol *protocol, const gint8 value, + GError **error); + +gint32 thrift_protocol_write_i16 (ThriftProtocol *protocol, const gint16 value, + GError **error); + +gint32 thrift_protocol_write_i32 (ThriftProtocol *protocol, const gint32 value, + GError **error); + +gint32 thrift_protocol_write_i64 (ThriftProtocol *protocol, const gint64 value, + GError **error); + +gint32 thrift_protocol_write_double (ThriftProtocol *protocol, + const gdouble value, GError **error); + +gint32 thrift_protocol_write_string (ThriftProtocol *protocol, + const gchar *str, GError **error); + +gint32 thrift_protocol_write_binary (ThriftProtocol *protocol, + const gpointer buf, + const guint32 len, GError **error); + +gint32 thrift_protocol_read_message_begin (ThriftProtocol *thrift_protocol, + gchar **name, + ThriftMessageType *message_type, + gint32 *seqid, GError **error); + +gint32 thrift_protocol_read_message_end (ThriftProtocol *protocol, + GError **error); + +gint32 thrift_protocol_read_struct_begin (ThriftProtocol *protocol, + gchar **name, + GError **error); + +gint32 thrift_protocol_read_struct_end (ThriftProtocol *protocol, + GError **error); + +gint32 thrift_protocol_read_field_begin (ThriftProtocol *protocol, + gchar **name, + ThriftType *field_type, + gint16 *field_id, + GError **error); + +gint32 thrift_protocol_read_field_end (ThriftProtocol *protocol, + GError **error); + +gint32 thrift_protocol_read_map_begin (ThriftProtocol *protocol, + ThriftType *key_type, + ThriftType *value_type, guint32 *size, + GError **error); + +gint32 thrift_protocol_read_map_end (ThriftProtocol *protocol, GError **error); + +gint32 thrift_protocol_read_list_begin (ThriftProtocol *protocol, + ThriftType *element_type, + guint32 *size, GError **error); + +gint32 thrift_protocol_read_list_end (ThriftProtocol *protocol, GError **error); + +gint32 thrift_protocol_read_set_begin (ThriftProtocol *protocol, + ThriftType *element_type, + guint32 *size, GError **error); + +gint32 thrift_protocol_read_set_end (ThriftProtocol *protocol, GError **error); + +gint32 thrift_protocol_read_bool (ThriftProtocol *protocol, gboolean *value, + GError **error); + +gint32 thrift_protocol_read_byte (ThriftProtocol *protocol, gint8 *value, + GError **error); + +gint32 thrift_protocol_read_i16 (ThriftProtocol *protocol, gint16 *value, + GError **error); + +gint32 thrift_protocol_read_i32 (ThriftProtocol *protocol, gint32 *value, + GError **error); + +gint32 thrift_protocol_read_i64 (ThriftProtocol *protocol, gint64 *value, + GError **error); + +gint32 thrift_protocol_read_double (ThriftProtocol *protocol, + gdouble *value, GError **error); + +gint32 thrift_protocol_read_string (ThriftProtocol *protocol, + gchar **str, GError **error); + +gint32 thrift_protocol_read_binary (ThriftProtocol *protocol, + gpointer *buf, guint32 *len, + GError **error); + +gint32 thrift_protocol_skip (ThriftProtocol *protocol, ThriftType type, + GError **error); + +/* define error types */ +typedef enum +{ + THRIFT_PROTOCOL_ERROR_UNKNOWN, + THRIFT_PROTOCOL_ERROR_INVALID_DATA, + THRIFT_PROTOCOL_ERROR_NEGATIVE_SIZE, + THRIFT_PROTOCOL_ERROR_SIZE_LIMIT, + THRIFT_PROTOCOL_ERROR_BAD_VERSION, + THRIFT_PROTOCOL_ERROR_NOT_IMPLEMENTED +} ThriftProtocolError; + +/* define an error domain for GError to use */ +GQuark thrift_protocol_error_quark (void); +#define THRIFT_PROTOCOL_ERROR (thrift_protocol_error_quark ()) + +#endif /* _THRIFT_PROTOCOL_H */ diff --git a/lib/c_glib/src/protocol/thrift_protocol_factory.c b/lib/c_glib/src/protocol/thrift_protocol_factory.c new file mode 100644 index 00000000..53b468d8 --- /dev/null +++ b/lib/c_glib/src/protocol/thrift_protocol_factory.c @@ -0,0 +1,50 @@ +#include "thrift.h" +#include "protocol/thrift_protocol_factory.h" + +/* forward declarations */ +static void thrift_protocol_factory_class_init (ThriftProtocolFactoryClass *cls); +ThriftProtocol *thrift_protocol_factory_get_protocol(ThriftProtocolFactory *factory, ThriftTransport *transport); + + +/* define ThriftProtocolFactoryInterface's type */ +GType +thrift_protocol_factory_get_type (void) +{ + static GType type = 0; + + if (type == 0) + { + static const GTypeInfo info = { + sizeof (ThriftProtocolFactoryClass), + NULL, /* base_init */ + NULL, /* base_finalize */ + (GClassInitFunc) thrift_protocol_factory_class_init, + NULL, /* class_finalize */ + NULL, /* class_data */ + sizeof (ThriftProtocolFactory), + 0, /* n_preallocs */ + NULL, /* instance_init */ + NULL, /* value_table */ + }; + + type = g_type_register_static (G_TYPE_OBJECT, "ThriftProtocolFactory", + &info, G_TYPE_FLAG_ABSTRACT); + } + + return type; +} + +static void +thrift_protocol_factory_class_init (ThriftProtocolFactoryClass *cls) +{ + cls->get_protocol = thrift_protocol_factory_get_protocol; +} + +ThriftProtocol * +thrift_protocol_factory_get_protocol(ThriftProtocolFactory *factory, + ThriftTransport *transport) +{ + return THRIFT_PROTOCOL_FACTORY_GET_CLASS (factory)->get_protocol (factory, + transport); +} + diff --git a/lib/c_glib/src/protocol/thrift_protocol_factory.h b/lib/c_glib/src/protocol/thrift_protocol_factory.h new file mode 100644 index 00000000..d1532557 --- /dev/null +++ b/lib/c_glib/src/protocol/thrift_protocol_factory.h @@ -0,0 +1,57 @@ +#ifndef _THRIFT_PROTOCOL_FACTORY_H +#define _THRIFT_PROTOCOL_FACTORY_H + +#include + +#include "transport/thrift_transport.h" +#include "protocol/thrift_protocol.h" + +/*! \file thrift_protocol_factory.h + * \brief Abstract class for Thrift protocol factory implementations. + */ + +/* type macros */ +#define THRIFT_TYPE_PROTOCOL_FACTORY (thrift_protocol_factory_get_type ()) +#define THRIFT_PROTOCOL_FACTORY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), \ + THRIFT_TYPE_PROTOCOL_FACTORY, \ + ThriftProtocolFactory)) +#define THRIFT_IS_PROTOCOL_FACTORY(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \ + THRIFT_TYPE_PROTOCOL_FACTORY)) +#define THRIFT_PROTOCOL_FACTORY_CLASS(c) (G_TYPE_CHECK_CLASS_CAST ((c), \ + THRIFT_TYPE_PROTOCOL_FACTORY, \ + ThriftProtocolFactoryClass)) +#define THRIFT_IS_PROTOCOL_FACTORY_CLASS(c) (G_TYPE_CHECK_CLASS_TYPE ((c), \ + THRIFT_TYPE_PROTOCOL_FACTORY)) +#define THRIFT_PROTOCOL_FACTORY_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS ((obj), \ + THRIFT_TYPE_PROTOCOL_FACTORY, \ + ThriftProtocolFactoryClass)) + +/*! + * Thrift Protocol Factory object + */ +struct _ThriftProtocolFactory +{ + GObject parent; +}; +typedef struct _ThriftProtocolFactory ThriftProtocolFactory; + +/*! + * Thrift Protocol Factory class + */ +struct _ThriftProtocolFactoryClass +{ + GObjectClass parent; + + ThriftProtocol *(*get_protocol) (ThriftProtocolFactory *factory, + ThriftTransport *transport); +}; +typedef struct _ThriftProtocolFactoryClass ThriftProtocolFactoryClass; + +/* used by THRIFT_TYPE_PROTOCOL_FACTORY */ +GType thrift_protocol_factory_get_type (void); + +/* virtual public methods */ +ThriftProtocol *thrift_protocol_factory_get_protocol(ThriftProtocolFactory *factory, ThriftTransport *transport); + +#endif /* _THRIFT_PROTOCOL_FACTORY_H */ diff --git a/lib/c_glib/src/server/thrift_server.c b/lib/c_glib/src/server/thrift_server.c new file mode 100644 index 00000000..c7e6c1fb --- /dev/null +++ b/lib/c_glib/src/server/thrift_server.c @@ -0,0 +1,192 @@ +#include "thrift.h" +#include "thrift_server.h" + +/* object properties */ +enum _ThriftServerProperties +{ + PROP_0, + PROP_THRIFT_SERVER_PROCESSOR, + PROP_THRIFT_SERVER_SERVER_TRANSPORT, + PROP_THRIFT_SERVER_INPUT_TRANSPORT_FACTORY, + PROP_THRIFT_SERVER_OUTPUT_TRANSPORT_FACTORY, + PROP_THRIFT_SERVER_INPUT_PROTOCOL_FACTORY, + PROP_THRIFT_SERVER_OUTPUT_PROTOCOL_FACTORY +}; + +/* forward declarations */ +static void thrift_server_instance_init (ThriftServer *server); +static void thrift_server_class_init (ThriftServerClass *cls); +void thrift_server_get_property (GObject *object, guint property_id, + GValue *value, GParamSpec *pspec); +void thrift_server_set_property (GObject *object, guint property_id, + const GValue *value, GParamSpec *pspec); + + +/* define ThriftServerClass's type */ +GType +thrift_server_get_type (void) +{ + static GType type = 0; + + if (type == 0) + { + static const GTypeInfo info = + { + sizeof (ThriftServerClass), + NULL, /* base_init */ + NULL, /* base_finalize */ + (GClassInitFunc) thrift_server_class_init, + NULL, /* class_finalize */ + NULL, /* class_data */ + sizeof (ThriftServer), + 0, /* n_preallocs */ + (GInstanceInitFunc) thrift_server_instance_init, + NULL, /* value_table */ + }; + + type = g_type_register_static (G_TYPE_OBJECT, "ThriftServer", + &info, G_TYPE_FLAG_ABSTRACT); + } + + return type; +} + +/* instance initializer for Thrift Server */ +static void +thrift_server_instance_init (ThriftServer *server) +{ + server->processor = NULL; + server->server_transport = NULL; + server->input_transport_factory = NULL; + server->output_transport_factory = NULL; + server->input_protocol_factory = NULL; + server->output_protocol_factory = NULL; +} + +/* class initializer for ThriftServer + * TODO: implement ServerEventHandler as a GClosure + */ +static void +thrift_server_class_init (ThriftServerClass *cls) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (cls); + + gobject_class->get_property = thrift_server_get_property; + gobject_class->set_property = thrift_server_set_property; + + g_object_class_install_property (gobject_class, + PROP_THRIFT_SERVER_PROCESSOR, + g_param_spec_object ("processor", "Processor", "Thrift Processor", + THRIFT_TYPE_PROCESSOR, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); + g_object_class_install_property (gobject_class, + PROP_THRIFT_SERVER_SERVER_TRANSPORT, + g_param_spec_object ("server_transport", "Server Transport", + "Thrift Server Transport", + THRIFT_TYPE_SERVER_TRANSPORT, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); + g_object_class_install_property (gobject_class, + PROP_THRIFT_SERVER_INPUT_TRANSPORT_FACTORY, + g_param_spec_object ("input_transport_factory", "Input Transport Factory", + "Thrift Server Input Transport Factory", + THRIFT_TYPE_TRANSPORT_FACTORY, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); + g_object_class_install_property (gobject_class, + PROP_THRIFT_SERVER_OUTPUT_TRANSPORT_FACTORY, + g_param_spec_object ("output_transport_factory", + "Output Transport Factory", + "Thrift Server Output Transport Factory", + THRIFT_TYPE_TRANSPORT_FACTORY, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); + g_object_class_install_property (gobject_class, + PROP_THRIFT_SERVER_INPUT_PROTOCOL_FACTORY, + g_param_spec_object ("input_protocol_factory", "Input Protocol Factory", + "Thrift Server Input Protocol Factory", + THRIFT_TYPE_PROTOCOL_FACTORY, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); + g_object_class_install_property (gobject_class, + PROP_THRIFT_SERVER_OUTPUT_PROTOCOL_FACTORY, + g_param_spec_object ("output_protocol_factory", "Output Protocol Factory", + "Thrift Server Output Protocol Factory", + THRIFT_TYPE_PROTOCOL_FACTORY, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); + + /* set these as virtual methods to be implemented by a subclass */ + cls->serve = thrift_server_serve; + cls->stop = thrift_server_stop; +} + +void +thrift_server_get_property (GObject *object, guint property_id, + GValue *value, GParamSpec *pspec) +{ + ThriftServer *server = THRIFT_SERVER (object); + + THRIFT_UNUSED_VAR (pspec); + + switch (property_id) + { + case PROP_THRIFT_SERVER_PROCESSOR: + g_value_set_object (value, server->processor); + break; + case PROP_THRIFT_SERVER_SERVER_TRANSPORT: + g_value_set_object (value, server->server_transport); + break; + case PROP_THRIFT_SERVER_INPUT_TRANSPORT_FACTORY: + g_value_set_object (value, server->input_transport_factory); + break; + case PROP_THRIFT_SERVER_OUTPUT_TRANSPORT_FACTORY: + g_value_set_object (value, server->output_transport_factory); + break; + case PROP_THRIFT_SERVER_INPUT_PROTOCOL_FACTORY: + g_value_set_object (value, server->input_protocol_factory); + break; + case PROP_THRIFT_SERVER_OUTPUT_PROTOCOL_FACTORY: + g_value_set_object (value, server->output_protocol_factory); + break; + } +} + +void +thrift_server_set_property (GObject *object, guint property_id, + const GValue *value, GParamSpec *pspec) +{ + ThriftServer *server = THRIFT_SERVER (object); + + THRIFT_UNUSED_VAR (pspec); + + switch (property_id) + { + case PROP_THRIFT_SERVER_PROCESSOR: + server->processor = g_value_get_object (value); + break; + case PROP_THRIFT_SERVER_SERVER_TRANSPORT: + server->server_transport = g_value_get_object (value); + break; + case PROP_THRIFT_SERVER_INPUT_TRANSPORT_FACTORY: + server->input_transport_factory = g_value_get_object (value); + break; + case PROP_THRIFT_SERVER_OUTPUT_TRANSPORT_FACTORY: + server->output_transport_factory = g_value_get_object (value); + break; + case PROP_THRIFT_SERVER_INPUT_PROTOCOL_FACTORY: + server->input_protocol_factory = g_value_get_object (value); + break; + case PROP_THRIFT_SERVER_OUTPUT_PROTOCOL_FACTORY: + server->output_protocol_factory = g_value_get_object (value); + break; + } +} + +void +thrift_server_serve (ThriftServer *server) +{ + THRIFT_SERVER_GET_CLASS (server)->serve (server); +} + +void +thrift_server_stop (ThriftServer *server) +{ + THRIFT_SERVER_GET_CLASS (server)->stop (server); +} + diff --git a/lib/c_glib/src/server/thrift_server.h b/lib/c_glib/src/server/thrift_server.h new file mode 100644 index 00000000..2bbe95b6 --- /dev/null +++ b/lib/c_glib/src/server/thrift_server.h @@ -0,0 +1,75 @@ +#ifndef _THRIFT_SERVER_H +#define _THRIFT_SERVER_H + +#include + +#include "processor/thrift_processor.h" +#include "transport/thrift_server_transport.h" +#include "transport/thrift_transport_factory.h" +#include "protocol/thrift_protocol_factory.h" + +/*! \file thrift_server.h + * \brief Abstract class for Thrift servers. + */ + +/* type macros */ +#define THRIFT_TYPE_SERVER (thrift_server_get_type ()) +#define THRIFT_SERVER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), \ + THRIFT_TYPE_SERVER, ThriftServer)) +#define THRIFT_IS_SERVER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \ + THRIFT_TYPE_SERVER)) +#define THRIFT_SERVER_CLASS(c) (G_TYPE_CHECK_CLASS_CAST ((c), \ + THRIFT_TYPE_SERVER, \ + ThriftServerClass)) +#define THRIFT_IS_SERVER_CLASS(c) (G_TYPE_CHECK_CLASS_TYPE ((c), \ + THRIFT_TYPE_SERVER)) +#define THRIFT_SERVER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), \ + THRIFT_TYPE_SERVER, \ + ThriftServerClass)) + +/*! + * Thrift Server object + */ +struct _ThriftServer +{ + GObject parent; + + /* protected */ + ThriftProcessor *processor; + ThriftServerTransport *server_transport; + ThriftTransportFactory *input_transport_factory; + ThriftTransportFactory *output_transport_factory; + ThriftProtocolFactory *input_protocol_factory; + ThriftProtocolFactory *output_protocol_factory; +}; +typedef struct _ThriftServer ThriftServer; + +/*! + * Thrift Server class + */ +struct _ThriftServerClass +{ + GObjectClass parent; + + /* vtable */ + void (*serve) (ThriftServer *server); + void (*stop) (ThriftServer *server); +}; +typedef struct _ThriftServerClass ThriftServerClass; + +/* used by THRIFT_TYPE_SERVER */ +GType thrift_server_get_type (void); + +/*! + * Processes the request. + * \public \memberof ThriftServerClass + */ +void thrift_server_serve (ThriftServer *server); + +/*! + * Stop handling requests. + */ +void thrift_server_stop (ThriftServer *server); + +#endif /* _THRIFT_SERVER_H */ + diff --git a/lib/c_glib/src/server/thrift_simple_server.c b/lib/c_glib/src/server/thrift_simple_server.c new file mode 100644 index 00000000..1957928e --- /dev/null +++ b/lib/c_glib/src/server/thrift_simple_server.c @@ -0,0 +1,133 @@ + +#include "server/thrift_simple_server.h" +#include "transport/thrift_transport_factory.h" +#include "protocol/thrift_protocol_factory.h" +#include "protocol/thrift_binary_protocol_factory.h" + +static void thrift_simple_server_instance_init (ThriftServer *server); +static void thrift_simple_server_class_init (ThriftServerClass *cls); + +/* forward declarations */ +void thrift_simple_server_serve (ThriftServer *server); +void thrift_simple_server_stop (ThriftServer *server); + +GType +thrift_simple_server_get_type (void) +{ + static GType type = 0; + + if (type == 0) + { + static const GTypeInfo info = + { + sizeof (ThriftSimpleServerClass), + NULL, /* base_init */ + NULL, /* base_finalize */ + (GClassInitFunc) thrift_simple_server_class_init, + NULL, /* class_finalize */ + NULL, /* class_data */ + sizeof (ThriftSimpleServer), + 0, /* n_preallocs */ + (GInstanceInitFunc) thrift_simple_server_instance_init, + NULL, /* value_table */ + }; + + type = g_type_register_static (THRIFT_TYPE_SERVER, + "ThriftSimpleServerType", + &info, 0); + } + + return type; +} + +static void +thrift_simple_server_instance_init (ThriftServer *server) +{ + (THRIFT_SIMPLE_SERVER (server))->running = FALSE; + + if (server->input_transport_factory == NULL) + { + server->input_transport_factory = + g_object_new (THRIFT_TYPE_TRANSPORT_FACTORY, NULL); + } + if (server->output_transport_factory == NULL) + { + server->output_transport_factory = + g_object_new (THRIFT_TYPE_TRANSPORT_FACTORY, NULL); + } + if (server->input_protocol_factory == NULL) + { + server->input_protocol_factory = + g_object_new (THRIFT_TYPE_BINARY_PROTOCOL_FACTORY, NULL); + } + if (server->output_protocol_factory == NULL) + { + server->output_protocol_factory = + g_object_new (THRIFT_TYPE_BINARY_PROTOCOL_FACTORY, NULL); + } +} + + +/* initialize the class */ +static void +thrift_simple_server_class_init (ThriftServerClass *cls) +{ + cls->serve = thrift_simple_server_serve; + cls->stop = thrift_simple_server_stop; +} + +void +thrift_simple_server_serve (ThriftServer *server) +{ + g_return_if_fail (THRIFT_IS_SIMPLE_SERVER (server)); + + ThriftTransport *t = NULL; + ThriftTransport *input_transport = NULL, *output_transport = NULL; + ThriftProtocol *input_protocol = NULL, *output_protocol = NULL; + ThriftSimpleServer *tss = THRIFT_SIMPLE_SERVER (server); + + THRIFT_SERVER_TRANSPORT_GET_CLASS (server->server_transport) + ->listen (server->server_transport, NULL); + + tss->running = TRUE; + while (tss->running == TRUE) + { + t = thrift_server_transport_accept (server->server_transport, NULL); + input_transport = + THRIFT_TRANSPORT_FACTORY_GET_CLASS (server->input_transport_factory) + ->get_transport (server->input_transport_factory, t); + output_transport = + THRIFT_TRANSPORT_FACTORY_GET_CLASS (server->output_transport_factory) + ->get_transport (server->output_transport_factory, t); + input_protocol = + THRIFT_PROTOCOL_FACTORY_GET_CLASS (server->input_protocol_factory) + ->get_protocol (server->input_protocol_factory, t); + output_protocol = + THRIFT_PROTOCOL_FACTORY_GET_CLASS (server->output_protocol_factory) + ->get_protocol (server->output_protocol_factory, t); + + while (THRIFT_PROCESSOR_GET_CLASS (server->processor) + ->process (server->processor, input_protocol, output_protocol)) + { + // TODO: implement transport peek () + } + + // TODO: handle exceptions + THRIFT_TRANSPORT_GET_CLASS (input_transport)->close (input_transport, NULL); + THRIFT_TRANSPORT_GET_CLASS (output_transport)->close (output_transport, + NULL); + } + + // attempt to shutdown + THRIFT_SERVER_TRANSPORT_GET_CLASS (server->server_transport) + ->close (server->server_transport, NULL); +} + +void +thrift_simple_server_stop (ThriftServer *server) +{ + g_return_if_fail (THRIFT_IS_SIMPLE_SERVER (server)); + (THRIFT_SIMPLE_SERVER (server))->running = FALSE; +} + + diff --git a/lib/c_glib/src/server/thrift_simple_server.h b/lib/c_glib/src/server/thrift_simple_server.h new file mode 100644 index 00000000..137f623c --- /dev/null +++ b/lib/c_glib/src/server/thrift_simple_server.h @@ -0,0 +1,53 @@ +#ifndef _THRIFT_SIMPLE_SERVER_H +#define _THRIFT_SIMPLE_SERVER_H + +#include + +#include "server/thrift_server.h" + +/*! \file thrift_simple_server.h + * \brief A simple Thrift server, single-threaded. + */ + +/* type macros */ +#define THRIFT_TYPE_SIMPLE_SERVER (thrift_simple_server_get_type ()) +#define THRIFT_SIMPLE_SERVER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), \ + THRIFT_TYPE_SIMPLE_SERVER, \ + ThriftSimpleServer)) +#define THRIFT_IS_SIMPLE_SERVER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \ + THRIFT_TYPE_SIMPLE_SERVER)) +#define THRIFT_SIMPLE_SERVER_CLASS(c) (G_TYPE_CHECK_CLASS_CAST ((c) \ + THRIFT_TYPE_SIMPLE_SERVER, \ + ThriftSimpleServerClass)) +#define THRIFT_IS_SIMPLE_SERVER_CLASS(c) (G_TYPE_CHECK_CLASS_TYPE ((c), \ + THRIFT_TYPE_SIMPLE_SERVER)) +#define THRIFT_SIMPLE_SERVER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), \ + THRIFT_TYPE_SIMPLE_SERVER, \ + ThriftSimpleServerClass)) + +/** + * Thrift Simple Server instance. + */ +struct _ThriftSimpleServer +{ + ThriftServer parent; + + /* private */ + volatile gboolean running; +}; +typedef struct _ThriftSimpleServer ThriftSimpleServer; + +/** + * Thrift Simple Server class. + */ +struct _ThriftSimpleServerClass +{ + ThriftServerClass parent; +}; +typedef struct _ThriftSimpleServerClass ThriftSimpleServerClass; + +/* used by THRIFT_TYPE_SIMPLE_SERVER */ +GType thrift_simple_server_get_type (void); + +#endif /* _THRIFT_SIMPLE_SERVER_H */ + diff --git a/lib/c_glib/src/thrift.c b/lib/c_glib/src/thrift.c new file mode 100644 index 00000000..18a36aa6 --- /dev/null +++ b/lib/c_glib/src/thrift.c @@ -0,0 +1,13 @@ +#include "thrift.h" + +/** + * GHashTable callback to add keys to a GList. + */ +void +thrift_hash_table_get_keys (gpointer key, gpointer value, gpointer user_data) +{ + THRIFT_UNUSED_VAR (value); + GList **list = (GList **) user_data; + *list = g_list_append (*list, key); +} + diff --git a/lib/c_glib/src/thrift.h b/lib/c_glib/src/thrift.h new file mode 100644 index 00000000..59db6fdd --- /dev/null +++ b/lib/c_glib/src/thrift.h @@ -0,0 +1,18 @@ +#ifndef _THRIFT_H +#define _THRIFT_H + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include + +/* this macro is called to satisfy -Wall hardcore compilation */ +#ifndef THRIFT_UNUSED_VAR +# define THRIFT_UNUSED_VAR(x) ((void) x) +#endif + +void thrift_hash_table_get_keys (gpointer key, gpointer value, + gpointer user_data); + +#endif // #ifndef _THRIFT_THRIFT_H diff --git a/lib/c_glib/src/thrift_application_exception.c b/lib/c_glib/src/thrift_application_exception.c new file mode 100644 index 00000000..fa57a814 --- /dev/null +++ b/lib/c_glib/src/thrift_application_exception.c @@ -0,0 +1,192 @@ +#include "thrift_application_exception.h" +#include "protocol/thrift_protocol.h" + +/* forward declarations */ +void thrift_application_exception_instance_init (ThriftApplicationException *object); +void thrift_application_exception_class_init (ThriftStructClass *cls); +gint32 thrift_application_exception_read (ThriftStruct *object, ThriftProtocol *protocol, GError **error); +gint32 thrift_application_exception_write (ThriftStruct *object, ThriftProtocol *protocol, GError **error); + +GType +thrift_application_exception_get_type (void) +{ + static GType type = 0; + + if (type == 0) + { + static const GTypeInfo type_info = + { + sizeof (ThriftApplicationExceptionClass), + NULL, /* base_init */ + NULL, /* base_finalize */ + (GClassInitFunc) thrift_application_exception_class_init, + NULL, /* class_finalize */ + NULL, /* class_data */ + sizeof (ThriftApplicationException), + 0, /* n_preallocs */ + (GInstanceInitFunc) thrift_application_exception_instance_init, + NULL, /* value_table */ + }; + + type = g_type_register_static (THRIFT_TYPE_STRUCT, + "ThriftApplicationExceptionType", + &type_info, 0); + } + return type; +} + +void +thrift_application_exception_instance_init (ThriftApplicationException *object) +{ + object->type = 0; + object->__isset_type = FALSE; + object->message = NULL; + object->__isset_message = FALSE; +} + +void +thrift_application_exception_class_init (ThriftStructClass *cls) +{ + cls->read = thrift_application_exception_read; + cls->write = thrift_application_exception_write; +} + +gint32 +thrift_application_exception_read (ThriftStruct *object, + ThriftProtocol *protocol, GError **error) +{ + gint32 ret; + gint32 xfer = 0; + gchar *name; + ThriftType ftype; + gint16 fid; + ThriftApplicationException *this = THRIFT_APPLICATION_EXCEPTION (object); + + /* read the struct begin marker */ + if ((ret = thrift_protocol_read_struct_begin (protocol, &name, error)) < 0) + { + if (name) g_free (name); + return -1; + } + xfer += ret; + if (name) g_free (name); + + while (1) + { + if ((ret = thrift_protocol_read_field_begin (protocol, &name, &ftype, + &fid, error)) < 0) + { + if (name) g_free (name); + return -1; + } + xfer += ret; + if (name) g_free (name); + + /* break if we get a STOP field */ + if (ftype == T_STOP) + { + break; + } + + switch (fid) + { + case 1: + if (ftype == T_STRING) + { + if ((ret = thrift_protocol_read_string (protocol, &this->message, + error)) < 0) + return -1; + xfer += ret; + this->__isset_message = TRUE; + } else { + if ((ret = thrift_protocol_skip (protocol, ftype, error)) < 0) + return -1; + xfer += ret; + } + break; + case 2: + if (ftype == T_I32) + { + if ((ret = thrift_protocol_read_i32 (protocol, &this->type, + error)) < 0) + return -1; + xfer += ret; + this->__isset_type = TRUE; + } else { + if ((ret = thrift_protocol_skip (protocol, ftype, error)) < 0) + return -1; + xfer += ret; + } + break; + default: + if ((ret = thrift_protocol_skip (protocol, ftype, error)) < 0) + return -1; + xfer += ret; + break; + } + if ((ret = thrift_protocol_read_field_end (protocol, error)) < 0) + return -1; + xfer += ret; + } + + if ((ret = thrift_protocol_read_struct_end (protocol, error)) < 0) + return -1; + xfer += ret; + + return xfer; +} + +gint32 +thrift_application_exception_write (ThriftStruct *object, + ThriftProtocol *protocol, GError **error) +{ + gint32 ret; + gint32 xfer = 0; + + ThriftApplicationException *this = THRIFT_APPLICATION_EXCEPTION (object); + + if ((ret = thrift_protocol_write_struct_begin (protocol, + "TApplicationException", + error)) < 0) + return -1; + xfer += ret; + if ((ret = thrift_protocol_write_field_begin (protocol, "message", + T_STRING, 1, error)) < 0) + return -1; + xfer += ret; + if ((ret = thrift_protocol_write_string (protocol, this->message, error)) < 0) + return -1; + xfer += ret; + if ((ret = thrift_protocol_write_field_end (protocol, error)) < 0) + return -1; + xfer += ret; + if ((ret = thrift_protocol_write_field_begin (protocol, "type", + T_I32, 2, error)) < 0) + return -1; + xfer += ret; + if ((ret = thrift_protocol_write_i32 (protocol, this->type, error)) < 0) + return -1; + xfer += ret; + if ((ret = thrift_protocol_write_field_end (protocol, error)) < 0) + return -1; + xfer += ret; + if ((ret = thrift_protocol_write_field_stop (protocol, error)) < 0) + return -1; + xfer += ret; + if ((ret = thrift_protocol_write_struct_end (protocol, error)) < 0) + return -1; + xfer += ret; + + return xfer; +} + + +/* GError domain */ +#define THRIFT_APPLICATION_EXCEPTION_ERROR_DOMAIN "thrift-application-exception-error-quark" + +GQuark +thrift_application_exception_error_quark (void) +{ + return g_quark_from_static_string (THRIFT_APPLICATION_EXCEPTION_ERROR_DOMAIN); +} + diff --git a/lib/c_glib/src/thrift_application_exception.h b/lib/c_glib/src/thrift_application_exception.h new file mode 100644 index 00000000..8639df21 --- /dev/null +++ b/lib/c_glib/src/thrift_application_exception.h @@ -0,0 +1,67 @@ +#ifndef _THRIFT_APPLICATION_EXCEPTION_H +#define _THRIFT_APPLICATION_EXCEPTION_H + +#include +#include "thrift_struct.h" + +/*! \file thrift_application_exception.h + * \brief C Implementation of a TApplicationException. + */ + +/* type macros */ +#define THRIFT_TYPE_APPLICATION_EXCEPTION \ + (thrift_application_exception_get_type ()) +#define THRIFT_APPLICATION_EXCEPTION(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST ((obj), \ + THRIFT_TYPE_APPLICATION_EXCEPTION, \ + ThriftApplicationException)) +#define THRIFT_IS_APPLICATION_EXCEPTION(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \ + THRIFT_TYPE_APPLICATION_EXCEPTION)) +#define THRIFT_APPLICATION_EXCEPTION_CLASS(c) \ + (G_TYPE_CHECK_CLASS_CAST ((c), \ + THRIFT_TYPE_APPLICATION_EXCEPTION, \ + ThriftApplicationExceptionClass)) +#define THRIFT_IS_APPLICATION_EXCEPTION_CLASS(c) \ + (G_TYPE_CHECK_CLASS_TYPE ((c), THRIFT_TYPE_APPLICATION_EXCEPTION)) +#define THRIFT_APPLICATION_EXCEPTION_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS ((obj), \ + THRIFT_TYPE_APPLICATION_EXCEPTION, \ + ThriftApplicationExceptionClass)) + +struct _ThriftApplicationException +{ + ThriftStruct parent; + + /* public */ + gint32 type; + gboolean __isset_type; + gchar *message; + gboolean __isset_message; +}; +typedef struct _ThriftApplicationException ThriftApplicationException; + +struct _ThriftApplicationExceptionClass +{ + ThriftStructClass parent; +}; +typedef struct _ThriftApplicationExceptionClass ThriftApplicationExceptionClass; + +GType thrift_application_exception_get_type (void); + +/* gerror codes */ +typedef enum +{ + THRIFT_APPLICATION_EXCEPTION_ERROR_UNKNOWN, + THRIFT_APPLICATION_EXCEPTION_ERROR_UNKNOWN_METHOD, + THRIFT_APPLICATION_EXCEPTION_ERROR_INVALID_MESSAGE_TYPE, + THRIFT_APPLICATION_EXCEPTION_ERROR_WRONG_METHOD_NAME, + THRIFT_APPLICATION_EXCEPTION_ERROR_BAD_SEQUENCE_ID, + THRIFT_APPLICATION_EXCEPTION_ERROR_MISSING_RESULT +} ThriftApplicationExceptionError; + +/* define error domain for GError */ +GQuark thrift_application_exception_error_quark (void); +#define THRIFT_APPLICATION_EXCEPTION_ERROR (thrift_application_exception_error_quark ()) + +#endif /* _THRIFT_APPLICATION_EXCEPTION_H */ diff --git a/lib/c_glib/src/thrift_struct.c b/lib/c_glib/src/thrift_struct.c new file mode 100644 index 00000000..3f73c38c --- /dev/null +++ b/lib/c_glib/src/thrift_struct.c @@ -0,0 +1,55 @@ +#include "thrift_struct.h" + +static void +thrift_struct_class_init (ThriftStructClass *cls) +{ + ThriftStructClass *c = THRIFT_STRUCT_CLASS (cls); + c->read = thrift_struct_read; + c->write = thrift_struct_write; +} + +GType +thrift_struct_get_type (void) +{ + static GType type = 0; + + if (type == 0) + { + static const GTypeInfo type_info = + { + sizeof (ThriftStructClass), + NULL, /* base_init */ + NULL, /* base finalize */ + (GClassInitFunc) thrift_struct_class_init, + NULL, /* class finalize */ + NULL, /* class data */ + sizeof (ThriftStruct), + 0, /* n_preallocs */ + NULL, /* instance_init */ + NULL, /* value_table */ + }; + + type = g_type_register_static (G_TYPE_OBJECT, + "ThriftStructType", + &type_info, G_TYPE_FLAG_ABSTRACT); + } + + return type; +} + +gint32 +thrift_struct_read (ThriftStruct *object, ThriftProtocol *protocol, + GError **error) +{ + g_return_val_if_fail (THRIFT_IS_STRUCT (object), -1); + return THRIFT_STRUCT_GET_CLASS (object)->read (object, protocol, error); +} + +gint32 +thrift_struct_write (ThriftStruct *object, ThriftProtocol *protocol, + GError **error) +{ + g_return_val_if_fail (THRIFT_IS_STRUCT (object), -1); + return THRIFT_STRUCT_GET_CLASS (object)->write (object, protocol, error); +} + diff --git a/lib/c_glib/src/thrift_struct.h b/lib/c_glib/src/thrift_struct.h new file mode 100644 index 00000000..f3e60608 --- /dev/null +++ b/lib/c_glib/src/thrift_struct.h @@ -0,0 +1,50 @@ +#ifndef THRIFT_STRUCT_H +#define THRIFT_STRUCT_H + +#include + +#include "protocol/thrift_protocol.h" + +#define THRIFT_TYPE_STRUCT (thrift_struct_get_type ()) +#define THRIFT_STRUCT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), \ + THRIFT_TYPE_STRUCT, ThriftStruct)) +#define THRIFT_STRUCT_CLASS(c) (G_TYPE_CHECK_CLASS_CAST ((c), \ + THRIFT_TYPE_STRUCT, ThriftStructClass)) +#define THRIFT_IS_STRUCT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \ + THRIFT_TYPE_STRUCT)) +#define THRIFT_IS_STRUCT_CLASS(c) (G_TYPE_CHECK_CLASS_TYPE ((c), \ + THRIFT_TYPE_STRUCT)) +#define THRIFT_STRUCT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), \ + THRIFT_TYPE_STRUCT, \ + ThriftStructClass)) + +/* struct */ +struct _ThriftStruct +{ + GObject parent; + + /* private */ +}; +typedef struct _ThriftStruct ThriftStruct; + +struct _ThriftStructClass +{ + GObjectClass parent; + + /* public */ + gint32 (*read) (ThriftStruct *object, ThriftProtocol *protocol, + GError **error); + gint32 (*write) (ThriftStruct *object, ThriftProtocol *protocol, + GError **error); +}; +typedef struct _ThriftStructClass ThriftStructClass; + +GType thrift_struct_get_type (void); + +gint32 thrift_struct_read (ThriftStruct *object, ThriftProtocol *protocol, + GError **error); + +gint32 thrift_struct_write (ThriftStruct *object, ThriftProtocol *protocol, + GError **error); + +#endif diff --git a/lib/c_glib/src/transport/thrift_buffered_transport.c b/lib/c_glib/src/transport/thrift_buffered_transport.c new file mode 100644 index 00000000..45ea31c9 --- /dev/null +++ b/lib/c_glib/src/transport/thrift_buffered_transport.c @@ -0,0 +1,406 @@ +#include +#include +#include +#include +#include +#include + +#include "thrift.h" +#include "transport/thrift_transport.h" +#include "transport/thrift_buffered_transport.h" + +/* object properties */ +enum _ThriftBufferedTransportProperties +{ + PROP_0, + PROP_THRIFT_BUFFERED_TRANSPORT_TRANSPORT, + PROP_THRIFT_BUFFERED_TRANSPORT_READ_BUFFER_SIZE, + PROP_THRIFT_BUFFERED_TRANSPORT_WRITE_BUFFER_SIZE +}; + +/* forward declarations */ +static void thrift_buffered_transport_instance_init (ThriftBufferedTransport *self); +static void thrift_buffered_transport_class_init (ThriftBufferedTransportClass *cls); + + +gboolean thrift_buffered_transport_is_open (ThriftTransport *transport); +gboolean thrift_buffered_transport_open (ThriftTransport *transport, + GError **error); +gboolean thrift_buffered_transport_close (ThriftTransport *transport, + GError **error); +gint32 thrift_buffered_transport_read (ThriftTransport *transport, gpointer buf, + guint32 len, GError **error); +gboolean thrift_buffered_transport_read_end (ThriftTransport *transport, + GError **error); +gint32 thrift_buffered_transport_read_slow (ThriftTransport *transport, + gpointer buf, guint32 len, + GError **error); +gboolean thrift_buffered_transport_write (ThriftTransport *transport, + const gpointer buf, + const guint32 len, GError **error); +gboolean thrift_buffered_transport_write_end (ThriftTransport *transport, + GError **error); +gint32 thrift_buffered_transport_write_slow (ThriftTransport *transport, + gpointer buf, guint32 len, + GError **error); +gboolean thrift_buffered_transport_flush (ThriftTransport *transport, + GError **error); + +GType +thrift_buffered_transport_get_type (void) +{ + static GType type = 0; + + if (type == 0) + { + static const GTypeInfo info = + { + sizeof (ThriftBufferedTransportClass), + NULL, /* base_init */ + NULL, /* base_finalize */ + (GClassInitFunc) thrift_buffered_transport_class_init, + NULL, /* class finalize */ + NULL, /* class data */ + sizeof (ThriftBufferedTransport), + 0, /* n_preallocs */ + (GInstanceInitFunc) thrift_buffered_transport_instance_init, + NULL, /* value_table */ + }; + + type = g_type_register_static (THRIFT_TYPE_TRANSPORT, + "ThriftBufferedTransport", &info, 0); + } + + return type; +} + +/* initializes the instance */ +static void +thrift_buffered_transport_instance_init (ThriftBufferedTransport *transport) +{ + transport->transport = NULL; + transport->r_buf = g_byte_array_new (); + transport->w_buf = g_byte_array_new (); +} + +/* destructor */ +static void +thrift_buffered_transport_finalize (GObject *object) +{ + ThriftBufferedTransport *transport = THRIFT_BUFFERED_TRANSPORT (object); + + if (transport->r_buf != NULL) + { + g_byte_array_free (transport->r_buf, TRUE); + } + transport->r_buf = NULL; + + if (transport->w_buf != NULL) + { + g_byte_array_free (transport->w_buf, TRUE); + } + transport->w_buf = NULL; +} + +/* property accessor */ +void +thrift_buffered_transport_get_property (GObject *object, guint property_id, + GValue *value, GParamSpec *pspec) +{ + THRIFT_UNUSED_VAR (pspec); + ThriftBufferedTransport *transport = THRIFT_BUFFERED_TRANSPORT (object); + + switch (property_id) + { + case PROP_THRIFT_BUFFERED_TRANSPORT_TRANSPORT: + g_value_set_object (value, transport->transport); + break; + case PROP_THRIFT_BUFFERED_TRANSPORT_READ_BUFFER_SIZE: + g_value_set_uint (value, transport->r_buf_size); + break; + case PROP_THRIFT_BUFFERED_TRANSPORT_WRITE_BUFFER_SIZE: + g_value_set_uint (value, transport->w_buf_size); + break; + } +} + +/* property mutator */ +void +thrift_buffered_transport_set_property (GObject *object, guint property_id, + const GValue *value, GParamSpec *pspec) +{ + THRIFT_UNUSED_VAR (pspec); + ThriftBufferedTransport *transport = THRIFT_BUFFERED_TRANSPORT (object); + + switch (property_id) + { + case PROP_THRIFT_BUFFERED_TRANSPORT_TRANSPORT: + transport->transport = g_value_get_object (value); + break; + case PROP_THRIFT_BUFFERED_TRANSPORT_READ_BUFFER_SIZE: + transport->r_buf_size = g_value_get_uint (value); + break; + case PROP_THRIFT_BUFFERED_TRANSPORT_WRITE_BUFFER_SIZE: + transport->w_buf_size = g_value_get_uint (value); + break; + } +} + +/* initializes the class */ +static void +thrift_buffered_transport_class_init (ThriftBufferedTransportClass *cls) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (cls); + GParamSpec *param_spec = NULL; + + /* setup accessors and mutators */ + gobject_class->get_property = thrift_buffered_transport_get_property; + gobject_class->set_property = thrift_buffered_transport_set_property; + + param_spec = g_param_spec_object ("transport", "transport (construct)", + "Thrift transport", + THRIFT_TYPE_TRANSPORT, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY); + g_object_class_install_property (gobject_class, + PROP_THRIFT_BUFFERED_TRANSPORT_TRANSPORT, + param_spec); + + param_spec = g_param_spec_uint ("r_buf_size", + "read buffer size (construct)", + "Set the read buffer size", + 0, /* min */ + 1048576, /* max, 1024*1024 */ + 512, /* default value */ + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_READWRITE); + g_object_class_install_property (gobject_class, + PROP_THRIFT_BUFFERED_TRANSPORT_READ_BUFFER_SIZE, + param_spec); + + param_spec = g_param_spec_uint ("w_buf_size", + "write buffer size (construct)", + "Set the write buffer size", + 0, /* min */ + 1048576, /* max, 1024*1024 */ + 512, /* default value */ + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_READWRITE); + g_object_class_install_property (gobject_class, + PROP_THRIFT_BUFFERED_TRANSPORT_WRITE_BUFFER_SIZE, + param_spec); + + + ThriftTransportClass *ttc = THRIFT_TRANSPORT_CLASS (cls); + + gobject_class->finalize = thrift_buffered_transport_finalize; + ttc->is_open = thrift_buffered_transport_is_open; + ttc->open = thrift_buffered_transport_open; + ttc->close = thrift_buffered_transport_close; + ttc->read = thrift_buffered_transport_read; + ttc->read_end = thrift_buffered_transport_read_end; + ttc->write = thrift_buffered_transport_write; + ttc->write_end = thrift_buffered_transport_write_end; + ttc->flush = thrift_buffered_transport_flush; +} + +/* implements thrift_transport_is_open */ +gboolean +thrift_buffered_transport_is_open (ThriftTransport *transport) +{ + ThriftBufferedTransport *t = THRIFT_BUFFERED_TRANSPORT (transport); + return THRIFT_TRANSPORT_GET_CLASS (t->transport)->is_open (t->transport); +} + +/* implements thrift_transport_open */ +gboolean +thrift_buffered_transport_open (ThriftTransport *transport, GError **error) +{ + ThriftBufferedTransport *t = THRIFT_BUFFERED_TRANSPORT (transport); + return THRIFT_TRANSPORT_GET_CLASS (t->transport)->open (t->transport, error); +} + +/* implements thrift_transport_close */ +gboolean +thrift_buffered_transport_close (ThriftTransport *transport, GError **error) +{ + ThriftBufferedTransport *t = THRIFT_BUFFERED_TRANSPORT (transport); + return THRIFT_TRANSPORT_GET_CLASS (t->transport)->close (t->transport, error); +} + +/* implements thrift_transport_read */ +gint32 +thrift_buffered_transport_read (ThriftTransport *transport, gpointer buf, + guint32 len, GError **error) +{ + ThriftBufferedTransport *t = THRIFT_BUFFERED_TRANSPORT (transport); + + /* if we have enough buffer data to fulfill the read, just use + * a memcpy */ + if (len <= t->r_buf->len) + { + memcpy (buf, t->r_buf->data, len); + g_byte_array_remove_range (t->r_buf, 0, len); + return len; + } + + return thrift_buffered_transport_read_slow (transport, buf, len, error); +} + +/* implements thrift_transport_read_end + * called when write is complete. nothing to do on our end. */ +gboolean +thrift_buffered_transport_read_end (ThriftTransport *transport, GError **error) +{ + /* satisfy -Wall */ + THRIFT_UNUSED_VAR (transport); + THRIFT_UNUSED_VAR (error); + return TRUE; +} + +/* the actual read is "slow" because it calls the underlying transport */ +gint32 +thrift_buffered_transport_read_slow (ThriftTransport *transport, gpointer buf, + guint32 len, GError **error) +{ + ThriftBufferedTransport *t = THRIFT_BUFFERED_TRANSPORT (transport); + guint32 want = len; + guint32 got = 0; + guchar tmpdata[t->r_buf_size]; + guint32 have = t->r_buf->len; + + // we shouldn't hit this unless the buffer doesn't have enough to read + assert (t->r_buf->len < want); + + // first copy what we have in our buffer. + if (have > 0) + { + memcpy (buf, t->r_buf, t->r_buf->len); + want -= t->r_buf->len; + t->r_buf = g_byte_array_remove_range (t->r_buf, 0, t->r_buf->len); + } + + // if the buffer is still smaller than what we want to read, then just + // read it directly. otherwise, fill the buffer and then give out + // enough to satisfy the read. + if (t->r_buf_size < want) + { + got += THRIFT_TRANSPORT_GET_CLASS (t->transport)->read (t->transport, + tmpdata, + want, + error); + + // copy the data starting from where we left off + memcpy (buf + have, tmpdata, got); + return got + have; + } else { + got += THRIFT_TRANSPORT_GET_CLASS (t->transport)->read (t->transport, + tmpdata, + t->r_buf_size, + error); + t->r_buf = g_byte_array_append (t->r_buf, tmpdata, got); + + // hand over what we have up to what the caller wants + guint32 give = want < t->r_buf->len ? want : t->r_buf->len; + + + memcpy (buf + len - want, t->r_buf->data, give); + t->r_buf = g_byte_array_remove_range (t->r_buf, 0, give); + want -= give; + + return (len - want); + } +} + + +/* implements thrift_transport_write */ +gboolean +thrift_buffered_transport_write (ThriftTransport *transport, + const gpointer buf, + const guint32 len, GError **error) +{ + ThriftBufferedTransport *t = THRIFT_BUFFERED_TRANSPORT (transport); + + /* the length of the current buffer plus the length of the data being read */ + if (t->w_buf->len + len <= t->w_buf_size) + { + t->w_buf = g_byte_array_append (t->w_buf, buf, len); + return len; + } + + return thrift_buffered_transport_write_slow (transport, buf, len, error); +} + +/* implements thrift_transport_write_end + * called when write is complete. nothing to do on our end. */ +gboolean +thrift_buffered_transport_write_end (ThriftTransport *transport, GError **error) +{ + /* satisfy -Wall */ + THRIFT_UNUSED_VAR (transport); + THRIFT_UNUSED_VAR (error); + return TRUE; +} + +gboolean +thrift_buffered_transport_write_slow (ThriftTransport *transport, gpointer buf, + guint32 len, GError **error) +{ + ThriftBufferedTransport *t = THRIFT_BUFFERED_TRANSPORT (transport); + guint32 have_bytes = t->w_buf->len; + guint32 space = t->w_buf_size - t->w_buf->len; + + // we need two syscalls because the buffered data plus the buffer itself + // is too big. + if ((have_bytes + len >= 2*t->w_buf->len) || (have_bytes == 0)) + { + if (have_bytes > 0) + { + THRIFT_TRANSPORT_GET_CLASS (t->transport)->write (t->transport, + t->w_buf->data, + have_bytes, + error); + } + THRIFT_TRANSPORT_GET_CLASS (t->transport)->write (t->transport, + buf, len, error); + if (t->w_buf->len > 0) + { + t->w_buf = g_byte_array_remove_range (t->w_buf, 0, t->w_buf->len); + } + + return TRUE; + } + + t->w_buf = g_byte_array_append (t->w_buf, buf, space); + THRIFT_TRANSPORT_GET_CLASS (t->transport)->write (t->transport, + t->w_buf->data, + t->w_buf->len, + error); + + t->w_buf = g_byte_array_remove_range (t->w_buf, 0, t->w_buf->len); + t->w_buf = g_byte_array_append (t->w_buf, buf+space, len-space); + + return TRUE; +} + +/* implements thrift_transport_flush */ +gboolean +thrift_buffered_transport_flush (ThriftTransport *transport, GError **error) +{ + ThriftBufferedTransport *t = THRIFT_BUFFERED_TRANSPORT (transport); + + if (t->w_buf != NULL && t->w_buf->len > 0) + { + // write the buffer and then empty it + THRIFT_TRANSPORT_GET_CLASS (t->transport)->write (t->transport, + t->w_buf->data, + t->w_buf->len, + error); + t->w_buf = g_byte_array_remove_range (t->w_buf, 0, t->w_buf->len); + } + THRIFT_TRANSPORT_GET_CLASS (t->transport)->flush (t->transport, + error); + + return TRUE; +} + + diff --git a/lib/c_glib/src/transport/thrift_buffered_transport.h b/lib/c_glib/src/transport/thrift_buffered_transport.h new file mode 100644 index 00000000..6b0b17e1 --- /dev/null +++ b/lib/c_glib/src/transport/thrift_buffered_transport.h @@ -0,0 +1,61 @@ +#ifndef _THRIFT_BUFFERED_TRANSPORT_H +#define _THRIFT_BUFFERED_TRANSPORT_H + +#include +#include + +#include "transport/thrift_transport.h" + +/*! \file thrift_buffered_transport.h + * \brief Implementation of a Thrift buffered transport. Subclasses + * the ThriftTransport class. + */ + +/* type macros */ +#define THRIFT_TYPE_BUFFERED_TRANSPORT (thrift_buffered_transport_get_type ()) +#define THRIFT_BUFFERED_TRANSPORT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), \ + THRIFT_TYPE_BUFFERED_TRANSPORT, \ + ThriftBufferedTransport)) +#define THRIFT_IS_BUFFERED_TRANSPORT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \ + THRIFT_TYPE_BUFFERED_TRANSPORT)) +#define THRIFT_BUFFERED_TRANSPORT_CLASS(c) (G_TYPE_CHECK_CLASS_CAST ((c), \ + THRIFT_TYPE_BUFFERED_TRANSPORT, \ + ThriftBufferedTransportClass)) +#define THRIFT_IS_BUFFERED_TRANSPORT_CLASS(c) (G_TYPE_CHECK_CLASS_TYPE ((c), \ + THRIFT_TYPE_BUFFERED_TRANSPORT) +#define THRIFT_BUFFERED_TRANSPORT_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS ((obj), \ + THRIFT_TYPE_BUFFERED_TRANSPORT, \ + ThriftBufferedTransportClass)) + +/*! + * ThriftBufferedTransport instance. + */ +struct _ThriftBufferedTransport +{ + ThriftTransport parent; + + /* protected */ + ThriftTransport *transport; + + /* private */ + GByteArray *r_buf; + GByteArray *w_buf; + guint32 r_buf_size; + guint32 w_buf_size; +}; +typedef struct _ThriftBufferedTransport ThriftBufferedTransport; + +/*! + * ThriftBufferedTransport class. + */ +struct _ThriftBufferedTransportClass +{ + ThriftTransportClass parent; +}; +typedef struct _ThriftBufferedTransportClass ThriftBufferedTransportClass; + +/* used by THRIFT_TYPE_BUFFERED_TRANSPORT */ +GType thrift_buffered_transport_get_type (void); + +#endif diff --git a/lib/c_glib/src/transport/thrift_framed_transport.c b/lib/c_glib/src/transport/thrift_framed_transport.c new file mode 100644 index 00000000..de9cb00d --- /dev/null +++ b/lib/c_glib/src/transport/thrift_framed_transport.c @@ -0,0 +1,397 @@ +#include +#include +#include +#include +#include +#include + +#include "thrift.h" +#include "transport/thrift_transport.h" +#include "transport/thrift_framed_transport.h" + +/* object properties */ +enum _ThriftFramedTransportProperties +{ + PROP_0, + PROP_THRIFT_FRAMED_TRANSPORT_TRANSPORT, + PROP_THRIFT_FRAMED_TRANSPORT_READ_BUFFER_SIZE, + PROP_THRIFT_FRAMED_TRANSPORT_WRITE_BUFFER_SIZE +}; + +/* forward declarations */ +static void thrift_framed_transport_instance_init (ThriftFramedTransport *self); +static void thrift_framed_transport_class_init (ThriftFramedTransportClass *cls); + + +gboolean thrift_framed_transport_is_open (ThriftTransport *transport); +gboolean thrift_framed_transport_open (ThriftTransport *transport, + GError **error); +gboolean thrift_framed_transport_close (ThriftTransport *transport, + GError **error); +gint32 thrift_framed_transport_read (ThriftTransport *transport, gpointer buf, + guint32 len, GError **error); +gboolean thrift_framed_transport_read_end (ThriftTransport *transport, + GError **error); +gint32 thrift_framed_transport_read_slow (ThriftTransport *transport, + gpointer buf, guint32 len, + GError **error); +gboolean thrift_framed_transport_read_frame (ThriftTransport *transport, + GError **error); +gboolean thrift_framed_transport_write (ThriftTransport *transport, + const gpointer buf, + const guint32 len, GError **error); +gboolean thrift_framed_transport_write_end (ThriftTransport *transport, + GError **error); +gint32 thrift_framed_transport_write_slow (ThriftTransport *transport, + gpointer buf, guint32 len, + GError **error); +gboolean thrift_framed_transport_flush (ThriftTransport *transport, + GError **error); + +GType +thrift_framed_transport_get_type (void) +{ + static GType type = 0; + + if (type == 0) + { + static const GTypeInfo info = + { + sizeof (ThriftFramedTransportClass), + NULL, /* base_init */ + NULL, /* base_finalize */ + (GClassInitFunc) thrift_framed_transport_class_init, + NULL, /* class finalize */ + NULL, /* class data */ + sizeof (ThriftFramedTransport), + 0, /* n_preallocs */ + (GInstanceInitFunc) thrift_framed_transport_instance_init, + NULL, /* value_table */ + }; + + type = g_type_register_static (THRIFT_TYPE_TRANSPORT, + "ThriftFramedTransport", &info, 0); + } + + return type; +} + +/* initializes the instance */ +static void +thrift_framed_transport_instance_init (ThriftFramedTransport *transport) +{ + transport->transport = NULL; + transport->r_buf = g_byte_array_new (); + transport->w_buf = g_byte_array_new (); +} + +/* destructor */ +static void +thrift_framed_transport_finalize (GObject *object) +{ + ThriftFramedTransport *transport = THRIFT_FRAMED_TRANSPORT (object); + + if (transport->r_buf != NULL) + { + g_byte_array_free (transport->r_buf, TRUE); + } + transport->r_buf = NULL; + + if (transport->w_buf != NULL) + { + g_byte_array_free (transport->w_buf, TRUE); + } + transport->w_buf = NULL; +} + +/* property accessor */ +void +thrift_framed_transport_get_property (GObject *object, guint property_id, + GValue *value, GParamSpec *pspec) +{ + THRIFT_UNUSED_VAR (pspec); + ThriftFramedTransport *transport = THRIFT_FRAMED_TRANSPORT (object); + + switch (property_id) + { + case PROP_THRIFT_FRAMED_TRANSPORT_TRANSPORT: + g_value_set_object (value, transport->transport); + break; + case PROP_THRIFT_FRAMED_TRANSPORT_READ_BUFFER_SIZE: + g_value_set_uint (value, transport->r_buf_size); + break; + case PROP_THRIFT_FRAMED_TRANSPORT_WRITE_BUFFER_SIZE: + g_value_set_uint (value, transport->w_buf_size); + break; + } +} + +/* property mutator */ +void +thrift_framed_transport_set_property (GObject *object, guint property_id, + const GValue *value, GParamSpec *pspec) +{ + THRIFT_UNUSED_VAR (pspec); + ThriftFramedTransport *transport = THRIFT_FRAMED_TRANSPORT (object); + + switch (property_id) + { + case PROP_THRIFT_FRAMED_TRANSPORT_TRANSPORT: + transport->transport = g_value_get_object (value); + break; + case PROP_THRIFT_FRAMED_TRANSPORT_READ_BUFFER_SIZE: + transport->r_buf_size = g_value_get_uint (value); + break; + case PROP_THRIFT_FRAMED_TRANSPORT_WRITE_BUFFER_SIZE: + transport->w_buf_size = g_value_get_uint (value); + break; + } +} + +/* initializes the class */ +static void +thrift_framed_transport_class_init (ThriftFramedTransportClass *cls) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (cls); + GParamSpec *param_spec = NULL; + + /* setup accessors and mutators */ + gobject_class->get_property = thrift_framed_transport_get_property; + gobject_class->set_property = thrift_framed_transport_set_property; + + param_spec = g_param_spec_object ("transport", "transport (construct)", + "Thrift transport", + THRIFT_TYPE_TRANSPORT, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY); + g_object_class_install_property (gobject_class, + PROP_THRIFT_FRAMED_TRANSPORT_TRANSPORT, + param_spec); + + param_spec = g_param_spec_uint ("r_buf_size", + "read buffer size (construct)", + "Set the read buffer size", + 0, /* min */ + 1048576, /* max, 1024*1024 */ + 512, /* default value */ + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_READWRITE); + g_object_class_install_property (gobject_class, + PROP_THRIFT_FRAMED_TRANSPORT_READ_BUFFER_SIZE, + param_spec); + + param_spec = g_param_spec_uint ("w_buf_size", + "write buffer size (construct)", + "Set the write buffer size", + 0, /* min */ + 1048576, /* max, 1024*1024 */ + 512, /* default value */ + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_READWRITE); + g_object_class_install_property (gobject_class, + PROP_THRIFT_FRAMED_TRANSPORT_WRITE_BUFFER_SIZE, + param_spec); + + + ThriftTransportClass *ttc = THRIFT_TRANSPORT_CLASS (cls); + + gobject_class->finalize = thrift_framed_transport_finalize; + ttc->is_open = thrift_framed_transport_is_open; + ttc->open = thrift_framed_transport_open; + ttc->close = thrift_framed_transport_close; + ttc->read = thrift_framed_transport_read; + ttc->read_end = thrift_framed_transport_read_end; + ttc->write = thrift_framed_transport_write; + ttc->write_end = thrift_framed_transport_write_end; + ttc->flush = thrift_framed_transport_flush; +} + +/* implements thrift_transport_is_open */ +gboolean +thrift_framed_transport_is_open (ThriftTransport *transport) +{ + ThriftFramedTransport *t = THRIFT_FRAMED_TRANSPORT (transport); + return THRIFT_TRANSPORT_GET_CLASS (t->transport)->is_open (t->transport); +} + +/* implements thrift_transport_open */ +gboolean +thrift_framed_transport_open (ThriftTransport *transport, GError **error) +{ + ThriftFramedTransport *t = THRIFT_FRAMED_TRANSPORT (transport); + return THRIFT_TRANSPORT_GET_CLASS (t->transport)->open (t->transport, error); +} + +/* implements thrift_transport_close */ +gboolean +thrift_framed_transport_close (ThriftTransport *transport, GError **error) +{ + ThriftFramedTransport *t = THRIFT_FRAMED_TRANSPORT (transport); + return THRIFT_TRANSPORT_GET_CLASS (t->transport)->close (t->transport, error); +} + +/* implements thrift_transport_read */ +gint32 +thrift_framed_transport_read (ThriftTransport *transport, gpointer buf, + guint32 len, GError **error) +{ + ThriftFramedTransport *t = THRIFT_FRAMED_TRANSPORT (transport); + + /* if we have enough buffer data to fulfill the read, just use + * a memcpy from the buffer */ + if (len <= t->r_buf->len) + { + memcpy (buf, t->r_buf->data, len); + g_byte_array_remove_range (t->r_buf, 0, len); + return len; + } + + return thrift_framed_transport_read_slow (transport, buf, len, error); +} + +/* implements thrift_transport_read_end + * called when read is complete. nothing to do on our end. */ +gboolean +thrift_framed_transport_read_end (ThriftTransport *transport, GError **error) +{ + /* satisfy -Wall */ + THRIFT_UNUSED_VAR (transport); + THRIFT_UNUSED_VAR (error); + return TRUE; +} + +/* the actual read is "slow" because it calls the underlying transport */ +gint32 +thrift_framed_transport_read_slow (ThriftTransport *transport, gpointer buf, + guint32 len, GError **error) +{ + ThriftFramedTransport *t = THRIFT_FRAMED_TRANSPORT (transport); + guint32 want = len; + guint32 have = t->r_buf->len; + + // we shouldn't hit this unless the buffer doesn't have enough to read + assert (t->r_buf->len < want); + + // first copy what we have in our buffer, if there is anything left + if (have > 0) + { + memcpy (buf, t->r_buf, t->r_buf->len); + want -= t->r_buf->len; + t->r_buf = g_byte_array_remove_range (t->r_buf, 0, t->r_buf->len); + } + + // read a frame of input and buffer it + thrift_framed_transport_read_frame (transport, error); + + // hand over what we have up to what the caller wants + guint32 give = want < t->r_buf->len ? want : t->r_buf->len; + + // copy the data into the buffer + memcpy (buf + len - want, t->r_buf->data, give); + t->r_buf = g_byte_array_remove_range (t->r_buf, 0, give); + want -= give; + + return (len - want); +} + +/* reads a frame and puts it into the buffer */ +gboolean +thrift_framed_transport_read_frame (ThriftTransport *transport, + GError **error) +{ + ThriftFramedTransport *t = THRIFT_FRAMED_TRANSPORT (transport); + gint32 sz, bytes; + + /* read the size */ + THRIFT_TRANSPORT_GET_CLASS (t->transport)->read (t->transport, + (guint32 *) &sz, + sizeof (sz), error); + sz = ntohl (sz); + + /* create a buffer to hold the data and read that much data */ + guchar tmpdata[sz]; + bytes = THRIFT_TRANSPORT_GET_CLASS (t->transport)->read (t->transport, + tmpdata, + sz - sizeof (sz), + error); + + /* add the data to the buffer */ + g_byte_array_append (t->r_buf, tmpdata, bytes); + + return TRUE; +} + +/* implements thrift_transport_write */ +gboolean +thrift_framed_transport_write (ThriftTransport *transport, + const gpointer buf, + const guint32 len, GError **error) +{ + ThriftFramedTransport *t = THRIFT_FRAMED_TRANSPORT (transport); + + /* the length of the current buffer plus the length of the data being read */ + if (t->w_buf->len + len <= t->w_buf_size) + { + t->w_buf = g_byte_array_append (t->w_buf, buf, len); + return TRUE; + } + + return thrift_framed_transport_write_slow (transport, buf, len, error); +} + +/* implements thrift_transport_write_end + * called when write is complete. nothing to do on our end. */ +gboolean +thrift_framed_transport_write_end (ThriftTransport *transport, GError **error) +{ + /* satisfy -Wall */ + THRIFT_UNUSED_VAR (transport); + THRIFT_UNUSED_VAR (error); + return TRUE; +} + +gboolean +thrift_framed_transport_write_slow (ThriftTransport *transport, gpointer buf, + guint32 len, GError **error) +{ + THRIFT_UNUSED_VAR (error); + ThriftFramedTransport *t = THRIFT_FRAMED_TRANSPORT (transport); + + // append the data to the buffer and we're done + g_byte_array_append (t->w_buf, buf, len); + + return TRUE; +} + +/* implements thrift_transport_flush */ +gboolean +thrift_framed_transport_flush (ThriftTransport *transport, GError **error) +{ + ThriftFramedTransport *t = THRIFT_FRAMED_TRANSPORT (transport); + gint32 sz_hbo, sz_nbo; + + // get the size of the frame in host and network byte order + sz_hbo = t->w_buf->len + sizeof(sz_nbo); + sz_nbo = (gint32) htonl ((guint32) sz_hbo); + + // copy the size of the frame and then the frame itself + guchar tmpdata[sz_hbo]; + memcpy (tmpdata, (guint8 *) &sz_nbo, sizeof (sz_nbo)); + + if (t->w_buf->len > 0) + { + memcpy (tmpdata + sizeof (sz_nbo), t->w_buf->data, t->w_buf->len); + t->w_buf = g_byte_array_remove_range (t->w_buf, 0, t->w_buf->len); + } + + // write the buffer and then empty it + THRIFT_TRANSPORT_GET_CLASS (t->transport)->write (t->transport, + tmpdata, sz_hbo, + error); + + THRIFT_TRANSPORT_GET_CLASS (t->transport)->flush (t->transport, + error); + + return TRUE; +} + + diff --git a/lib/c_glib/src/transport/thrift_framed_transport.h b/lib/c_glib/src/transport/thrift_framed_transport.h new file mode 100644 index 00000000..d859b97a --- /dev/null +++ b/lib/c_glib/src/transport/thrift_framed_transport.h @@ -0,0 +1,61 @@ +#ifndef _THRIFT_FRAMED_TRANSPORT_H +#define _THRIFT_FRAMED_TRANSPORT_H + +#include +#include + +#include "transport/thrift_transport.h" + +/*! \file thrift_framed_transport.h + * \brief Implementation of a Thrift framed transport. Subclasses + * the ThriftTransport class. + */ + +/* type macros */ +#define THRIFT_TYPE_FRAMED_TRANSPORT (thrift_framed_transport_get_type ()) +#define THRIFT_FRAMED_TRANSPORT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), \ + THRIFT_TYPE_FRAMED_TRANSPORT, \ + ThriftFramedTransport)) +#define THRIFT_IS_FRAMED_TRANSPORT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \ + THRIFT_TYPE_FRAMED_TRANSPORT)) +#define THRIFT_FRAMED_TRANSPORT_CLASS(c) (G_TYPE_CHECK_CLASS_CAST ((c), \ + THRIFT_TYPE_FRAMED_TRANSPORT, \ + ThriftFramedTransportClass)) +#define THRIFT_IS_FRAMED_TRANSPORT_CLASS(c) (G_TYPE_CHECK_CLASS_TYPE ((c), \ + THRIFT_TYPE_FRAMED_TRANSPORT) +#define THRIFT_FRAMED_TRANSPORT_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS ((obj), \ + THRIFT_TYPE_FRAMED_TRANSPORT, \ + ThriftFramedTransportClass)) + +/*! + * ThriftFramedTransport instance. + */ +struct _ThriftFramedTransport +{ + ThriftTransport parent; + + /* protected */ + ThriftTransport *transport; + + /* private */ + GByteArray *r_buf; + GByteArray *w_buf; + guint32 r_buf_size; + guint32 w_buf_size; +}; +typedef struct _ThriftFramedTransport ThriftFramedTransport; + +/*! + * ThriftFramedTransport class. + */ +struct _ThriftFramedTransportClass +{ + ThriftTransportClass parent; +}; +typedef struct _ThriftFramedTransportClass ThriftFramedTransportClass; + +/* used by THRIFT_TYPE_FRAMED_TRANSPORT */ +GType thrift_framed_transport_get_type (void); + +#endif diff --git a/lib/c_glib/src/transport/thrift_memory_buffer.c b/lib/c_glib/src/transport/thrift_memory_buffer.c new file mode 100644 index 00000000..34a4dfa2 --- /dev/null +++ b/lib/c_glib/src/transport/thrift_memory_buffer.c @@ -0,0 +1,260 @@ +#include +#include +#include +#include +#include +#include + +#include "thrift.h" +#include "transport/thrift_transport.h" +#include "transport/thrift_memory_buffer.h" + +/* object properties */ +enum _ThriftMemoryBufferProperties +{ + PROP_0, + PROP_THRIFT_MEMORY_BUFFER_BUFFER_SIZE, +}; + +/* forward declarations */ +static void thrift_memory_buffer_instance_init (ThriftMemoryBuffer *self); +static void thrift_memory_buffer_class_init (ThriftMemoryBufferClass *cls); + + +gboolean thrift_memory_buffer_is_open (ThriftTransport *transport); +gboolean thrift_memory_buffer_open (ThriftTransport *transport, + GError **error); +gboolean thrift_memory_buffer_close (ThriftTransport *transport, + GError **error); +gint32 thrift_memory_buffer_read (ThriftTransport *transport, gpointer buf, + guint32 len, GError **error); +gboolean thrift_memory_buffer_read_end (ThriftTransport *transport, + GError **error); +gboolean thrift_memory_buffer_write (ThriftTransport *transport, + const gpointer buf, + const guint32 len, GError **error); +gboolean thrift_memory_buffer_write_end (ThriftTransport *transport, + GError **error); +gboolean thrift_memory_buffer_flush (ThriftTransport *transport, + GError **error); + +GType +thrift_memory_buffer_get_type (void) +{ + static GType type = 0; + + if (type == 0) + { + static const GTypeInfo info = + { + sizeof (ThriftMemoryBufferClass), + NULL, /* base_init */ + NULL, /* base_finalize */ + (GClassInitFunc) thrift_memory_buffer_class_init, + NULL, /* class finalize */ + NULL, /* class data */ + sizeof (ThriftMemoryBuffer), + 0, /* n_preallocs */ + (GInstanceInitFunc) thrift_memory_buffer_instance_init, + NULL, /* value_table */ + }; + + type = g_type_register_static (THRIFT_TYPE_TRANSPORT, + "ThriftMemoryBuffer", &info, 0); + } + + return type; +} + +/* initializes the instance */ +static void +thrift_memory_buffer_instance_init (ThriftMemoryBuffer *transport) +{ + transport->buf = g_byte_array_new (); +} + +/* destructor */ +static void +thrift_memory_buffer_finalize (GObject *object) +{ + ThriftMemoryBuffer *transport = THRIFT_MEMORY_BUFFER (object); + + if (transport->buf != NULL) + { + g_byte_array_free (transport->buf, TRUE); + } + transport->buf = NULL; +} + +/* property accessor */ +void +thrift_memory_buffer_get_property (GObject *object, guint property_id, + GValue *value, GParamSpec *pspec) +{ + THRIFT_UNUSED_VAR (pspec); + ThriftMemoryBuffer *transport = THRIFT_MEMORY_BUFFER (object); + + switch (property_id) + { + case PROP_THRIFT_MEMORY_BUFFER_BUFFER_SIZE: + g_value_set_uint (value, transport->buf_size); + break; + } +} + +/* property mutator */ +void +thrift_memory_buffer_set_property (GObject *object, guint property_id, + const GValue *value, GParamSpec *pspec) +{ + THRIFT_UNUSED_VAR (pspec); + ThriftMemoryBuffer *transport = THRIFT_MEMORY_BUFFER (object); + + switch (property_id) + { + case PROP_THRIFT_MEMORY_BUFFER_BUFFER_SIZE: + transport->buf_size = g_value_get_uint (value); + break; + } +} + +/* initializes the class */ +static void +thrift_memory_buffer_class_init (ThriftMemoryBufferClass *cls) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (cls); + GParamSpec *param_spec = NULL; + + /* setup accessors and mutators */ + gobject_class->get_property = thrift_memory_buffer_get_property; + gobject_class->set_property = thrift_memory_buffer_set_property; + + param_spec = g_param_spec_uint ("buf_size", + "buffer size (construct)", + "Set the read buffer size", + 0, /* min */ + 1048576, /* max, 1024*1024 */ + 512, /* default value */ + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_READWRITE); + g_object_class_install_property (gobject_class, + PROP_THRIFT_MEMORY_BUFFER_BUFFER_SIZE, + param_spec); + + ThriftTransportClass *ttc = THRIFT_TRANSPORT_CLASS (cls); + + gobject_class->finalize = thrift_memory_buffer_finalize; + ttc->is_open = thrift_memory_buffer_is_open; + ttc->open = thrift_memory_buffer_open; + ttc->close = thrift_memory_buffer_close; + ttc->read = thrift_memory_buffer_read; + ttc->read_end = thrift_memory_buffer_read_end; + ttc->write = thrift_memory_buffer_write; + ttc->write_end = thrift_memory_buffer_write_end; + ttc->flush = thrift_memory_buffer_flush; +} + +/* implements thrift_transport_is_open */ +gboolean +thrift_memory_buffer_is_open (ThriftTransport *transport) +{ + THRIFT_UNUSED_VAR (transport); + return TRUE; +} + +/* implements thrift_transport_open */ +gboolean +thrift_memory_buffer_open (ThriftTransport *transport, GError **error) +{ + THRIFT_UNUSED_VAR (transport); + THRIFT_UNUSED_VAR (error); + return TRUE; +} + +/* implements thrift_transport_close */ +gboolean +thrift_memory_buffer_close (ThriftTransport *transport, GError **error) +{ + THRIFT_UNUSED_VAR (transport); + THRIFT_UNUSED_VAR (error); + return TRUE; +} + +/* implements thrift_transport_read */ +gint32 +thrift_memory_buffer_read (ThriftTransport *transport, gpointer buf, + guint32 len, GError **error) +{ + THRIFT_UNUSED_VAR (error); + ThriftMemoryBuffer *t = THRIFT_MEMORY_BUFFER (transport); + guint32 give = len; + + /* if the requested bytes are more than what we have available, + * just give all that we have the buffer */ + if (t->buf->len < len) + { + give = t->buf->len; + } + + memcpy (buf, t->buf->data, give); + g_byte_array_remove_range (t->buf, 0, give); + + return give; +} + +/* implements thrift_transport_read_end + * called when read is complete. nothing to do on our end. */ +gboolean +thrift_memory_buffer_read_end (ThriftTransport *transport, GError **error) +{ + /* satisfy -Wall */ + THRIFT_UNUSED_VAR (transport); + THRIFT_UNUSED_VAR (error); + return TRUE; +} + +/* implements thrift_transport_write */ +gboolean +thrift_memory_buffer_write (ThriftTransport *transport, + const gpointer buf, + const guint32 len, GError **error) +{ + THRIFT_UNUSED_VAR (error); + + ThriftMemoryBuffer *t = THRIFT_MEMORY_BUFFER (transport); + + /* return an exception if the buffer doesn't have enough space. */ + if (len > t->buf_size - t->buf->len) + { + g_set_error (error, THRIFT_TRANSPORT_ERROR, THRIFT_TRANSPORT_ERROR_SEND, + "unable to write %d bytes to buffer of length %d", + len, t->buf_size); + return FALSE; + } else { + t->buf = g_byte_array_append (t->buf, buf, len); + return TRUE; + } +} + +/* implements thrift_transport_write_end + * called when write is complete. nothing to do on our end. */ +gboolean +thrift_memory_buffer_write_end (ThriftTransport *transport, GError **error) +{ + /* satisfy -Wall */ + THRIFT_UNUSED_VAR (transport); + THRIFT_UNUSED_VAR (error); + return TRUE; +} + +/* implements thrift_transport_flush */ +gboolean +thrift_memory_buffer_flush (ThriftTransport *transport, GError **error) +{ + THRIFT_UNUSED_VAR (transport); + THRIFT_UNUSED_VAR (error); + + return TRUE; +} + + diff --git a/lib/c_glib/src/transport/thrift_memory_buffer.h b/lib/c_glib/src/transport/thrift_memory_buffer.h new file mode 100644 index 00000000..7529d1fc --- /dev/null +++ b/lib/c_glib/src/transport/thrift_memory_buffer.h @@ -0,0 +1,55 @@ +#ifndef _THRIFT_MEMORY_BUFFER_H +#define _THRIFT_MEMORY_BUFFER_H + +#include +#include + +#include "transport/thrift_transport.h" + +/*! \file thrift_memory_buffer.h + * \brief Implementation of a Thrift memory buffer transport. + */ + +/* type macros */ +#define THRIFT_TYPE_MEMORY_BUFFER (thrift_memory_buffer_get_type ()) +#define THRIFT_MEMORY_BUFFER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), \ + THRIFT_TYPE_MEMORY_BUFFER, \ + ThriftMemoryBuffer)) +#define THRIFT_IS_MEMORY_BUFFER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \ + THRIFT_TYPE_MEMORY_BUFFER)) +#define THRIFT_MEMORY_BUFFER_CLASS(c) (G_TYPE_CHECK_CLASS_CAST ((c), \ + THRIFT_TYPE_MEMORY_BUFFER, \ + ThriftMemoryBufferClass)) +#define THRIFT_IS_MEMORY_BUFFER_CLASS(c) (G_TYPE_CHECK_CLASS_TYPE ((c), \ + THRIFT_TYPE_MEMORY_BUFFER) +#define THRIFT_MEMORY_BUFFER_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS ((obj), \ + THRIFT_TYPE_MEMORY_BUFFER, \ + ThriftMemoryBufferClass)) + +/*! + * ThriftMemoryBuffer instance. + */ +struct _ThriftMemoryBuffer +{ + ThriftTransport parent; + + /* private */ + GByteArray *buf; + guint32 buf_size; +}; +typedef struct _ThriftMemoryBuffer ThriftMemoryBuffer; + +/*! + * ThriftMemoryBuffer class. + */ +struct _ThriftMemoryBufferClass +{ + ThriftTransportClass parent; +}; +typedef struct _ThriftMemoryBufferClass ThriftMemoryBufferClass; + +/* used by THRIFT_TYPE_MEMORY_BUFFER */ +GType thrift_memory_buffer_get_type (void); + +#endif diff --git a/lib/c_glib/src/transport/thrift_server_socket.c b/lib/c_glib/src/transport/thrift_server_socket.c new file mode 100644 index 00000000..fe11648f --- /dev/null +++ b/lib/c_glib/src/transport/thrift_server_socket.c @@ -0,0 +1,272 @@ +#include +#include +#include +#include +#include + +#include "thrift.h" +#include "transport/thrift_socket.h" +#include "transport/thrift_transport.h" +#include "transport/thrift_server_transport.h" +#include "transport/thrift_server_socket.h" + +/* object properties */ +enum _ThriftServerSocketProperties +{ + PROP_0, + PROP_THRIFT_SERVER_SOCKET_PORT, + PROP_THRIFT_SERVER_SOCKET_BACKLOG +}; + +/* define the GError domain string */ +#define THRIFT_SERVER_SOCKET_ERROR_DOMAIN "thrift-server-socket-error-quark" + +/* for errors coming from socket() and connect() */ +extern int errno; + +/* forward declarations */ +static void thrift_server_socket_instance_init (ThriftServerSocket *self); +static void thrift_server_socket_class_init (ThriftServerSocketClass *cls); + +gboolean thrift_server_socket_listen (ThriftServerTransport *transport, + GError **error); +ThriftTransport *thrift_server_socket_accept (ThriftServerTransport *transport, + GError **error); +gboolean thrift_server_socket_close (ThriftServerTransport *transport, + GError **error); + +GType +thrift_server_socket_get_type (void) +{ + static GType type = 0; + + if (type == 0) + { + static const GTypeInfo info = + { + sizeof (ThriftServerSocketClass), + NULL, /* base_init */ + NULL, /* base_finalize */ + (GClassInitFunc) thrift_server_socket_class_init, + NULL, /* class finalize */ + NULL, /* class data */ + sizeof (ThriftServerSocket), + 0, /* n_preallocs */ + (GInstanceInitFunc) thrift_server_socket_instance_init, + NULL, /* value_table */ + }; + + type = g_type_register_static (THRIFT_TYPE_SERVER_TRANSPORT, + "ThriftServerSocket", &info, 0); + } + + return type; +} + +/* define the GError domain for this implementation */ +GQuark +thrift_server_socket_error_quark (void) +{ + return g_quark_from_static_string(THRIFT_SERVER_SOCKET_ERROR_DOMAIN); +} + +/* initializes the instance */ +static void +thrift_server_socket_instance_init (ThriftServerSocket *socket) +{ + socket->sd = 0; +} + +/* destructor */ +static void +thrift_server_socket_finalize (GObject *object) +{ + ThriftServerSocket *socket = THRIFT_SERVER_SOCKET (object); + + if (socket->sd != 0) + { + close (socket->sd); + } + socket->sd = 0; +} + +/* property accessor */ +void +thrift_server_socket_get_property (GObject *object, guint property_id, + GValue *value, GParamSpec *pspec) +{ + ThriftServerSocket *socket = THRIFT_SERVER_SOCKET (object); + + switch (property_id) + { + case PROP_THRIFT_SERVER_SOCKET_PORT: + g_value_set_uint (value, socket->port); + break; + case PROP_THRIFT_SERVER_SOCKET_BACKLOG: + g_value_set_uint (value, socket->backlog); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +/* property mutator */ +void +thrift_server_socket_set_property (GObject *object, guint property_id, + const GValue *value, GParamSpec *pspec) +{ + ThriftServerSocket *socket = THRIFT_SERVER_SOCKET (object); + + switch (property_id) + { + case PROP_THRIFT_SERVER_SOCKET_PORT: + socket->port = g_value_get_uint (value); + break; + case PROP_THRIFT_SERVER_SOCKET_BACKLOG: + socket->backlog = g_value_get_uint (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +/* initializes the class */ +static void +thrift_server_socket_class_init (ThriftServerSocketClass *cls) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (cls); + GParamSpec *param_spec = NULL; + + /* setup accessors and mutators */ + gobject_class->get_property = thrift_server_socket_get_property; + gobject_class->set_property = thrift_server_socket_set_property; + + param_spec = g_param_spec_uint ("port", + "port (construct)", + "Set the port to listen to", + 0, /* min */ + 65534, /* max */ + 9090, /* default by convention */ + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_READWRITE); + g_object_class_install_property (gobject_class, + PROP_THRIFT_SERVER_SOCKET_PORT, + param_spec); + + param_spec = g_param_spec_uint ("backlog", + "backlog (construct)", + "Set the accept backlog", + 0, /* max */ + 65534, /* max */ + 1024, /* default */ + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_READWRITE); + g_object_class_install_property (gobject_class, + PROP_THRIFT_SERVER_SOCKET_BACKLOG, + param_spec); + + gobject_class->finalize = thrift_server_socket_finalize; + + ThriftServerTransportClass *tstc = THRIFT_SERVER_TRANSPORT_CLASS (cls); + tstc->listen = thrift_server_socket_listen; + tstc->accept = thrift_server_socket_accept; + tstc->close = thrift_server_socket_close; +} + +gboolean +thrift_server_socket_listen (ThriftServerTransport *transport, GError **error) +{ + int enabled = 1; /* for setsockopt() */ + struct sockaddr_in pin; + ThriftServerSocket *tsocket = THRIFT_SERVER_SOCKET (transport); + + /* create a address structure */ + memset (&pin, 0, sizeof(pin)); + pin.sin_family = AF_INET; + pin.sin_addr.s_addr = INADDR_ANY; + pin.sin_port = htons(tsocket->port); + + /* create a socket */ + if ((tsocket->sd = socket (AF_INET, SOCK_STREAM, 0)) == -1) + { + g_set_error (error, THRIFT_SERVER_SOCKET_ERROR, + THRIFT_SERVER_SOCKET_ERROR_SOCKET, + "failed to create socket - %s", strerror (errno)); + return FALSE; + } + + if (setsockopt(tsocket->sd, SOL_SOCKET, SO_REUSEADDR, &enabled, + sizeof(enabled)) == -1) + { + g_set_error (error, THRIFT_SERVER_SOCKET_ERROR, + THRIFT_SERVER_SOCKET_ERROR_SETSOCKOPT, + "unable to set SO_REUSEADDR - %s", strerror(errno)); + return FALSE; + } + + /* bind to the socket */ + if (bind(tsocket->sd, (struct sockaddr *) &pin, sizeof(pin)) == -1) + { + g_set_error (error, THRIFT_SERVER_SOCKET_ERROR, + THRIFT_SERVER_SOCKET_ERROR_BIND, + "failed to bind to port %d - %s", + tsocket->port, strerror(errno)); + return FALSE; + } + + if (listen(tsocket->sd, tsocket->backlog) == -1) + { + g_set_error (error, THRIFT_SERVER_SOCKET_ERROR, + THRIFT_SERVER_SOCKET_ERROR_LISTEN, + "failed to listen to port %d - %s", + tsocket->port, strerror(errno)); + return FALSE; + } + + return TRUE; +} + +ThriftTransport * +thrift_server_socket_accept (ThriftServerTransport *transport, GError **error) +{ + int sd = 0; + guint addrlen = 0; + struct sockaddr_in address; + ThriftSocket *socket = NULL; + + ThriftServerSocket *tsocket = THRIFT_SERVER_SOCKET (transport); + + if ((sd = accept(tsocket->sd, (struct sockaddr *) &address, &addrlen)) == -1) + { + g_set_error (error, THRIFT_SERVER_SOCKET_ERROR, + THRIFT_SERVER_SOCKET_ERROR_ACCEPT, + "failed to accept connection - %s", + strerror(errno)); + return FALSE; + } + + socket = g_object_new (THRIFT_TYPE_SOCKET, NULL); + socket->sd = sd; + + return THRIFT_TRANSPORT(socket); +} + +gboolean +thrift_server_socket_close (ThriftServerTransport *transport, GError **error) +{ + ThriftServerSocket *tsocket = THRIFT_SERVER_SOCKET (transport); + + if (close (tsocket->sd) == -1) + { + g_set_error (error, THRIFT_SERVER_SOCKET_ERROR, + THRIFT_SERVER_SOCKET_ERROR_CLOSE, + "unable to close socket - %s", strerror(errno)); + return FALSE; + } + tsocket->sd = 0; + + return TRUE; +} + diff --git a/lib/c_glib/src/transport/thrift_server_socket.h b/lib/c_glib/src/transport/thrift_server_socket.h new file mode 100644 index 00000000..c56bd846 --- /dev/null +++ b/lib/c_glib/src/transport/thrift_server_socket.h @@ -0,0 +1,73 @@ +#ifndef _THRIFT_SERVER_SOCKET_H +#define _THRIFT_SERVER_SOCKET_H + +#include + +#include "thrift_server_transport.h" + +/*! \file thrift_server_socket.h + * \brief Socket implementation of a Thrift server transport. Implements the + * ThriftServerTransport class. + */ + +/* type macros */ +#define THRIFT_TYPE_SERVER_SOCKET (thrift_server_socket_get_type ()) +#define THRIFT_SERVER_SOCKET(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), \ + THRIFT_TYPE_SERVER_SOCKET, \ + ThriftServerSocket)) +#define THRIFT_IS_SERVER_SOCKET(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \ + THRIFT_TYPE_SERVER_SOCKET)) +#define THRIFT_SERVER_SOCKET_CLASS(c) (G_TYPE_CHECK_CLASS_CAST ((c), \ + THRIFT_TYPE_SERVER_SOCKET, \ + ThriftServerSocketClass)) +#define THRIFT_IS_SERVER_SOCKET_CLASS(c) (G_TYPE_CHECK_CLASS_TYPE ((c), \ + THRIFT_TYPE_SERVER_SOCKET)) +#define THRIFT_SERVER_SOCKET_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), \ + THRIFT_TYPE_SERVER_SOCKET, \ + ThriftServerSocketClass)) + +/*! + * Thrift ServerSocket instance. + */ +struct _ThriftServerSocket +{ + ThriftServerTransport parent; + + /* private */ + gshort port; + gshort backlog; + int sd; + guint8 *buf; + guint32 buf_size; + guint32 buf_len; +}; +typedef struct _ThriftServerSocket ThriftServerSocket; + +/*! + * Thrift ServerSocket class. + */ +struct _ThriftServerSocketClass +{ + ThriftServerTransportClass parent; +}; +typedef struct _ThriftServerSocketClass ThriftServerSocketClass; + +/* used by THRIFT_TYPE_SERVER_SOCKET */ +GType thrift_server_socket_get_type (void); + +/* define error/exception types */ +typedef enum +{ + THRIFT_SERVER_SOCKET_ERROR_SOCKET, + THRIFT_SERVER_SOCKET_ERROR_SETSOCKOPT, + THRIFT_SERVER_SOCKET_ERROR_BIND, + THRIFT_SERVER_SOCKET_ERROR_LISTEN, + THRIFT_SERVER_SOCKET_ERROR_ACCEPT, + THRIFT_SERVER_SOCKET_ERROR_CLOSE +} ThriftServerSocketError; + +/* define a error domain for GError to use */ +GQuark thrift_server_socket_error_quark (void); +#define THRIFT_SERVER_SOCKET_ERROR (thrift_server_socket_error_quark ()) + +#endif diff --git a/lib/c_glib/src/transport/thrift_server_transport.c b/lib/c_glib/src/transport/thrift_server_transport.c new file mode 100644 index 00000000..89cbaf34 --- /dev/null +++ b/lib/c_glib/src/transport/thrift_server_transport.c @@ -0,0 +1,70 @@ +#include "thrift.h" +#include "transport/thrift_transport.h" +#include "transport/thrift_server_transport.h" + +/* forward declarations */ +static void thrift_server_transport_class_init (ThriftServerTransportClass *c); + +/* define ThriftTransportClass type */ +GType +thrift_server_transport_get_type (void) +{ + static GType type = 0; + + if (type == 0) + { + static const GTypeInfo info = + { + sizeof (ThriftServerTransportClass), + NULL, /* base_init */ + NULL, /* base_finalize */ + (GClassInitFunc) thrift_server_transport_class_init, + NULL, /* class_finalize */ + NULL, /* class_data */ + sizeof (ThriftServerTransport), + 0, /* n_preallocs */ + NULL, /* instance_init */ + NULL, /* value_table */ + }; + + type = g_type_register_static (G_TYPE_OBJECT, + "ThriftServerTransport", + &info, G_TYPE_FLAG_ABSTRACT); + } + + return type; +} + +/* base initializer for the server transport interface */ +static void +thrift_server_transport_class_init (ThriftServerTransportClass *c) +{ + c->listen = thrift_server_transport_listen; + c->accept = thrift_server_transport_accept; + c->close = thrift_server_transport_close; +} + +gboolean +thrift_server_transport_listen (ThriftServerTransport *transport, + GError **error) +{ + return THRIFT_SERVER_TRANSPORT_GET_CLASS (transport)->listen (transport, + error); +} + +ThriftTransport * +thrift_server_transport_accept (ThriftServerTransport *transport, + GError **error) +{ + return THRIFT_SERVER_TRANSPORT_GET_CLASS (transport)->accept (transport, + error); +} + +gboolean +thrift_server_transport_close (ThriftServerTransport *transport, GError **error) +{ + return THRIFT_SERVER_TRANSPORT_GET_CLASS (transport)->close (transport, + error); +} + + diff --git a/lib/c_glib/src/transport/thrift_server_transport.h b/lib/c_glib/src/transport/thrift_server_transport.h new file mode 100644 index 00000000..a74fca04 --- /dev/null +++ b/lib/c_glib/src/transport/thrift_server_transport.h @@ -0,0 +1,72 @@ +#ifndef _THRIFT_SERVER_TRANSPORT_H +#define _THRIFT_SERVER_TRANSPORT_H + +#include + +#include "thrift_transport.h" + +/*! \file thrift_server_transport.h + * \brief Abstract class for Thrift server transports. + */ + +/* type macros */ +#define THRIFT_TYPE_SERVER_TRANSPORT (thrift_server_transport_get_type ()) +#define THRIFT_SERVER_TRANSPORT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), \ + THRIFT_TYPE_SERVER_TRANSPORT, \ + ThriftServerTransport)) +#define THRIFT_IS_SERVER_TRANSPORT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \ + THRIFT_TYPE_SERVER_TRANSPORT)) +#define THRIFT_SERVER_TRANSPORT_CLASS(c) (G_TYPE_CHECK_CLASS_CAST ((c), \ + THRIFT_TYPE_SERVER_TRANSPORT, \ + ThriftServerTransportClass)) +#define THRIFT_IS_SERVER_TRANSPORT_CLASS(c) (G_TYPE_CHECK_CLASS_TYPE ((c), \ + THRIFT_TYPE_SERVER_TRANSPORT)) +#define THRIFT_SERVER_TRANSPORT_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS ((obj), THRIFT_TYPE_SERVER_TRANSPORT, \ + ThriftServerTransportClass)) + +struct _ThriftServerTransport +{ + GObject parent; +}; +typedef struct _ThriftServerTransport ThriftServerTransport; + +/*! + * Thrift Transport class + */ +struct _ThriftServerTransportClass +{ + GObjectClass parent; + + /* vtable */ + gboolean (*listen) (ThriftServerTransport *transport, GError **error); + ThriftTransport *(*accept) (ThriftServerTransport *transport, GError **error); + gboolean (*close) (ThriftServerTransport *transport, GError **error); +}; +typedef struct _ThriftServerTransportClass ThriftServerTransportClass; + +/* used by THRIFT_TYPE_SERVER_TRANSPORT */ +GType thrift_server_transport_get_type (void); + +/*! + * Listen for new connections. + * \public \memberof ThriftServerTransportClass + */ +gboolean thrift_server_transport_listen (ThriftServerTransport *transport, + GError **error); + +/*! + * Accept a connection. + * \public \memberof ThriftServerTransportClass + */ +ThriftTransport *thrift_server_transport_accept + (ThriftServerTransport *transport, GError **error); + +/*! + * Close the transport. + * \public \memberof ThriftServerTransportClass + */ +gboolean thrift_server_transport_close (ThriftServerTransport *transport, + GError **error); + +#endif /* _THRIFT_SERVER_TRANSPORT_H */ diff --git a/lib/c_glib/src/transport/thrift_socket.c b/lib/c_glib/src/transport/thrift_socket.c new file mode 100644 index 00000000..951ae906 --- /dev/null +++ b/lib/c_glib/src/transport/thrift_socket.c @@ -0,0 +1,334 @@ +#include +#include +#include +#include +#include + +#include "thrift.h" +#include "transport/thrift_transport.h" +#include "transport/thrift_socket.h" + +/* object properties */ +enum _ThriftSocketProperties +{ + PROP_0, + PROP_THRIFT_SOCKET_HOSTNAME, + PROP_THRIFT_SOCKET_PORT +}; + +/* for errors coming from socket() and connect() */ +extern int errno; + +/* forward declarations */ +static void thrift_socket_instance_init (ThriftSocket *self); +static void thrift_socket_class_init (ThriftSocketClass *cls); + +gboolean thrift_socket_is_open (ThriftTransport *transport); +gboolean thrift_socket_open (ThriftTransport *transport, GError **error); +gboolean thrift_socket_close (ThriftTransport *transport, GError **error); +gint32 thrift_socket_read (ThriftTransport *transport, gpointer buf, + guint32 len, GError **error); +gboolean thrift_socket_read_end (ThriftTransport *transport, GError **error); +gboolean thrift_socket_write (ThriftTransport *transport, const gpointer buf, + const guint32 len, GError **error); +gboolean thrift_socket_write_end (ThriftTransport *transport, GError **error); +gboolean thrift_socket_flush (ThriftTransport *transport, GError **error); + +GType +thrift_socket_get_type (void) +{ + static GType type = 0; + + if (type == 0) + { + static const GTypeInfo info = + { + sizeof (ThriftSocketClass), + NULL, /* base_init */ + NULL, /* base_finalize */ + (GClassInitFunc) thrift_socket_class_init, + NULL, /* class finalize */ + NULL, /* class data */ + sizeof (ThriftSocket), + 0, /* n_preallocs */ + (GInstanceInitFunc) thrift_socket_instance_init, + NULL, /* value_table */ + }; + + type = g_type_register_static (THRIFT_TYPE_TRANSPORT, + "ThriftSocket", &info, 0); + } + + return type; +} + +/* initializes the instance */ +static void +thrift_socket_instance_init (ThriftSocket *socket) +{ + socket->sd = 0; +} + +/* destructor */ +static void +thrift_socket_finalize (GObject *object) +{ + ThriftSocket *socket = THRIFT_SOCKET (object); + + if (socket->hostname != NULL) + { + g_free (socket->hostname); + } + socket->hostname = NULL; + + if (socket->sd != 0) + { + close (socket->sd); + } + socket->sd = 0; +} + +/* property accessor */ +void +thrift_socket_get_property (GObject *object, guint property_id, + GValue *value, GParamSpec *pspec) +{ + THRIFT_UNUSED_VAR (pspec); + ThriftSocket *socket = THRIFT_SOCKET (object); + + switch (property_id) + { + case PROP_THRIFT_SOCKET_HOSTNAME: + g_value_set_string (value, socket->hostname); + break; + case PROP_THRIFT_SOCKET_PORT: + g_value_set_uint (value, socket->port); + break; + } +} + +/* property mutator */ +void +thrift_socket_set_property (GObject *object, guint property_id, + const GValue *value, GParamSpec *pspec) +{ + THRIFT_UNUSED_VAR (pspec); + ThriftSocket *socket = THRIFT_SOCKET (object); + + switch (property_id) + { + case PROP_THRIFT_SOCKET_HOSTNAME: + socket->hostname = g_strdup (g_value_get_string (value)); + break; + case PROP_THRIFT_SOCKET_PORT: + socket->port = g_value_get_uint (value); + break; + } +} + +/* initializes the class */ +static void +thrift_socket_class_init (ThriftSocketClass *cls) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (cls); + GParamSpec *param_spec = NULL; + + /* setup accessors and mutators */ + gobject_class->get_property = thrift_socket_get_property; + gobject_class->set_property = thrift_socket_set_property; + + param_spec = g_param_spec_string ("hostname", + "hostname (construct)", + "Set the hostname of the remote host", + "localhost", /* default value */ + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_READWRITE); + g_object_class_install_property (gobject_class, PROP_THRIFT_SOCKET_HOSTNAME, + param_spec); + + param_spec = g_param_spec_uint ("port", + "port (construct)", + "Set the port of the remote host", + 0, /* min */ + 65534, /* max */ + 9090, /* default by convention */ + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_READWRITE); + g_object_class_install_property (gobject_class, PROP_THRIFT_SOCKET_PORT, + param_spec); + + ThriftTransportClass *ttc = THRIFT_TRANSPORT_CLASS (cls); + + gobject_class->finalize = thrift_socket_finalize; + ttc->is_open = thrift_socket_is_open; + ttc->open = thrift_socket_open; + ttc->close = thrift_socket_close; + ttc->read = thrift_socket_read; + ttc->read_end = thrift_socket_read_end; + ttc->write = thrift_socket_write; + ttc->write_end = thrift_socket_write_end; + ttc->flush = thrift_socket_flush; +} + +/* implements thrift_transport_is_open */ +gboolean +thrift_socket_is_open (ThriftTransport *transport) +{ + ThriftSocket *socket = THRIFT_SOCKET (transport); + return socket->sd != 0; +} + +/* implements thrift_transport_open */ +gboolean +thrift_socket_open (ThriftTransport *transport, GError **error) +{ + struct hostent *hp = NULL; + struct sockaddr_in pin; + + ThriftSocket *tsocket = THRIFT_SOCKET (transport); + g_return_val_if_fail (tsocket->sd == 0, FALSE); + + /* lookup the destination host */ + if ((hp = gethostbyname (tsocket->hostname)) == NULL) + { + /* host lookup failed, bail out with an error */ + g_set_error (error, THRIFT_TRANSPORT_ERROR, THRIFT_TRANSPORT_ERROR_HOST, + "host lookup failed for %s:%d - %s", + tsocket->hostname, tsocket->port, + hstrerror (h_errno)); + return FALSE; + } + + /* create a socket structure */ + memset (&pin, 0, sizeof(pin)); + pin.sin_family = AF_INET; + pin.sin_addr.s_addr = ((struct in_addr *) (hp->h_addr))->s_addr; + pin.sin_port = htons (tsocket->port); + + /* create the socket */ + if ((tsocket->sd = socket (AF_INET, SOCK_STREAM, 0)) == -1) + { + g_set_error (error, THRIFT_TRANSPORT_ERROR, THRIFT_TRANSPORT_ERROR_SOCKET, + "failed to create socket for host %s:%d - %s", + tsocket->hostname, tsocket->port, + strerror(errno)); + return FALSE; + } + + /* open a connection */ + if (connect (tsocket->sd, (struct sockaddr *) &pin, sizeof(pin)) == -1) + { + g_set_error (error, THRIFT_TRANSPORT_ERROR, THRIFT_TRANSPORT_ERROR_CONNECT, + "failed to connect to host %s:%d - %s", + tsocket->hostname, tsocket->port, strerror(errno)); + return FALSE; + } + + return TRUE; +} + +/* implements thrift_transport_close */ +gboolean +thrift_socket_close (ThriftTransport *transport, GError **error) +{ + ThriftSocket *socket = THRIFT_SOCKET (transport); + + if (close (socket->sd) == -1) + { + g_set_error (error, THRIFT_TRANSPORT_ERROR, THRIFT_TRANSPORT_ERROR_CLOSE, + "unable to close socket - %s", + strerror(errno)); + return FALSE; + } + + socket->sd = 0; + return TRUE; +} + +/* implements thrift_transport_read */ +gint32 +thrift_socket_read (ThriftTransport *transport, gpointer buf, + guint32 len, GError **error) +{ + gint ret = 0; + guint got = 0; + + ThriftSocket *socket = THRIFT_SOCKET (transport); + + while (got < len) + { + ret = recv (socket->sd, buf, len, 0); + if (ret < 0) + { + g_set_error (error, THRIFT_TRANSPORT_ERROR, + THRIFT_TRANSPORT_ERROR_RECEIVE, + "failed to read %d bytes - %s", len, strerror(errno)); + return -1; + } + got += ret; + } + + return got; +} + +/* implements thrift_transport_read_end + * called when write is complete. nothing to do on our end. */ +gboolean +thrift_socket_read_end (ThriftTransport *transport, GError **error) +{ + /* satisfy -Wall */ + THRIFT_UNUSED_VAR (transport); + THRIFT_UNUSED_VAR (error); + return TRUE; +} + +/* implements thrift_transport_write */ +gboolean +thrift_socket_write (ThriftTransport *transport, const gpointer buf, + const guint32 len, GError **error) +{ + gint ret = 0; + guint sent = 0; + + ThriftSocket *socket = THRIFT_SOCKET (transport); + g_return_val_if_fail (socket->sd != 0, FALSE); + + while (sent < len) + { + ret = send (socket->sd, buf + sent, len - sent, 0); + if (ret < 0) + { + g_set_error (error, THRIFT_TRANSPORT_ERROR, + THRIFT_TRANSPORT_ERROR_SEND, + "failed to send %d bytes - %s", len, strerror(errno)); + return FALSE; + } + sent += ret; + } + + return TRUE; +} + +/* implements thrift_transport_write_end + * called when write is complete. nothing to do on our end. */ +gboolean +thrift_socket_write_end (ThriftTransport *transport, GError **error) +{ + /* satisfy -Wall */ + THRIFT_UNUSED_VAR (transport); + THRIFT_UNUSED_VAR (error); + return TRUE; +} + +/* implements thrift_transport_flush + * flush pending data. since we are not buffered, this is a no-op */ +gboolean +thrift_socket_flush (ThriftTransport *transport, GError **error) +{ + /* satisfy -Wall */ + THRIFT_UNUSED_VAR (transport); + THRIFT_UNUSED_VAR (error); + return TRUE; +} + + diff --git a/lib/c_glib/src/transport/thrift_socket.h b/lib/c_glib/src/transport/thrift_socket.h new file mode 100644 index 00000000..5fc24028 --- /dev/null +++ b/lib/c_glib/src/transport/thrift_socket.h @@ -0,0 +1,56 @@ +#ifndef _THRIFT_SOCKET_H +#define _THRIFT_SOCKET_H + +#include + +#include "transport/thrift_transport.h" + +/*! \file thrift_socket.h + * \brief Socket implementation of a Thrift transport. Subclasses the + * ThriftTransport class. + */ + +/* type macros */ +#define THRIFT_TYPE_SOCKET (thrift_socket_get_type ()) +#define THRIFT_SOCKET(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), \ + THRIFT_TYPE_SOCKET, ThriftSocket)) +#define THRIFT_IS_SOCKET(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \ + THRIFT_TYPE_SOCKET)) +#define THRIFT_SOCKET_CLASS(c) (G_TYPE_CHECK_CLASS_CAST ((c), \ + THRIFT_TYPE_SOCKET, ThriftSocketClass)) +#define THRIFT_IS_SOCKET_CLASS(c) (G_TYPE_CHECK_CLASS_TYPE ((c), \ + THRIFT_TYPE_SOCKET)) +#define THRIFT_SOCKET_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), \ + THRIFT_TYPE_SOCKET, \ + ThriftSocketClass)) + +/*! + * Thrift Socket instance. + */ +struct _ThriftSocket +{ + ThriftTransport parent; + + /* private */ + gchar *hostname; + gshort port; + int sd; + guint8 *buf; + guint32 buf_size; + guint32 buf_len; +}; +typedef struct _ThriftSocket ThriftSocket; + +/*! + * Thrift Socket class. + */ +struct _ThriftSocketClass +{ + ThriftTransportClass parent; +}; +typedef struct _ThriftSocketClass ThriftSocketClass; + +/* used by THRIFT_TYPE_SOCKET */ +GType thrift_socket_get_type (void); + +#endif diff --git a/lib/c_glib/src/transport/thrift_transport.c b/lib/c_glib/src/transport/thrift_transport.c new file mode 100644 index 00000000..9c57a75f --- /dev/null +++ b/lib/c_glib/src/transport/thrift_transport.c @@ -0,0 +1,114 @@ +#include "thrift.h" +#include "transport/thrift_transport.h" + +/* define the GError domain string */ +#define THRIFT_TRANSPORT_ERROR_DOMAIN "thrift-transport-error-quark" + +/* forward declarations */ +static void thrift_transport_class_init (ThriftTransportClass *cls); + +/* define ThriftTransportInterface's type */ +GType +thrift_transport_get_type (void) +{ + static GType type = 0; + + if (type == 0) + { + static const GTypeInfo info = + { + sizeof (ThriftTransportClass), + NULL, /* base_init */ + NULL, /* base_finalize */ + (GClassInitFunc) thrift_transport_class_init, + NULL, /* class_finalize */ + NULL, /* class_data */ + sizeof (ThriftTransport), + 0, /* n_preallocs */ + NULL, /* instance_init */ + NULL, /* value_table */ + }; + + type = g_type_register_static (G_TYPE_OBJECT, "ThriftTransport", + &info, G_TYPE_FLAG_ABSTRACT); + } + + return type; +} + +/* class initializer for ThriftTransport */ +static void +thrift_transport_class_init (ThriftTransportClass *cls) +{ + /* set these as virtual methods to be implemented by a subclass */ + cls->is_open = thrift_transport_is_open; + cls->open = thrift_transport_open; + cls->close = thrift_transport_close; + cls->read = thrift_transport_read; + cls->read_end = thrift_transport_read_end; + cls->write = thrift_transport_write; + cls->write_end = thrift_transport_write_end; + cls->flush = thrift_transport_flush; +} + +gboolean +thrift_transport_is_open (ThriftTransport *transport) +{ + return THRIFT_TRANSPORT_GET_CLASS (transport)->is_open (transport); +} + +gboolean +thrift_transport_open (ThriftTransport *transport, GError **error) +{ + return THRIFT_TRANSPORT_GET_CLASS (transport)->open (transport, error); +} + +gboolean +thrift_transport_close (ThriftTransport *transport, GError **error) +{ + return THRIFT_TRANSPORT_GET_CLASS (transport)->close (transport, error); +} + +gint32 +thrift_transport_read (ThriftTransport *transport, gpointer buf, + guint32 len, GError **error) +{ + return THRIFT_TRANSPORT_GET_CLASS (transport)->read (transport, buf, + len, error); +} + +gboolean +thrift_transport_read_end (ThriftTransport *transport, GError **error) +{ + return THRIFT_TRANSPORT_GET_CLASS (transport)->read_end (transport, + error); +} + +gboolean +thrift_transport_write (ThriftTransport *transport, const gpointer buf, + const guint32 len, GError **error) +{ + return THRIFT_TRANSPORT_GET_CLASS (transport)->write (transport, buf, + len, error); +} + +gboolean +thrift_transport_write_end (ThriftTransport *transport, GError **error) +{ + return THRIFT_TRANSPORT_GET_CLASS (transport)->write_end (transport, + error); +} + +gboolean +thrift_transport_flush (ThriftTransport *transport, GError **error) +{ + return THRIFT_TRANSPORT_GET_CLASS (transport)->flush (transport, error); +} + +/* define the GError domain for Thrift transports */ +GQuark +thrift_transport_error_quark (void) +{ + return g_quark_from_static_string (THRIFT_TRANSPORT_ERROR_DOMAIN); +} + diff --git a/lib/c_glib/src/transport/thrift_transport.h b/lib/c_glib/src/transport/thrift_transport.h new file mode 100644 index 00000000..18edc4d5 --- /dev/null +++ b/lib/c_glib/src/transport/thrift_transport.h @@ -0,0 +1,135 @@ +#ifndef _THRIFT_TRANSPORT_H +#define _THRIFT_TRANSPORT_H + +#include + +/*! \file thrift_transport.h + * \brief Abstract class for Thrift transports. + * + * An abstract class is used instead of an interface because: + * - interfaces can't seem to be used as properties. ThriftProtocol has + * a ThriftTransport as an object property. + * - if a method needs to be added that all subclasses can use, a class + * is necessary. + */ + +/* type macros */ +#define THRIFT_TYPE_TRANSPORT (thrift_transport_get_type ()) +#define THRIFT_TRANSPORT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), \ + THRIFT_TYPE_TRANSPORT, ThriftTransport)) +#define THRIFT_IS_TRANSPORT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \ + THRIFT_TYPE_TRANSPORT)) +#define THRIFT_TRANSPORT_CLASS(c) (G_TYPE_CHECK_CLASS_CAST ((c), \ + THRIFT_TYPE_TRANSPORT, \ + ThriftTransportClass)) +#define THRIFT_IS_TRANSPORT_CLASS(c) (G_TYPE_CHECK_CLASS_TYPE ((c), \ + THRIFT_TYPE_TRANSPORT)) +#define THRIFT_TRANSPORT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), \ + THRIFT_TYPE_TRANSPORT, \ + ThriftTransportClass)) + +/*! + * Thrift Protocol object + */ +struct _ThriftTransport +{ + GObject parent; +}; +typedef struct _ThriftTransport ThriftTransport; + +/*! + * Thrift Transport class + */ +struct _ThriftTransportClass +{ + GObjectClass parent; + + /* vtable */ + gboolean (*is_open) (ThriftTransport *transport); + gboolean (*open) (ThriftTransport *transport, GError **error); + gboolean (*close) (ThriftTransport *transport, GError **error); + gint32 (*read) (ThriftTransport *transport, gpointer buf, + guint32 len, GError **error); + gboolean (*read_end) (ThriftTransport *transport, GError **error); + gboolean (*write) (ThriftTransport *transport, const gpointer buf, + const guint32 len, GError **error); + gboolean (*write_end) (ThriftTransport *transport, GError **error); + gboolean (*flush) (ThriftTransport *transport, GError **error); +}; +typedef struct _ThriftTransportClass ThriftTransportClass; + +/* used by THRIFT_TYPE_TRANSPORT */ +GType thrift_transport_get_type (void); + +/* virtual public methods */ + +/*! + * Checks if this transport is opened. + * \public \memberof ThriftTransportInterface + */ +gboolean thrift_transport_is_open (ThriftTransport *transport); + +/*! + * Open the transport for reading and writing. + * \public \memberof ThriftTransportInterface + */ +gboolean thrift_transport_open (ThriftTransport *transport, GError **error); + +/*! + * Close the transport. + * \public \memberof ThriftTransportInterface + */ +gboolean thrift_transport_close (ThriftTransport *transport, GError **error); + +/*! + * Read some data into the buffer buf. + * \public \memberof ThriftTransportInterface + */ +gint32 thrift_transport_read (ThriftTransport *transport, gpointer buf, + guint32 len, GError **error); + +/*! + * Called when read is completed. + * \public \memberof ThriftTransportInterface + */ +gboolean thrift_transport_read_end (ThriftTransport *transport, GError **error); + +/*! + * Writes data from a buffer to the transport. + * \public \memberof ThriftTransportInterface + */ +gboolean thrift_transport_write (ThriftTransport *transport, const gpointer buf, + const guint32 len, GError **error); + +/*! + * Called when write is completed. + * \public \memberof ThriftTransportInterface + */ +gboolean thrift_transport_write_end (ThriftTransport *transport, + GError **error); + +/*! + * Flushes any pending data to be written. Typically used with buffered + * transport mechanisms. + * \public \memberof ThriftTransportInterface + */ +gboolean thrift_transport_flush (ThriftTransport *transport, GError **error); + +/* define error/exception types */ +typedef enum +{ + THRIFT_TRANSPORT_ERROR_UNKNOWN, + THRIFT_TRANSPORT_ERROR_HOST, + THRIFT_TRANSPORT_ERROR_SOCKET, + THRIFT_TRANSPORT_ERROR_CONNECT, + THRIFT_TRANSPORT_ERROR_SEND, + THRIFT_TRANSPORT_ERROR_RECEIVE, + THRIFT_TRANSPORT_ERROR_CLOSE +} ThriftTransportError; + +/* define an error domain for GError to use */ +GQuark thrift_transport_error_quark (void); +#define THRIFT_TRANSPORT_ERROR (thrift_transport_error_quark ()) + + +#endif /* _THRIFT_TRANSPORT_H */ diff --git a/lib/c_glib/src/transport/thrift_transport_factory.c b/lib/c_glib/src/transport/thrift_transport_factory.c new file mode 100644 index 00000000..6f0199fc --- /dev/null +++ b/lib/c_glib/src/transport/thrift_transport_factory.c @@ -0,0 +1,51 @@ +#include "thrift.h" +#include "transport/thrift_transport_factory.h" + +/* forward declaration s*/ +static void thrift_transport_factory_class_init (ThriftTransportFactoryClass *cls); +ThriftTransport *thrift_transport_factory_get_transport (ThriftTransportFactory *factory, ThriftTransport *transport); + +GType +thrift_transport_factory_get_type (void) +{ + static GType type = 0; + + if (type == 0) + { + static const GTypeInfo info = { + sizeof (ThriftTransportFactoryClass), + NULL, /* base_init */ + NULL, /* base_finalize */ + (GClassInitFunc) thrift_transport_factory_class_init, + NULL, /* class_finalize */ + NULL, /* class_data */ + sizeof (ThriftTransportFactory), + 0, /* n_preallocs */ + NULL, /* instance_init */ + NULL, /* value_table */ + }; + + type = g_type_register_static (G_TYPE_OBJECT, "ThriftTransportFactory", + &info, 0); + } + + return type; +} + +static void +thrift_transport_factory_class_init (ThriftTransportFactoryClass *cls) +{ + cls->get_transport = thrift_transport_factory_get_transport; +} + +/* builds a transport from the base transport. */ +ThriftTransport * +thrift_transport_factory_get_transport (ThriftTransportFactory *factory, + ThriftTransport *transport) +{ + THRIFT_UNUSED_VAR (factory); + return transport; +} + + + diff --git a/lib/c_glib/src/transport/thrift_transport_factory.h b/lib/c_glib/src/transport/thrift_transport_factory.h new file mode 100644 index 00000000..d987a803 --- /dev/null +++ b/lib/c_glib/src/transport/thrift_transport_factory.h @@ -0,0 +1,55 @@ +#ifndef _THRIFT_TRANSPORT_FACTORY_H +#define _THRIFT_TRANSPORT_FACTORY_H + +#include + +#include "thrift_transport.h" + +/*! \file thrift_transport_factory.h + * \brief Base class for Thrift Transport Factories. Used by Thrift Servers + * to obtain a client transport from an existing transport. The default + * implementation simply clones the provided transport. + */ + +/* type macros */ +#define THRIFT_TYPE_TRANSPORT_FACTORY (thrift_transport_factory_get_type ()) +#define THRIFT_TRANSPORT_FACTORY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), \ + THRIFT_TYPE_TRANSPORT_FACTORY, \ + ThriftTransportFactory)) +#define THRIFT_IS_TRANSPORT_FACTORY(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \ + THRIFT_TYPE_TRANSPORT_FACTORY)) +#define THRIFT_TRANSPORT_FACTORY_CLASS(c) (G_TYPE_CHECK_CLASS_CAST ((c), \ + THRIFT_TYPE_TRANSPORT_FACTORY, \ + ThriftTransportFactoryClass)) +#define THRIFT_IS_TRANSPORT_FACTORY_CLASS(c) (G_TYPE_CHECK_CLASS_TYPE ((c), \ + THRIFT_TYPE_TRANSPORT_FACTORY)) +#define THRIFT_TRANSPORT_FACTORY_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS ((obj), THRIFT_TYPE_TRANSPORT_FACTORY, \ + ThriftTransportFactoryClass)) + +/* Thrift Transport Factory instance */ +struct _ThriftTransportFactory +{ + GObject parent; +}; +typedef struct _ThriftTransportFactory ThriftTransportFactory; + +/* Thrift Transport Factory class */ +struct _ThriftTransportFactoryClass +{ + GObjectClass parent; + + /* vtable */ + ThriftTransport *(*get_transport) (ThriftTransportFactory *factory, + ThriftTransport *transport); +}; +typedef struct _ThriftTransportFactoryClass ThriftTransportFactoryClass; + +/* used by THRIFT_TYPE_TRANSPORT_FACTORY */ +GType thrift_transport_factory_get_type (void); + +/* virtual public methods */ +ThriftTransport *thrift_transport_factory_get_transport (ThriftTransportFactory *factory, ThriftTransport *transport); + + +#endif /* _THRIFT_TRANSPORT_FACTORY_H */ diff --git a/lib/c_glib/test/Makefile.am b/lib/c_glib/test/Makefile.am new file mode 100644 index 00000000..a8889be7 --- /dev/null +++ b/lib/c_glib/test/Makefile.am @@ -0,0 +1,229 @@ +SUBDIRS = + +AM_CPPFLAGS = -g -Wall -I../src $(GLIB_CFLAGS) +AM_LDFLAGS = $(GLIB_LIBS) $(GOBJECT_LIBS) @GCOV_LDFLAGS@ + +CFLAGS = @GCOV_CFLAGS@ +CXXFLAGS = -g + +check_SCRIPTS = \ + testwrapper-testtransportsocket \ + testwrapper-testprotocolbinary \ + testwrapper-testbufferedtransport \ + testwrapper-testframedtransport \ + testwrapper-testmemorybuffer \ + testwrapper-teststruct \ + testwrapper-testsimpleserver \ + testwrapper-testdebugproto \ + testwrapper-testoptionalrequired \ + testwrapper-testthrifttest + +if WITH_CPP + check_SCRIPTS += testwrapper-testthrifttestclient +endif + +check_PROGRAMS = \ + testtransportsocket \ + testprotocolbinary \ + testbufferedtransport \ + testframedtransport \ + testmemorybuffer \ + teststruct \ + testsimpleserver \ + testdebugproto \ + testoptionalrequired \ + testthrifttest + +if WITH_CPP + check_PROGRAMS += testthrifttestclient +endif + +testtransportsocket_SOURCES = testtransportsocket.c +testtransportsocket_LDADD = \ + ../libthrift_c_glib_la-thrift_transport.o \ + ../libthrift_c_glib_la-thrift_server_transport.o \ + ../libthrift_c_glib_la-thrift_server_socket.o + +testprotocolbinary_SOURCES = testprotocolbinary.c +testprotocolbinary_LDADD = \ + ../libthrift_c_glib_la-thrift_protocol.o \ + ../libthrift_c_glib_la-thrift_transport.o \ + ../libthrift_c_glib_la-thrift_socket.o \ + ../libthrift_c_glib_la-thrift_server_transport.o \ + ../libthrift_c_glib_la-thrift_server_socket.o + +testbufferedtransport_SOURCES = testbufferedtransport.c +testbufferedtransport_LDADD = \ + ../libthrift_c_glib_la-thrift_transport.o \ + ../libthrift_c_glib_la-thrift_socket.o \ + ../libthrift_c_glib_la-thrift_server_transport.o \ + ../libthrift_c_glib_la-thrift_server_socket.o + +testframedtransport_SOURCES = testframedtransport.c +testframedtransport_LDADD = \ + ../libthrift_c_glib_la-thrift_transport.o \ + ../libthrift_c_glib_la-thrift_socket.o \ + ../libthrift_c_glib_la-thrift_server_transport.o \ + ../libthrift_c_glib_la-thrift_server_socket.o + +testmemorybuffer_SOURCES = testmemorybuffer.c +testmemorybuffer_LDADD = \ + ../libthrift_c_glib_la-thrift_transport.o + +teststruct_SOURCES = teststruct.c +teststruct_LDADD = \ + ../libthrift_c_glib_la-thrift_protocol.o \ + ../libthrift_c_glib_la-thrift_transport.o + +testsimpleserver_SOURCES = testsimpleserver.c +testsimpleserver_LDADD = \ + ../libthrift_c_glib_la-thrift_protocol.o \ + ../libthrift_c_glib_la-thrift_transport.o \ + ../libthrift_c_glib_la-thrift_transport_factory.o \ + ../libthrift_c_glib_la-thrift_processor.o \ + ../libthrift_c_glib_la-thrift_protocol_factory.o \ + ../libthrift_c_glib_la-thrift_binary_protocol.o \ + ../libthrift_c_glib_la-thrift_binary_protocol_factory.o \ + ../libthrift_c_glib_la-thrift_socket.o \ + ../libthrift_c_glib_la-thrift_server_transport.o \ + ../libthrift_c_glib_la-thrift_server_socket.o \ + ../libthrift_c_glib_la-thrift_server.o + +testdebugproto_SOURCES = testdebugproto.c +testdebugproto_LDADD = libtestgenc.la + +testoptionalrequired_SOURCES = testoptionalrequired.c +testoptionalrequired_LDADD = \ + ../libthrift_c_glib_la-thrift_protocol.o \ + ../libthrift_c_glib_la-thrift_transport.o \ + libtestgenc.la + +testthrifttest_SOURCES = testthrifttest.c +testthrifttest_LDADD = libtestgenc.la + +testthrifttestclient_SOURCES = testthrifttestclient.cpp +testthrifttestclient_CPPFLAGS = -I../../cpp/src $(BOOST_CPPFLAGS) -I./gen-cpp -I../src -I./gen-c_glib $(GLIB_CFLAGS) +testthrifttestclient_LDADD = ../../cpp/.libs/libthrift.la ../libthrift_c_glib.la libtestgenc.la libtestgencpp.la +testthrifttestclient_LDFLAGS = -L../.libs -L../../cpp/.libs $(GLIB_LIBS) $(GOBJECT_LIBS) + +check_LTLIBRARIES = libtestgenc.la + +if WITH_CPP + check_LTLIBRARIES += libtestgencpp.la +endif + +libtestgenc_la_SOURCES = \ + gen-c_glib/t_test_debug_proto_test_types.c \ + gen-c_glib/t_test_empty_service.c \ + gen-c_glib/t_test_inherited.c \ + gen-c_glib/t_test_optional_required_test_types.c \ + gen-c_glib/t_test_reverse_order_service.c \ + gen-c_glib/t_test_second_service.c \ + gen-c_glib/t_test_service_for_exception_with_a_map.c \ + gen-c_glib/t_test_srv.c \ + gen-c_glib/t_test_thrift_test.c \ + gen-c_glib/t_test_thrift_test_types.c \ + gen-c_glib/t_test_debug_proto_test_types.h \ + gen-c_glib/t_test_empty_service.h \ + gen-c_glib/t_test_inherited.h \ + gen-c_glib/t_test_optional_required_test_types.h \ + gen-c_glib/t_test_reverse_order_service.h \ + gen-c_glib/t_test_second_service.h \ + gen-c_glib/t_test_service_for_exception_with_a_map.h \ + gen-c_glib/t_test_srv.h \ + gen-c_glib/t_test_thrift_test.h \ + gen-c_glib/t_test_thrift_test_types.h +libtestgenc_la_LIBADD = $(top_builddir)/lib/c_glib/libthrift_c_glib.la + +libtestgencpp_la_SOURCES = \ + gen-cpp/ThriftTest.cpp \ + gen-cpp/ThriftTest_constants.cpp \ + gen-cpp/ThriftTest_types.cpp \ + gen-cpp/ThriftTest.h \ + gen-cpp/ThriftTest_constants.h \ + gen-cpp/ThriftTest_types.h +libtestgencpp_la_CPPFLAGS = -I../../cpp/src $(BOOST_CPPFLAGS) -I./gen-cpp + +THRIFT = $(top_builddir)/compiler/cpp/thrift + +gen-c_glib/t_test_debug_proto_test_types.c gen-c_glib/t_test_debug_proto_test_types.h gen-c_glib/t_test_empty_service.c gen-c_glib/t_test_empty_service.h gen-c_glib/t_test_inherited.c gen-c_glib/t_test_inherited.h gen-c_glib/t_test_reverse_order_service.c gen-c_glib/t_test_reverse_order_service.h gen-c_glib/t_test_service_for_exception_with_a_map.c gen-c_glib/t_test_service_for_exception_with_a_map.h gen-c_glib/t_test_srv.c gen-c_glib/t_test_srv.h: ../../../test/DebugProtoTest.thrift + $(THRIFT) --gen c_glib $< + +gen-c_glib/t_test_optional_required_test_types.c gen-c_glib/t_test_optional_required_test_types.h: ../../../test/OptionalRequiredTest.thrift + $(THRIFT) --gen c_glib $< + +gen-c_glib/t_test_second_service.c gen-c_glib/t_test_thrift_test.c gen-c_glib/t_test_thrift_test_types.c gen-c_glib/t_test_second_service.h gen-c_glib/t_test_thrift_test-.h gen-c_glib/t_test_thrift_test_types.h: ../../../test/ThriftTest.thrift + $(THRIFT) --gen c_glib $< + +gen-cpp/ThriftTest.cpp gen-cpp/ThriftTest.h: ../../../test/ThriftTest.thrift + $(THRIFT) --gen cpp $< + + +TESTS = \ + $(testwrapper-%) \ + $(check_PROGRAMS) \ + $(check_SCRIPTS) + +# globally added to all instances of valgrind calls +# VALGRIND_OPTS = --suppressions=glib.suppress +VALGRIND_OPTS = + +# globally added to all memcheck calls +VALGRIND_MEM_OPTS = --tool=memcheck \ + --num-callers=10 \ + ${myextravalgrindmemopts} + +# globally added to all leakcheck calls +VALGRIND_LEAK_OPTS = --tool=memcheck \ + --num-callers=10 \ + --leak-check=full \ + --leak-resolution=high \ + ${myextravalgrindleakopts} + +memcheck: $(check_PROGRAMS) + @for x in $(check_PROGRAMS); \ + do \ + $(MAKE) memcheck-$$x; \ + done + +leakcheck: $(check_PROGRAMS) + @for x in $(check_PROGRAMS); \ + do \ + $(MAKE) leakcheck-$$x; \ + done + +memcheck-%: % + @echo "*****************************************"; \ + echo "MEMCHECK: $<"; \ + echo "ARGS: ${VALGRIND_OPTS} ${VALGRIND_MEM_OPTS} ${$<_VALGRIND_MEM_OPTS}"; \ + $(LIBTOOL) --mode=execute \ + valgrind \ + ${VALGRIND_OPTS} \ + ${VALGRIND_MEM_OPTS} \ + ${$<_VALGRIND_MEM_OPTS} ./$< + +leakcheck-%: % + @echo "*****************************************"; \ + echo "LEAKCHECK: $<"; \ + echo "ARGS: ${VALGRIND_OPTS} ${VALGRIND_LEAK_OPTS} ${$<_VALGRIND_LEAK_OPTS}"; \ + G_SLICE=always-malloc $(LIBTOOL) --mode=execute \ + valgrind \ + ${VALGRIND_OPTS} \ + ${VALGRIND_LEAK_OPTS} \ + ${$<_VALGRIND_LEAK_OPTS} ./$< + +testwrapper-%: % test-wrapper.sh + @ln -sf test-wrapper.sh $@ + +clean-local: + $(RM) -r gen-c_glib gen-cpp + +CLEANFILES = \ + testwrapper-* \ + *.bb \ + *.bbg \ + *.da \ + *.gcno \ + *.gcda \ + *.gcov \ + test-wrapper.sh diff --git a/lib/c_glib/test/glib.suppress b/lib/c_glib/test/glib.suppress new file mode 100644 index 00000000..0e0e9fe2 --- /dev/null +++ b/lib/c_glib/test/glib.suppress @@ -0,0 +1,64 @@ +{ + g_type_init_1 + Memcheck:Leak + fun:malloc + ... + fun:g_type_init_with_debug_flags +} + +{ + g_type_init_2 + Memcheck:Leak + fun:calloc + ... + fun:g_type_init_with_debug_flags +} + +{ + g_type_init_3 + Memcheck:Leak + fun:realloc + ... + fun:g_type_init_with_debug_flags +} + +{ + g_type_register_static_1 + Memcheck:Leak + fun:realloc + ... + fun:g_type_register_static +} + +{ + g_type_register_statuc_2 + Memcheck:Leak + fun:malloc + fun:realloc + fun:g_realloc + ... + fun:g_type_register_static +} + +{ + type_class_init_Wm1 + Memcheck:Leak + fun:calloc + fun:g_malloc0 + fun:type_class_init_Wm + fun:g_type_class_ref + ... + fun:g_object_newv +} + +{ + type_class_init_Wm2 + Memcheck:Leak + fun:calloc + fun:g_malloc0 + fun:type_class_init_Wm + fun:g_type_class_ref + ... + fun:type_class_init_Wm +} + diff --git a/lib/c_glib/test/test-wrapper.sh.in b/lib/c_glib/test/test-wrapper.sh.in new file mode 100644 index 00000000..956b2d15 --- /dev/null +++ b/lib/c_glib/test/test-wrapper.sh.in @@ -0,0 +1,58 @@ +# /bin/sh + +command="$0" + +stripcommand=`echo "$command" | sed 's/testwrapper-//'` + +"$stripcommand" "$@" || exit $? + +if test "x@ENABLE_COVERAGE@" = "x1"; then + # linux: 97.67% of 86 lines executed in file ../src/test123.h + # bsd: 100.00% of 196 source lines executed in file testbimap.c + + extrastripcommand=`echo "$stripcommand" | sed 's/\.\///'` + ${GCOV:-gcov} "$extrastripcommand" 2>&1 \ + | perl -ne 'BEGIN { $file = undef; } + next if m!^Creating!; + next if m!creating!; + next if m!^$!; + next if m!not exhausted!; + next if m!^Unexpected EOF!; + if (m!([\d\.]+)\% of \d+( source)? lines executed in file (.+)!) + { + do + { + if ( $3 !~ m#^/# ) + { + $a = $3 =~ m%([\-\w\.]+)$%; + print STDERR $_; + print "$1.gcov\n"; + } + } if $1 < 110.0; + } + elsif (m#^File .(.*?).$#) + { + $file = $1; + } + elsif (m#Lines executed:([\d\.]+)\% of (\d+)#) + { + $percent = $1; + $lines = $2; + do + { + if ( $file !~ m#^/# ) + { + $a = $file =~ m%([\-\w\.]+)$%; + print STDERR "$percent% of $lines executed in file $file\n"; + print "$1.gcov\n"; + } + } if $percent < 110.0; + } + else + { + print + }' \ + | xargs grep -n -A2 -B2 '#####.*\w' + exit 0 +fi + diff --git a/lib/c_glib/test/testbufferedtransport.c b/lib/c_glib/test/testbufferedtransport.c new file mode 100644 index 00000000..6759509f --- /dev/null +++ b/lib/c_glib/test/testbufferedtransport.c @@ -0,0 +1,188 @@ +#include +#include + +#include "transport/thrift_transport.h" +#include "transport/thrift_socket.h" +#include "transport/thrift_server_transport.h" +#include "transport/thrift_server_socket.h" + +#define TEST_DATA { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j' } + +#include "../src/transport/thrift_buffered_transport.c" + +static const char TEST_ADDRESS[] = "localhost"; +static const short TEST_PORT = 64444; + +static void thrift_server (const int port); + +/* test object creation and destruction */ +static void +test_create_and_destroy(void) +{ + ThriftTransport *transport = NULL; + guint r_buf_size = 0; + guint w_buf_size = 0; + + GObject *object = NULL; + object = g_object_new (THRIFT_TYPE_BUFFERED_TRANSPORT, NULL); + assert (object != NULL); + g_object_get (G_OBJECT (object), "transport", &transport, + "r_buf_size", &r_buf_size, + "w_buf_size", &w_buf_size, NULL); + g_object_unref (object); +} + +static void +test_open_and_close(void) +{ + ThriftSocket *tsocket = NULL; + ThriftTransport *transport = NULL; + GError *err = NULL; + + /* create a ThriftSocket */ + tsocket = g_object_new (THRIFT_TYPE_SOCKET, "hostname", "localhost", + "port", 51188, NULL); + + /* create a BufferedTransport wrapper of the Socket */ + transport = g_object_new (THRIFT_TYPE_BUFFERED_TRANSPORT, + "transport", THRIFT_TRANSPORT (tsocket), NULL); + + /* this shouldn't work */ + assert (thrift_buffered_transport_open (transport, NULL) == FALSE); + assert (thrift_buffered_transport_is_open (transport) == TRUE); + assert (thrift_buffered_transport_close (transport, NULL) == TRUE); + g_object_unref (transport); + g_object_unref (tsocket); + + /* try and underlying socket failure */ + tsocket = g_object_new (THRIFT_TYPE_SOCKET, "hostname", "localhost.broken", + NULL); + + /* create a BufferedTransport wrapper of the Socket */ + transport = g_object_new (THRIFT_TYPE_BUFFERED_TRANSPORT, + "transport", THRIFT_TRANSPORT (tsocket), NULL); + + assert (thrift_buffered_transport_open (transport, &err) == FALSE); + g_object_unref (transport); + g_object_unref (tsocket); + g_error_free (err); + err = NULL; +} + +static void +test_read_and_write(void) +{ + int status; + pid_t pid; + ThriftSocket *tsocket = NULL; + ThriftTransport *transport = NULL; + int port = 51199; + guchar buf[10] = TEST_DATA; /* a buffer */ + + pid = fork (); + assert ( pid >= 0 ); + + if ( pid == 0 ) + { + /* child listens */ + thrift_server (port); + exit (0); + } else { + /* parent connects, wait a bit for the socket to be created */ + sleep (1); + + tsocket = g_object_new (THRIFT_TYPE_SOCKET, "hostname", "localhost", + "port", port, NULL); + transport = g_object_new (THRIFT_TYPE_BUFFERED_TRANSPORT, + "transport", THRIFT_TRANSPORT (tsocket), + "w_buf_size", 4, NULL); + + assert (thrift_buffered_transport_open (transport, NULL) == TRUE); + assert (thrift_buffered_transport_is_open (transport)); + + /* write 10 bytes */ + thrift_buffered_transport_write (transport, buf, 10, NULL); + + /* write 1 byte at a time */ + thrift_buffered_transport_write (transport, buf, 1, NULL); + thrift_buffered_transport_write (transport, buf, 1, NULL); + thrift_buffered_transport_write (transport, buf, 1, NULL); + + /* overflow the buffer */ + thrift_buffered_transport_write (transport, buf, 2, NULL); + thrift_buffered_transport_write (transport, buf, 1, NULL); + thrift_buffered_transport_flush (transport, NULL); + + /* write 1 byte and flush */ + thrift_buffered_transport_write (transport, buf, 1, NULL); + thrift_buffered_transport_flush (transport, NULL); + + /* write and overflow buffer with 2 system calls */ + thrift_buffered_transport_write (transport, buf, 1, NULL); + thrift_buffered_transport_write (transport, buf, 3, NULL); + + /* write 10 bytes */ + thrift_buffered_transport_write (transport, buf, 10, NULL); + + thrift_buffered_transport_write_end (transport, NULL); + thrift_buffered_transport_flush (transport, NULL); + thrift_buffered_transport_close (transport, NULL); + + g_object_unref (transport); + g_object_unref (tsocket); + + assert ( wait (&status) == pid ); + assert ( status == 0 ); + } +} + +static void +thrift_server (const int port) +{ + int bytes = 0; + ThriftServerTransport *transport = NULL; + ThriftTransport *client = NULL; + guchar buf[10]; /* a buffer */ + guchar match[10] = TEST_DATA; + + ThriftServerSocket *tsocket = g_object_new (THRIFT_TYPE_SERVER_SOCKET, + "port", port, NULL); + + transport = THRIFT_SERVER_TRANSPORT (tsocket); + thrift_server_transport_listen (transport, NULL); + + /* wrap the client in a BufferedTransport */ + client = g_object_new (THRIFT_TYPE_BUFFERED_TRANSPORT, "transport", + thrift_server_transport_accept (transport, NULL), + "r_buf_size", 5, NULL); + assert (client != NULL); + + /* read 10 bytes */ + bytes = thrift_buffered_transport_read (client, buf, 10, NULL); + assert (bytes == 10); /* make sure we've read 10 bytes */ + assert ( memcmp (buf, match, 10) == 0 ); /* make sure what we got matches */ + + /* read 1 byte */ + bytes = thrift_buffered_transport_read (client, buf, 1, NULL); + + bytes = thrift_buffered_transport_read (client, buf, 6, NULL); + bytes = thrift_buffered_transport_read (client, buf, 2, NULL); + bytes = thrift_buffered_transport_read (client, buf, 1, NULL); + + thrift_buffered_transport_read_end (client, NULL); + thrift_buffered_transport_close (client, NULL); + g_object_unref (client); + g_object_unref (tsocket); +} + +int +main(void) +{ + g_type_init(); + test_create_and_destroy(); + test_open_and_close(); + test_read_and_write(); + + return 0; +} + diff --git a/lib/c_glib/test/testdebugproto.c b/lib/c_glib/test/testdebugproto.c new file mode 100644 index 00000000..b111e12f --- /dev/null +++ b/lib/c_glib/test/testdebugproto.c @@ -0,0 +1,64 @@ +#include +#include + +#ifndef M_PI +#define M_PI 3.1415926535897932385 +#endif + +#include "gen-c_glib/t_test_debug_proto_test_types.h" + + +int +main(void) +{ + g_type_init (); + + TTestOneOfEach *ooe = NULL; + TTestNesting *n = NULL; + TTestHolyMoley *hm = NULL; + + ooe = g_object_new (T_TEST_TYPE_ONE_OF_EACH, NULL); + ooe->im_true = TRUE; + ooe->im_false = FALSE; + ooe->a_bite = 0xd6; + ooe->integer16 = 27000; + ooe->integer32 = 1<<24; + ooe->integer64 = (guint64) 6000 * 1000 * 1000; + ooe->double_precision = M_PI; + ooe->some_characters = "Debug THIS!"; + ooe->zomg_unicode = "\xd7\n\a\t"; + + n = g_object_new (T_TEST_TYPE_NESTING, NULL); + n->my_ooe = ooe; + n->my_ooe->integer16 = 16; + n->my_ooe->integer32 = 32; + n->my_ooe->integer64 = 64; + n->my_ooe->double_precision = (sqrt(5.0) + 1) / 2; + n->my_ooe->some_characters = ":R (me going \"rrrr\")"; + n->my_ooe->zomg_unicode = "\xd3\x80\xe2\x85\xae\xce\x9d\x20"; + n->my_bonk->type = 31337; + n->my_bonk->message = "I am a bonk... xor!"; + + hm = g_object_new (T_TEST_TYPE_HOLY_MOLEY, NULL); + g_ptr_array_add (hm->big, ooe); + g_ptr_array_add (hm->big, n->my_ooe); + ((TTestOneOfEach *) g_ptr_array_index (hm->big, 0))->a_bite = 0x22; + ((TTestOneOfEach *) g_ptr_array_index (hm->big, 1))->a_bite = 0x33; + + g_hash_table_insert (hm->contain, "random string", "random string"); + + TTestBonk *bonk = NULL; + bonk = g_object_new (T_TEST_TYPE_BONK, NULL); + GPtrArray *bonks = g_ptr_array_new (); + g_ptr_array_add (bonks, bonk); + g_hash_table_insert (hm->bonks, "nothing", bonks); + + g_ptr_array_free (bonks, TRUE); + g_object_unref (bonk); + g_object_unref (ooe); + g_object_unref (n); + g_object_unref (hm); + + return 0; +} + diff --git a/lib/c_glib/test/testframedtransport.c b/lib/c_glib/test/testframedtransport.c new file mode 100644 index 00000000..23951f77 --- /dev/null +++ b/lib/c_glib/test/testframedtransport.c @@ -0,0 +1,176 @@ +#include +#include + +#include "transport/thrift_transport.h" +#include "transport/thrift_socket.h" +#include "transport/thrift_server_transport.h" +#include "transport/thrift_server_socket.h" + +#define TEST_DATA { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j' } + +#include "../src/transport/thrift_framed_transport.c" + +static const char TEST_ADDRESS[] = "localhost"; +static const short TEST_PORT = 64444; + +static void thrift_server (const int port); + +/* test object creation and destruction */ +static void +test_create_and_destroy(void) +{ + ThriftTransport *transport = NULL; + guint r_buf_size = 0; + guint w_buf_size = 0; + + GObject *object = NULL; + object = g_object_new (THRIFT_TYPE_FRAMED_TRANSPORT, NULL); + assert (object != NULL); + g_object_get (G_OBJECT (object), "transport", &transport, + "r_buf_size", &r_buf_size, + "w_buf_size", &w_buf_size, NULL); + g_object_unref (object); +} + +static void +test_open_and_close(void) +{ + ThriftSocket *tsocket = NULL; + ThriftTransport *transport = NULL; + GError *err = NULL; + + /* create a ThriftSocket */ + tsocket = g_object_new (THRIFT_TYPE_SOCKET, "hostname", "localhost", + "port", 51188, NULL); + + /* create a BufferedTransport wrapper of the Socket */ + transport = g_object_new (THRIFT_TYPE_FRAMED_TRANSPORT, + "transport", THRIFT_TRANSPORT (tsocket), NULL); + + /* this shouldn't work */ + assert (thrift_framed_transport_open (transport, NULL) == FALSE); + assert (thrift_framed_transport_is_open (transport) == TRUE); + assert (thrift_framed_transport_close (transport, NULL) == TRUE); + g_object_unref (transport); + g_object_unref (tsocket); + + /* try and underlying socket failure */ + tsocket = g_object_new (THRIFT_TYPE_SOCKET, "hostname", "localhost.broken", + NULL); + + /* create a BufferedTransport wrapper of the Socket */ + transport = g_object_new (THRIFT_TYPE_FRAMED_TRANSPORT, + "transport", THRIFT_TRANSPORT (tsocket), NULL); + + assert (thrift_framed_transport_open (transport, &err) == FALSE); + g_object_unref (transport); + g_object_unref (tsocket); + g_error_free (err); + err = NULL; +} + +static void +test_read_and_write(void) +{ + int status; + pid_t pid; + ThriftSocket *tsocket = NULL; + ThriftTransport *transport = NULL; + int port = 51199; + guchar buf[10] = TEST_DATA; /* a buffer */ + + pid = fork (); + assert ( pid >= 0 ); + + if ( pid == 0 ) + { + /* child listens */ + thrift_server (port); + exit (0); + } else { + /* parent connects, wait a bit for the socket to be created */ + sleep (1); + + tsocket = g_object_new (THRIFT_TYPE_SOCKET, "hostname", "localhost", + "port", port, NULL); + transport = g_object_new (THRIFT_TYPE_FRAMED_TRANSPORT, + "transport", THRIFT_TRANSPORT (tsocket), + "w_buf_size", 4, NULL); + + assert (thrift_framed_transport_open (transport, NULL) == TRUE); + assert (thrift_framed_transport_is_open (transport)); + + /* write 10 bytes */ + thrift_framed_transport_write (transport, buf, 10, NULL); + thrift_framed_transport_flush (transport, NULL); + + thrift_framed_transport_write (transport, buf, 1, NULL); + thrift_framed_transport_flush (transport, NULL); + + thrift_framed_transport_write (transport, buf, 10, NULL); + thrift_framed_transport_flush (transport, NULL); + + thrift_framed_transport_write (transport, buf, 10, NULL); + thrift_framed_transport_flush (transport, NULL); + + thrift_framed_transport_write_end (transport, NULL); + thrift_framed_transport_flush (transport, NULL); + thrift_framed_transport_close (transport, NULL); + + g_object_unref (transport); + g_object_unref (tsocket); + + assert ( wait (&status) == pid ); + assert ( status == 0 ); + } +} + +static void +thrift_server (const int port) +{ + int bytes = 0; + ThriftServerTransport *transport = NULL; + ThriftTransport *client = NULL; + guchar buf[10]; /* a buffer */ + guchar match[10] = TEST_DATA; + + ThriftServerSocket *tsocket = g_object_new (THRIFT_TYPE_SERVER_SOCKET, + "port", port, NULL); + + transport = THRIFT_SERVER_TRANSPORT (tsocket); + thrift_server_transport_listen (transport, NULL); + + /* wrap the client in a BufferedTransport */ + client = g_object_new (THRIFT_TYPE_FRAMED_TRANSPORT, "transport", + thrift_server_transport_accept (transport, NULL), + "r_buf_size", 5, NULL); + assert (client != NULL); + + /* read 10 bytes */ + bytes = thrift_framed_transport_read (client, buf, 10, NULL); + assert (bytes == 10); /* make sure we've read 10 bytes */ + assert ( memcmp (buf, match, 10) == 0 ); /* make sure what we got matches */ + + bytes = thrift_framed_transport_read (client, buf, 6, NULL); + bytes = thrift_framed_transport_read (client, buf, 5, NULL); + bytes = thrift_framed_transport_read (client, buf, 1, NULL); + + bytes = thrift_framed_transport_read (client, buf, 12, NULL); + + thrift_framed_transport_read_end (client, NULL); + thrift_framed_transport_close (client, NULL); + g_object_unref (client); + g_object_unref (tsocket); +} + +int +main(void) +{ + g_type_init(); + test_create_and_destroy(); + test_open_and_close(); + test_read_and_write(); + + return 0; +} + diff --git a/lib/c_glib/test/testmemorybuffer.c b/lib/c_glib/test/testmemorybuffer.c new file mode 100644 index 00000000..52b18bfa --- /dev/null +++ b/lib/c_glib/test/testmemorybuffer.c @@ -0,0 +1,75 @@ +#include +#include + +#include "transport/thrift_transport.h" +#include "transport/thrift_socket.h" +#include "transport/thrift_server_transport.h" +#include "transport/thrift_server_socket.h" + +#define TEST_DATA { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j' } + +#include "../src/transport/thrift_memory_buffer.c" + +/* test object creation and destruction */ +static void +test_create_and_destroy(void) +{ + GObject *object = NULL; + object = g_object_new (THRIFT_TYPE_MEMORY_BUFFER, NULL); + assert (object != NULL); + g_object_unref (object); +} + +static void +test_open_and_close(void) +{ + ThriftMemoryBuffer *tbuffer = NULL; + + /* create a ThriftMemoryBuffer */ + tbuffer = g_object_new (THRIFT_TYPE_MEMORY_BUFFER, NULL); + + /* this shouldn't work */ + assert (thrift_memory_buffer_open (THRIFT_TRANSPORT (tbuffer), NULL) == TRUE); + assert (thrift_memory_buffer_is_open (THRIFT_TRANSPORT (tbuffer)) == TRUE); + assert (thrift_memory_buffer_close (THRIFT_TRANSPORT (tbuffer), NULL) == TRUE); + g_object_unref (tbuffer); +} + +static void +test_read_and_write(void) +{ + ThriftMemoryBuffer *tbuffer = NULL; + guchar buf[10] = TEST_DATA; + guchar read[10]; + GError *error = NULL; + + tbuffer = g_object_new (THRIFT_TYPE_MEMORY_BUFFER, "buf_size", 5, NULL); + assert (thrift_memory_buffer_write (THRIFT_TRANSPORT (tbuffer), + (gpointer) buf, + 10, &error) == FALSE); + assert (error != NULL); + g_error_free (error); + error = NULL; + g_object_unref (tbuffer); + + tbuffer = g_object_new (THRIFT_TYPE_MEMORY_BUFFER, "buf_size", 15, NULL); + assert (thrift_memory_buffer_write (THRIFT_TRANSPORT (tbuffer), + (gpointer) buf, 10, &error) == TRUE); + assert (error == NULL); + + assert (thrift_memory_buffer_read (THRIFT_TRANSPORT (tbuffer), + &read, 10, &error) > 0); + assert (error == NULL); +} + +int +main(void) +{ + g_type_init(); + test_create_and_destroy(); + test_open_and_close(); + test_read_and_write(); + + return 0; +} + diff --git a/lib/c_glib/test/testoptionalrequired.c b/lib/c_glib/test/testoptionalrequired.c new file mode 100644 index 00000000..cf444133 --- /dev/null +++ b/lib/c_glib/test/testoptionalrequired.c @@ -0,0 +1,182 @@ +#include +#include + +#include "thrift_struct.h" +#include "protocol/thrift_protocol.h" +#include "protocol/thrift_binary_protocol.h" +#include "transport/thrift_memory_buffer.h" +#include "gen-c_glib/t_test_optional_required_test_types.h" + +#include "gen-c_glib/t_test_optional_required_test_types.c" + +static void +write_to_read (ThriftStruct *w, ThriftStruct *r, GError **write_error, + GError **read_error) +{ + ThriftMemoryBuffer *tbuffer = NULL; + ThriftProtocol *protocol = NULL; + + tbuffer = g_object_new (THRIFT_TYPE_MEMORY_BUFFER, NULL); + protocol = g_object_new (THRIFT_TYPE_BINARY_PROTOCOL, "transport", + tbuffer, NULL); + + thrift_struct_write (w, protocol, write_error); + thrift_struct_read (r, protocol, read_error); + + g_object_unref (protocol); + g_object_unref (tbuffer); +} + +static void +test_old_school1 (void) +{ + TTestOldSchool *o = NULL; + + o = g_object_new (T_TEST_TYPE_OLD_SCHOOL, NULL); + o->im_int = 10; + o->im_str = g_strdup ("test"); + o->im_big = g_ptr_array_new (); + g_ptr_array_free (o->im_big, FALSE); + g_free (o->im_str); + g_object_unref (o); +} + +/** + * Write to read with optional fields + */ +static void +test_simple (void) +{ + TTestSimple *s1 = NULL, *s2 = NULL, *s3 = NULL; + + s1 = g_object_new (T_TEST_TYPE_SIMPLE, NULL); + s2 = g_object_new (T_TEST_TYPE_SIMPLE, NULL); + s3 = g_object_new (T_TEST_TYPE_SIMPLE, NULL); + + // write-to-read with optional fields + s1->im_optional = 10; + assert (s1->__isset_im_default == FALSE); + assert (s1->__isset_im_optional == FALSE); + write_to_read (THRIFT_STRUCT (s1), THRIFT_STRUCT (s2), NULL, NULL); + assert (s2->__isset_im_default = TRUE); + assert (s2->__isset_im_optional == FALSE); + assert (s2->im_optional == 0); + + s1->__isset_im_optional = TRUE; + write_to_read (THRIFT_STRUCT (s1), THRIFT_STRUCT (s3), NULL, NULL); + assert (s3->__isset_im_default == TRUE); + assert (s3->__isset_im_optional == TRUE); + assert (s3->im_optional == 10); + + g_object_unref (s1); + g_object_unref (s2); +} + +/** + * Writing between optional and default + */ +static void +test_tricky1 (void) +{ + TTestTricky1 *t1 = NULL; + TTestTricky2 *t2 = NULL; + + t1 = g_object_new (T_TEST_TYPE_TRICKY1, NULL); + t2 = g_object_new (T_TEST_TYPE_TRICKY2, NULL); + + t2->im_optional = 10; + write_to_read (THRIFT_STRUCT (t2), THRIFT_STRUCT (t1), NULL, NULL); + write_to_read (THRIFT_STRUCT (t1), THRIFT_STRUCT (t2), NULL, NULL); + + assert (t1->__isset_im_default == FALSE); + assert (t2->__isset_im_optional == TRUE); + assert (t1->im_default == t2->im_optional); + assert (t1->im_default == 0); + + g_object_unref (t1); + g_object_unref (t2); +} + +/** + * Writing between default and required. + */ +static void +test_tricky2 (void) +{ + TTestTricky1 *t1 = NULL; + TTestTricky3 *t3 = NULL; + + t1 = g_object_new (T_TEST_TYPE_TRICKY1, NULL); + t3 = g_object_new (T_TEST_TYPE_TRICKY3, NULL); + + write_to_read (THRIFT_STRUCT (t1), THRIFT_STRUCT (t3), NULL, NULL); + write_to_read (THRIFT_STRUCT (t3), THRIFT_STRUCT (t1), NULL, NULL); + + assert (t1->__isset_im_default == TRUE); + + g_object_unref (t1); + g_object_unref (t3); +} + +/** + * Writing between optional and required. + */ +static void +test_tricky3 (void) +{ + TTestTricky2 *t2 = NULL; + TTestTricky3 *t3 = NULL; + + t2 = g_object_new (T_TEST_TYPE_TRICKY2, NULL); + t3 = g_object_new (T_TEST_TYPE_TRICKY3, NULL); + + t2->__isset_im_optional = TRUE; + + write_to_read (THRIFT_STRUCT (t2), THRIFT_STRUCT (t3), NULL, NULL); + write_to_read (THRIFT_STRUCT (t3), THRIFT_STRUCT (t2), NULL, NULL); + + g_object_unref (t2); + g_object_unref (t3); +} + +/** + * Catch an optional not set exception. To quote the + * C++ test, "Mu-hu-ha-ha-ha!" + */ +static void +test_tricky4 (void) +{ + TTestTricky2 *t2 = NULL; + TTestTricky3 *t3 = NULL; + GError *read_error = NULL; + + t2 = g_object_new (T_TEST_TYPE_TRICKY2, NULL); + t3 = g_object_new (T_TEST_TYPE_TRICKY3, NULL); + + // throws protocol exception + write_to_read (THRIFT_STRUCT (t2), THRIFT_STRUCT (t3), NULL, &read_error); + assert (read_error != NULL); + g_error_free (read_error); + + write_to_read (THRIFT_STRUCT (t3), THRIFT_STRUCT (t2), NULL, NULL); + + assert (t2->__isset_im_optional); + + g_object_unref (t2); + g_object_unref (t3); +} + +int +main(void) +{ + g_type_init (); + test_old_school1 (); + test_simple (); + test_tricky1 (); + test_tricky2 (); + test_tricky3 (); + test_tricky4 (); + return 0; +} + + diff --git a/lib/c_glib/test/testprotocolbinary.c b/lib/c_glib/test/testprotocolbinary.c new file mode 100644 index 00000000..c8a54b92 --- /dev/null +++ b/lib/c_glib/test/testprotocolbinary.c @@ -0,0 +1,652 @@ +#include +#include +#include +#include +#include +#include + +#include "protocol/thrift_protocol.h" +#include "transport/thrift_socket.h" +#include "transport/thrift_server_socket.h" + +#define TEST_BOOL TRUE +#define TEST_BYTE 123 +#define TEST_I16 12345 +#define TEST_I32 1234567890 +#define TEST_I64 123456789012345LL +#define TEST_DOUBLE 1234567890.123 +#define TEST_STRING "this is a test string 1234567890!@#$%^&*()" +#define TEST_PORT 51199 + +static int transport_read_count = 0; +static int transport_read_error = 0; +static int transport_read_error_at = -1; +gint32 +my_thrift_transport_read (ThriftTransport *transport, gpointer buf, + guint32 len, GError **error) +{ + if (transport_read_count != transport_read_error_at + && transport_read_error == 0) + { + transport_read_count++; + return thrift_transport_read (transport, buf, len, error); + } + return -1; +} + +static int transport_write_count = 0; +static int transport_write_error = 0; +static int transport_write_error_at = -1; +gboolean +my_thrift_transport_write (ThriftTransport *transport, const gpointer buf, + const guint32 len, GError **error) +{ + if (transport_write_count != transport_write_error_at + && transport_write_error == 0) + { + transport_write_count++; + return thrift_transport_write (transport, buf, len, error); + } + return FALSE; +} + +#define thrift_transport_read my_thrift_transport_read +#define thrift_transport_write my_thrift_transport_write +#include "../src/protocol/thrift_binary_protocol.c" +#undef thrift_transport_read +#undef thrift_transport_write + +static void thrift_server_primitives (const int port); +static void thrift_server_complex_types (const int port); + +static void +test_create_and_destroy(void) +{ + GObject *object = NULL; + + /* create an object and then destroy it */ + object = g_object_new (THRIFT_TYPE_BINARY_PROTOCOL, NULL); + assert (object != NULL); + g_object_unref (object); +} + +static void +test_initialize(void) +{ + ThriftSocket *tsocket = NULL; + ThriftBinaryProtocol *protocol = NULL; + ThriftSocket *temp = NULL; + + /* create a ThriftTransport */ + tsocket = g_object_new (THRIFT_TYPE_SOCKET, "hostname", "localhost", + "port", 51188, NULL); + assert (tsocket != NULL); + /* create a ThriftBinaryProtocol using the Transport */ + protocol = g_object_new (THRIFT_TYPE_BINARY_PROTOCOL, "transport", + tsocket, NULL); + assert (protocol != NULL); + /* fetch the properties */ + g_object_get (G_OBJECT(protocol), "transport", &temp, NULL); + g_object_unref (temp); + + /* clean up memory */ + g_object_unref (protocol); + g_object_unref (tsocket); +} + +static void +test_read_and_write_primitives(void) +{ + int status; + pid_t pid; + ThriftSocket *tsocket = NULL; + ThriftTransport *transport = NULL; + ThriftBinaryProtocol *tb = NULL; + ThriftProtocol *protocol = NULL; + gpointer binary = (gpointer *) TEST_STRING; + guint32 len = strlen (TEST_STRING); + int port = TEST_PORT; + + /* fork a server from the client */ + pid = fork (); + assert (pid >= 0); + + if (pid == 0) + { + /* child listens */ + thrift_server_primitives (port); + exit (0); + } else { + /* parent. wait a bit for the socket to be created. */ + sleep (1); + + /* create a ThriftSocket */ + tsocket = g_object_new (THRIFT_TYPE_SOCKET, "hostname", "localhost", + "port", port, NULL); + transport = THRIFT_TRANSPORT (tsocket); + thrift_transport_open (transport, NULL); + assert (thrift_transport_is_open (transport)); + + /* create a ThriftBinaryTransport */ + tb = g_object_new (THRIFT_TYPE_BINARY_PROTOCOL, "transport", + tsocket, NULL); + protocol = THRIFT_PROTOCOL (tb); + assert (protocol != NULL); + + /* write a bunch of primitives */ + assert (thrift_binary_protocol_write_bool (protocol, TEST_BOOL, NULL) > 0); + assert (thrift_binary_protocol_write_byte (protocol, TEST_BYTE, NULL) > 0); + assert (thrift_binary_protocol_write_i16 (protocol, TEST_I16, NULL) > 0); + assert (thrift_binary_protocol_write_i32 (protocol, TEST_I32, NULL) > 0); + assert (thrift_binary_protocol_write_i64 (protocol, TEST_I64, NULL) > 0); + assert (thrift_binary_protocol_write_double (protocol, + TEST_DOUBLE, NULL) > 0); + assert (thrift_binary_protocol_write_string (protocol, + TEST_STRING, NULL) > 0); + assert (thrift_binary_protocol_write_binary (protocol, binary, + len, NULL) > 0); + assert (thrift_binary_protocol_write_binary (protocol, NULL, 0, NULL) > 0); + assert (thrift_binary_protocol_write_binary (protocol, binary, + len, NULL) > 0); + + /* test write errors */ + transport_write_error = 1; + assert (thrift_binary_protocol_write_byte (protocol, TEST_BYTE, + NULL) == -1); + assert (thrift_binary_protocol_write_i16 (protocol, TEST_I16, NULL) == -1); + assert (thrift_binary_protocol_write_i32 (protocol, TEST_I32, NULL) == -1); + assert (thrift_binary_protocol_write_i64 (protocol, TEST_I64, NULL) == -1); + assert (thrift_binary_protocol_write_double (protocol, TEST_DOUBLE, + NULL) == -1); + assert (thrift_binary_protocol_write_binary (protocol, binary, len, + NULL) == -1); + transport_write_error = 0; + + /* test binary partial failure */ + transport_write_count = 0; + transport_write_error_at = 1; + assert (thrift_binary_protocol_write_binary (protocol, binary, + len, NULL) == -1); + transport_write_error_at = -1; + + /* clean up */ + thrift_transport_close (transport, NULL); + g_object_unref (tsocket); + g_object_unref (protocol); + assert (wait (&status) == pid); + assert (status == 0); + } +} + +static void +test_read_and_write_complex_types (void) +{ + int status; + pid_t pid; + ThriftSocket *tsocket = NULL; + ThriftTransport *transport = NULL; + ThriftBinaryProtocol *tb = NULL; + ThriftProtocol *protocol = NULL; + int port = TEST_PORT; + + /* fork a server from the client */ + pid = fork (); + assert (pid >= 0); + + if (pid == 0) + { + /* child listens */ + thrift_server_complex_types (port); + exit (0); + } else { + /* parent. wait a bit for the socket to be created. */ + sleep (1); + + /* create a ThriftSocket */ + tsocket = g_object_new (THRIFT_TYPE_SOCKET, "hostname", "localhost", + "port", port, NULL); + transport = THRIFT_TRANSPORT (tsocket); + thrift_transport_open (transport, NULL); + assert (thrift_transport_is_open (transport)); + + /* create a ThriftBinaryTransport */ + tb = g_object_new (THRIFT_TYPE_BINARY_PROTOCOL, "transport", + tsocket, NULL); + protocol = THRIFT_PROTOCOL (tb); + assert (protocol != NULL); + + /* test structures */ + assert (thrift_binary_protocol_write_struct_begin (protocol, + NULL, NULL) == 0); + assert (thrift_binary_protocol_write_struct_end (protocol, NULL) == 0); + + assert (thrift_binary_protocol_write_field_begin (protocol, "test", T_VOID, + 1, NULL) > 0); + assert (thrift_binary_protocol_write_field_end (protocol, NULL) == 0); + + /* test write error */ + transport_write_error = 1; + assert (thrift_binary_protocol_write_field_begin (protocol, "test", T_VOID, + 1, NULL) == -1); + transport_write_error = 0; + + /* test 2nd write error */ + transport_write_count = 0; + transport_write_error_at = 1; + assert (thrift_binary_protocol_write_field_begin (protocol, "test", T_VOID, + 1, NULL) == -1); + transport_write_error_at = -1; + + /* test 2nd read failure on a field */ + thrift_binary_protocol_write_byte (protocol, T_VOID, NULL); + + /* test write_field_stop */ + assert (thrift_binary_protocol_write_field_stop (protocol, NULL) > 0); + + /* write a map */ + assert (thrift_binary_protocol_write_map_begin (protocol, T_VOID, T_VOID, + 1, NULL) > 0); + assert (thrift_binary_protocol_write_map_end (protocol, NULL) == 0); + + /* test 2nd read failure on a map */ + thrift_binary_protocol_write_byte (protocol, T_VOID, NULL); + + /* test 3rd read failure on a map */ + thrift_binary_protocol_write_byte (protocol, T_VOID, NULL); + thrift_binary_protocol_write_byte (protocol, T_VOID, NULL); + + /* test 1st write failure on a map */ + transport_write_error = 1; + assert (thrift_binary_protocol_write_map_begin (protocol, T_VOID, T_VOID, + 1, NULL) == -1); + transport_write_error = 0; + + /* test 2nd write failure on a map */ + transport_write_count = 0; + transport_write_error_at = 1; + assert (thrift_binary_protocol_write_map_begin (protocol, T_VOID, T_VOID, + 1, NULL) == -1); + transport_write_error_at = -1; + + /* test 3rd write failure on a map */ + transport_write_count = 0; + transport_write_error_at = 2; + assert (thrift_binary_protocol_write_map_begin (protocol, T_VOID, T_VOID, + 1, NULL) == -1); + transport_write_error_at = -1; + + /* test negative map size */ + thrift_binary_protocol_write_byte (protocol, T_VOID, NULL); + thrift_binary_protocol_write_byte (protocol, T_VOID, NULL); + thrift_binary_protocol_write_i32 (protocol, -10, NULL); + + /* test list operations */ + assert (thrift_binary_protocol_write_list_begin (protocol, T_VOID, + 1, NULL) > 0); + assert (thrift_binary_protocol_write_list_end (protocol, NULL) == 0); + + /* test 2nd read failure on a list */ + thrift_binary_protocol_write_byte (protocol, T_VOID, NULL); + + /* test negative list size */ + thrift_binary_protocol_write_byte (protocol, T_VOID, NULL); + thrift_binary_protocol_write_i32 (protocol, -10, NULL); + + /* test first write error on a list */ + transport_write_error = 1; + assert (thrift_binary_protocol_write_list_begin (protocol, T_VOID, + 1, NULL) == -1); + transport_write_error = 0; + + /* test 2nd write error on a list */ + transport_write_count = 0; + transport_write_error_at = 1; + assert (thrift_binary_protocol_write_list_begin (protocol, T_VOID, + 1, NULL) == -1); + transport_write_error_at = -1; + + /* test set operation s*/ + assert (thrift_binary_protocol_write_set_begin (protocol, T_VOID, + 1, NULL) > 0); + assert (thrift_binary_protocol_write_set_end (protocol, NULL) == 0); + + /* invalid version */ + assert (thrift_binary_protocol_write_i32 (protocol, -1, NULL) > 0); + + /* sz > 0 for a message */ + assert (thrift_binary_protocol_write_i32 (protocol, 1, NULL) > 0); + + /* send a valid message */ + thrift_binary_protocol_write_i32 (protocol, 0x80010000, NULL); + thrift_binary_protocol_write_string (protocol, "test", NULL); + thrift_binary_protocol_write_i32 (protocol, 1, NULL); + + /* broken 2nd read */ + thrift_binary_protocol_write_i32 (protocol, 0x80010000, NULL); + + /* send a broken 3rd read */ + thrift_binary_protocol_write_i32 (protocol, 0x80010000, NULL); + thrift_binary_protocol_write_string (protocol, "test", NULL); + + /* send a valid message */ + assert (thrift_binary_protocol_write_message_begin (protocol, "test", + T_CALL, 1, NULL) > 0); + + assert (thrift_binary_protocol_write_message_end (protocol, NULL) == 0); + + /* send broken writes */ + transport_write_error = 1; + assert (thrift_binary_protocol_write_message_begin (protocol, "test", + T_CALL, 1, NULL) == -1); + transport_write_error = 0; + + transport_write_count = 0; + transport_write_error_at = 2; + assert (thrift_binary_protocol_write_message_begin (protocol, "test", + T_CALL, 1, NULL) == -1); + transport_write_error_at = -1; + + transport_write_count = 0; + transport_write_error_at = 3; + assert (thrift_binary_protocol_write_message_begin (protocol, "test", + T_CALL, 1, NULL) == -1); + transport_write_error_at = -1; + + /* clean up */ + thrift_transport_close (transport, NULL); + g_object_unref (tsocket); + g_object_unref (protocol); + assert (wait (&status) == pid); + assert (status == 0); + } +} + + +static void +thrift_server_primitives (const int port) +{ + ThriftServerTransport *transport = NULL; + ThriftTransport *client = NULL; + ThriftBinaryProtocol *tbp = NULL; + ThriftProtocol *protocol = NULL; + gboolean value_boolean = FALSE; + gint8 value_byte = 0; + gint16 value_16 = 0; + gint32 value_32 = 0; + gint64 value_64 = 0; + gdouble value_double = 0; + gchar *string = NULL; + gpointer binary = NULL; + guint32 len = 0; + void *comparator = (void *) TEST_STRING; + + ThriftServerSocket *tsocket = g_object_new (THRIFT_TYPE_SERVER_SOCKET, + "port", port, NULL); + transport = THRIFT_SERVER_TRANSPORT (tsocket); + thrift_server_transport_listen (transport, NULL); + client = thrift_server_transport_accept (transport, NULL); + assert (client != NULL); + + tbp = g_object_new (THRIFT_TYPE_BINARY_PROTOCOL, "transport", + client, NULL); + protocol = THRIFT_PROTOCOL (tbp); + + assert (thrift_binary_protocol_read_bool (protocol, + &value_boolean, NULL) > 0); + assert (thrift_binary_protocol_read_byte (protocol, &value_byte, NULL) > 0); + assert (thrift_binary_protocol_read_i16 (protocol, &value_16, NULL) > 0); + assert (thrift_binary_protocol_read_i32 (protocol, &value_32, NULL) > 0); + assert (thrift_binary_protocol_read_i64 (protocol, &value_64, NULL) > 0); + assert (thrift_binary_protocol_read_double (protocol, + &value_double, NULL) > 0); + assert (thrift_binary_protocol_read_string (protocol, &string, NULL) > 0); + assert (thrift_binary_protocol_read_binary (protocol, &binary, + &len, NULL) > 0); + + assert (value_boolean == TEST_BOOL); + assert (value_byte = TEST_BYTE); + assert (value_16 = TEST_I16); + assert (value_32 = TEST_I32); + assert (value_64 = TEST_I64); + assert (value_double = TEST_DOUBLE); + assert (strcmp (TEST_STRING, string) == 0); + assert (memcmp (comparator, binary, len) == 0); + + g_free (string); + g_free (binary); + + thrift_binary_protocol_read_binary (protocol, &binary, &len, NULL); + g_free (binary); + + transport_read_count = 0; + transport_read_error_at = 0; + assert (thrift_binary_protocol_read_binary (protocol, &binary, + &len, NULL) == -1); + transport_read_error_at = -1; + + transport_read_count = 0; + transport_read_error_at = 1; + assert (thrift_binary_protocol_read_binary (protocol, &binary, + &len, NULL) == -1); + transport_read_error_at = -1; + + transport_read_error = 1; + assert (thrift_binary_protocol_read_bool (protocol, + &value_boolean, NULL) == -1); + assert (thrift_binary_protocol_read_byte (protocol, + &value_byte, NULL) == -1); + assert (thrift_binary_protocol_read_i16 (protocol, + &value_16, NULL) == -1); + assert (thrift_binary_protocol_read_i32 (protocol, &value_32, NULL) == -1); + assert (thrift_binary_protocol_read_i64 (protocol, &value_64, NULL) == -1); + assert (thrift_binary_protocol_read_double (protocol, + &value_double, NULL) == -1); + transport_read_error = 0; + + /* test partial write failure */ + thrift_protocol_read_i32 (protocol, &value_32, NULL); + + thrift_transport_read_end (client, NULL); + thrift_transport_close (client, NULL); + + g_object_unref (tbp); + g_object_unref (client); + g_object_unref (tsocket); +} + +static void +thrift_server_complex_types (const int port) +{ + ThriftServerTransport *transport = NULL; + ThriftTransport *client = NULL; + ThriftBinaryProtocol *tbp = NULL; + ThriftProtocol *protocol = NULL; + gchar *struct_name = NULL; + gchar *field_name = NULL; + gchar *message_name = NULL; + ThriftType element_type, key_type, value_type, field_type; + ThriftMessageType message_type; + gint8 value = 0; + gint16 field_id = 0; + guint32 size = 0; + gint32 seqid = 0; + gint32 version = 0; + + ThriftServerSocket *tsocket = g_object_new (THRIFT_TYPE_SERVER_SOCKET, + "port", port, NULL); + transport = THRIFT_SERVER_TRANSPORT (tsocket); + thrift_server_transport_listen (transport, NULL); + client = thrift_server_transport_accept (transport, NULL); + assert (client != NULL); + + tbp = g_object_new (THRIFT_TYPE_BINARY_PROTOCOL, "transport", + client, NULL); + protocol = THRIFT_PROTOCOL (tbp); + + thrift_binary_protocol_read_struct_begin (protocol, &struct_name, NULL); + thrift_binary_protocol_read_struct_end (protocol, NULL); + + thrift_binary_protocol_read_field_begin (protocol, &field_name, &field_type, + &field_id, NULL); + thrift_binary_protocol_read_field_end (protocol, NULL); + + /* test first read error on a field */ + transport_read_error = 1; + assert (thrift_binary_protocol_read_field_begin (protocol, + &field_name, &field_type, + &field_id, NULL) == -1); + transport_read_error = 0; + + /* test 2nd write failure */ + thrift_binary_protocol_read_byte (protocol, &value, NULL); + + /* test 2nd read failure on a field */ + transport_read_count = 0; + transport_read_error_at = 1; + assert (thrift_binary_protocol_read_field_begin (protocol, + &field_name, &field_type, + &field_id, NULL) == -1); + transport_read_error_at = -1; + + /* test field stop */ + thrift_binary_protocol_read_field_begin (protocol, &field_name, &field_type, + &field_id, NULL); + + thrift_binary_protocol_read_map_begin (protocol, &key_type, &value_type, + &size, NULL); + thrift_binary_protocol_read_map_end (protocol, NULL); + + /* test read failure on a map */ + transport_read_count = 0; + transport_read_error_at = 0; + assert (thrift_binary_protocol_read_map_begin (protocol, + &key_type, &value_type, + &size, NULL) == -1); + transport_read_error_at = -1; + + /* test 2nd read failure on a map */ + transport_read_count = 0; + transport_read_error_at = 1; + assert (thrift_binary_protocol_read_map_begin (protocol, + &key_type, &value_type, + &size, NULL) == -1); + transport_read_error_at = -1; + + /* test 3rd read failure on a map */ + transport_read_count = 0; + transport_read_error_at = 2; + assert (thrift_binary_protocol_read_map_begin (protocol, + &key_type, &value_type, + &size, NULL) == -1); + transport_read_error_at = -1; + + /* test 2nd write failure */ + thrift_binary_protocol_read_byte (protocol, &value, NULL); + + /* test 3rd write failure */ + thrift_binary_protocol_read_byte (protocol, &value, NULL); + thrift_binary_protocol_read_byte (protocol, &value, NULL); + + /* test negative map size */ + assert (thrift_binary_protocol_read_map_begin (protocol, + &key_type, &value_type, + &size, NULL) == -1); + + /* test list operations */ + thrift_binary_protocol_read_list_begin (protocol, &element_type, &size, NULL); + thrift_binary_protocol_read_list_end (protocol, NULL); + + /* test read failure */ + transport_read_error = 1; + assert (thrift_binary_protocol_read_list_begin (protocol, &element_type, + &size, NULL) == -1); + transport_read_error = 0; + + /* test 2nd read failure */ + transport_read_count = 0; + transport_read_error_at = 1; + thrift_binary_protocol_read_list_begin (protocol, &element_type, &size, NULL); + transport_read_error_at = -1; + + /* test negative list size failure */ + thrift_binary_protocol_read_list_begin (protocol, &element_type, &size, NULL); + + /* test 2nd write failure */ + thrift_binary_protocol_read_byte (protocol, &value, NULL); + + /* test set operations */ + thrift_binary_protocol_read_set_begin (protocol, &element_type, &size, NULL); + thrift_binary_protocol_read_set_end (protocol, NULL); + + /* broken read */ + transport_read_error = 1; + assert (thrift_binary_protocol_read_message_begin (protocol, &message_name, + &message_type, &seqid, + NULL) == -1); + transport_read_error = 0; + + /* invalid protocol version */ + assert (thrift_binary_protocol_read_message_begin (protocol, &message_name, + &message_type, &seqid, + NULL) == -1); + + /* sz > 0 */ + assert (thrift_binary_protocol_read_message_begin (protocol, &message_name, + &message_type, &seqid, + NULL) > 0); + + /* read a valid message */ + assert (thrift_binary_protocol_read_message_begin (protocol, &message_name, + &message_type, &seqid, + NULL) > 0); + g_free (message_name); + + /* broken 2nd read on a message */ + transport_read_count = 0; + transport_read_error_at = 1; + assert (thrift_binary_protocol_read_message_begin (protocol, &message_name, + &message_type, &seqid, + NULL) == -1); + transport_read_error_at = -1; + + /* broken 3rd read on a message */ + transport_read_count = 0; + transport_read_error_at = 3; /* read_string does two reads */ + assert (thrift_binary_protocol_read_message_begin (protocol, &message_name, + &message_type, &seqid, + NULL) == -1); + g_free (message_name); + transport_read_error_at = -1; + + /* read a valid message */ + assert (thrift_binary_protocol_read_message_begin (protocol, &message_name, + &message_type, &seqid, + NULL) > 0); + g_free (message_name); + + assert (thrift_binary_protocol_read_message_end (protocol, NULL) == 0); + + /* handle 2nd write failure on a message */ + thrift_binary_protocol_read_i32 (protocol, &version, NULL); + + /* handle 2nd write failure on a message */ + thrift_binary_protocol_read_i32 (protocol, &version, NULL); + thrift_binary_protocol_read_string (protocol, &message_name, NULL); + + g_object_unref (client); + // TODO: investigate g_object_unref (tbp); + g_object_unref (tsocket); +} + +int +main(void) +{ + g_type_init (); + test_create_and_destroy (); + test_initialize (); + test_read_and_write_primitives (); + test_read_and_write_complex_types (); + + return 0; +} + diff --git a/lib/c_glib/test/testsimpleserver.c b/lib/c_glib/test/testsimpleserver.c new file mode 100644 index 00000000..182e9ef7 --- /dev/null +++ b/lib/c_glib/test/testsimpleserver.c @@ -0,0 +1,113 @@ +#include +#include +#include +#include +#include +#include + +#include "processor/thrift_processor.h" +#include "transport/thrift_server_socket.h" + +#define TEST_PORT 51199 + +#include "server/thrift_simple_server.c" + +/* create a rudimentary processor */ +#define TEST_PROCESSOR_TYPE (test_processor_get_type ()) + +struct _TestProcessor +{ + ThriftProcessor parent; +}; +typedef struct _TestProcessor TestProcessor; + +struct _TestProcessorClass +{ + ThriftProcessorClass parent; +}; +typedef struct _TestProcessorClass TestProcessorClass; + +gboolean +test_processor_process (ThriftProcessor *processor, ThriftProtocol *in, + ThriftProtocol *out) +{ + return FALSE; +} + +static void +test_processor_class_init (ThriftProcessorClass *proc) +{ + proc->process = test_processor_process; +} + +GType +test_processor_get_type (void) +{ + static GType type = 0; + + if (type == 0) + { + static const GTypeInfo info = + { + sizeof (TestProcessorClass), + NULL, /* base_init */ + NULL, /* base_finalize */ + (GClassInitFunc) test_processor_class_init, + NULL, /* class_finalize */ + NULL, /* class_data */ + sizeof (TestProcessor), + 0, /* n_preallocs */ + NULL, /* instance_init */ + NULL, /* value_table */ + }; + + type = g_type_register_static (THRIFT_TYPE_PROCESSOR, + "TestProcessorType", + &info, 0); + } + + return type; +} + +static void +test_server (void) +{ + int status; + pid_t pid; + TestProcessor *p = NULL; + ThriftServerSocket *tss = NULL; + ThriftSimpleServer *ss = NULL; + + p = g_object_new (TEST_PROCESSOR_TYPE, NULL); + tss = g_object_new (THRIFT_TYPE_SERVER_SOCKET, "port", TEST_PORT, NULL); + ss = g_object_new (THRIFT_TYPE_SIMPLE_SERVER, "processor", p, + "server_transport", THRIFT_SERVER_TRANSPORT (tss), NULL); + + /* run the server in a child process */ + pid = fork (); + assert (pid >= 0); + + if (pid == 0) + { + THRIFT_SERVER_GET_CLASS (THRIFT_SERVER (ss))->serve (THRIFT_SERVER (ss)); + exit (0); + } else { + sleep (5); + kill (pid, SIGINT); + + g_object_unref (ss); + g_object_unref (tss); + g_object_unref (p); + assert (wait (&status) == pid); + assert (status == SIGINT); + } +} + +int +main (void) +{ + g_type_init (); + test_server (); + + return 0; +} diff --git a/lib/c_glib/test/teststruct.c b/lib/c_glib/test/teststruct.c new file mode 100644 index 00000000..cb401e2e --- /dev/null +++ b/lib/c_glib/test/teststruct.c @@ -0,0 +1,121 @@ +#include +#include + +#include "../src/thrift_struct.c" + +/* tests to ensure we can extend a ThriftStruct */ + +struct _ThriftTestStruct +{ + ThriftStruct parent; +}; +typedef struct _ThriftTestStruct ThriftTestStruct; + +struct _ThriftTestStructClass +{ + ThriftStructClass parent; +}; +typedef struct _ThriftTestStructClass ThriftTestStructClass; + +GType thrift_test_struct_get_type (void); + +#define THRIFT_TYPE_TEST_STRUCT (thrift_test_struct_get_type ()) +#define THRIFT_TEST_STRUCT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), \ + THRIFT_TYPE_TEST_STRUCT, \ + ThriftTestStruct)) +#define THRIFT_TEST_STRUCT_CLASS(c) (G_TYPE_CHECK_CLASS_CAST ((c), \ + THRIFT_TYPE_TEST_STRUCT, \ + ThriftTestStructClass)) +#define THRIFT_IS_TEST_STRUCT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \ + THRIFT_TYPE_TEST_STRUCT)) +#define THRIFT_IS_TEST_STRUCT_CLASS(c) (G_TYPE_CHECK_CLASS_TYPE ((c), \ + THRIFT_TYPE_TEST_STRUCT)) +#define THRIFT_TEST_STRUCT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), \ + THRIFT_TYPE_TEST_STRUCT, \ + ThriftTestStructClass)) + +/* test declarations */ +gint32 thrift_test_struct_read (ThriftStruct *object, ThriftProtocol *protocol, + GError **error); +gint32 thrift_test_struct_write (ThriftStruct *object, ThriftProtocol *protocol, + GError **error); + +static void +thrift_test_struct_class_init (ThriftTestStructClass *cls) +{ + ThriftStructClass *ts_cls = THRIFT_STRUCT_CLASS (cls); + ts_cls->read = thrift_test_struct_read; + ts_cls->write = thrift_test_struct_write; +} + +static void +thrift_test_struct_instance_init (ThriftTestStruct *s) +{ + (void) s; +} + +GType +thrift_test_struct_get_type (void) +{ + static GType type = 0; + + if (type == 0) + { + static const GTypeInfo type_info = + { + sizeof (ThriftTestStructClass), + NULL, + NULL, + (GClassInitFunc) thrift_test_struct_class_init, + NULL, + NULL, + sizeof (ThriftTestStruct), + 0, + (GInstanceInitFunc) thrift_test_struct_instance_init, + NULL, + }; + + type = g_type_register_static (THRIFT_TYPE_STRUCT, + "ThriftTestStructType", &type_info, 0); + } + + return type; +} + +gint32 +thrift_test_struct_read (ThriftStruct *object, ThriftProtocol *protocol, + GError **error) +{ + return 0; +} + +gint32 +thrift_test_struct_write (ThriftStruct *object, ThriftProtocol *protocol, + GError **error) +{ + return 0; +} + + +static void +test_initialize_object (void) +{ + ThriftTestStruct *t = NULL; + + t = g_object_new (THRIFT_TYPE_TEST_STRUCT, NULL); + assert ( THRIFT_IS_STRUCT (t)); + thrift_struct_read (THRIFT_STRUCT (t), NULL, NULL); + thrift_struct_write (THRIFT_STRUCT (t), NULL, NULL); + thrift_test_struct_read (THRIFT_STRUCT (t), NULL, NULL); + thrift_test_struct_write (THRIFT_STRUCT (t), NULL, NULL); + g_object_unref (t); +} + +int +main(void) +{ + g_type_init (); + test_initialize_object (); + + return 0; +} diff --git a/lib/c_glib/test/testthrifttest.c b/lib/c_glib/test/testthrifttest.c new file mode 100644 index 00000000..6020f9cd --- /dev/null +++ b/lib/c_glib/test/testthrifttest.c @@ -0,0 +1,28 @@ +#include +#include + +#include "transport/thrift_server_transport.h" +#include "transport/thrift_server_socket.h" + +static const char TEST_ADDRESS[] = "localhost"; +static const int TEST_PORT = 64444; + +static void thrift_server (const int port); + +static void +thrift_server (const int port) +{ + ThriftServerSocket *tsocket = g_object_new (THRIFT_TYPE_SERVER_SOCKET, + "port", port, NULL); + + g_object_unref (tsocket); +} + +int +main(void) +{ + g_type_init (); + thrift_server (TEST_PORT); + return 0; +} + diff --git a/lib/c_glib/test/testthrifttestclient.cpp b/lib/c_glib/test/testthrifttestclient.cpp new file mode 100644 index 00000000..4b5b8411 --- /dev/null +++ b/lib/c_glib/test/testthrifttestclient.cpp @@ -0,0 +1,551 @@ + +/* test a C client with a C++ server */ + +#include +#include +#include +#include +#include +#include +#include +#include "ThriftTest.h" +#include "ThriftTest_types.h" + +#include + +using namespace std; +using namespace boost; + +using namespace apache::thrift; +using namespace apache::thrift::concurrency; +using namespace apache::thrift::protocol; +using namespace apache::thrift::transport; +using namespace apache::thrift::server; + +using namespace thrift::test; + +#define TEST_PORT 9980 + +// Extra functions required for ThriftTest_types to work +namespace thrift { namespace test { + +bool Insanity::operator<(thrift::test::Insanity const& other) const { + using apache::thrift::ThriftDebugString; + return ThriftDebugString(*this) < ThriftDebugString(other); +} + +}} + +class TestHandler : public ThriftTestIf { + public: + TestHandler() {} + + void testVoid() { + printf("[C -> C++] testVoid()\n"); + } + + void testString(string& out, const string &thing) { + printf("[C -> C++] testString(\"%s\")\n", thing.c_str()); + out = thing; + } + + int8_t testByte(const int8_t thing) { + printf("[C -> C++] testByte(%d)\n", (int)thing); + return thing; + } + int32_t testI32(const int32_t thing) { + printf("[C -> C++] testI32(%d)\n", thing); + return thing; + } + + int64_t testI64(const int64_t thing) { + printf("[C -> C++] testI64(%lld)\n", thing); + return thing; + } + + double testDouble(const double thing) { + printf("[C -> C++] testDouble(%lf)\n", thing); + return thing; + } + + void testStruct(Xtruct& out, const Xtruct &thing) { + printf("[C -> C++] testStruct({\"%s\", %d, %d, %lld})\n", thing.string_thing.c_str(), (int)thing.byte_thing, thing.i32_thing, thing.i64_thing); + out = thing; + } + + void testNest(Xtruct2& out, const Xtruct2& nest) { + const Xtruct &thing = nest.struct_thing; + printf("[C -> C++] testNest({%d, {\"%s\", %d, %d, %lld}, %d})\n", (int)nest.byte_thing, thing.string_thing.c_str(), (int)thing.byte_thing, thing.i32_thing, thing.i64_thing, nest.i32_thing); + out = nest; + } + + void testMap(map &out, const map &thing) { + printf("[C -> C++] testMap({"); + map::const_iterator m_iter; + bool first = true; + for (m_iter = thing.begin(); m_iter != thing.end(); ++m_iter) { + if (first) { + first = false; + } else { + printf(", "); + } + printf("%d => %d", m_iter->first, m_iter->second); + } + printf("})\n"); + out = thing; + } + + void testSet(set &out, const set &thing) { + printf("[C -> C++] testSet({"); + set::const_iterator s_iter; + bool first = true; + for (s_iter = thing.begin(); s_iter != thing.end(); ++s_iter) { + if (first) { + first = false; + } else { + printf(", "); + } + printf("%d", *s_iter); + } + printf("})\n"); + out = thing; + } + + void testList(vector &out, const vector &thing) { + printf("[C -> C++] testList({"); + vector::const_iterator l_iter; + bool first = true; + for (l_iter = thing.begin(); l_iter != thing.end(); ++l_iter) { + if (first) { + first = false; + } else { printf(", "); + } + printf("%d", *l_iter); + } + printf("})\n"); + out = thing; + } + + Numberz::type testEnum(const Numberz::type thing) { + printf("[C -> C++] testEnum(%d)\n", thing); + return thing; + } + + UserId testTypedef(const UserId thing) { + printf("[C -> C++] testTypedef(%lld)\n", thing); + return thing; } + + void testMapMap(map > &mapmap, const int32_t hello) { + printf("[C -> C++] testMapMap(%d)\n", hello); + + map pos; + map neg; + for (int i = 1; i < 5; i++) { + pos.insert(make_pair(i,i)); + neg.insert(make_pair(-i,-i)); + } + + mapmap.insert(make_pair(4, pos)); + mapmap.insert(make_pair(-4, neg)); + + } + + void testInsanity(map > &insane, const Insanity &argument) { + printf("[C -> C++] testInsanity()\n"); + + Xtruct hello; + hello.string_thing = "Hello2"; + hello.byte_thing = 2; + hello.i32_thing = 2; + hello.i64_thing = 2; + + Xtruct goodbye; + goodbye.string_thing = "Goodbye4"; + goodbye.byte_thing = 4; + goodbye.i32_thing = 4; + goodbye.i64_thing = 4; + + Insanity crazy; + crazy.userMap.insert(make_pair(Numberz::EIGHT, 8)); + crazy.xtructs.push_back(goodbye); + + Insanity looney; + crazy.userMap.insert(make_pair(Numberz::FIVE, 5)); + crazy.xtructs.push_back(hello); + + map first_map; + map second_map; + + first_map.insert(make_pair(Numberz::TWO, crazy)); + first_map.insert(make_pair(Numberz::THREE, crazy)); + + second_map.insert(make_pair(Numberz::SIX, looney)); + + insane.insert(make_pair(1, first_map)); + insane.insert(make_pair(2, second_map)); + + printf("return"); + printf(" = {"); + map >::const_iterator i_iter; + for (i_iter = insane.begin(); i_iter != insane.end(); ++i_iter) { + printf("%lld => {", i_iter->first); + map::const_iterator i2_iter; + for (i2_iter = i_iter->second.begin(); + i2_iter != i_iter->second.end(); + ++i2_iter) { + printf("%d => {", i2_iter->first); + map userMap = i2_iter->second.userMap; + map::const_iterator um; + printf("{"); + for (um = userMap.begin(); um != userMap.end(); ++um) { + printf("%d => %lld, ", um->first, um->second); + } + printf("}, "); + + vector xtructs = i2_iter->second.xtructs; + vector::const_iterator x; + printf("{"); + for (x = xtructs.begin(); x != xtructs.end(); ++x) { + printf("{\"%s\", %d, %d, %lld}, ", x->string_thing.c_str(), (int)x->byte_thing, x->i32_thing, x->i64_thing); + } + printf("}"); + + printf("}, "); + } + printf("}, "); + } + printf("}\n"); + + + } + + void testMulti(Xtruct &hello, const int8_t arg0, const int32_t arg1, const int64_t arg2, const std::map &arg3, const Numberz::type arg4, const UserId arg5) { + printf("[C -> C++] testMulti()\n"); + + hello.string_thing = "Hello2"; + hello.byte_thing = arg0; + hello.i32_thing = arg1; + hello.i64_thing = (int64_t)arg2; + } + + void testException(const std::string &arg) + throw(Xception, apache::thrift::TException) + { + printf("[C -> C++] testException(%s)\n", arg.c_str()); + if (arg.compare("Xception") == 0) { + Xception e; + e.errorCode = 1001; + e.message = arg; + throw e; + } else if (arg.compare("ApplicationException") == 0) { + apache::thrift::TException e; + throw e; + } else { + Xtruct result; + result.string_thing = arg; + return; + } + } + + void testMultiException(Xtruct &result, const std::string &arg0, const std::string &arg1) throw(Xception, Xception2) { + + printf("[C -> C++] testMultiException(%s, %s)\n", arg0.c_str(), arg1.c_str()); + + if (arg0.compare("Xception") == 0) { + Xception e; + e.errorCode = 1001; + e.message = "This is an Xception"; + throw e; + } else if (arg0.compare("Xception2") == 0) { + Xception2 e; + e.errorCode = 2002; + e.struct_thing.string_thing = "This is an Xception2"; + throw e; + } else { + result.string_thing = arg1; + return; + } + } + + void testOneway(int sleepFor) { + printf("testOneway(%d): Sleeping...\n", sleepFor); + sleep(sleepFor); + printf("testOneway(%d): done sleeping!\n", sleepFor); + } +}; + +// C CLIENT +extern "C" { + +#include "t_test_thrift_test.h" +#include "t_test_thrift_test_types.h" +#include "transport/thrift_socket.h" +#include "protocol/thrift_protocol.h" +#include "protocol/thrift_binary_protocol.h" + +static void +test_thrift_client (void) +{ + ThriftSocket *tsocket = NULL; + ThriftBinaryProtocol *protocol = NULL; + TTestThriftTestClient *client = NULL; + TTestThriftTestIf *iface = NULL; + GError *error = NULL; + gchar *string = NULL; + gint8 byte = 0; + gint16 i16 = 0; + gint32 i32 = 0, another_i32 = 56789; + gint64 i64 = 0; + double dbl = 0.0; + TTestXtruct *xtruct_in, *xtruct_out; + TTestXtruct2 *xtruct2_in, *xtruct2_out; + GHashTable *map_in = NULL, *map_out = NULL; + GHashTable *set_in = NULL, *set_out = NULL; + GArray *list_in = NULL, *list_out = NULL; + TTestNumberz enum_in, enum_out; + TTestUserId user_id_in, user_id_out; + GHashTable *insanity_in = NULL; + TTestXtruct *xtruct1, *xtruct2; + TTestInsanity *insanity_out = NULL; + TTestXtruct *multi_in = NULL; + GHashTable *multi_map_out = NULL; + TTestXception *xception = NULL; + TTestXception2 *xception2 = NULL; + + // initialize gobject + g_type_init (); + + // create a C client + tsocket = (ThriftSocket *) g_object_new (THRIFT_TYPE_SOCKET, + "hostname", "localhost", + "port", TEST_PORT, NULL); + protocol = (ThriftBinaryProtocol *) g_object_new (THRIFT_TYPE_BINARY_PROTOCOL, + "transport", + tsocket, NULL); + client = (TTestThriftTestClient *) g_object_new (T_TEST_TYPE_THRIFT_TEST_CLIENT, "input_protocol", protocol, "output_protocol", protocol, NULL); + iface = T_TEST_THRIFT_TEST_IF (client); + + // open and send + thrift_transport_open (THRIFT_TRANSPORT(tsocket), NULL); + + assert (t_test_thrift_test_client_test_void (iface, &error) == TRUE); + assert (error == NULL); + + assert (t_test_thrift_test_client_test_string (iface, &string, "test123", &error) == TRUE); + assert (strcmp (string, "test123") == 0); + g_free (string); + assert (error == NULL); + + assert (t_test_thrift_test_client_test_byte (iface, &byte, (gint8) 5, &error) == TRUE); + assert (byte == 5); + assert (error == NULL); + + assert (t_test_thrift_test_client_test_i32 (iface, &i32, 123, &error) == TRUE); + assert (i32 == 123); + assert (error == NULL); + + assert (t_test_thrift_test_client_test_i64 (iface, &i64, 12345, &error) == TRUE); + assert (i64 == 12345); + assert (error == NULL); + + assert (t_test_thrift_test_client_test_double (iface, &dbl, 5.6, &error) == TRUE); + assert (dbl == 5.6); + assert (error == NULL); + + xtruct_out = (TTestXtruct *) g_object_new (T_TEST_TYPE_XTRUCT, NULL); + xtruct_out->byte_thing = 1; + xtruct_out->__isset_byte_thing = TRUE; + xtruct_out->i32_thing = 15; + xtruct_out->__isset_i32_thing = TRUE; + xtruct_out->i64_thing = 151; + xtruct_out->__isset_i64_thing = TRUE; + xtruct_out->string_thing = g_strdup ("abc123"); + xtruct_out->__isset_string_thing = TRUE; + assert (t_test_thrift_test_client_test_struct (iface, &xtruct_in, xtruct_out, &error) == TRUE); + assert (error == NULL); + + xtruct2_out = (TTestXtruct2 *) g_object_new (T_TEST_TYPE_XTRUCT2, NULL); + xtruct2_out->byte_thing = 1; + xtruct2_out->__isset_byte_thing = TRUE; + xtruct2_out->struct_thing = xtruct_out; + xtruct2_out->__isset_struct_thing = TRUE; + xtruct2_out->i32_thing = 123; + xtruct2_out->__isset_i32_thing = TRUE; + assert (t_test_thrift_test_client_test_nest (iface, &xtruct2_in, xtruct2_out, &error) == TRUE); + assert (error == NULL); + + g_object_unref (xtruct2_out); + g_object_unref (xtruct2_in); + g_free (xtruct_out->string_thing); + g_object_unref (xtruct_out); + g_object_unref (xtruct_in); + + map_out = g_hash_table_new (NULL, NULL); + map_in = g_hash_table_new (NULL, NULL); g_hash_table_insert (map_out, &i32, &i32); + assert (t_test_thrift_test_client_test_map (iface, &map_in, map_out, &error) == TRUE); + assert (error == NULL); + g_hash_table_destroy (map_out); + g_hash_table_destroy (map_in); + + set_out = g_hash_table_new (NULL, NULL); + set_in = g_hash_table_new (NULL, NULL); + g_hash_table_insert (set_out, &i32, &i32); + assert (t_test_thrift_test_client_test_set (iface, &set_in, set_out, &error) == TRUE); + assert (error == NULL); + g_hash_table_destroy (set_out); + g_hash_table_destroy (set_in); + + list_out = g_array_new(TRUE, TRUE, sizeof(gint32)); + list_in = g_array_new(TRUE, TRUE, sizeof(gint32)); + another_i32 = 456; + g_array_append_val (list_out, i32); + g_array_append_val (list_out, another_i32); + assert (t_test_thrift_test_client_test_list (iface, &list_in, list_out, &error) == TRUE); + assert (error == NULL); + g_array_free (list_out, TRUE); + g_array_free (list_in, TRUE); + + enum_out = T_TEST_NUMBERZ_ONE; + assert (t_test_thrift_test_client_test_enum (iface, &enum_in, enum_out, &error) == TRUE); + assert (enum_in == enum_out); + assert (error == NULL); + + user_id_out = 12345; + assert (t_test_thrift_test_client_test_typedef (iface, &user_id_in, user_id_out, &error) == TRUE); + assert (user_id_in == user_id_out); + assert (error == NULL); + + map_in = g_hash_table_new (NULL, NULL); + assert (t_test_thrift_test_client_test_map_map (iface, &map_in, i32, &error) == TRUE); + assert (error == NULL); + g_hash_table_destroy (map_in); + + // insanity + insanity_out = (TTestInsanity *) g_object_new (T_TEST_TYPE_INSANITY, NULL); + insanity_out->userMap = g_hash_table_new (NULL, NULL); + g_hash_table_insert (insanity_out->userMap, &enum_out, &user_id_out); + + xtruct1 = (TTestXtruct *) g_object_new (T_TEST_TYPE_XTRUCT, NULL); + xtruct1->byte_thing = 1; + xtruct1->__isset_byte_thing = TRUE; + xtruct1->i32_thing = 15; + xtruct1->__isset_i32_thing = TRUE; + xtruct1->i64_thing = 151; + xtruct1->__isset_i64_thing = TRUE; + xtruct1->string_thing = g_strdup ("abc123"); + xtruct1->__isset_string_thing = TRUE; + xtruct2 = (TTestXtruct *) g_object_new (T_TEST_TYPE_XTRUCT, NULL); + xtruct2->byte_thing = 1; + xtruct2->__isset_byte_thing = TRUE; + xtruct2->i32_thing = 15; + xtruct2->__isset_i32_thing = TRUE; + xtruct2->i64_thing = 151; + xtruct2->__isset_i64_thing = TRUE; + xtruct2->string_thing = g_strdup ("abc123"); + xtruct2->__isset_string_thing = TRUE; + + insanity_out->xtructs = g_ptr_array_new (); + insanity_in = g_hash_table_new (NULL, NULL); + g_ptr_array_add (insanity_out->xtructs, xtruct1); + g_ptr_array_add (insanity_out->xtructs, xtruct2); + assert (t_test_thrift_test_client_test_insanity (iface, &insanity_in, insanity_out, &error) == TRUE); + + g_hash_table_unref (insanity_in); + g_ptr_array_free (insanity_out->xtructs, TRUE); + g_free (xtruct1->string_thing); + g_free (xtruct2->string_thing); + g_object_unref (xtruct1); + g_object_unref (xtruct2); + + multi_map_out = g_hash_table_new (NULL, NULL); + string = g_strdup ("abc123"); + g_hash_table_insert (multi_map_out, &i16, string); + assert (t_test_thrift_test_client_test_multi (iface, &multi_in, byte, i32, i64, multi_map_out, enum_out, user_id_out, &error) == TRUE); + assert (multi_in->i32_thing == i32); + assert (multi_in->i64_thing == i64); + g_object_unref (multi_in); + g_hash_table_unref (multi_map_out); + g_free (string); + + assert (t_test_thrift_test_client_test_exception (iface, "Xception", &xception, &error) == FALSE); + assert (xception->errorCode == 1001); + g_error_free (error); + error = NULL; + + assert (t_test_thrift_test_client_test_exception (iface, "ApplicationException", &xception, &error) == FALSE); + g_error_free (error); + error = NULL; + g_object_unref (xception); + xception = NULL; + + assert (t_test_thrift_test_client_test_exception (iface, "Test", &xception, &error) == TRUE); + assert (error == NULL); + + assert (t_test_thrift_test_client_test_multi_exception (iface, &multi_in, "Xception", NULL, &xception, &xception2, &error) == FALSE); + assert (xception->errorCode == 1001); + g_error_free (error); + error = NULL; + g_object_unref (xception); + xception = NULL; + xception2 = NULL; + + assert (t_test_thrift_test_client_test_multi_exception (iface, &multi_in, "Xception2", NULL, &xception, &xception2, &error) == FALSE); + assert (xception2->errorCode == 2002); + g_error_free (error); + error = NULL; + g_object_unref (xception2); + xception2 = NULL; + + assert (t_test_thrift_test_client_test_multi_exception (iface, &multi_in, NULL , NULL, &xception, &xception2, &error) == TRUE); + assert (error == NULL); + + assert (t_test_thrift_test_client_test_oneway (iface, 1, &error) == TRUE); + assert (error == NULL); + + /* sleep to let the oneway call go through */ + sleep (5); + + thrift_transport_close (THRIFT_TRANSPORT(tsocket), NULL); + g_object_unref (client); + g_object_unref (protocol); + g_object_unref (tsocket); +} + + +} /* extern "C" */ + + +static void +bailout (int signum) +{ + exit (1); +} + +int +main (int argc, char **argv) +{ + int status; + int pid = fork (); + assert (pid >= 0); + + if (pid == 0) /* child */ + { + shared_ptr protocolFactory(new TBinaryProtocolFactory()); + shared_ptr testHandler(new TestHandler()); + shared_ptr testProcessor(new ThriftTestProcessor(testHandler)); + shared_ptr serverSocket(new TServerSocket(TEST_PORT)); + shared_ptr transportFactory(new TBufferedTransportFactory()); + TSimpleServer simpleServer(testProcessor, serverSocket, transportFactory, protocolFactory); + signal (SIGALRM, bailout); + alarm (60); + simpleServer.serve(); + } else { + sleep (1); + test_thrift_client (); + kill (pid, SIGINT); + wait (&status) == pid; + } + + return 0; +} + diff --git a/lib/c_glib/test/testtransportsocket.c b/lib/c_glib/test/testtransportsocket.c new file mode 100644 index 00000000..14579c8c --- /dev/null +++ b/lib/c_glib/test/testtransportsocket.c @@ -0,0 +1,200 @@ +#include +#include + +#include "transport/thrift_transport.h" +#include "transport/thrift_server_transport.h" +#include "transport/thrift_server_socket.h" + +#define TEST_DATA { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j' } + +/* substituted functions to test failures of system and library calls */ +static int socket_error = 0; +int +my_socket(int domain, int type, int protocol) +{ + if (socket_error == 0) + { + return socket (domain, type, protocol); + } + return -1; +} + +static int recv_error = 0; +ssize_t +my_recv(int socket, void *buffer, size_t length, int flags) +{ + if (recv_error == 0) + { + return recv (socket, buffer, length, flags); + } + return -1; +} + +static int send_error = 0; +ssize_t +my_send(int socket, const void *buffer, size_t length, int flags) +{ + if (send_error == 0) + { + return send (socket, buffer, length, flags); + } + return -1; +} + +#define socket my_socket +#define recv my_recv +#define send my_send +#include "../src/transport/thrift_socket.c" +#undef socket +#undef recv +#undef send + +static const char TEST_ADDRESS[] = "localhost"; +static const short TEST_PORT = 64444; + +static void thrift_socket_server (const int port); + +/* test object creation and destruction */ +static void +test_create_and_destroy(void) +{ + gchar *hostname = NULL; + guint port = 0; + + GObject *object = NULL; + object = g_object_new (THRIFT_TYPE_SOCKET, NULL); + assert (object != NULL); + g_object_get (G_OBJECT(object), "hostname", &hostname, "port", &port, NULL); + g_free (hostname); + + g_object_unref (object); +} + +static void +test_open_and_close(void) +{ + ThriftSocket *tsocket = NULL; + ThriftTransport *transport = NULL; + GError *err = NULL; + + /* open a connection and close it */ + tsocket = g_object_new (THRIFT_TYPE_SOCKET, "hostname", "localhost", + "port", 51188, NULL); + transport = THRIFT_TRANSPORT (tsocket); + thrift_socket_open (transport, NULL); + assert (thrift_socket_is_open (transport) == TRUE); + thrift_socket_close (transport, NULL); + assert (thrift_socket_is_open (transport) == FALSE); + + /* test close failure */ + tsocket->sd = -1; + thrift_socket_close (transport, NULL); + g_object_unref (tsocket); + + /* try a hostname lookup failure */ + tsocket = g_object_new (THRIFT_TYPE_SOCKET, "hostname", "localhost.broken", + NULL); + transport = THRIFT_TRANSPORT (tsocket); + assert (thrift_socket_open (transport, &err) == FALSE); + g_object_unref (tsocket); + g_error_free (err); + err = NULL; + + /* try an error call to socket() */ + tsocket = g_object_new (THRIFT_TYPE_SOCKET, "hostname", "localhost", NULL); + transport = THRIFT_TRANSPORT (tsocket); + socket_error = 1; + assert (thrift_socket_open (transport, &err) == FALSE); + socket_error = 0; + g_object_unref (tsocket); + g_error_free (err); +} + +static void +test_read_and_write(void) +{ + int status; + pid_t pid; + ThriftSocket *tsocket = NULL; + ThriftTransport *transport = NULL; + int port = 51199; + guchar buf[10] = TEST_DATA; /* a buffer */ + + pid = fork (); + assert ( pid >= 0 ); + + if ( pid == 0 ) + { + /* child listens */ + thrift_socket_server (port); + exit (0); + } else { + /* parent connects, wait a bit for the socket to be created */ + sleep (1); + + tsocket = g_object_new (THRIFT_TYPE_SOCKET, "hostname", "localhost", + "port", port, NULL); + transport = THRIFT_TRANSPORT (tsocket); + assert (thrift_socket_open (transport, NULL) == TRUE); + assert (thrift_socket_is_open (transport)); + thrift_socket_write (transport, buf, 10, NULL); + + /* write fail */ + send_error = 1; + thrift_socket_write (transport, buf, 1, NULL); + send_error = 0; + + thrift_socket_write_end (transport, NULL); + thrift_socket_flush (transport, NULL); + thrift_socket_close (transport, NULL); + g_object_unref (tsocket); + + assert ( wait (&status) == pid ); + assert ( status == 0 ); + } +} + +static void +thrift_socket_server (const int port) +{ + int bytes = 0; + ThriftServerTransport *transport = NULL; + ThriftTransport *client = NULL; + guchar buf[10]; /* a buffer */ + guchar match[10] = TEST_DATA; + + ThriftServerSocket *tsocket = g_object_new (THRIFT_TYPE_SERVER_SOCKET, + "port", port, NULL); + + transport = THRIFT_SERVER_TRANSPORT (tsocket); + thrift_server_transport_listen (transport, NULL); + client = thrift_server_transport_accept (transport, NULL); + assert (client != NULL); + + /* read 10 bytes */ + bytes = thrift_socket_read (client, buf, 10, NULL); + assert (bytes == 10); /* make sure we've read 10 bytes */ + assert ( memcmp(buf, match, 10) == 0 ); /* make sure what we got matches */ + + /* failed read */ + recv_error = 1; + thrift_socket_read (client, buf, 1, NULL); + recv_error = 0; + + thrift_socket_read_end (client, NULL); + thrift_socket_close (client, NULL); + g_object_unref (tsocket); + g_object_unref (client); +} + +int +main(void) +{ + g_type_init(); + test_create_and_destroy(); + test_open_and_close(); + test_read_and_write(); + + return 0; +} + diff --git a/lib/c_glib/thrift_c_glib.pc.in b/lib/c_glib/thrift_c_glib.pc.in new file mode 100644 index 00000000..44675dfa --- /dev/null +++ b/lib/c_glib/thrift_c_glib.pc.in @@ -0,0 +1,29 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: Thrift +Description: Thrift C API +Version: @VERSION@ +Libs: -L${libdir} -lthriftc +Cflags: -I${includedir}/thrift diff --git a/test/DebugProtoTest.thrift b/test/DebugProtoTest.thrift index b01129a8..79800475 100644 --- a/test/DebugProtoTest.thrift +++ b/test/DebugProtoTest.thrift @@ -17,6 +17,7 @@ * under the License. */ +namespace c_glib TTest namespace cpp thrift.test.debug namespace java thrift.test diff --git a/test/OptionalRequiredTest.thrift b/test/OptionalRequiredTest.thrift index 67e6f587..8108a7f7 100644 --- a/test/OptionalRequiredTest.thrift +++ b/test/OptionalRequiredTest.thrift @@ -21,6 +21,7 @@ * details. */ +namespace c_glib TTest namespace cpp thrift.test namespace java thrift.test diff --git a/test/ThriftTest.thrift b/test/ThriftTest.thrift index ce324ef6..237023bc 100644 --- a/test/ThriftTest.thrift +++ b/test/ThriftTest.thrift @@ -21,6 +21,7 @@ * details. */ +namespace c_glib TTest namespace java thrift.test namespace cpp thrift.test namespace rb Thrift.Test -- 2.17.1