From afc6d8f650cd7e500b07134d11b936dc90a62a02 Mon Sep 17 00:00:00 2001 From: Christian Lavoie Date: Sun, 20 Feb 2011 02:39:19 +0000 Subject: [PATCH] THRIFT-625: Add support for 'Go'; provided by Aalok Shah. git-svn-id: https://svn.apache.org/repos/asf/thrift/trunk@1072478 13f79535-47bb-0310-9956-ffa450edef68 --- compiler/cpp/Makefile.am | 3 + compiler/cpp/src/generate/t_go_generator.cc | 2980 ++++++++++++++++++ configure.ac | 61 +- lib/Makefile.am | 3 +- lib/go/Make.deps | 134 + lib/go/Makefile | 64 + lib/go/deps.bash | 41 + lib/go/thrift/Makefile | 58 + lib/go/thrift/_testmain.go | 68 + lib/go/thrift/tapplication_exception.go | 169 + lib/go/thrift/tapplication_exception_test.go | 42 + lib/go/thrift/tbase.go | 66 + lib/go/thrift/tbinary_protocol.go | 493 +++ lib/go/thrift/tbinary_protocol_test.go | 31 + lib/go/thrift/tcompact_protocol.go | 856 +++++ lib/go/thrift/tcompact_protocol_test.go | 56 + lib/go/thrift/tcompare.go | 127 + lib/go/thrift/tcontainer.go | 28 + lib/go/thrift/texception.go | 56 + lib/go/thrift/texception_test.go | 38 + lib/go/thrift/tfield.go | 281 ++ lib/go/thrift/tframed_transport.go | 133 + lib/go/thrift/tframed_transport_test.go | 30 + lib/go/thrift/thttp_client.go | 146 + lib/go/thrift/thttp_client_test.go | 45 + lib/go/thrift/tiostream_transport.go | 231 ++ lib/go/thrift/tiostream_transport_test.go | 31 + lib/go/thrift/tjson_protocol.go | 537 ++++ lib/go/thrift/tjson_protocol_test.go | 674 ++++ lib/go/thrift/tlist.go | 222 ++ lib/go/thrift/tmap.go | 763 +++++ lib/go/thrift/tmemory_buffer.go | 127 + lib/go/thrift/tmemory_buffer_test.go | 30 + lib/go/thrift/tmessage.go | 70 + lib/go/thrift/tmessagetype.go | 34 + lib/go/thrift/tnonblocking_server.go | 178 ++ lib/go/thrift/tnonblocking_server_socket.go | 169 + lib/go/thrift/tnonblocking_socket.go | 192 ++ lib/go/thrift/tnonblocking_transport.go | 24 + lib/go/thrift/tnonblocking_transport_test.go | 88 + lib/go/thrift/tnumeric.go | 165 + lib/go/thrift/tprocessor.go | 33 + lib/go/thrift/tprocessor_factory.go | 62 + lib/go/thrift/tprotocol.go | 201 ++ lib/go/thrift/tprotocol_exception.go | 130 + lib/go/thrift/tprotocol_factory.go | 28 + lib/go/thrift/tprotocol_test.go | 1817 +++++++++++ lib/go/thrift/tserver.go | 61 + lib/go/thrift/tserver_socket.go | 194 ++ lib/go/thrift/tserver_test.go | 28 + lib/go/thrift/tserver_transport.go | 41 + lib/go/thrift/tset.go | 207 ++ lib/go/thrift/tsimple_json_protocol.go | 1281 ++++++++ lib/go/thrift/tsimple_json_protocol_test.go | 662 ++++ lib/go/thrift/tsimple_server.go | 166 + lib/go/thrift/tsocket.go | 203 ++ lib/go/thrift/tstruct.go | 93 + lib/go/thrift/ttransport.go | 181 ++ lib/go/thrift/ttransport_exception.go | 84 + lib/go/thrift/ttransport_factory.go | 47 + lib/go/thrift/ttransport_test.go | 131 + lib/go/thrift/ttype.go | 975 ++++++ test/ThriftTest.thrift | 1 + tutorial/go/Make.deps | 135 + tutorial/go/Makefile | 44 + tutorial/go/deps.bash | 45 + tutorial/go/src/CalculatorHandler.go | 101 + tutorial/go/src/GoClient.go | 92 + tutorial/go/src/GoServer.go | 85 + tutorial/go/src/Makefile | 22 + tutorial/go/src/main.go | 86 + 71 files changed, 16778 insertions(+), 2 deletions(-) create mode 100644 compiler/cpp/src/generate/t_go_generator.cc create mode 100644 lib/go/Make.deps create mode 100644 lib/go/Makefile create mode 100644 lib/go/deps.bash create mode 100644 lib/go/thrift/Makefile create mode 100644 lib/go/thrift/_testmain.go create mode 100644 lib/go/thrift/tapplication_exception.go create mode 100644 lib/go/thrift/tapplication_exception_test.go create mode 100644 lib/go/thrift/tbase.go create mode 100644 lib/go/thrift/tbinary_protocol.go create mode 100644 lib/go/thrift/tbinary_protocol_test.go create mode 100644 lib/go/thrift/tcompact_protocol.go create mode 100644 lib/go/thrift/tcompact_protocol_test.go create mode 100644 lib/go/thrift/tcompare.go create mode 100644 lib/go/thrift/tcontainer.go create mode 100644 lib/go/thrift/texception.go create mode 100644 lib/go/thrift/texception_test.go create mode 100644 lib/go/thrift/tfield.go create mode 100644 lib/go/thrift/tframed_transport.go create mode 100644 lib/go/thrift/tframed_transport_test.go create mode 100644 lib/go/thrift/thttp_client.go create mode 100644 lib/go/thrift/thttp_client_test.go create mode 100644 lib/go/thrift/tiostream_transport.go create mode 100644 lib/go/thrift/tiostream_transport_test.go create mode 100644 lib/go/thrift/tjson_protocol.go create mode 100644 lib/go/thrift/tjson_protocol_test.go create mode 100644 lib/go/thrift/tlist.go create mode 100644 lib/go/thrift/tmap.go create mode 100644 lib/go/thrift/tmemory_buffer.go create mode 100644 lib/go/thrift/tmemory_buffer_test.go create mode 100644 lib/go/thrift/tmessage.go create mode 100644 lib/go/thrift/tmessagetype.go create mode 100644 lib/go/thrift/tnonblocking_server.go create mode 100644 lib/go/thrift/tnonblocking_server_socket.go create mode 100644 lib/go/thrift/tnonblocking_socket.go create mode 100644 lib/go/thrift/tnonblocking_transport.go create mode 100644 lib/go/thrift/tnonblocking_transport_test.go create mode 100644 lib/go/thrift/tnumeric.go create mode 100644 lib/go/thrift/tprocessor.go create mode 100644 lib/go/thrift/tprocessor_factory.go create mode 100644 lib/go/thrift/tprotocol.go create mode 100644 lib/go/thrift/tprotocol_exception.go create mode 100644 lib/go/thrift/tprotocol_factory.go create mode 100644 lib/go/thrift/tprotocol_test.go create mode 100644 lib/go/thrift/tserver.go create mode 100644 lib/go/thrift/tserver_socket.go create mode 100644 lib/go/thrift/tserver_test.go create mode 100644 lib/go/thrift/tserver_transport.go create mode 100644 lib/go/thrift/tset.go create mode 100644 lib/go/thrift/tsimple_json_protocol.go create mode 100644 lib/go/thrift/tsimple_json_protocol_test.go create mode 100644 lib/go/thrift/tsimple_server.go create mode 100644 lib/go/thrift/tsocket.go create mode 100644 lib/go/thrift/tstruct.go create mode 100644 lib/go/thrift/ttransport.go create mode 100644 lib/go/thrift/ttransport_exception.go create mode 100644 lib/go/thrift/ttransport_factory.go create mode 100644 lib/go/thrift/ttransport_test.go create mode 100644 lib/go/thrift/ttype.go create mode 100644 tutorial/go/Make.deps create mode 100644 tutorial/go/Makefile create mode 100644 tutorial/go/deps.bash create mode 100644 tutorial/go/src/CalculatorHandler.go create mode 100644 tutorial/go/src/GoClient.go create mode 100644 tutorial/go/src/GoServer.go create mode 100644 tutorial/go/src/Makefile create mode 100644 tutorial/go/src/main.go diff --git a/compiler/cpp/Makefile.am b/compiler/cpp/Makefile.am index cbb12f55..faaac252 100644 --- a/compiler/cpp/Makefile.am +++ b/compiler/cpp/Makefile.am @@ -114,6 +114,9 @@ endif if THRIFT_GEN_javame thrift_SOURCES += src/generate/t_javame_generator.cc endif +if THRIFT_GEN_go +thrift_SOURCES += src/generate/t_go_generator.cc +endif thrift_CPPFLAGS = -I$(srcdir)/src thrift_CXXFLAGS = -Wall diff --git a/compiler/cpp/src/generate/t_go_generator.cc b/compiler/cpp/src/generate/t_go_generator.cc new file mode 100644 index 00000000..6780fc5d --- /dev/null +++ b/compiler/cpp/src/generate/t_go_generator.cc @@ -0,0 +1,2980 @@ +/* + * 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 cogoright 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 cogo 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. + */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include "t_generator.h" +#include "platform.h" +using namespace std; + + +/** + * Go code generator. + * + */ +class t_go_generator : public t_generator { + public: + t_go_generator( + t_program* program, + const std::map& parsed_options, + const std::string& option_string) + : t_generator(program) + { + std::map::const_iterator iter; + out_dir_base_ = "gen-go"; + } + + /** + * Init and close methods + */ + + void init_generator(); + void close_generator(); + + /** + * Program-level generation functions + */ + + void generate_typedef (t_typedef* ttypedef); + void generate_enum (t_enum* tenum); + void generate_const (t_const* tconst); + void generate_struct (t_struct* tstruct); + void generate_xception (t_struct* txception); + void generate_service (t_service* tservice); + + std::string render_const_value(t_type* type, t_const_value* value, const string& name); + + /** + * Struct generation code + */ + + void generate_go_struct(t_struct* tstruct, bool is_exception); + void generate_go_struct_definition(std::ofstream& out, t_struct* tstruct, bool is_xception=false, bool is_result=false); + void generate_go_struct_reader(std::ofstream& out, t_struct* tstruct, const string& tstruct_name, bool is_result=false); + void generate_go_struct_writer(std::ofstream& out, t_struct* tstruct, const string& tstruct_name, bool is_result=false); + void generate_go_function_helpers(t_function* tfunction); + + /** + * Service-level generation functions + */ + + void generate_service_helpers (t_service* tservice); + void generate_service_interface (t_service* tservice); + void generate_service_client (t_service* tservice); + void generate_service_remote (t_service* tservice); + void generate_service_server (t_service* tservice); + void generate_process_function (t_service* tservice, t_function* tfunction); + + /** + * Serialization constructs + */ + + void generate_deserialize_field (std::ofstream &out, + t_field* tfield, + bool declare, + std::string prefix="", + std::string err="err", + bool inclass=false, + bool coerceData=false); + + void generate_deserialize_struct (std::ofstream &out, + t_struct* tstruct, + bool declare, + std::string prefix="", + std::string err="err"); + + void generate_deserialize_container (std::ofstream &out, + t_type* ttype, + bool declare, + std::string prefix="", + std::string err="err"); + + void generate_deserialize_set_element (std::ofstream &out, + t_set* tset, + bool declare, + std::string prefix="", + std::string err="err"); + + void generate_deserialize_map_element (std::ofstream &out, + t_map* tmap, + bool declare, + std::string prefix="", + std::string err="err"); + + void generate_deserialize_list_element (std::ofstream &out, + t_list* tlist, + bool declare, + std::string prefix="", + std::string err="err"); + + void generate_serialize_field (std::ofstream &out, + t_field* tfield, + std::string prefix="", + std::string err="err"); + + void generate_serialize_struct (std::ofstream &out, + t_struct* tstruct, + std::string prefix="", + std::string err="err"); + + void generate_serialize_container (std::ofstream &out, + t_type* ttype, + std::string prefix="", + std::string err="err"); + + void generate_serialize_map_element (std::ofstream &out, + t_map* tmap, + std::string kiter, + std::string viter, + std::string err="err"); + + void generate_serialize_set_element (std::ofstream &out, + t_set* tmap, + std::string iter, + std::string err="err"); + + void generate_serialize_list_element (std::ofstream &out, + t_list* tlist, + std::string iter, + std::string err="err"); + + void generate_go_docstring (std::ofstream& out, + t_struct* tstruct); + + void generate_go_docstring (std::ofstream& out, + t_function* tfunction); + + void generate_go_docstring (std::ofstream& out, + t_doc* tdoc, + t_struct* tstruct, + const char* subheader); + + void generate_go_docstring (std::ofstream& out, + t_doc* tdoc); + + /** + * Helper rendering functions + */ + + std::string go_autogen_comment(); + std::string go_package(); + std::string go_imports(); + std::string render_includes(); + std::string render_fastbinary_includes(); + std::string declare_argument(t_field* tfield); + std::string render_field_default_value(t_field* tfield, const string& name); + std::string type_name(t_type* ttype); + std::string function_signature(t_function* tfunction, std::string prefix=""); + std::string function_signature_if(t_function* tfunction, std::string prefix="", bool addOsError=false); + std::string argument_list(t_struct* tstruct); + std::string type_to_enum(t_type* ttype); + std::string type_to_go_type(t_type* ttype); + std::string type_to_spec_args(t_type* ttype); + + static std::string get_real_go_module(const t_program* program) { + std::string real_module = program->get_namespace("go"); + if (real_module.empty()) { + return program->get_name(); + } + return real_module; + } + + private: + + /** + * File streams + */ + + std::ofstream f_types_; + std::stringstream f_consts_; + std::ofstream f_service_; + + std::string package_name_; + std::string package_dir_; + + static std::string publicize(const std::string& value); + static std::string privatize(const std::string& value); + static bool can_be_nil(t_type* value); + +}; + + +std::string t_go_generator::publicize(const std::string& value) { + if(value.size() <= 0) return value; + std::string value2(value); + if(!isupper(value2[0])) + value2[0] = toupper(value2[0]); + // as long as we are changing things, let's change _ followed by lowercase to capital + for(string::size_type i=1; i services = program_->get_services(); + vector::iterator sv_iter; + string f_init_name = package_dir_+"/Makefile"; + ofstream f_init; + f_init.open(f_init_name.c_str()); + f_init << + endl << + "include $(GOROOT)/src/Make.inc" << endl << endl << + "all: install" << endl << endl << + "TARG=thriftlib/" << target << endl << endl << + "DIRS=\\" << endl; + for (sv_iter = services.begin(); sv_iter != services.end(); ++sv_iter) { + f_init << " " << (*sv_iter)->get_name() << "\\" << endl; + } + f_init << endl << + "GOFILES=\\" << endl << + " ttypes.go\\" << endl; + // " constants.go\\" << endl; + for (sv_iter = services.begin(); sv_iter != services.end(); ++sv_iter) { + f_init << " " << (*sv_iter)->get_name() << ".go\\" << endl; + } + f_init << endl << endl << + "include $(GOROOT)/src/Make.pkg" << endl << endl; + f_init.close(); + + for (sv_iter = services.begin(); sv_iter != services.end(); ++sv_iter) { + string service_dir = package_dir_+"/"+(*sv_iter)->get_name(); + mkdir(service_dir.c_str(), 0755); + string f_init_name = service_dir+"/Makefile"; + ofstream f_init; + f_init.open(f_init_name.c_str()); + f_init << + endl << + "include $(GOROOT)/src/Make.inc" << endl << endl << + "all: install" << endl << endl << + "TARG=" << publicize((*sv_iter)->get_name()) << "-remote" << endl << endl << + "DIRS=\\" << endl << endl << + "GOFILES=\\" << endl << + " " << (*sv_iter)->get_name() << "-remote.go\\" << endl << endl << + "include $(GOROOT)/src/Make.cmd" << endl << endl; + f_init.close(); + } + + // Print header + f_types_ << + go_autogen_comment() << + go_package() << + go_imports() << + render_includes() << + render_fastbinary_includes() << endl << endl; +} + +/** + * Renders all the imports necessary for including another Thrift program + */ +string t_go_generator::render_includes() { + const vector& includes = program_->get_includes(); + string result = ""; + for (size_t i = 0; i < includes.size(); ++i) { + result += "import \"thriftlib/" + get_real_go_module(includes[i]) + "\"\n"; + } + if (includes.size() > 0) { + result += "\n"; + } + return result; +} + +/** + * Renders all the imports necessary to use the accelerated TBinaryProtocol + */ +string t_go_generator::render_fastbinary_includes() { + return ""; +} + +/** + * Autogen'd comment + */ +string t_go_generator::go_autogen_comment() { + return + "/* Autogenerated by Thrift\n" + " *\n" + " * DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING\n" + " */\n"; +} + +/** + * Prints standard thrift package + */ +string t_go_generator::go_package() { + return + string("package ") + package_name_ + ";\n\n"; +} + +/** + * Prints standard thrift imports + */ +string t_go_generator::go_imports() { + return + string("import (\n" + " \"thrift\"\n" +// " \"strings\"\n" + " \"fmt\"\n" + ")\n\n"); +} + +/** + * Closes the type files + */ +void t_go_generator::close_generator() { + // Close types file + f_consts_ << "}" << endl; + f_types_ << f_consts_.str() << endl; + f_types_.close(); + f_consts_.clear(); +} + +/** + * Generates a typedef. This is not done in go, types are all implicit. + * + * @param ttypedef The type definition + */ +void t_go_generator::generate_typedef(t_typedef* ttypedef) { + + generate_go_docstring(f_types_, ttypedef); + string newTypeDef(publicize(ttypedef->get_symbolic())); + string baseType(type_to_go_type(ttypedef->get_type())); + if(baseType == newTypeDef) + return; + f_types_ << + "type " << newTypeDef << " " << baseType << endl << endl; +} + +/** + * Generates code for an enumerated type. Done using a class to scope + * the values. + * + * @param tenum The enumeration + */ +void t_go_generator::generate_enum(t_enum* tenum) { + std::ostringstream to_string_mapping, from_string_mapping; + std::string tenum_name(publicize(tenum->get_name())); + + generate_go_docstring(f_types_, tenum); + f_types_ << + "type " << tenum_name << " int" << endl << + "const (" << endl; + + to_string_mapping << + indent() << "func (p " << tenum_name << ") String() string {" << endl << + indent() << " switch p {" << endl; + from_string_mapping << + indent() << "func From" << tenum_name << "String(s string) " << tenum_name << " {" << endl << + indent() << " switch s {" << endl; + + vector constants = tenum->get_constants(); + vector::iterator c_iter; + int value = -1; + for (c_iter = constants.begin(); c_iter != constants.end(); ++c_iter) { + if ((*c_iter)->has_value()) { + value = (*c_iter)->get_value(); + } else { + ++value; + } + string iter_std_name(escape_string((*c_iter)->get_name())); + string iter_name((*c_iter)->get_name()); + f_types_ << + indent() << " " << iter_name << ' ' << tenum_name << " = " << value << endl; + + // Dictionaries to/from string names of enums + to_string_mapping << + indent() << " case " << iter_name << ": return \"" << iter_std_name << "\"" << endl; + if(iter_std_name != escape_string(iter_name)) { + from_string_mapping << + indent() << " case \"" << iter_std_name << "\", \"" << escape_string(iter_name) << "\": return " << iter_name << endl; + } else { + from_string_mapping << + indent() << " case \"" << iter_std_name << "\": return " << iter_name << endl; + } + } + to_string_mapping << + indent() << " }" << endl << + indent() << " return \"\"" << endl << + indent() << "}" << endl; + from_string_mapping << + indent() << " }" << endl << + indent() << " return " << tenum_name << "(-10000)" << endl << + indent() << "}" << endl; + + f_types_ << + indent() << ")" << endl << + to_string_mapping.str() << endl << from_string_mapping.str() << endl << + indent() << "func (p " << tenum_name << ") Value() int {" << endl << + indent() << " return int(p)" << endl << + indent() << "}" << endl << endl << + indent() << "func (p " << tenum_name << ") IsEnum() bool {" << endl << + indent() << " return true" << endl << + indent() << "}" << endl << endl; +} + +/** + * Generate a constant value + */ +void t_go_generator::generate_const(t_const* tconst) { + t_type* type = tconst->get_type(); + string name = publicize(tconst->get_name()); + t_const_value* value = tconst->get_value(); + + if(type->is_base_type() || type->is_enum()) { + indent(f_types_) << "const " << name << " = " << render_const_value(type, value, name) << endl; + } else { + f_types_ << + indent() << "var " << name << " " << " " << type_to_go_type(type) << endl; + f_consts_ << + " " << name << " = " << render_const_value(type, value, name) << endl; + } +} + +/** + * Prints the value of a constant with the given type. Note that type checking + * is NOT performed in this function as it is always run beforehand using the + * validate_types method in main.cc + */ +string t_go_generator::render_const_value(t_type* type, t_const_value* value, const string& name) { + type = get_true_type(type); + std::ostringstream out; + + if (type->is_base_type()) { + t_base_type::t_base tbase = ((t_base_type*)type)->get_base(); + switch (tbase) { + case t_base_type::TYPE_STRING: + out << '"' << get_escaped_string(value) << '"'; + break; + case t_base_type::TYPE_BOOL: + out << (value->get_integer() > 0 ? "true" : "false"); + break; + case t_base_type::TYPE_BYTE: + case t_base_type::TYPE_I16: + case t_base_type::TYPE_I32: + case t_base_type::TYPE_I64: + out << value->get_integer(); + break; + case t_base_type::TYPE_DOUBLE: + if (value->get_type() == t_const_value::CV_INTEGER) { + out << value->get_integer(); + } else { + out << 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()) { + indent(out) << value->get_integer(); + } else if (type->is_struct() || type->is_xception()) { + out << + "New" << publicize(type->get_name()) << "()" << endl << + indent() << "{" << endl; + indent_up(); + const vector& fields = ((t_struct*)type)->get_members(); + vector::const_iterator f_iter; + const map& val = value->get_map(); + map::const_iterator v_iter; + for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) { + t_type* field_type = NULL; + for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { + if ((*f_iter)->get_name() == v_iter->first->get_string()) { + field_type = (*f_iter)->get_type(); + } + } + if (field_type == NULL) { + throw "type error: " + type->get_name() + " has no field " + v_iter->first->get_string(); + } + if(field_type->is_base_type() || field_type->is_enum()) { + out << + indent() << name << "." << publicize(v_iter->first->get_string()) << " = " << render_const_value(field_type, v_iter->second, name) << endl; + } else { + string k(tmp("k")); + string v(tmp("v")); + out << + indent() << v << " := " << render_const_value(field_type, v_iter->second, v) << endl << + indent() << name << "." << publicize(v_iter->first->get_string()) << " = " << v << endl; + } + } + indent_down(); + out << + indent() << "}"; + } else if (type->is_map()) { + t_type* ktype = ((t_map*)type)->get_key_type(); + t_type* vtype = ((t_map*)type)->get_val_type(); + const map& val = value->get_map(); + out << + "thrift.NewTMap(" << type_to_enum(ktype) << ", " << type_to_enum(vtype) << ", " << val.size() << ")" << endl << + indent() << "{" << endl; + indent_up(); + map::const_iterator v_iter; + for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) { + string k(tmp("k")); + string v(tmp("v")); + out << + indent() << k << " := " << render_const_value(ktype, v_iter->first, k) << endl << + indent() << v << " := " << render_const_value(vtype, v_iter->second, v) << endl << + indent() << name << ".Set(" << k << ", " << v << ")" << endl; + } + indent_down(); + out << + indent() << "}" << endl; + } else if (type->is_list()) { + t_type* etype = ((t_list*)type)->get_elem_type(); + const vector& val = value->get_list(); + out << + "thrift.NewTList(" << type_to_enum(etype) << ", " << val.size() << ")" << endl << + indent() << "{" << endl; + indent_up(); + vector::const_iterator v_iter; + for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) { + string v(tmp("v")); + out << + indent() << v << " := " << render_const_value(etype, *v_iter, v) << endl << + indent() << name << ".Push(" << v << ")" << endl; + } + indent_down(); + out << + indent() << "}" << endl; + } else if (type->is_set()) { + t_type* etype = ((t_set*)type)->get_elem_type(); + const vector& val = value->get_list(); + out << + "thrift.NewTSet(" << type_to_enum(etype) << ", " << val.size() << ")" << endl << + indent() << "{" << endl; + indent_up(); + vector::const_iterator v_iter; + for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) { + string v(tmp("v")); + out << + indent() << v << " := " << render_const_value(etype, *v_iter, v) << endl << + indent() << name << ".Add(" << v << ")" << endl; + } + indent_down(); + out << + indent() << "}" << endl; + } else { + throw "CANNOT GENERATE CONSTANT FOR TYPE: " + type->get_name(); + } + + return out.str(); +} + +/** + * Generates a go struct + */ +void t_go_generator::generate_struct(t_struct* tstruct) { + generate_go_struct(tstruct, false); +} + +/** + * Generates a struct definition for a thrift exception. Basically the same + * as a struct but extends the Exception class. + * + * @param txception The struct definition + */ +void t_go_generator::generate_xception(t_struct* txception) { + generate_go_struct(txception, true); +} + +/** + * Generates a go struct + */ +void t_go_generator::generate_go_struct(t_struct* tstruct, + bool is_exception) { + generate_go_struct_definition(f_types_, tstruct, is_exception); +} + +/** + * Generates a struct definition for a thrift data type. + * + * @param tstruct The struct definition + */ +void t_go_generator::generate_go_struct_definition(ofstream& out, + t_struct* tstruct, + bool is_exception, + bool is_result) { + + const vector& members = tstruct->get_members(); + const vector& sorted_members = tstruct->get_sorted_members(); + vector::const_iterator m_iter; + + generate_go_docstring(out, tstruct); + std::string tstruct_name(publicize(tstruct->get_name())); + out << + indent() << "type " << tstruct_name << " struct {" << endl << + indent() << " thrift.TStruct" << endl; + + /* + Here we generate the structure specification for the fastbinary codec. + These specifications have the following structure: + thrift_spec -> tuple of item_spec + item_spec -> nil | (tag, type_enum, name, spec_args, default) + tag -> integer + type_enum -> TType.I32 | TType.STRING | TType.STRUCT | ... + name -> string_literal + default -> nil # Handled by __init__ + spec_args -> nil # For simple types + | (type_enum, spec_args) # Value type for list/set + | (type_enum, spec_args, type_enum, spec_args) + # Key and value for map + | (class_name, spec_args_ptr) # For struct/exception + class_name -> identifier # Basically a pointer to the class + spec_args_ptr -> expression # just class_name.spec_args + + TODO(dreiss): Consider making this work for structs with negative tags. + */ + + // TODO(dreiss): Look into generating an empty tuple instead of nil + // for structures with no members. + // TODO(dreiss): Test encoding of structs where some inner structs + // don't have thrift_spec. + indent_up(); + if (sorted_members.empty() || (sorted_members[0]->get_key() >= 0)) { + int sorted_keys_pos = 0; + for (m_iter = sorted_members.begin(); m_iter != sorted_members.end(); ++m_iter) { + + for (; sorted_keys_pos != (*m_iter)->get_key(); sorted_keys_pos++) { + if (sorted_keys_pos != 0) { + indent(out) << "_ interface{} \"" << escape_string((*m_iter)->get_name()) << "\"; // nil # " << sorted_keys_pos << endl; + } + } + t_type* fieldType = (*m_iter)->get_type(); + string goType(type_to_go_type(fieldType)); + indent(out) << publicize((*m_iter)->get_name()) << " " + << goType << " \"" << escape_string((*m_iter)->get_name()) + << "\"; // " << sorted_keys_pos + << endl; + + sorted_keys_pos ++; + } + + } else { + for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { + // This fills in default values, as opposed to nulls + out << + indent() << publicize((*m_iter)->get_name()) << " " << + type_to_enum((*m_iter)->get_type()) << endl; + } + } + indent_down(); + out << + indent() << "}" << endl << endl << + indent() << "func New" << tstruct_name << "() *" << tstruct_name << " {" << endl << + indent() << " output := &" << tstruct_name << "{" << endl << + indent() << " TStruct:thrift.NewTStruct(\"" << escape_string(tstruct->get_name()) << "\", []thrift.TField{" << endl; + indent_up(); + indent_up(); + for(m_iter = members.begin(); m_iter != members.end(); ++m_iter) { + string thrift_name((*m_iter)->get_name()); + out << + indent() << "thrift.NewTField(\"" << escape_string(thrift_name) << "\", " << type_to_enum((*m_iter)->get_type()) << ", "<< (*m_iter)->get_key() << ")," << endl; + } + out << + indent() << "})," << endl; + indent_down(); + out << + indent() << "}" << endl << + indent() << "{" << endl; + indent_up(); + + for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { + // Initialize fields + //t_type* type = (*m_iter)->get_type(); + string fieldName(publicize((*m_iter)->get_name())); + string fullFieldName = "output." + fieldName; + if ((*m_iter)->get_value() != NULL) { + out << + indent() << fullFieldName << " = " << + render_field_default_value(*m_iter, fullFieldName) << endl; + } + } + indent_down(); + out << + indent() << "}" << endl << + indent() << "return output" << endl; + indent_down(); + out << + indent() << "}" << endl << endl; + + generate_go_struct_reader(out, tstruct, tstruct_name, is_result); + generate_go_struct_writer(out, tstruct, tstruct_name, is_result); + + // Printing utilities so that on the command line thrift + // structs look pretty like dictionaries + out << + indent() << "func (p *" << tstruct_name << ") TStructName() string {" << endl << + indent() << " return \"" << escape_string(tstruct_name) << "\"" << endl << + indent() << "}" << endl << endl; + + out << + indent() << "func (p *" << tstruct_name << ") ThriftName() string {" << endl << + indent() << " return \"" << escape_string(tstruct->get_name()) << "\"" << endl << + indent() << "}" << endl << endl; + + out << + indent() << "func (p *" << tstruct_name << ") String() string {" << endl << + indent() << " if p == nil {" << endl << + indent() << " return \"\"" << endl << + indent() << " }" << endl << + indent() << " return fmt.Sprintf(\"" << escape_string(tstruct_name) << "(%+v)\", *p)" << endl << + indent() << "}" << endl << endl; + + // Equality and inequality methods that compare by value + if(members.size() <= 0) { + out << + indent() << "func (p *" << tstruct_name << ") CompareTo(other interface{}) (int, bool) {" << endl << + indent() << " if other == nil {" << endl << + indent() << " return 1, true" << endl << + indent() << " }" << endl << + indent() << " _, ok := other.(*" << tstruct_name << ")" << endl << + indent() << " if !ok {" << endl << + indent() << " return 0, false" << endl << + indent() << " }" << endl << + indent() << " return 0, true" << endl << + indent() << "}" << endl << endl; + } else { + out << + indent() << "func (p *" << tstruct_name << ") CompareTo(other interface{}) (int, bool) {" << endl << + indent() << " if other == nil {" << endl << + indent() << " return 1, true" << endl << + indent() << " }" << endl << + indent() << " data, ok := other.(*" << tstruct_name << ")" << endl << + indent() << " if !ok {" << endl << + indent() << " return 0, false" << endl << + indent() << " }" << endl; + indent_up(); + for(m_iter = members.begin(); m_iter != members.end(); ++m_iter) { + t_type* orig_type = (*m_iter)->get_type(); + t_type* type = get_true_type(orig_type); + string field_name(publicize((*m_iter)->get_name())); + if(type->is_base_type() || type->is_enum()) { + if(type->is_bool()) { + out << + indent() << "if cmp := thrift.CompareBool(p." << field_name << ", data." << field_name << "); cmp != 0 {" << endl << + indent() << " return cmp, true" << endl << + indent() << "}" << endl; + } else { + out << + indent() << "if p." << field_name << " != data." << field_name << " {" << endl << + indent() << " if p." << field_name << " < data." << field_name << " {" << endl << + indent() << " return -1, true" << endl << + indent() << " }" << endl << + indent() << " return 1, true" << endl << + indent() << "}" << endl; + } + } else if(type->is_container() || type->is_struct() || type->is_xception()) { + out << + indent() << "if cmp, ok := p." << field_name << ".CompareTo(data." << field_name << "); !ok || cmp != 0 {" << endl << + indent() << " return cmp, ok" << endl << + indent() << "}" << endl; + } else { + throw "INVALID TYPE IN generate_go_struct_definition: " + type->get_name(); + } + } + indent_down(); + out << + indent() << " return 0, true" << endl << + indent() << "}" << endl << endl; + } + + // Equality and inequality methods that compare by value + out << + indent() << "func (p *" << tstruct_name << ") AttributeByFieldId(id int) interface{} {" << endl << + indent() << " switch id {" << endl << + indent() << " default: return nil" << endl; + indent_up(); + for(m_iter = members.begin(); m_iter != members.end(); ++m_iter) { + string field_name(publicize((*m_iter)->get_name())); + out << + indent() << "case " << (*m_iter)->get_key() << ": return p." << field_name << endl; + } + indent_down(); + out << + indent() << " }" << endl << + indent() << " return nil" << endl << + indent() << "}" << endl << endl; + + out << + indent() << "func (p *" << tstruct_name << ") TStructFields() thrift.TFieldContainer {" << endl << + indent() << " return thrift.NewTFieldContainer([]thrift.TField{" << endl; + indent_up(); + indent_up(); + for(m_iter = members.begin(); m_iter != members.end(); ++m_iter) { + string thrift_name((*m_iter)->get_name()); + out << + indent() << "thrift.NewTField(\"" << escape_string(thrift_name) << "\", " << type_to_enum((*m_iter)->get_type()) << ", "<< (*m_iter)->get_key() << ")," << endl; + } + out << + indent() << "})" << endl; + indent_down(); + indent_down(); + out << + indent() << "}" << endl << endl; +} + +/** + * Generates the read method for a struct + */ +void t_go_generator::generate_go_struct_reader(ofstream& out, + t_struct* tstruct, + const string& tstruct_name, + bool is_result) { + const vector& fields = tstruct->get_members(); + vector::const_iterator f_iter; + string escaped_tstruct_name(escape_string(tstruct->get_name())); + + out << + indent() << "func (p *" << tstruct_name << ") Read(iprot thrift.TProtocol) (err thrift.TProtocolException) {" << endl; + indent_up(); + + out << + indent() << "_, err = iprot.ReadStructBegin()" << endl << + indent() << "if err != nil { return thrift.NewTProtocolExceptionReadStruct(p.ThriftName(), err); }" << endl; + + // Loop over reading in fields + indent(out) << "for {" << endl; + indent_up(); + + // Read beginning field marker + out << + indent() << "fieldName, fieldTypeId, fieldId, err := iprot.ReadFieldBegin()" << endl << + indent() << "if fieldId < 0 {" << endl << + indent() << " fieldId = int16(p.FieldIdFromFieldName(fieldName))" << endl << + indent() << "} else if fieldName == \"\" {" << endl << + indent() << " fieldName = p.FieldNameFromFieldId(int(fieldId))" << endl << + indent() << "}" << endl << + indent() << "if fieldTypeId == thrift.GENERIC {" << endl << + indent() << " fieldTypeId = p.FieldFromFieldId(int(fieldId)).TypeId()" << endl << + indent() << "}" << endl << + indent() << "if err != nil {" << endl << + indent() << " return thrift.NewTProtocolExceptionReadField(int(fieldId), fieldName, p.ThriftName(), err)" << endl << + indent() << "}" << endl; + + // Check for field STOP marker and break + out << + indent() << "if fieldTypeId == thrift.STOP { break; }" << endl; + + // Switch statement on the field we are reading + bool first = true; + + // Generate deserialization code for known cases + int32_t field_id = -1; + for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { + field_id = (*f_iter)->get_key(); + if (first) { + first = false; + indent(out); + } else { + indent(out) << "} else "; + } + out << "if fieldId == " << field_id << " || fieldName == \"" << escape_string((*f_iter)->get_name()) << "\" {" << endl; + indent_up(); + out << + indent() << "if fieldTypeId == " << type_to_enum((*f_iter)->get_type()) << " {" << endl << + indent() << " err = p.ReadField" << field_id << "(iprot)" << endl << + indent() << " if err != nil { return thrift.NewTProtocolExceptionReadField(int(fieldId), fieldName, p.ThriftName(), err); }" << endl << + indent() << "} else if fieldTypeId == thrift.VOID {" << endl << + indent() << " err = iprot.Skip(fieldTypeId)" << endl << + indent() << " if err != nil { return thrift.NewTProtocolExceptionReadField(int(fieldId), fieldName, p.ThriftName(), err); }" << endl << + indent() << "} else {" << endl << + indent() << " err = p.ReadField" << field_id << "(iprot)" << endl << + indent() << " if err != nil { return thrift.NewTProtocolExceptionReadField(int(fieldId), fieldName, p.ThriftName(), err); }" << endl << + indent() << "}" << endl; + indent_down(); + } + + // In the default case we skip the field + if (first) { + out << + indent() << "if {" << endl; + } else { + out << + indent() << "} else {" << endl; + } + out << + indent() << " err = iprot.Skip(fieldTypeId)" << endl << + indent() << " if err != nil { return thrift.NewTProtocolExceptionReadField(int(fieldId), fieldName, p.ThriftName(), err); }" << endl << + indent() << "}" << endl; + + // Read field end marker + out << + indent() << "err = iprot.ReadFieldEnd()" << endl << + indent() << "if err != nil { return thrift.NewTProtocolExceptionReadField(int(fieldId), fieldName, p.ThriftName(), err); }" << endl; + + indent_down(); + out << + indent() << "}" << endl << + indent() << "err = iprot.ReadStructEnd()" << endl << + indent() << "if err != nil { return thrift.NewTProtocolExceptionReadStruct(p.ThriftName(), err); }" << endl << + indent() << "return err" << endl; + + indent_down(); + out << + indent() << "}" << endl << endl; + for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { + string field_type_name(publicize((*f_iter)->get_type()->get_name())); + string field_name(publicize((*f_iter)->get_name())); + int32_t field_id = (*f_iter)->get_key(); + out << + indent() << "func (p *" << tstruct_name << ") ReadField" << field_id << "(iprot thrift.TProtocol) (err thrift.TProtocolException) {" << endl; + indent_up(); + generate_deserialize_field(out, *f_iter, false, "p."); + indent_down(); + out << + indent() << " return err" << endl << + indent() << "}" << endl << endl << + indent() << "func (p *" << tstruct_name << ") ReadField" << field_name << "(iprot thrift.TProtocol) (thrift.TProtocolException) {" << endl << + indent() << " return p.ReadField" << field_id << "(iprot)" << endl << + indent() << "}" << endl << endl; + } +} + +void t_go_generator::generate_go_struct_writer(ofstream& out, + t_struct* tstruct, + const string& tstruct_name, + bool is_result) { + string name(tstruct->get_name()); + const vector& fields = tstruct->get_sorted_members(); + vector::const_iterator f_iter; + + indent(out) << + "func (p *" << tstruct_name << ") Write(oprot thrift.TProtocol) (err thrift.TProtocolException) {" << endl; + indent_up(); + + out << + indent() << "err = oprot.WriteStructBegin(\"" << name << "\")" << endl << + indent() << "if err != nil { return thrift.NewTProtocolExceptionWriteStruct(" << + "p.ThriftName(), err); }" << endl; + + string field_name; + string escape_field_name; + int32_t fieldId = -1; + if (is_result && fields.size()) { + out << + indent() << "switch {" << endl; + vector::const_reverse_iterator fr_iter; + for (fr_iter = fields.rbegin(); fr_iter != fields.rend(); ++fr_iter) { + field_name = (*fr_iter)->get_name(); + fieldId = (*fr_iter)->get_key(); + if(can_be_nil((*fr_iter)->get_type()) && fieldId != 0) { + out << + indent() << "case p." << publicize(field_name) << " != nil:" << endl << + indent() << " if err = p.WriteField" << fieldId << "(oprot); err != nil {" << endl << + indent() << " return err" << endl << + indent() << " }" << endl; + } else { + out << + indent() << "default:" << endl << + indent() << " if err = p.WriteField" << fieldId << "(oprot); err != nil {" << endl << + indent() << " return err" << endl << + indent() << " }" << endl; + } + } + out << + indent() << "}" << endl; + } else { + for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { + field_name = (*f_iter)->get_name(); + escape_field_name = escape_string(field_name); + fieldId = (*f_iter)->get_key(); + out << + indent() << "err = p.WriteField" << fieldId << "(oprot)" << endl << + indent() << "if err != nil { return err }" << endl; + } + } + + // Write the struct map + out << + indent() << "err = oprot.WriteFieldStop()" << endl << + indent() << "if err != nil { return thrift.NewTProtocolExceptionWriteField(-1, \"STOP\", p.ThriftName(), err); }" << endl << + indent() << "err = oprot.WriteStructEnd()" << endl << + indent() << "if err != nil { return thrift.NewTProtocolExceptionWriteStruct(" << + "p.ThriftName(), err); }" << endl << + indent() << "return err" << endl; + + indent_down(); + out << + indent() << "}" << endl << endl; + for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { + fieldId = (*f_iter)->get_key(); + field_name = (*f_iter)->get_name(); + escape_field_name = escape_string(field_name); + out << + indent() << "func (p *" << tstruct_name << ") WriteField" << fieldId << "(oprot thrift.TProtocol) (err thrift.TProtocolException) {" << endl; + indent_up(); + // Write field header + if (can_be_nil((*f_iter)->get_type())) { + out << + indent() << "if p." << publicize(field_name) << " != nil {" << endl; + indent_up(); + } + out << + indent() << "err = oprot.WriteFieldBegin(\"" << + escape_field_name << "\", " << + type_to_enum((*f_iter)->get_type()) << ", " << + fieldId << ")" << endl << + indent() << "if err != nil { return thrift.NewTProtocolExceptionWriteField(" << + fieldId << ", \"" << + escape_field_name << "\", " << + "p.ThriftName(), err); }" << endl; + + // Write field contents + generate_serialize_field(out, *f_iter, "p."); + + // Write field closer + out << + indent() << "err = oprot.WriteFieldEnd()" << endl << + indent() << "if err != nil { return thrift.NewTProtocolExceptionWriteField(" << + fieldId << ", \"" << + escape_field_name << "\", " << + "p.ThriftName(), err); }" << endl; + + if (can_be_nil((*f_iter)->get_type())) { + indent_down(); + out << + indent() << "}" << endl; + } + indent_down(); + out << + indent() << " return err" << endl << + indent() << "}" << endl << endl << + indent() << "func (p *" << tstruct_name << ") WriteField" << publicize(field_name) << "(oprot thrift.TProtocol) (thrift.TProtocolException) {" << endl << + indent() << " return p.WriteField" << fieldId << "(oprot)" << endl << + indent() << "}" << endl << endl; + } +} + +/** + * Generates a thrift service. + * + * @param tservice The service definition + */ +void t_go_generator::generate_service(t_service* tservice) { + string f_service_name = package_dir_+"/"+service_name_+".go"; + f_service_.open(f_service_name.c_str()); + + f_service_ << + go_autogen_comment() << + go_package() << + go_imports(); + + if (tservice->get_extends() != NULL) { + f_service_ << + "import \"thriftlib/" << get_real_go_module(tservice->get_extends()->get_program()) << "\"" << endl; + } + + f_service_ << + "import (" << endl << + indent() << " \"os\"" << endl << + indent() << ")" << endl << endl << + render_fastbinary_includes(); + + f_service_ << endl; + + // Generate the three main parts of the service (well, two for now in PHP) + generate_service_interface(tservice); + generate_service_client(tservice); + generate_service_server(tservice); + generate_service_helpers(tservice); + generate_service_remote(tservice); + + // Close service file + f_service_ << endl; + f_service_.close(); +} + +/** + * Generates helper functions for a service. + * + * @param tservice The service to generate a header definition for + */ +void t_go_generator::generate_service_helpers(t_service* tservice) { + vector functions = tservice->get_functions(); + vector::iterator f_iter; + + f_service_ << + "// HELPER FUNCTIONS AND STRUCTURES" << endl << endl; + + for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) { + t_struct* ts = (*f_iter)->get_arglist(); + generate_go_struct_definition(f_service_, ts, false); + generate_go_function_helpers(*f_iter); + } +} + +/** + * Generates a struct and helpers for a function. + * + * @param tfunction The function + */ +void t_go_generator::generate_go_function_helpers(t_function* tfunction) { + if (true || !tfunction->is_oneway()) { + t_struct result(program_, tfunction->get_name() + "_result"); + t_field success(tfunction->get_returntype(), "success", 0); + if (!tfunction->get_returntype()->is_void()) { + result.append(&success); + } + + t_struct* xs = tfunction->get_xceptions(); + const vector& fields = xs->get_members(); + vector::const_iterator f_iter; + for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { + result.append(*f_iter); + } + generate_go_struct_definition(f_service_, &result, false, true); + } +} + +/** + * Generates a service interface definition. + * + * @param tservice The service to generate a header definition for + */ +void t_go_generator::generate_service_interface(t_service* tservice) { + string extends = ""; + string extends_if = ""; + string serviceName(publicize(tservice->get_name())); + string interfaceName = "I" + serviceName; + if (tservice->get_extends() != NULL) { + extends = type_name(tservice->get_extends()); + size_t index = extends.rfind("."); + if(index != string::npos) { + extends_if = "\n" + indent() + " " + extends.substr(0, index + 1) + "I" + publicize(extends.substr(index + 1)) + "\n"; + } else { + extends_if = "\n" + indent() + "I" + publicize(extends) + "\n"; + } + } + + f_service_ << + indent() << "type " << interfaceName << " interface {" << extends_if; + indent_up(); + generate_go_docstring(f_service_, tservice); + vector functions = tservice->get_functions(); + if (!functions.empty()) { + f_service_ << endl; + vector::iterator f_iter; + for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) { + generate_go_docstring(f_service_, (*f_iter)); + f_service_ << + indent() << function_signature_if(*f_iter, "", true) << endl; + } + } + + indent_down(); + f_service_ << + indent() << "}" << endl << endl; +} + +/** + * Generates a service client definition. + * + * @param tservice The service to generate a server for. + */ +void t_go_generator::generate_service_client(t_service* tservice) { + string extends = ""; + string extends_client = ""; + string extends_client_new = ""; + string serviceName(publicize(tservice->get_name())); + if (tservice->get_extends() != NULL) { + extends = type_name(tservice->get_extends()); + size_t index = extends.rfind("."); + if(index != string::npos) { + extends_client = extends.substr(0, index + 1) + publicize(extends.substr(index + 1)) + "Client"; + extends_client_new = extends.substr(0, index + 1) + "New" + publicize(extends.substr(index + 1)) + "Client"; + } else { + extends_client = publicize(extends) + "Client"; + extends_client_new = "New" + extends_client; + } + } + + generate_go_docstring(f_service_, tservice); + f_service_ << + indent() << "type " << serviceName << "Client struct {" << endl; + indent_up(); + if(!extends_client.empty()) { + f_service_ << + indent() << "*" << extends_client << endl; + } else { + f_service_ << + indent() << "Transport thrift.TTransport" << endl << + indent() << "ProtocolFactory thrift.TProtocolFactory" << endl << + indent() << "InputProtocol thrift.TProtocol" << endl << + indent() << "OutputProtocol thrift.TProtocol" << endl << + indent() << "SeqId int32" << endl /*<< + indent() << "reqs map[int32]Deferred" << endl*/; + } + indent_down(); + f_service_ << + indent() << "}" << endl << endl; + + // Constructor function + f_service_ << + indent() << "func New" << serviceName << "ClientFactory(t thrift.TTransport, f thrift.TProtocolFactory) *" << serviceName << "Client {" << endl; + indent_up(); + f_service_ << + indent() << "return &" << serviceName << "Client"; + if (!extends.empty()) { + f_service_ << + "{" << extends_client << ": " << extends_client_new << "Factory(t, f)}"; + } else { + indent_up(); + f_service_ << "{Transport: t," << endl << + indent() << "ProtocolFactory: f," << endl << + indent() << "InputProtocol: f.GetProtocol(t)," << endl << + indent() << "OutputProtocol: f.GetProtocol(t)," << endl << + indent() << "SeqId: 0," << endl /*<< + indent() << "Reqs: make(map[int32]Deferred)" << endl*/; + indent_down(); + f_service_ << + indent() << "}" << endl; + } + indent_down(); + f_service_ << + indent() << "}" << endl << endl; + + + // Constructor function + f_service_ << + indent() << "func New" << serviceName << "ClientProtocol(t thrift.TTransport, iprot thrift.TProtocol, oprot thrift.TProtocol) *" << serviceName << "Client {" << endl; + indent_up(); + f_service_ << + indent() << "return &" << serviceName << "Client"; + if (!extends.empty()) { + f_service_ << + "{" << extends_client << ": " << extends_client_new << "Protocol(t, iprot, oprot)}" << endl; + } else { + indent_up(); + f_service_ << "{Transport: t," << endl << + indent() << "ProtocolFactory: nil," << endl << + indent() << "InputProtocol: iprot," << endl << + indent() << "OutputProtocol: oprot," << endl << + indent() << "SeqId: 0," << endl /*<< + indent() << "Reqs: make(map[int32]interface{})" << endl*/; + indent_down(); + f_service_ << + indent() << "}" << endl; + } + indent_down(); + f_service_ << + indent() << "}" << endl << endl; + + // Generate client method implementations + vector functions = tservice->get_functions(); + vector::const_iterator f_iter; + for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) { + t_struct* arg_struct = (*f_iter)->get_arglist(); + const vector& fields = arg_struct->get_members(); + vector::const_iterator fld_iter; + string funname = publicize((*f_iter)->get_name()); + + // Open function + generate_go_docstring(f_service_, (*f_iter)); + f_service_ << + indent() << "func (p *" << serviceName << "Client) " << function_signature_if(*f_iter, "", true) << " {" << endl; + indent_up(); + /* + f_service_ << + indent() << "p.SeqId += 1" << endl; + if (!(*f_iter)->is_oneway()) { + f_service_ << + indent() << "d := defer.Deferred()" << endl << + indent() << "p.Reqs[p.SeqId] = d" << endl; + } + */ + f_service_ << + indent() << "err = p.Send" << funname << "("; + + bool first = true; + for (fld_iter = fields.begin(); fld_iter != fields.end(); ++fld_iter) { + if (first) { + first = false; + } else { + f_service_ << ", "; + } + f_service_ << (*fld_iter)->get_name(); + } + f_service_ << ")" << endl << + indent() << "if err != nil { return }" << endl; + + + if (!(*f_iter)->is_oneway()) { + f_service_ << + indent() << "return p.Recv" << funname << "()" << endl; + } else { + f_service_ << + indent() << "return" << endl; + } + indent_down(); + f_service_ << + indent() << "}" << endl << endl << + indent() << "func (p *" << serviceName << "Client) Send" << function_signature(*f_iter) << "(err os.Error) {" << endl; + + indent_up(); + + std::string argsname = privatize((*f_iter)->get_name()) + "Args"; + + // Serialize the request header + string args(tmp("args")); + f_service_ << + indent() << "oprot := p.OutputProtocol" << endl << + indent() << "if oprot != nil {" << endl << + indent() << " oprot = p.ProtocolFactory.GetProtocol(p.Transport)" << endl << + indent() << " p.OutputProtocol = oprot" << endl << + indent() << "}" << endl << + indent() << "p.SeqId++" << endl << + indent() << "oprot.WriteMessageBegin(\"" << (*f_iter)->get_name() << "\", thrift.CALL, p.SeqId)" << endl << + indent() << args << " := New" << publicize(argsname) << "()" << endl; + + for (fld_iter = fields.begin(); fld_iter != fields.end(); ++fld_iter) { + f_service_ << + indent() << args << "." << publicize((*fld_iter)->get_name()) << " = " << (*fld_iter)->get_name() << endl; + } + + // Write to the stream + f_service_ << + indent() << "err = " << args << ".Write(oprot)" << endl << + indent() << "oprot.WriteMessageEnd()" << endl << + indent() << "oprot.Transport().Flush()" << endl << + indent() << "return" << endl; + indent_down(); + f_service_ << + indent() << "}" << endl << endl; + + if (true) { //!(*f_iter)->is_oneway() || true) {} + std::string resultname = privatize((*f_iter)->get_name()) + "Result"; + // Open function + f_service_ << endl << + indent() << "func (p *" << serviceName << "Client) Recv" << publicize((*f_iter)->get_name()) << + "() ("; + if(!(*f_iter)->get_returntype()->is_void()) { + f_service_ << + "value " << type_to_go_type((*f_iter)->get_returntype()) << ", "; + } + t_struct* exceptions = (*f_iter)->get_xceptions(); + string errs = argument_list(exceptions); + if(errs.size()) { + f_service_ << errs << ", "; + } + f_service_ << + "err os.Error) {" << endl; + indent_up(); + + // TODO(mcslee): Validate message reply here, seq ids etc. + + string result(tmp("result")); + string error(tmp("error")); + string error2(tmp("error")); + + f_service_ << + indent() << "iprot := p.InputProtocol" << endl << + indent() << "if iprot == nil {" << endl << + indent() << " iprot = p.ProtocolFactory.GetProtocol(p.Transport)" << endl << + indent() << " p.InputProtocol = iprot" << endl << + indent() << "}" << endl << + indent() << "_, mTypeId, seqId, err := iprot.ReadMessageBegin()" << endl << + indent() << "if err != nil {" << endl << + indent() << " return" << endl << + indent() << "}" << endl << + indent() << "if mTypeId == thrift.EXCEPTION {" << endl << + indent() << " " << error << " := thrift.NewTApplicationExceptionDefault()" << endl << + indent() << " " << error2 << ", err := " << error << ".Read(iprot)" << endl << + indent() << " if err != nil {" << endl << + indent() << " return" << endl << + indent() << " }" << endl << + indent() << " if err = iprot.ReadMessageEnd(); err != nil {" << endl << + indent() << " return" << endl << + indent() << " }" << endl << + indent() << " err = " << error2 << endl << + indent() << " return" << endl << + indent() << "}" << endl << + indent() << "if p.SeqId != seqId {" << endl << + indent() << " err = thrift.NewTApplicationException(thrift.BAD_SEQUENCE_ID, \"ping failed: out of sequence response\")" << endl << + indent() << " return" << endl << + indent() << "}" << endl << + indent() << result << " := New" << publicize(resultname) << "()" << endl << + indent() << "err = " << result << ".Read(iprot)" << endl << + indent() << "iprot.ReadMessageEnd()" << endl; + + // Careful, only return _result if not a void function + if (!(*f_iter)->get_returntype()->is_void()) { + f_service_ << + indent() << "value = " << result << ".Success" << endl; + } + + t_struct* xs = (*f_iter)->get_xceptions(); + const std::vector& xceptions = xs->get_members(); + vector::const_iterator x_iter; + for (x_iter = xceptions.begin(); x_iter != xceptions.end(); ++x_iter) { + f_service_ << + indent() << "if " << result << "." << publicize((*x_iter)->get_name()) << " != nil {" << endl << + indent() << " " << (*x_iter)->get_name() << " = " << result << "." << publicize((*x_iter)->get_name()) << endl << + indent() << "}" << endl; + } + + f_service_ << + indent() << "return" << endl; + // Close function + indent_down(); + f_service_ << + indent() << "}" << endl << endl; + } + } + + //indent_down(); + f_service_ << + endl; +} + +/** + * Generates a command line tool for making remote requests + * + * @param tservice The service to generate a remote for. + */ +void t_go_generator::generate_service_remote(t_service* tservice) { + vector functions = tservice->get_functions(); + vector::iterator f_iter; + + string f_remote_name = package_dir_+"/"+service_name_+"/"+service_name_+"-remote.go"; + ofstream f_remote; + f_remote.open(f_remote_name.c_str()); + string service_module = get_real_go_module(program_); + string::size_type loc; + while((loc = service_module.find(".")) != string::npos) { + service_module.replace(loc, 1, 1, '/'); + } + + f_remote << + go_autogen_comment() << + indent() << "package main" << endl << endl << + indent() << "import (" << endl << + indent() << " \"flag\"" << endl << + indent() << " \"fmt\"" << endl << + indent() << " \"http\"" << endl << + indent() << " \"net\"" << endl << + indent() << " \"os\"" << endl << + indent() << " \"strconv\"" << endl << + indent() << " \"thrift\"" << endl << + indent() << " \"thriftlib/" << service_module << "\"" << endl << + indent() << ")" << endl << + indent() << endl << + indent() << "func Usage() {" << endl << + indent() << " fmt.Fprint(os.Stderr, \"Usage of \", os.Args[0], \" [-h host:port] [-u url] [-f[ramed]] function [arg1 [arg2...]]:\\n\")" << endl << + indent() << " flag.PrintDefaults()" << endl << + indent() << " fmt.Fprint(os.Stderr, \"Functions:\\n\")" << endl; + for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) { + string funcName((*f_iter)->get_name()); + string funcSignature(function_signature_if(*f_iter, "", true)); + f_remote << + indent() << " fmt.Fprint(os.Stderr, \" " << funcName << funcSignature.substr(funcSignature.find("(")) << "\\n\")" << endl; + } + f_remote << + indent() << " fmt.Fprint(os.Stderr, \"\\n\")" << endl << + indent() << " os.Exit(0)" << endl << + indent() << "}" << endl << + indent() << endl << + indent() << "func main() {" << endl; + indent_up(); + f_remote << + indent() << "flag.Usage = Usage" << endl << + indent() << "var host string" << endl << + indent() << "var port int" << endl << + indent() << "var protocol string" << endl << + indent() << "var urlString string" << endl << + indent() << "var framed bool" << endl << + indent() << "var useHttp bool" << endl << + indent() << "var help bool" << endl << + indent() << "var url http.URL" << endl << + indent() << "var trans thrift.TTransport" << endl << + indent() << "flag.Usage = Usage" << endl << + indent() << "flag.StringVar(&host, \"h\", \"localhost\", \"Specify host and port\")" << endl << + indent() << "flag.IntVar(&port, \"p\", 9090, \"Specify port\")" << endl << + indent() << "flag.StringVar(&protocol, \"P\", \"binary\", \"Specify the protocol (binary, compact, simplejson, json)\")" << endl << + indent() << "flag.StringVar(&urlString, \"u\", \"\", \"Specify the url\")" << endl << + indent() << "flag.BoolVar(&framed, \"framed\", false, \"Use framed transport\")" << endl << + indent() << "flag.BoolVar(&useHttp, \"http\", false, \"Use http\")" << endl << + indent() << "flag.BoolVar(&help, \"help\", false, \"See usage string\")" << endl << + indent() << "flag.Parse()" << endl << + indent() << "if help || flag.NArg() == 0 {" << endl << + indent() << " flag.Usage()" << endl << + indent() << "}" << endl << + indent() << endl << + indent() << "if len(urlString) > 0 {" << endl << + indent() << " url, err := http.ParseURL(urlString)" << endl << + indent() << " if err != nil {" << endl << + indent() << " fmt.Fprint(os.Stderr, \"Error parsing URL: \", err.String(), \"\\n\")" << endl << + indent() << " flag.Usage()" << endl << + indent() << " }" << endl << + indent() << " host = url.Host" << endl << + //indent() << " if len(url.Port) == 0 { url.Port = \"80\"; }" << endl << + //indent() << " port = int(url.Port)" << endl << + indent() << " useHttp = len(url.Scheme) <= 0 || url.Scheme == \"http\"" << endl << + indent() << "} else if useHttp {" << endl << + indent() << " _, err := http.ParseURL(fmt.Sprint(\"http://\", host, \":\", port))" << endl << + indent() << " if err != nil {" << endl << + indent() << " fmt.Fprint(os.Stderr, \"Error parsing URL: \", err.String(), \"\\n\")" << endl << + indent() << " flag.Usage()" << endl << + indent() << " }" << endl << + indent() << "}" << endl << + indent() << endl << + indent() << "cmd := flag.Arg(0)" << endl << + indent() << "var err os.Error" << endl << + indent() << "if useHttp {" << endl << + indent() << " trans, err = thrift.NewTHttpClient(url.Raw)" << endl << + indent() << "} else {" << endl << + indent() << " addr, err := net.ResolveTCPAddr(fmt.Sprint(host, \":\", port))" << endl << + indent() << " if err != nil {" << endl << + indent() << " fmt.Fprint(os.Stderr, \"Error resolving address\", err.String())" << endl << + indent() << " os.Exit(1)" << endl << + indent() << " }" << endl << + indent() << " trans, err = thrift.NewTNonblockingSocketAddr(addr)" << endl << + indent() << " if framed {" << endl << + indent() << " trans = thrift.NewTFramedTransport(trans)" << endl << + indent() << " }" << endl << + indent() << "}" << endl << + indent() << "if err != nil {" << endl << + indent() << " fmt.Fprint(os.Stderr, \"Error creating transport\", err.String())" << endl << + indent() << " os.Exit(1)" << endl << + indent() << "}" << endl << + indent() << "defer trans.Close()" << endl << + indent() << "var protocolFactory thrift.TProtocolFactory" << endl << + indent() << "switch protocol {" << endl << + indent() << "case \"compact\":" << endl << + indent() << " protocolFactory = thrift.NewTCompactProtocolFactory()" << endl << + indent() << " break" << endl << + indent() << "case \"simplejson\":" << endl << + indent() << " protocolFactory = thrift.NewTSimpleJSONProtocolFactory()" << endl << + indent() << " break" << endl << + indent() << "case \"json\":" << endl << + indent() << " protocolFactory = thrift.NewTJSONProtocolFactory()" << endl << + indent() << " break" << endl << + indent() << "case \"binary\", \"\":" << endl << + indent() << " protocolFactory = thrift.NewTBinaryProtocolFactoryDefault()" << endl << + indent() << " break" << endl << + indent() << "default:" << endl << + indent() << " fmt.Fprint(os.Stderr, \"Invalid protocol specified: \", protocol, \"\\n\")" << endl << + indent() << " Usage()" << endl << + indent() << " os.Exit(1)" << endl << + indent() << "}" << endl << + indent() << "client := " << package_name_ << ".New" << publicize(service_name_) << "ClientFactory(trans, protocolFactory)" << endl << + indent() << "if err = trans.Open(); err != nil {" << endl << + indent() << " fmt.Fprint(os.Stderr, \"Error opening socket to \", host, \":\", port, \" \", err.String())" << endl << + indent() << " os.Exit(1)" << endl << + indent() << "}" << endl << + indent() << endl << + indent() << "switch cmd {" << endl; + for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) { + + t_struct* arg_struct = (*f_iter)->get_arglist(); + const std::vector& args = arg_struct->get_members(); + vector::const_iterator a_iter; + int num_args = args.size(); + string funcName((*f_iter)->get_name()); + string pubName(publicize(funcName)); + + f_remote << + indent() << "case \"" << escape_string(funcName) << "\":" << endl; + indent_up(); + f_remote << + indent() << "if flag.NArg() - 1 != " << num_args << " {" << endl << + indent() << " fmt.Fprint(os.Stderr, \"" << escape_string(pubName) << " requires " << num_args << " args\\n\")" << endl << + indent() << " flag.Usage()" << endl << + indent() << "}" << endl; + for (int i = 0; i < num_args; ++i) { + int flagArg = i + 1; + t_type* the_type(args[i]->get_type()); + t_type* the_type2(get_true_type(the_type)); + if(the_type2->is_enum()) { + f_remote << + indent() << "tmp" << i << ", err := (strconv.Atoi(flag.Arg(" << flagArg << ")))" << endl << + indent() << "if err != nil {" << endl << + indent() << " Usage()" << endl << + indent() << " return" << endl << + indent() << "}" << endl << + indent() << "argvalue" << i << " := " << package_name_ << "." << publicize(the_type->get_name()) << "(tmp" << i << ")" << endl; + } else if(the_type2->is_base_type()) { + t_base_type::t_base e = ((t_base_type*)the_type2)->get_base(); + string err(tmp("err")); + switch(e) { + case t_base_type::TYPE_VOID: break; + case t_base_type::TYPE_STRING: + f_remote << + indent() << "argvalue" << i << " := flag.Arg(" << flagArg << ")" << endl; + break; + case t_base_type::TYPE_BOOL: + f_remote << + indent() << "argvalue" << i << " := flag.Arg(" << flagArg << ") == \"true\"" << endl; + break; + case t_base_type::TYPE_BYTE: + f_remote << + indent() << "tmp" << i << ", " << err << " := (strconv.Atoi(flag.Arg(" << flagArg << ")))" << endl << + indent() << "if " << err << " != nil {" << endl << + indent() << " Usage()" << endl << + indent() << " return" << endl << + indent() << "}" << endl << + indent() << "argvalue" << i << " := byte(tmp" << i << ")" << endl; + break; + case t_base_type::TYPE_I16: + f_remote << + indent() << "tmp" << i << ", " << err << " := (strconv.Atoi(flag.Arg(" << flagArg << ")))" << endl << + indent() << "if " << err << " != nil {" << endl << + indent() << " Usage()" << endl << + indent() << " return" << endl << + indent() << "}" << endl << + indent() << "argvalue" << i << " := byte(tmp" << i << ")" << endl; + break; + case t_base_type::TYPE_I32: + f_remote << + indent() << "tmp" << i << ", " << err << " := (strconv.Atoi(flag.Arg(" << flagArg << ")))" << endl << + indent() << "if " << err << " != nil {" << endl << + indent() << " Usage()" << endl << + indent() << " return" << endl << + indent() << "}" << endl << + indent() << "argvalue" << i << " := int32(tmp" << i << ")" << endl; + break; + case t_base_type::TYPE_I64: + f_remote << + indent() << "argvalue" << i << ", " << err << " := (strconv.Atoi64(flag.Arg(" << flagArg << ")))" << endl << + indent() << "if " << err << " != nil {" << endl << + indent() << " Usage()" << endl << + indent() << " return" << endl << + indent() << "}" << endl; + break; + case t_base_type::TYPE_DOUBLE: + f_remote << + indent() << "argvalue" << i << ", " << err << " := (strconv.Atof64(flag.Arg(" << flagArg << ")))" << endl << + indent() << "if " << err << " != nil {" << endl << + indent() << " Usage()" << endl << + indent() << " return" << endl << + indent() << "}" << endl; + break; + default: + throw("Invalid base type in generate_service_remote"); + } + //f_remote << publicize(args[i]->get_name()) << "(strconv.Atoi(flag.Arg(" << flagArg << ")))"; + } else if(the_type2->is_struct()) { + string arg(tmp("arg")); + string mbTrans(tmp("mbTrans")); + string err1(tmp("err")); + string factory(tmp("factory")); + string jsProt(tmp("jsProt")); + string err2(tmp("err")); + std::string tstruct_name(publicize(the_type->get_name())); + f_remote << + indent() << arg << " := flag.Arg(" << flagArg << ")" << endl << + indent() << mbTrans << " := thrift.NewTMemoryBufferLen(len(" << arg << "))" << endl << + indent() << "defer " << mbTrans << ".Close()" << endl << + indent() << "_, " << err1 << " := " << mbTrans << ".WriteString(" << arg << ")" << endl << + indent() << "if " << err1 << " != nil {" << endl << + indent() << " Usage()" << endl << + indent() << " return" << endl << + indent() << "}" << endl << + indent() << factory << " := thrift.NewTSimpleJSONProtocolFactory()" << endl << + indent() << jsProt << " := " << factory << ".GetProtocol(" << mbTrans << ")" << endl << + indent() << "argvalue" << i << " := " << package_name_ << ".New" << tstruct_name << "()" << endl << + indent() << err2 << " := argvalue" << i << ".Read(" << jsProt << ")" << endl << + indent() << "if " << err2 << " != nil {" << endl << + indent() << " Usage()" << endl << + indent() << " return" << endl << + indent() << "}" << endl; + } else if(the_type2->is_container() || the_type2->is_xception()) { + string arg(tmp("arg")); + string mbTrans(tmp("mbTrans")); + string err1(tmp("err")); + string factory(tmp("factory")); + string jsProt(tmp("jsProt")); + string err2(tmp("err")); + std::string argName(publicize(args[i]->get_name())); + f_remote << + indent() << arg << " := flag.Arg(" << flagArg << ")" << endl << + indent() << mbTrans << " := thrift.NewTMemoryBufferLen(len(" << arg << "))" << endl << + indent() << "defer " << mbTrans << ".Close()" << endl << + indent() << "_, " << err1 << " := " << mbTrans << ".WriteString(" << arg << ")" << endl << + indent() << "if " << err1 << " != nil { " << endl << + indent() << " Usage()" << endl << + indent() << " return" << endl << + indent() << "}" << endl << + indent() << factory << " := thrift.NewTSimpleJSONProtocolFactory()" << endl << + indent() << jsProt << " := " << factory << ".GetProtocol(" << mbTrans << ")" << endl << + indent() << "containerStruct" << i << " := " << package_name_ << ".New" << pubName << "Args()" << endl << + indent() << err2 << " := containerStruct" << i << ".ReadField" << (i + 1) << "(" << jsProt << ")" << endl << + indent() << "if " << err2 << " != nil {" << endl << + indent() << " Usage()" << endl << + indent() << " return" << endl << + indent() << "}" << endl << + indent() << "argvalue" << i << " := containerStruct" << i << "." << argName << endl; + } else { + throw("Invalid argument type in generate_service_remote"); + string err1(tmp("err")); + f_remote << + indent() << "argvalue" << i << ", " << err1 << " := eval(flag.Arg(" << flagArg << "))" << endl << + indent() << "if " << err1 << " != nil {" << endl << + indent() << " Usage()" << endl << + indent() << " return" << endl << + indent() << "}" << endl; + } + if(the_type->is_typedef()) { + f_remote << + indent() << "value" << i << " := " << package_name_ << "." << publicize(the_type->get_name()) << "(argvalue" << i << ")" << endl; + } else { + f_remote << + indent() << "value" << i << " := argvalue" << i << endl; + } + } + f_remote << + indent() << "fmt.Print(client." << pubName << "("; + bool argFirst = true; + for (int i = 0; i < num_args; ++i) { + if (argFirst) { + argFirst = false; + } else { + f_remote << ", "; + } + if(args[i]->get_type()->is_enum()) { + f_remote << "value" << i; + } else if(args[i]->get_type()->is_base_type()) { + t_base_type::t_base e = ((t_base_type*)(args[i]->get_type()))->get_base(); + switch(e) { + case t_base_type::TYPE_VOID: break; + case t_base_type::TYPE_STRING: + 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: + f_remote << "value" << i; + break; + default: + throw("Invalid base type in generate_service_remote"); + } + //f_remote << publicize(args[i]->get_name()) << "(strconv.Atoi(flag.Arg(" << flagArg << ")))"; + } else { + f_remote << "value" << i; + } + } + f_remote << + "))" << endl << + indent() << "fmt.Print(\"\\n\")" << endl << + indent() << "break" << endl; + indent_down(); + } + f_remote << + indent() << "case \"\":" << endl << + indent() << " Usage()" << endl << + indent() << " break" << endl << + indent() << "default:" << endl << + indent() << " fmt.Fprint(os.Stderr, \"Invalid function \", cmd, \"\\n\")" << endl << + indent() << "}" << endl; + indent_down(); + + f_remote << + indent() << "}" << endl; + + + // Close service file + f_remote.close(); + + // Make file executable, love that bitwise OR action + chmod(f_remote_name.c_str(), + S_IRUSR + | S_IWUSR + | S_IXUSR +#ifndef MINGW + | S_IRGRP + | S_IXGRP + | S_IROTH + | S_IXOTH +#endif + ); +} + +/** + * Generates a service server definition. + * + * @param tservice The service to generate a server for. + */ +void t_go_generator::generate_service_server(t_service* tservice) { + // Generate the dispatch methods + vector functions = tservice->get_functions(); + vector::iterator f_iter; + + string extends = ""; + string extends_processor = ""; + string extends_processor_new = ""; + string serviceName(publicize(tservice->get_name())); + if (tservice->get_extends() != NULL) { + extends = type_name(tservice->get_extends()); + size_t index = extends.rfind("."); + if(index != string::npos) { + extends_processor = extends.substr(0, index + 1) + publicize(extends.substr(index + 1)) + "Processor"; + extends_processor_new = extends.substr(0, index + 1) + "New" + publicize(extends.substr(index + 1)) + "Processor"; + } else { + extends_processor = publicize(extends) + "Processor"; + extends_processor_new = "New" + extends_processor; + } + } + string pServiceName(privatize(serviceName)); + + // Generate the header portion + string self(tmp("self")); + if(extends_processor.empty()) { + f_service_ << + indent() << "type " << serviceName << "Processor struct {" << endl << + indent() << " handler I" << serviceName << endl << + indent() << " processorMap map[string]thrift.TProcessorFunction" << endl << + indent() << "}" << endl << endl << + indent() << "func (p *" << serviceName << "Processor) Handler() I" << serviceName << " {" << endl << + indent() << " return p.handler" << endl << + indent() << "}" << endl << endl << + indent() << "func (p *" << serviceName << "Processor) AddToProcessorMap(key string, processor thrift.TProcessorFunction) {" << endl << + indent() << " p.processorMap[key] = processor" << endl << + indent() << "}" << endl << endl << + indent() << "func (p *" << serviceName << "Processor) GetProcessorFunction(key string) (processor thrift.TProcessorFunction, exists bool) {" << endl << + indent() << " processor, exists = p.processorMap[key]" << endl << + indent() << " return processor, exists" << endl << + indent() << "}" << endl << endl << + indent() << "func (p *" << serviceName << "Processor) ProcessorMap() map[string]thrift.TProcessorFunction {" << endl << + indent() << " return p.processorMap" << endl << + indent() << "}" << endl << endl << + indent() << "func New" << serviceName << "Processor(handler I" << serviceName << ") *" << serviceName << "Processor {" << endl << endl << + indent() << " " << self << " := &" << serviceName << "Processor{handler:handler, processorMap:make(map[string]thrift.TProcessorFunction)}" << endl; + for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) { + string escapedFuncName(escape_string((*f_iter)->get_name())); + f_service_ << + indent() << " " << self << ".processorMap[\"" << escapedFuncName << "\"] = &" << pServiceName << "Processor" << publicize((*f_iter)->get_name()) << "{handler:handler}" << endl; + } + string x(tmp("x")); + f_service_ << + indent() << "return " << self << endl << + indent() << "}" << endl << endl << + indent() << "func (p *" << serviceName << "Processor) Process(iprot, oprot thrift.TProtocol) (success bool, err thrift.TException) {" << endl << + indent() << " name, _, seqId, err := iprot.ReadMessageBegin()" << endl << + indent() << " if err != nil { return }" << endl << + indent() << " process, nameFound := p.GetProcessorFunction(name)" << endl << + indent() << " if !nameFound || process == nil {" << endl << + indent() << " iprot.Skip(thrift.STRUCT)" << endl << + indent() << " iprot.ReadMessageEnd()" << endl << + indent() << " " << x << " := thrift.NewTApplicationException(thrift.UNKNOWN_METHOD, \"Unknown function \" + name)" << endl << + indent() << " oprot.WriteMessageBegin(name, thrift.EXCEPTION, seqId)" << endl << + indent() << " " << x << ".Write(oprot)" << endl << + indent() << " oprot.WriteMessageEnd()" << endl << + indent() << " oprot.Transport().Flush()" << endl << + indent() << " return false, " << x << endl << + indent() << " }" << endl << + indent() << " return process.Process(seqId, iprot, oprot)" << endl << + indent() << "}" << endl << endl; + } else { + f_service_ << + indent() << "type " << serviceName << "Processor struct {" << endl << + indent() << " super *" << extends_processor << endl << + indent() << "}" << endl << endl << + indent() << "func (p *" << serviceName << "Processor) Handler() I" << serviceName << " {" << endl << + indent() << " return p.super.Handler().(I" << serviceName << ")" << endl << + indent() << "}" << endl << endl << + indent() << "func (p *" << serviceName << "Processor) AddToProcessorMap(key string, processor thrift.TProcessorFunction) {" << endl << + indent() << " p.super.AddToProcessorMap(key, processor)" << endl << + indent() << "}" << endl << endl << + indent() << "func (p *" << serviceName << "Processor) GetProcessorFunction(key string) (processor thrift.TProcessorFunction, exists bool) {" << endl << + indent() << " return p.super.GetProcessorFunction(key)" << endl << + indent() << "}" << endl << endl << + indent() << "func (p *" << serviceName << "Processor) ProcessorMap() map[string]thrift.TProcessorFunction {" << endl << + indent() << " return p.super.ProcessorMap()" << endl << + indent() << "}" << endl << endl << + indent() << "func New" << serviceName << "Processor(handler I" << serviceName << ") *" << serviceName << "Processor {" << endl << + indent() << " " << self << " := &" << serviceName << "Processor{super: " << extends_processor_new << "(handler)}" << endl; + for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) { + string escapedFuncName(escape_string((*f_iter)->get_name())); + f_service_ << + indent() << " " << self << ".AddToProcessorMap(\"" << escapedFuncName << "\", &" << pServiceName << "Processor" << publicize((*f_iter)->get_name()) << "{handler:handler})" << endl; + } + f_service_ << + indent() << " return " << self << endl << + indent() << "}" << endl << endl << + indent() << "func (p *" << serviceName << "Processor) Process(iprot, oprot thrift.TProtocol) (bool, thrift.TException) {" << endl << + indent() << " return p.super.Process(iprot, oprot)" << endl << + indent() << "}" << endl << endl; + } + + // Generate the process subfunctions + for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) { + generate_process_function(tservice, *f_iter); + } + + f_service_ << endl; +} + +/** + * Generates a process function definition. + * + * @param tfunction The function to write a dispatcher for + */ +void t_go_generator::generate_process_function(t_service* tservice, + t_function* tfunction) { + // Open function + string processorName = privatize(tservice->get_name()) + "Processor" + publicize(tfunction->get_name()); + + string argsname = publicize(tfunction->get_name()) + "Args"; + string resultname = publicize(tfunction->get_name()) + "Result"; + + //t_struct* xs = tfunction->get_xceptions(); + //const std::vector& xceptions = xs->get_members(); + vector::const_iterator x_iter; + + f_service_ << + indent() << "type " << processorName << " struct {" << endl << + indent() << " handler I" << publicize(tservice->get_name()) << endl << + indent() << "}" << endl << endl << + indent() << "func (p *" << processorName << ") Process(seqId int32, iprot, oprot thrift.TProtocol) (success bool, err thrift.TException) {" << endl; + indent_up(); + + f_service_ << + indent() << "args := New" << argsname << "()" << endl << + indent() << "if err = args.Read(iprot); err != nil {" << endl << + indent() << " iprot.ReadMessageEnd()" << endl << + indent() << " x := thrift.NewTApplicationException(thrift.PROTOCOL_ERROR, err.String())" << endl << + indent() << " oprot.WriteMessageBegin(\"" << escape_string(tfunction->get_name()) << "\", thrift.EXCEPTION, seqId)" << endl << + indent() << " x.Write(oprot)" << endl << + indent() << " oprot.WriteMessageEnd()" << endl << + indent() << " oprot.Transport().Flush()" << endl << + indent() << " return" << endl << + indent() << "}" << endl << + indent() << "iprot.ReadMessageEnd()" << endl << + indent() << "result := New" << resultname << "()" << endl << + indent() << "if "; + if (!tfunction->is_oneway()) { + if(!tfunction->get_returntype()->is_void()) { + f_service_ << "result.Success, "; + } + t_struct* exceptions = tfunction->get_xceptions(); + const vector& fields = exceptions->get_members(); + vector::const_iterator f_iter; + for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { + f_service_ << "result." << publicize((*f_iter)->get_name()) << ", "; + } + } + + + // Generate the function call + t_struct* arg_struct = tfunction->get_arglist(); + const std::vector& fields = arg_struct->get_members(); + vector::const_iterator f_iter; + + f_service_ << + "err = p.handler." << publicize(tfunction->get_name()) << "("; + bool first = true; + for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { + if (first) { + first = false; + } else { + f_service_ << ", "; + } + f_service_ << "args." << publicize((*f_iter)->get_name()); + } + f_service_ << "); err != nil {" << endl << + indent() << " x := thrift.NewTApplicationException(thrift.INTERNAL_ERROR, \"Internal error processing " << escape_string(tfunction->get_name()) << ": \" + err.String())" << endl << + indent() << " oprot.WriteMessageBegin(\"" << escape_string(tfunction->get_name()) << "\", thrift.EXCEPTION, seqId)" << endl << + indent() << " x.Write(oprot)" << endl << + indent() << " oprot.WriteMessageEnd()" << endl << + indent() << " oprot.Transport().Flush()" << endl << + indent() << " return" << endl << + indent() << "}" << endl << + indent() << "if err2 := oprot.WriteMessageBegin(\"" << escape_string(tfunction->get_name()) << "\", thrift.REPLY, seqId); err2 != nil {" << endl << + indent() << " err = err2" << endl << + indent() << "}" << endl << + indent() << "if err2 := result.Write(oprot); err == nil && err2 != nil {" << endl << + indent() << " err = err2" << endl << + indent() << "}" << endl << + indent() << "if err2 := oprot.WriteMessageEnd(); err == nil && err2 != nil {" << endl << + indent() << " err = err2" << endl << + indent() << "}" << endl << + indent() << "if err2 := oprot.Transport().Flush(); err == nil && err2 != nil {" << endl << + indent() << " err = err2" << endl << + indent() << "}" << endl << + indent() << "if err != nil {" << endl << + indent() << " return" << endl << + indent() << "}" << endl << + indent() << "return true, err" << endl; + indent_down(); + f_service_ << + indent() << "}" << endl << endl; + /* + indent(f_service_) << + "func (p *" << publicize(tservice->get_name()) << "Client) WriteResultsSuccess" << publicize(tfunction->get_name()) << + "(success bool, result " << publicize(tfunction->get_name()) << "Result, seqid int32, oprot thrift.TProtocol) (err os.Error) {" << endl; + indent_up(); + f_service_ << + indent() << "result.Success = success" << endl << + indent() << "oprot.WriteMessageBegin(\"" << escape_string(tfunction->get_name()) << "\", thrift.REPLY, seqid)" << endl << + indent() << "result.Write(oprot)" << endl << + indent() << "oprot.WriteMessageEnd()" << endl << + indent() << "oprot.Transport().Flush()" << endl << + indent() << "return" << endl; + indent_down(); + f_service_ << + indent() << "}" << endl << endl; + */ + // Try block for a function with exceptions + /* + if (!tfunction->is_oneway() && xceptions.size() > 0) { + indent(f_service_) << + "func (p *" << publicize(tservice->get_name()) << "Client) WriteResultsException" << publicize(tfunction->get_name()) << + "(error *" << publicize(tfunction->get_name()) << ", result *, seqid, oprot) (err os.Error) {" << endl; + indent_up(); + + // Kinda absurd + for (x_iter = xceptions.begin(); x_iter != xceptions.end(); ++x_iter) { + f_service_ << + indent() << "except " << type_name((*x_iter)->get_type()) << ", " << (*x_iter)->get_name() << ":" << endl; + if (!tfunction->is_oneway()) { + indent_up(); + f_service_ << + indent() << "result." << (*x_iter)->get_name() << " = " << (*x_iter)->get_name() << endl; + indent_down(); + } else { + f_service_ << + indent() << "pass" << endl; + } + } + f_service_ << + indent() << "err = oprot.WriteMessageBegin(\"" << escape_string(tfunction->get_name()) << "\", thrift.REPLY, seqid)" << endl << + indent() << "if err != nil { return err }" << endl << + indent() << "err = result.Write(oprot)" << endl << + indent() << "if err != nil { return err }" << endl << + indent() << "err = oprot.WriteMessageEnd()" << endl << + indent() << "if err != nil { return err }" << endl << + indent() << "err = oprot.Transport().Flush()" << endl << + indent() << "if err != nil { return err }" << endl; + indent_down(); + f_service_ << "}" << endl << endl; + } + */ +} + +/** + * Deserializes a field of any type. + */ +void t_go_generator::generate_deserialize_field(ofstream &out, + t_field* tfield, + bool declare, + string prefix, + string err, + bool inclass, + bool coerceData) { + t_type* orig_type = tfield->get_type(); + t_type* type = get_true_type(orig_type); + string name(prefix + publicize(tfield->get_name())); + + if (type->is_void()) { + throw "CANNOT GENERATE DESERIALIZE CODE FOR void TYPE: " + name; + } + string v = tmp("v"); + string err2 = tmp("err"); + + if (type->is_struct() || type->is_xception()) { + generate_deserialize_struct(out, + (t_struct*)type, + declare, + name, + err); + } else if (type->is_container()) { + generate_deserialize_container(out, type, declare, name, err); + } else if (type->is_base_type() || type->is_enum()) { + indent(out) << + v << ", " << err2 << " := iprot."; + + if (type->is_base_type()) { + t_base_type::t_base tbase = ((t_base_type*)type)->get_base(); + switch (tbase) { + case t_base_type::TYPE_VOID: + throw "compiler error: cannot serialize void field in a struct: " + + name; + break; + case t_base_type::TYPE_STRING: + out << "ReadString()"; + break; + case t_base_type::TYPE_BOOL: + out << "ReadBool()"; + break; + case t_base_type::TYPE_BYTE: + out << "ReadByte()"; + break; + case t_base_type::TYPE_I16: + out << "ReadI16()"; + break; + case t_base_type::TYPE_I32: + out << "ReadI32()"; + break; + case t_base_type::TYPE_I64: + out << "ReadI64()"; + break; + case t_base_type::TYPE_DOUBLE: + out << "ReadDouble()"; + break; + default: + throw "compiler error: no Go name for base type " + t_base_type::t_base_name(tbase); + } + } else if (type->is_enum()) { + out << "ReadI32()"; + } + string structName("\"\""); + if(!prefix.size() || prefix.find(".") == string::npos) { + structName = "\"\""; + } else { + structName = prefix + "ThriftName()"; + } + out << endl << + indent() << "if " << err2 << " != nil { return thrift.NewTProtocolExceptionReadField(" << + tfield->get_key() << + ", \"" << escape_string(tfield->get_name()) << + "\", " << structName << ", " << err2 << "); }" << endl; + if(!prefix.size() || prefix.find(".") == string::npos) { + if(type->is_enum() || orig_type->is_typedef()) { + indent(out) << name << " := " << publicize(orig_type->get_name()) << "("<< v << ")" << endl; + } else { + indent(out) << name << " := " << v << endl; + } + } else { + if(type->is_enum() || orig_type->is_typedef()) { + indent(out) << name << " = " << publicize(orig_type->get_name()) << "("<< v << ")" << endl; + } else { + indent(out) << name << " = " << v << endl; + } + } + + } else { + throw "INVALID TYPE IN generate_deserialize_field '" + type->get_name() + "' for field '" + tfield->get_name() + "'"; + } +} + +/** + * Generates an unserializer for a struct, calling read() + */ +void t_go_generator::generate_deserialize_struct(ofstream &out, + t_struct* tstruct, + bool declare, + string prefix, + string err) { + string err2(tmp("err")); + string eq(" := "); + if(!declare) { + eq = " = "; + } + out << + indent() << prefix << eq << "New" << publicize(type_name(tstruct)) << "()" << endl << + indent() << err2 << " := " << prefix << ".Read(iprot)" << endl << + indent() << "if " << err2 << " != nil { return thrift.NewTProtocolExceptionReadStruct(\"" << + escape_string(prefix + tstruct->get_name()) << "\", " << + err2 << "); }\n"; +} + +/** + * Serialize a container by writing out the header followed by + * data and then a footer. + */ +void t_go_generator::generate_deserialize_container(ofstream &out, + t_type* ttype, + bool declare, + string prefix, + string err) { + string size = tmp("_size"); + string ktype = tmp("_ktype"); + string vtype = tmp("_vtype"); + string etype = tmp("_etype"); + + t_field fsize(g_type_i32, size); + t_field fktype(g_type_byte, ktype); + t_field fvtype(g_type_byte, vtype); + t_field fetype(g_type_byte, etype); + + string eq(" = "); + if(declare) + eq = " := "; + + // Declare variables, read header + if (ttype->is_map()) { + out << + indent() << ktype << ", " << vtype << ", " << size << ", " << err << " := iprot.ReadMapBegin()" << endl << + indent() << "if " << err << " != nil {" << endl << + indent() << " return thrift.NewTProtocolExceptionReadField(" << + -1 << ", \"" << + escape_string(prefix) << "\", \"\", " << + err << ")" << endl << + indent() << "}" << endl << + indent() << prefix << eq << "thrift.NewTMap(" << ktype << ", " << vtype << ", " << size << ")" << endl; + } else if (ttype->is_set()) { + out << + indent() << etype << ", " << size << ", " << err << " := iprot.ReadSetBegin()" << endl << + indent() << "if " << err << " != nil {" << endl << + indent() <<" return thrift.NewTProtocolExceptionReadField(" << + -1 << ", \"" << + escape_string(prefix) << "\", \"\", " << + err << "); }" << endl << + indent() << "}" << endl << + indent() << prefix << eq << "thrift.NewTSet(" << etype << ", " << size << ")" << endl; + } else if (ttype->is_list()) { + out << + indent() << etype << ", " << size << ", " << err << " := iprot.ReadListBegin()" << endl << + indent() << "if " << err << " != nil {" << endl << + indent() <<" return thrift.NewTProtocolExceptionReadField(" << + -1 << ", \"" << + escape_string(prefix) << "\", \"\", " << + err << ")" << endl << + indent() << "}" << endl << + indent() << prefix << eq << "thrift.NewTList(" << etype << ", " << size << ")" << endl; + } else { + throw "INVALID TYPE IN generate_deserialize_container '" + ttype->get_name() + "' for prefix '" + prefix + "'"; + } + + // For loop iterates over elements + string i = tmp("_i"); + out << + indent() << "for " << i << ":= 0; " << i << " < " << size << "; " << i << "++ {" << endl; + indent_up(); + + if (ttype->is_map()) { + generate_deserialize_map_element(out, (t_map*)ttype, declare, prefix); + } else if (ttype->is_set()) { + generate_deserialize_set_element(out, (t_set*)ttype, declare, prefix); + } else if (ttype->is_list()) { + generate_deserialize_list_element(out, (t_list*)ttype, declare, prefix); + } + + indent_down(); + out << + indent() << "}" << endl; + // Read container end + if (ttype->is_map()) { + out << + indent() << err << " = iprot.ReadMapEnd()" << endl << + indent() << "if " << err << " != nil { return thrift.NewTProtocolExceptionReadField(" + << -1 + << ", \"" << escape_string(((t_map*)ttype)->get_cpp_name()) + << "\", " << "\"map\", " << err << "); }" << endl; + } else if (ttype->is_set()) { + out << + indent() << err << " = iprot.ReadSetEnd()" << endl << + indent() << "if " << err << " != nil { return thrift.NewTProtocolExceptionReadField(" + << -1 + << ", \"" << escape_string(((t_set*)ttype)->get_cpp_name()) + << "\", " << "\"set\", " << err << "); }" << endl; + } else if (ttype->is_list()) { + out << + indent() << err << " = iprot.ReadListEnd()" << endl << + indent() << "if " << err << " != nil { return thrift.NewTProtocolExceptionReadField(" + << -1 + << ", \"" << escape_string(((t_list*)ttype)->get_cpp_name()) + << "\", " << "\"list\"," << err << "); }" << endl; + } +} + + +/** + * Generates code to deserialize a map + */ +void t_go_generator::generate_deserialize_map_element(ofstream &out, + t_map* tmap, + bool declare, + string prefix, + string err) { + string key = tmp("_key"); + string val = tmp("_val"); + t_field fkey(tmap->get_key_type(), key); + t_field fval(tmap->get_val_type(), val); + + generate_deserialize_field(out, &fkey, true); + generate_deserialize_field(out, &fval, true); + + indent(out) << + prefix << ".Set(" << key << ", " << val << ")" << endl; +} + +/** + * Write a set element + */ +void t_go_generator::generate_deserialize_set_element(ofstream &out, + t_set* tset, + bool declare, + string prefix, + string err) { + string elem = tmp("_elem"); + t_field felem(tset->get_elem_type(), elem); + + generate_deserialize_field(out, &felem, true, "", err); + + indent(out) << + prefix << ".Add(" << elem << ")" << endl; +} + +/** + * Write a list element + */ +void t_go_generator::generate_deserialize_list_element(ofstream &out, + t_list* tlist, + bool declare, + string prefix, + string err) { + string elem = tmp("_elem"); + t_field felem(tlist->get_elem_type(), elem); + + generate_deserialize_field(out, &felem, true, "", err); + + indent(out) << + prefix << ".Push(" << elem << ")" << endl; +} + + +/** + * Serializes a field of any type. + * + * @param tfield The field to serialize + * @param prefix Name to prepend to field name + */ +void t_go_generator::generate_serialize_field(ofstream &out, + t_field* tfield, + string prefix, + string err) { + t_type* type = get_true_type(tfield->get_type()); + string name(prefix + publicize(tfield->get_name())); + + // Do nothing for void types + 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, + err); + } else if (type->is_container()) { + generate_serialize_container(out, + type, + name, + err); + } else if (type->is_base_type() || type->is_enum()) { + + indent(out) << + err << " = oprot."; + + if (type->is_base_type()) { + t_base_type::t_base tbase = ((t_base_type*)type)->get_base(); + switch (tbase) { + case t_base_type::TYPE_VOID: + throw + "compiler error: cannot serialize void field in a struct: " + name; + break; + case t_base_type::TYPE_STRING: + out << "WriteString(string(" << name << "))"; + break; + case t_base_type::TYPE_BOOL: + out << "WriteBool(bool(" << name << "))"; + break; + case t_base_type::TYPE_BYTE: + out << "WriteByte(byte(" << name << "))"; + break; + case t_base_type::TYPE_I16: + out << "WriteI16(int16(" << name << "))"; + break; + case t_base_type::TYPE_I32: + out << "WriteI32(int32(" << name << "))"; + break; + case t_base_type::TYPE_I64: + out << "WriteI64(int64(" << name << "))"; + break; + case t_base_type::TYPE_DOUBLE: + out << "WriteDouble(float64(" << name << "))"; + break; + default: + throw "compiler error: no PHP name for base type " + t_base_type::t_base_name(tbase); + } + } else if (type->is_enum()) { + out << "WriteI32(int32(" << name << "))"; + } + string structName = (prefix.size()) ? prefix + "ThriftName()" : "\"\""; + out << endl << + indent() << "if " << err << " != nil { return thrift.NewTProtocolExceptionWriteField(" + << tfield->get_key() + << ", \"" << escape_string(tfield->get_name()) + << "\", " << structName << ", " << err << "); }\n"; + } else { + throw "INVALID TYPE IN generate_serialize_field '" + type->get_name() + "' for field '" + prefix + publicize(tfield->get_name()) + "'"; + } +} + +/** + * Serializes all the members of a struct. + * + * @param tstruct The struct to serialize + * @param prefix String prefix to attach to all fields + */ +void t_go_generator::generate_serialize_struct(ofstream &out, + t_struct* tstruct, + string prefix, + string err) { + out << + indent() << err << " = " << prefix << ".Write(oprot)" << endl << + indent() << "if " << err << " != nil { return thrift.NewTProtocolExceptionWriteStruct(" + << "\"" << escape_string(tstruct->get_name()) << "\", " << err << "); }\n"; +} + +void t_go_generator::generate_serialize_container(ofstream &out, + t_type* ttype, + string prefix, + string err) { + if (ttype->is_map()) { + out << + indent() << err << " = oprot.WriteMapBegin(" << + type_to_enum(((t_map*)ttype)->get_key_type()) << ", " << + type_to_enum(((t_map*)ttype)->get_val_type()) << ", " << + prefix << ".Len())" << endl << + indent() << "if " << err << " != nil { return thrift.NewTProtocolExceptionWriteField(" + << -1 + << ", \"" << escape_string(ttype->get_name()) + << "\", " << "\"map\", " << err << "); }\n"; + } else if (ttype->is_set()) { + out << + indent() << err << " = oprot.WriteSetBegin(" << + type_to_enum(((t_set*)ttype)->get_elem_type()) << ", " << + prefix << ".Len())" << endl << + indent() << "if " << err << " != nil { return thrift.NewTProtocolExceptionWriteField(" + << -1 + << ", \"" << escape_string(ttype->get_name()) + << "\", " << "\"set\", " << err << "); }\n"; + } else if (ttype->is_list()) { + out << + indent() << err << " = oprot.WriteListBegin(" << + type_to_enum(((t_list*)ttype)->get_elem_type()) << ", " << + prefix << ".Len())" << endl << + indent() << "if " << err << " != nil { return thrift.NewTProtocolExceptionWriteField(" + << -1 + << ", \"" << escape_string(ttype->get_name()) + << "\", " << "\"list\", " << err << "); }\n"; + } else { + throw "INVALID TYPE IN generate_serialize_container '" + ttype->get_name() + "' for prefix '" + prefix + "'"; + } + + if (ttype->is_map()) { + string miter = tmp("Miter"); + string kiter = tmp("Kiter"); + string viter = tmp("Viter"); + t_map* tmap = (t_map*)ttype; + out << + indent() << "for " << miter << " := range " << prefix << ".Iter() {" << endl << + indent() << " " << kiter << ", " << viter << " := " << miter << ".Key().(" << type_to_go_type(tmap->get_key_type()) << "), " << miter << ".Value().(" << type_to_go_type(tmap->get_val_type()) << ")" << endl; + indent_up(); + generate_serialize_map_element(out, tmap, kiter, viter); + indent_down(); + indent(out) << "}" << endl; + } else if (ttype->is_set()) { + t_set* tset = (t_set*)ttype; + string iter = tmp("Iter"); + string iter2 = tmp("Iter"); + out << + indent() << "for " << iter << " := " << prefix << ".Front(); " << iter << " != nil; " << iter << " = " << iter << ".Next() {" << endl << + indent() << " " << iter2 << " := " << iter << ".Value.(" << type_to_go_type(tset->get_elem_type()) << ")" << endl; + indent_up(); + generate_serialize_set_element(out, tset, iter2); + indent_down(); + indent(out) << "}" << endl; + } else if (ttype->is_list()) { + t_list* tlist = (t_list*)ttype; + string iter = tmp("Iter"); + string iter2 = tmp("Iter"); + out << + indent() << "for " << iter << " := range " << prefix << ".Iter() {" << endl << + indent() << " " << iter2 << " := " << iter << ".(" << type_to_go_type(tlist->get_elem_type()) << ")" << endl; + indent_up(); + generate_serialize_list_element(out, tlist, iter2); + indent_down(); + indent(out) << "}" << endl; + } + + if (ttype->is_map()) { + out << + indent() << err << " = oprot.WriteMapEnd()" << endl << + indent() << "if " << err << " != nil { return thrift.NewTProtocolExceptionWriteField(" + << -1 + << ", \"" << escape_string(ttype->get_name()) + << "\", " << "\"map\", " << err << "); }\n"; + } else if (ttype->is_set()) { + out << + indent() << err << " = oprot.WriteSetEnd()" << endl << + indent() << "if " << err << " != nil { return thrift.NewTProtocolExceptionWriteField(" + << -1 + << ", \"" << escape_string(ttype->get_name()) + << "\", " << "\"set\", " << err << "); }\n"; + } else if (ttype->is_list()) { + out << + indent() << err << " = oprot.WriteListEnd()" << endl << + indent() << "if " << err << " != nil { return thrift.NewTProtocolExceptionWriteField(" + << -1 + << ", \"" << escape_string(ttype->get_name()) + << "\", " << "\"list\", " << err << "); }\n"; + } +} + +/** + * Serializes the members of a map. + * + */ +void t_go_generator::generate_serialize_map_element(ofstream &out, + t_map* tmap, + string kiter, + string viter, + string err) { + t_field kfield(tmap->get_key_type(), kiter); + generate_serialize_field(out, &kfield, "", err); + + t_field vfield(tmap->get_val_type(), viter); + generate_serialize_field(out, &vfield, "", err); +} + +/** + * Serializes the members of a set. + */ +void t_go_generator::generate_serialize_set_element(ofstream &out, + t_set* tset, + string iter, + string err) { + t_field efield(tset->get_elem_type(), iter); + generate_serialize_field(out, &efield, "", err); +} + +/** + * Serializes the members of a list. + */ +void t_go_generator::generate_serialize_list_element(ofstream &out, + t_list* tlist, + string iter, + string err) { + t_field efield(tlist->get_elem_type(), iter); + generate_serialize_field(out, &efield, "", err); +} + +/** + * Generates the docstring for a given struct. + */ +void t_go_generator::generate_go_docstring(ofstream& out, + t_struct* tstruct) { + generate_go_docstring(out, tstruct, tstruct, "Attributes"); +} + +/** + * Generates the docstring for a given function. + */ +void t_go_generator::generate_go_docstring(ofstream& out, + t_function* tfunction) { + generate_go_docstring(out, tfunction, tfunction->get_arglist(), "Parameters"); +} + +/** + * Generates the docstring for a struct or function. + */ +void t_go_generator::generate_go_docstring(ofstream& out, + t_doc* tdoc, + t_struct* tstruct, + const char* subheader) { + bool has_doc = false; + stringstream ss; + if (tdoc->has_doc()) { + has_doc = true; + ss << tdoc->get_doc(); + } + + const vector& fields = tstruct->get_members(); + if (fields.size() > 0) { + if (has_doc) { + ss << endl; + } + has_doc = true; + ss << subheader << ":\n"; + vector::const_iterator p_iter; + for (p_iter = fields.begin(); p_iter != fields.end(); ++p_iter) { + t_field* p = *p_iter; + ss << " - " << publicize(p->get_name()); + if (p->has_doc()) { + ss << ": " << p->get_doc(); + } else { + ss << endl; + } + } + } + + if (has_doc) { + generate_docstring_comment(out, + "/**\n", + " * ", ss.str(), + " */\n"); + } +} + +/** + * Generates the docstring for a generic object. + */ +void t_go_generator::generate_go_docstring(ofstream& out, + t_doc* tdoc) { + if (tdoc->has_doc()) { + generate_docstring_comment(out, + "/**\n", + " *", tdoc->get_doc(), + " */\n"); + } +} + +/** + * Declares an argument, which may include initialization as necessary. + * + * @param tfield The field + */ +string t_go_generator::declare_argument(t_field* tfield) { + std::ostringstream result; + result << publicize(tfield->get_name()) << "="; + if (tfield->get_value() != NULL) { + result << "thrift_spec[" << + tfield->get_key() << "][4]"; + } else { + result << "nil"; + } + return result.str(); +} + +/** + * Renders a field default value, returns nil otherwise. + * + * @param tfield The field + */ +string t_go_generator::render_field_default_value(t_field* tfield, const string& name) { + t_type* type = get_true_type(tfield->get_type()); + if (tfield->get_value() != NULL) { + return render_const_value(type, tfield->get_value(), name); + } else { + return "nil"; + } +} + +/** + * Renders a function signature of the form 'type name(args)' + * + * @param tfunction Function definition + * @return String of rendered function definition + */ +string t_go_generator::function_signature(t_function* tfunction, + string prefix) { + // TODO(mcslee): Nitpicky, no ',' if argument_list is empty + return + publicize(prefix + tfunction->get_name()) + + "(" + argument_list(tfunction->get_arglist()) + ")"; +} + +/** + * Renders an interface function signature of the form 'type name(args)' + * + * @param tfunction Function definition + * @return String of rendered function definition + */ +string t_go_generator::function_signature_if(t_function* tfunction, + string prefix, + bool addOsError) { + // TODO(mcslee): Nitpicky, no ',' if argument_list is empty + string signature = publicize(prefix + tfunction->get_name()) + "("; + signature += argument_list(tfunction->get_arglist()) + ") ("; + t_type* ret = tfunction->get_returntype(); + t_struct* exceptions = tfunction->get_xceptions(); + string errs = argument_list(exceptions); + string retval(tmp("retval")); + if(!ret->is_void()) { + signature += retval + " " + type_to_go_type(ret); + if(addOsError || errs.size()==0) { + signature += ", "; + } + } + if(errs.size()>0) { + signature += errs; + if(addOsError) + signature += ", "; + } + if(addOsError) { + signature += "err os.Error"; + } + signature += ")"; + return signature; +} + + +/** + * Renders a field list + */ +string t_go_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 += (*f_iter)->get_name() + " " + type_to_go_type((*f_iter)->get_type()); + } + return result; +} + +string t_go_generator::type_name(t_type* ttype) { + t_program* program = ttype->get_program(); + if (ttype->is_service()) { + return get_real_go_module(program) + "." + ttype->get_name(); + } + if (program != NULL && program != program_) { + return get_real_go_module(program) + ".ttypes." + ttype->get_name(); + } + return ttype->get_name(); +} + +/** + * Converts the parse type to a go tyoe + */ +string t_go_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 "thrift.STRING"; + case t_base_type::TYPE_BOOL: + return "thrift.BOOL"; + case t_base_type::TYPE_BYTE: + return "thrift.BYTE"; + case t_base_type::TYPE_I16: + return "thrift.I16"; + case t_base_type::TYPE_I32: + return "thrift.I32"; + case t_base_type::TYPE_I64: + return "thrift.I64"; + case t_base_type::TYPE_DOUBLE: + return "thrift.DOUBLE"; + } + } else if (type->is_enum()) { + return "thrift.I32"; + } else if (type->is_struct() || type->is_xception()) { + return "thrift.STRUCT"; + } else if (type->is_map()) { + return "thrift.MAP"; + } else if (type->is_set()) { + return "thrift.SET"; + } else if (type->is_list()) { + return "thrift.LIST"; + } + + throw "INVALID TYPE IN type_to_enum: " + type->get_name(); +} + +/** + * Converts the parse type to a go tyoe + */ +string t_go_generator::type_to_go_type(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 ""; + case t_base_type::TYPE_STRING: + return "string"; + case t_base_type::TYPE_BOOL: + return "bool"; + case t_base_type::TYPE_BYTE: + return "byte"; + case t_base_type::TYPE_I16: + return "int16"; + case t_base_type::TYPE_I32: + return "int32"; + case t_base_type::TYPE_I64: + return "int64"; + case t_base_type::TYPE_DOUBLE: + return "float64"; + } + } else if (type->is_enum()) { + return publicize(type->get_name()); + } else if (type->is_struct() || type->is_xception()) { + return string("*") + publicize(type->get_name()); + } else if (type->is_map()) { + return "thrift.TMap"; + //t_map* t = (t_map*)type; + //string keyType = type_to_go_type(t->get_key_type()); + //string valueType = type_to_go_type(t->get_val_type()); + //return string("map[") + keyType + "]" + valueType; + } else if (type->is_set()) { + return "thrift.TSet"; + //t_set* t = (t_set*)type; + //string elemType = type_to_go_type(t->get_elem_type()); + //return string("[]") + elemType; + } else if (type->is_list()) { + return "thrift.TList"; + //t_list* t = (t_list*)type; + //string elemType = type_to_go_type(t->get_elem_type()); + //return string("[]") + elemType; + } else if (type->is_typedef()) { + return publicize(((t_typedef*)type)->get_symbolic()); + } + + throw "INVALID TYPE IN type_to_go_type: " + type->get_name(); +} + + +/** + * Converts the parse type to a go tyoe + */ +bool t_go_generator::can_be_nil(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 "Invalid Type for can_be_nil"; + case t_base_type::TYPE_STRING: + 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: + return false; + } + } else if (type->is_enum()) { + return false; + } else if (type->is_struct() || type->is_xception()) { + return true; + } else if (type->is_map()) { + return true; + } else if (type->is_set()) { + return true; + } else if (type->is_list()) { + return true; + } + + throw "INVALID TYPE IN can_be_nil: " + type->get_name(); +} + + + +/** See the comment inside generate_go_struct_definition for what this is. */ +string t_go_generator::type_to_spec_args(t_type* ttype) { + while (ttype->is_typedef()) { + ttype = ((t_typedef*)ttype)->get_type(); + } + + if (ttype->is_base_type() || ttype->is_enum()) { + return "nil"; + } else if (ttype->is_struct() || ttype->is_xception()) { + return "(" + type_name(ttype) + ", " + type_name(ttype) + ".thrift_spec)"; + } else if (ttype->is_map()) { + return "(" + + type_to_enum(((t_map*)ttype)->get_key_type()) + "," + + type_to_spec_args(((t_map*)ttype)->get_key_type()) + "," + + type_to_enum(((t_map*)ttype)->get_val_type()) + "," + + type_to_spec_args(((t_map*)ttype)->get_val_type()) + + ")"; + + } else if (ttype->is_set()) { + return "(" + + type_to_enum(((t_set*)ttype)->get_elem_type()) + "," + + type_to_spec_args(((t_set*)ttype)->get_elem_type()) + + ")"; + + } else if (ttype->is_list()) { + return "(" + + type_to_enum(((t_list*)ttype)->get_elem_type()) + "," + + type_to_spec_args(((t_list*)ttype)->get_elem_type()) + + ")"; + } + + throw "INVALID TYPE IN type_to_spec_args: " + ttype->get_name(); +} + + +THRIFT_REGISTER_GENERATOR(go, "Go", ""); diff --git a/configure.ac b/configure.ac index d5071b07..8cad0a1d 100644 --- a/configure.ac +++ b/configure.ac @@ -34,7 +34,7 @@ AS_IF([test "x$PY_PREFIX" = x], [PY_PREFIX="/usr"]) AC_ARG_VAR([JAVA_PREFIX], [Prefix for installing the Java lib jar. (Normal --prefix is ignored for Java because - Java has different conevntions.) + Java has different conventions.) Default = "/usr/local/lib"]) AS_IF([test "x$JAVA_PREFIX" = x], [JAVA_PREFIX="/usr/local/lib"]) @@ -68,6 +68,20 @@ AC_ARG_VAR([CABAL_CONFIGURE_FLAGS], AC_SUBST(CABAL_CONFIGURE_FLAGS) +AC_ARG_VAR([GOROOT], [Prefix for the Go source directory. + (Normal --prefix is ignored for Go because + Go has different conventions.) + Default = "/usr/local/share/go"]) +AS_IF([test "x$GOROOT" = x], [GOROOT="/usr/local/share/go"]) + +AC_ARG_VAR([GOARCH], [Architecture default for Go. + Default = "amd64"]) +AS_IF([test "x$GOARCH" = x], [GOARCH="amd64"]) + +AC_ARG_VAR([GOBIN], [Binary directory for Go. + Default = "/usr/local/bin"]) +AS_IF([test "x$GOBIN" = x], [GOBIN="/usr/local/bin"]) + AC_PROG_CC AC_PROG_CPP AC_PROG_CXX @@ -79,6 +93,8 @@ AC_PROG_LEX AM_PROG_LEX AC_PROG_LN_S AC_PROG_MKDIR_P +AC_PROG_AWK +AC_PROG_RANLIB AC_LANG([C++]) @@ -231,6 +247,36 @@ AC_SUBST(CABAL) AC_SUBST(RUNHASKELL) AM_CONDITIONAL(WITH_HASKELL, [test "$have_haskell" = "yes"]) +AX_THRIFT_LIB(go, [Go], yes) +if test "$with_go" = "yes"; then + case X"$GOARCH" in + Xamd64) + GOARCH_NUM=6 + ;; + X386) + GOARCH_NUM=8 + ;; + Xarm) + GOARCH_NUM=5 + ;; + *) + GOARCH_NUM=6 + ;; + esac + GO_C=${GOBIN}/${GOARCH_NUM}g + GO_L=${GOBIN}/${GOARCH_NUM}l + GOMAKE=${GOBIN}/gomake + GOINSTALL=${GOBIN}/goinstall + AC_PATH_PROG([GO_C], [${GOARCH_NUM}g]) + AC_PATH_PROG([GO_L], [${GOARCH_NUM}l]) + AC_PATH_PROG([GOMAKE], [gomake]) + AC_PATH_PROG([GOINSTALL], [goinstall]) + if [[ -x "$GO_C" -a -x "$GO_L" -a -x "$GOMAKE" -a -x "$GOINSTALL" ]] ; then + have_go="yes" + fi +fi +AM_CONDITIONAL(WITH_GO, [test "$have_go" = "yes"]) + AC_C_CONST AC_C_INLINE AC_C_VOLATILE @@ -238,6 +284,8 @@ AC_C_VOLATILE AC_HEADER_STDBOOL AC_HEADER_STDC AC_HEADER_TIME +AC_HEADER_SYS_WAIT +AC_TYPE_SIGNAL AC_CHECK_HEADERS([arpa/inet.h]) AC_CHECK_HEADERS([sys/param.h]) AC_CHECK_HEADERS([fcntl.h]) @@ -370,6 +418,8 @@ AX_THRIFT_GEN(js, [JavaScript], yes) 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"]) +AX_THRIFT_GEN(go, [GO_C], yes) +AM_CONDITIONAL([THRIFT_GEN_go], [test "$ax_thrift_gen_go" = "yes"]) # --- Coverage hooks --- @@ -444,6 +494,7 @@ echo "Building Haskell Library ..... : $have_haskell" echo "Building Perl Library ........ : $have_perl" echo "Building PHP Library ......... : $have_php" echo "Building Erlang Library ...... : $have_erlang" +echo "Building Go Library .......... : $have_go" if test "$have_cpp" = "yes" ; then echo echo "Building TZlibTransport ...... : $have_zlib" @@ -485,6 +536,14 @@ if test "$have_erlang" = "yes" ; then echo echo "Using erlc ................... : $ERLC" fi +if test "$have_go" = "yes" ; then + echo + echo "Using GOROOT.................. : $GOROOT" + echo "Using GOBIN................... : $GOBIN" + echo "Using GOARCH.................. : $GOARCH" + echo "Using GO Compiler............. : $GO_C" + echo "Using GO Linker............... : $GO_L" +fi echo echo "If something is missing that you think should be present," echo "please skim the output of configure to find the missing" diff --git a/lib/Makefile.am b/lib/Makefile.am index 9dbc1c1b..8538afa3 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -66,4 +66,5 @@ EXTRA_DIST = \ ocaml \ js \ as3 \ - st + st \ + go diff --git a/lib/go/Make.deps b/lib/go/Make.deps new file mode 100644 index 00000000..c233e7e6 --- /dev/null +++ b/lib/go/Make.deps @@ -0,0 +1,134 @@ +thrift/.install: bufio.install bytes.install container/list.install container/vector.install encoding/base64.install encoding/binary.install fmt.install http.install io.install json.install log.install math.install net.install os.install reflect.install sort.install strconv.install strings.install +archive/tar.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/archive/tar.a +archive/zip.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/archive/zip.a +asn1.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/asn1.a +big.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/big.a +bufio.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/bufio.a +bytes.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/bytes.a +cmath.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/cmath.a +compress/flate.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/compress/flate.a +compress/gzip.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/compress/gzip.a +compress/zlib.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/compress/zlib.a +container/heap.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/container/heap.a +container/list.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/container/list.a +container/ring.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/container/ring.a +container/vector.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/container/vector.a +crypto.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/crypto.a +crypto/aes.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/crypto/aes.a +crypto/block.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/crypto/block.a +crypto/blowfish.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/crypto/blowfish.a +crypto/cast5.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/crypto/cast5.a +crypto/cipher.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/crypto/cipher.a +crypto/dsa.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/crypto/dsa.a +crypto/elliptic.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/crypto/elliptic.a +crypto/hmac.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/crypto/hmac.a +crypto/md4.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/crypto/md4.a +crypto/md5.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/crypto/md5.a +crypto/ocsp.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/crypto/ocsp.a +crypto/rand.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/crypto/rand.a +crypto/rc4.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/crypto/rc4.a +crypto/ripemd160.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/crypto/ripemd160.a +crypto/rsa.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/crypto/rsa.a +crypto/sha1.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/crypto/sha1.a +crypto/sha256.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/crypto/sha256.a +crypto/sha512.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/crypto/sha512.a +crypto/subtle.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/crypto/subtle.a +crypto/tls.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/crypto/tls.a +crypto/twofish.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/crypto/twofish.a +crypto/x509.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/crypto/x509.a +crypto/xtea.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/crypto/xtea.a +debug/dwarf.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/debug/dwarf.a +debug/macho.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/debug/macho.a +debug/elf.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/debug/elf.a +debug/gosym.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/debug/gosym.a +debug/pe.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/debug/pe.a +debug/proc.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/debug/proc.a +ebnf.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/ebnf.a +encoding/ascii85.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/encoding/ascii85.a +encoding/base32.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/encoding/base32.a +encoding/base64.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/encoding/base64.a +encoding/binary.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/encoding/binary.a +encoding/git85.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/encoding/git85.a +encoding/hex.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/encoding/hex.a +encoding/line.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/encoding/line.a +encoding/pem.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/encoding/pem.a +exec.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/exec.a +exp/datafmt.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/exp/datafmt.a +exp/draw.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/exp/draw.a +exp/draw/x11.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/exp/draw/x11.a +exp/eval.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/exp/eval.a +expvar.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/expvar.a +flag.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/flag.a +fmt.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/fmt.a +go/ast.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/go/ast.a +go/doc.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/go/doc.a +go/parser.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/go/parser.a +go/printer.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/go/printer.a +go/scanner.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/go/scanner.a +go/token.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/go/token.a +go/typechecker.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/go/typechecker.a +gob.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/gob.a +hash.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/hash.a +hash/adler32.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/hash/adler32.a +hash/crc32.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/hash/crc32.a +hash/crc64.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/hash/crc64.a +html.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/html.a +http.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/http.a +http/pprof.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/http/pprof.a +image.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/image.a +image/jpeg.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/image/jpeg.a +image/png.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/image/png.a +index/suffixarray.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/index/suffixarray.a +io.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/io.a +io/ioutil.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/io/ioutil.a +json.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/json.a +log.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/log.a +math.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/math.a +mime.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/mime.a +mime/multipart.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/mime/multipart.a +net.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/net.a +net/dict.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/net/dict.a +net/textproto.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/net/textproto.a +netchan.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/netchan.a +os.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/os.a +os/signal.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/os/signal.a +patch.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/patch.a +path.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/path.a +rand.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/rand.a +reflect.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/reflect.a +regexp.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/regexp.a +rpc.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/rpc.a +rpc/jsonrpc.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/rpc/jsonrpc.a +runtime.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/runtime.a +runtime/cgo.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/runtime/cgo.a +runtime/debug.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/runtime/debug.a +runtime/pprof.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/runtime/pprof.a +scanner.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/scanner.a +smtp.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/smtp.a +sort.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/sort.a +strconv.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/strconv.a +strings.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/strings.a +sync.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/sync.a +syscall.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/syscall.a +syslog.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/syslog.a +tabwriter.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/tabwriter.a +template.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/template.a +testing.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/testing.a +testing/iotest.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/testing/iotest.a +testing/quick.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/testing/quick.a +testing/script.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/testing/script.a +time.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/time.a +try.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/try.a +unicode.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/unicode.a +utf16.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/utf16.a +utf8.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/utf8.a +websocket.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/websocket.a +xml.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/xml.a +../cmd/cgo.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/../cmd/cgo.a +../cmd/ebnflint.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/../cmd/ebnflint.a +../cmd/godoc.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/../cmd/godoc.a +../cmd/gofmt.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/../cmd/gofmt.a +../cmd/goinstall.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/../cmd/goinstall.a +../cmd/govet.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/../cmd/govet.a +../cmd/goyacc.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/../cmd/goyacc.a +../cmd/hgpatch.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/../cmd/hgpatch.a diff --git a/lib/go/Makefile b/lib/go/Makefile new file mode 100644 index 00000000..8e81d8d3 --- /dev/null +++ b/lib/go/Makefile @@ -0,0 +1,64 @@ +# Copyright 2009 The Go Authors. All rights reserved. +# Use of this source code is governed by a BSD-style +# license that can be found in the LICENSE file. + +# After editing the DIRS= list or adding imports to any Go files +# in any of those directories, run: +# +# ./deps.bash +# +# to rebuild the dependency information in Make.deps. + + +include $(GOROOT)/src/Make.inc + +all: Make.deps install + +DIRS=\ + thrift/\ + +TEST=\ + $(filter-out $(NOTEST),$(DIRS)) + + +clean.dirs: $(addsuffix .clean, $(DIRS)) +install.dirs: $(addsuffix .install, $(DIRS)) +nuke.dirs: $(addsuffix .nuke, $(DIRS)) +test.dirs: $(addsuffix .test, $(DIRS)) +check.dirs: $(addsuffix .check, $(DIRS)) + +%.clean: + +cd $* && gomake clean + +%.install: + +cd $* && gomake install + +%.nuke: + +cd $* && gomake nuke + +%.test: + +cd $* && gomake test + +%.check: + +cd $* && gomake check + +clean: clean.dirs + +install: install.dirs + +test: test.dirs + +check: check.dirs + +#nuke: nuke.dirs +# rm -rf "$(GOROOT)"/pkg/thrift.* + +echo-dirs: + @echo $(DIRS) + +Make.deps: + ./deps.bash + +deps: + ./deps.bash + diff --git a/lib/go/deps.bash b/lib/go/deps.bash new file mode 100644 index 00000000..dabd404f --- /dev/null +++ b/lib/go/deps.bash @@ -0,0 +1,41 @@ +#!/usr/bin/env bash +# Copyright 2009 The Go Authors. All rights reserved. +# Use of this source code is governed by a BSD-style +# license that can be found in the LICENSE file. + +OUT="Make.deps" +TMP="Make.deps.tmp" + +if [ -f $OUT ] && ! [ -w $OUT ]; then + echo "$0: $OUT is read-only; aborting." 1>&2 + exit 1 +fi + +# Get list of directories from Makefile +dirs=$(sed '1,/^DIRS=/d; /^$/,$d; s/\\//g' Makefile) +dirs2=$(sed '1,/^DIRS=/d; /^$/,$d; s/\\//g' $GOROOT/src/pkg/Makefile) +dirpat=$(echo $dirs $dirs2 | sed 's/ /|/g; s/.*/^(&)$/') + +for dir in $dirs; do ( + cd $dir || exit 1 + + sources=$(sed -n 's/\.go\\/.go/p' Makefile) + sources=$(ls $sources 2> /dev/null) # remove .s, .c, etc. + + deps=$( + sed -n '/^import.*"/p; /^import[ \t]*(/,/^)/p' $sources /dev/null | + cut -d '"' -f2 | + egrep "$dirpat" | + grep -v "^$dir\$" | + sed 's/$/.install/' | + sort -u + ) + + echo $dir.install: $deps +) done > $TMP + +for dir in $dirs2; do ( + echo $dir.install: \${GOROOT}/pkg/\${GOOS}_\${GOARCH}/${dir}.a +) done >> $TMP + +mv $TMP $OUT diff --git a/lib/go/thrift/Makefile b/lib/go/thrift/Makefile new file mode 100644 index 00000000..59f78625 --- /dev/null +++ b/lib/go/thrift/Makefile @@ -0,0 +1,58 @@ +# Copyright 2009 The Go Authors. All rights reserved. +# Use of this source code is governed by a BSD-style +# license that can be found in the LICENSE file. + +include $(GOROOT)/src/Make.inc + +TARG=thrift +GOFILES=\ + tapplication_exception.go\ + tbase.go\ + tbinary_protocol.go\ + tcompact_protocol.go\ + tcompare.go\ + tcontainer.go\ + texception.go\ + tfield.go\ + tframed_transport.go\ + thttp_client.go\ + tiostream_transport.go\ + tlist.go\ + tjson_protocol.go\ + tmap.go\ + tmemory_buffer.go\ + tmessage.go\ + tmessagetype.go\ + tnonblocking_server.go\ + tnonblocking_server_socket.go\ + tnonblocking_socket.go\ + tnonblocking_transport.go\ + tnumeric.go\ + tprocessor.go\ + tprocessor_factory.go\ + tprotocol.go\ + tprotocol_exception.go\ + tprotocol_factory.go\ + tserver.go\ + tserver_socket.go\ + tserver_transport.go\ + tset.go\ + tsimple_server.go\ + tsimple_json_protocol.go\ + tsocket.go\ + tstruct.go\ + ttransport.go\ + ttransport_exception.go\ + ttransport_factory.go\ + ttype.go + +DIRS=\ + +include $(GOROOT)/src/Make.pkg + +check: + gomake test + +-include ../Make.deps + + diff --git a/lib/go/thrift/_testmain.go b/lib/go/thrift/_testmain.go new file mode 100644 index 00000000..3b0abfb4 --- /dev/null +++ b/lib/go/thrift/_testmain.go @@ -0,0 +1,68 @@ +package main + +import "./_xtest_" +import "testing" +import __regexp__ "regexp" + +var tests = []testing.InternalTest{ + {"thrift_test.TestTApplicationException", thrift_test.TestTApplicationException}, + {"thrift_test.TestReadWriteBinaryProtocol", thrift_test.TestReadWriteBinaryProtocol}, + {"thrift_test.TestReadWriteCompactProtocol", thrift_test.TestReadWriteCompactProtocol}, + {"thrift_test.TestTException", thrift_test.TestTException}, + {"thrift_test.TestFramedTransport", thrift_test.TestFramedTransport}, + {"thrift_test.TestHttpClient", thrift_test.TestHttpClient}, + {"thrift_test.TestIOStreamTransport", thrift_test.TestIOStreamTransport}, + {"thrift_test.TestWriteJSONProtocolBool", thrift_test.TestWriteJSONProtocolBool}, + {"thrift_test.TestReadJSONProtocolBool", thrift_test.TestReadJSONProtocolBool}, + {"thrift_test.TestWriteJSONProtocolByte", thrift_test.TestWriteJSONProtocolByte}, + {"thrift_test.TestReadJSONProtocolByte", thrift_test.TestReadJSONProtocolByte}, + {"thrift_test.TestWriteJSONProtocolI16", thrift_test.TestWriteJSONProtocolI16}, + {"thrift_test.TestReadJSONProtocolI16", thrift_test.TestReadJSONProtocolI16}, + {"thrift_test.TestWriteJSONProtocolI32", thrift_test.TestWriteJSONProtocolI32}, + {"thrift_test.TestReadJSONProtocolI32", thrift_test.TestReadJSONProtocolI32}, + {"thrift_test.TestWriteJSONProtocolI64", thrift_test.TestWriteJSONProtocolI64}, + {"thrift_test.TestReadJSONProtocolI64", thrift_test.TestReadJSONProtocolI64}, + {"thrift_test.TestWriteJSONProtocolDouble", thrift_test.TestWriteJSONProtocolDouble}, + {"thrift_test.TestReadJSONProtocolDouble", thrift_test.TestReadJSONProtocolDouble}, + {"thrift_test.TestWriteJSONProtocolString", thrift_test.TestWriteJSONProtocolString}, + {"thrift_test.TestReadJSONProtocolString", thrift_test.TestReadJSONProtocolString}, + {"thrift_test.TestWriteJSONProtocolBinary", thrift_test.TestWriteJSONProtocolBinary}, + {"thrift_test.TestReadJSONProtocolBinary", thrift_test.TestReadJSONProtocolBinary}, + {"thrift_test.TestWriteJSONProtocolList", thrift_test.TestWriteJSONProtocolList}, + {"thrift_test.TestWriteJSONProtocolSet", thrift_test.TestWriteJSONProtocolSet}, + {"thrift_test.TestWriteJSONProtocolMap", thrift_test.TestWriteJSONProtocolMap}, + {"thrift_test.TestReadWriteJSONStruct", thrift_test.TestReadWriteJSONStruct}, + {"thrift_test.TestReadWriteJSONProtocol", thrift_test.TestReadWriteJSONProtocol}, + {"thrift_test.TestMemoryBuffer", thrift_test.TestMemoryBuffer}, + {"thrift_test.TestNonblockingTransportServerToClient", thrift_test.TestNonblockingTransportServerToClient}, + {"thrift_test.TestNonblockingTransportClientToServer", thrift_test.TestNonblockingTransportClientToServer}, + {"thrift_test.TestNothing", thrift_test.TestNothing}, + {"thrift_test.TestWriteSimpleJSONProtocolBool", thrift_test.TestWriteSimpleJSONProtocolBool}, + {"thrift_test.TestReadSimpleJSONProtocolBool", thrift_test.TestReadSimpleJSONProtocolBool}, + {"thrift_test.TestWriteSimpleJSONProtocolByte", thrift_test.TestWriteSimpleJSONProtocolByte}, + {"thrift_test.TestReadSimpleJSONProtocolByte", thrift_test.TestReadSimpleJSONProtocolByte}, + {"thrift_test.TestWriteSimpleJSONProtocolI16", thrift_test.TestWriteSimpleJSONProtocolI16}, + {"thrift_test.TestReadSimpleJSONProtocolI16", thrift_test.TestReadSimpleJSONProtocolI16}, + {"thrift_test.TestWriteSimpleJSONProtocolI32", thrift_test.TestWriteSimpleJSONProtocolI32}, + {"thrift_test.TestReadSimpleJSONProtocolI32", thrift_test.TestReadSimpleJSONProtocolI32}, + {"thrift_test.TestWriteSimpleJSONProtocolI64", thrift_test.TestWriteSimpleJSONProtocolI64}, + {"thrift_test.TestReadSimpleJSONProtocolI64", thrift_test.TestReadSimpleJSONProtocolI64}, + {"thrift_test.TestWriteSimpleJSONProtocolDouble", thrift_test.TestWriteSimpleJSONProtocolDouble}, + {"thrift_test.TestReadSimpleJSONProtocolDouble", thrift_test.TestReadSimpleJSONProtocolDouble}, + {"thrift_test.TestWriteSimpleJSONProtocolString", thrift_test.TestWriteSimpleJSONProtocolString}, + {"thrift_test.TestReadSimpleJSONProtocolString", thrift_test.TestReadSimpleJSONProtocolString}, + {"thrift_test.TestWriteSimpleJSONProtocolBinary", thrift_test.TestWriteSimpleJSONProtocolBinary}, + {"thrift_test.TestReadSimpleJSONProtocolBinary", thrift_test.TestReadSimpleJSONProtocolBinary}, + {"thrift_test.TestWriteSimpleJSONProtocolList", thrift_test.TestWriteSimpleJSONProtocolList}, + {"thrift_test.TestWriteSimpleJSONProtocolSet", thrift_test.TestWriteSimpleJSONProtocolSet}, + {"thrift_test.TestWriteSimpleJSONProtocolMap", thrift_test.TestWriteSimpleJSONProtocolMap}, + {"thrift_test.TestReadWriteSimpleJSONStruct", thrift_test.TestReadWriteSimpleJSONStruct}, + {"thrift_test.TestReadWriteSimpleJSONProtocol", thrift_test.TestReadWriteSimpleJSONProtocol}, +} +var benchmarks = []testing.InternalBenchmark{ // +} + +func main() { + testing.Main(__regexp__.MatchString, tests) + testing.RunBenchmarks(__regexp__.MatchString, benchmarks) +} diff --git a/lib/go/thrift/tapplication_exception.go b/lib/go/thrift/tapplication_exception.go new file mode 100644 index 00000000..fc8bf2ec --- /dev/null +++ b/lib/go/thrift/tapplication_exception.go @@ -0,0 +1,169 @@ +/* + * 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. + */ + +package thrift + +import ( + "os" +) + +const ( + UNKNOWN_APPLICATION_EXCEPTION = 0 + UNKNOWN_METHOD = 1 + INVALID_MESSAGE_TYPE_EXCEPTION = 2 + WRONG_METHOD_NAME = 3 + BAD_SEQUENCE_ID = 4 + MISSING_RESULT = 5 + INTERNAL_ERROR = 6 + PROTOCOL_ERROR = 7 +) + + +/** + * Application level exception + * + */ +type TApplicationException interface { + TException + TypeId() int32 + Read(iprot TProtocol) (TApplicationException, os.Error) + Write(oprot TProtocol) os.Error +} + +type tApplicationException struct { + TException + type_ int32 +} + +func NewTApplicationExceptionDefault() TApplicationException { + return NewTApplicationException(UNKNOWN_APPLICATION_EXCEPTION, "UNKNOWN") +} + +func NewTApplicationExceptionType(type_ int32) TApplicationException { + return NewTApplicationException(type_, "UNKNOWN") +} + +func NewTApplicationException(type_ int32, message string) TApplicationException { + return &tApplicationException{TException: NewTException(message), type_: type_} +} + +func NewTApplicationExceptionMessage(message string) TApplicationException { + return NewTApplicationException(UNKNOWN_APPLICATION_EXCEPTION, message) +} + +func (p *tApplicationException) TypeId() int32 { + return p.type_ +} + +func (p *tApplicationException) Read(iprot TProtocol) (error TApplicationException, err os.Error) { + _, err = iprot.ReadStructBegin() + if err != nil { + return + } + + message := "" + type_ := int32(UNKNOWN_APPLICATION_EXCEPTION) + + for { + _, ttype, id, err := iprot.ReadFieldBegin() + if err != nil { + return + } + if ttype == STOP { + break + } + switch id { + case 1: + if ttype == STRING { + message, err = iprot.ReadString() + if err != nil { + return + } + } else { + err = SkipDefaultDepth(iprot, ttype) + if err != nil { + return + } + } + break + case 2: + if ttype == I32 { + type_, err = iprot.ReadI32() + if err != nil { + return + } + } else { + err = SkipDefaultDepth(iprot, ttype) + if err != nil { + return + } + } + break + default: + err = SkipDefaultDepth(iprot, ttype) + if err != nil { + return + } + break + } + err = iprot.ReadFieldEnd() + if err != nil { + return + } + } + err = iprot.ReadStructEnd() + error = NewTApplicationException(type_, message) + return +} + +func (p *tApplicationException) Write(oprot TProtocol) (err os.Error) { + err = oprot.WriteStructBegin("TApplicationException") + if len(p.String()) > 0 { + err = oprot.WriteFieldBegin("message", STRING, 1) + if err != nil { + return + } + err = oprot.WriteString(p.String()) + if err != nil { + return + } + err = oprot.WriteFieldEnd() + if err != nil { + return + } + } + err = oprot.WriteFieldBegin("type", I32, 2) + if err != nil { + return + } + err = oprot.WriteI32(p.type_) + if err != nil { + return + } + err = oprot.WriteFieldEnd() + if err != nil { + return + } + err = oprot.WriteFieldStop() + if err != nil { + return + } + err = oprot.WriteStructEnd() + return +} diff --git a/lib/go/thrift/tapplication_exception_test.go b/lib/go/thrift/tapplication_exception_test.go new file mode 100644 index 00000000..d9572f49 --- /dev/null +++ b/lib/go/thrift/tapplication_exception_test.go @@ -0,0 +1,42 @@ +/* + * 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. + */ + +package thrift_test + +import ( + . "thrift" + "testing" +) + +func TestTApplicationException(t *testing.T) { + exc := NewTApplicationException(UNKNOWN_APPLICATION_EXCEPTION, "") + if exc.String() != "" { + t.Fatalf("Expected empty string for exception but found '%s'", exc.String()) + } + if exc.TypeId() != UNKNOWN_APPLICATION_EXCEPTION { + t.Fatalf("Expected type UNKNOWN for exception but found '%s'", exc.TypeId()) + } + exc = NewTApplicationException(WRONG_METHOD_NAME, "junk_method") + if exc.String() != "junk_method" { + t.Fatalf("Expected 'junk_method' for exception but found '%s'", exc.String()) + } + if exc.TypeId() != WRONG_METHOD_NAME { + t.Fatalf("Expected type WRONG_METHOD_NAME for exception but found '%s'", exc.TypeId()) + } +} diff --git a/lib/go/thrift/tbase.go b/lib/go/thrift/tbase.go new file mode 100644 index 00000000..adc52d00 --- /dev/null +++ b/lib/go/thrift/tbase.go @@ -0,0 +1,66 @@ +/* + * 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. + */ + +package thrift + +/** + * Generic base interface for generated Thrift objects. + * + */ +type TBase interface { + + /** + * Reads the TObject from the given input protocol + * + * @param iprot Input protocol + */ + Read(iprot TProtocol) (err TException) + + /** + * Writes the objects out to the protocol + * + * @param oprot Output protocol + */ + Write(oprot TProtocol) (err TException) + + /** + * Check if a field is currently set or unset. + * + * @param field + */ + IsSet(field TField) bool + + /** + * Get a field's value by field variable. Primitive types will be wrapped in + * the appropriate "boxed" types. + * + * @param field + */ + FieldValue(field TField) interface{} + + /** + * Set a field's value by field variable. Primitive types must be "boxed" in + * the appropriate object wrapper type. + * + * @param field + */ + SetFieldValue(field TField, value interface{}) + + DeepCopy() TBase +} diff --git a/lib/go/thrift/tbinary_protocol.go b/lib/go/thrift/tbinary_protocol.go new file mode 100644 index 00000000..1c88da64 --- /dev/null +++ b/lib/go/thrift/tbinary_protocol.go @@ -0,0 +1,493 @@ +/* + * 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. + */ + +package thrift + +import ( + "encoding/binary" + "math" + "strings" + "io" +) + +type TBinaryProtocol struct { + //TProtocolBase; + trans TTransport + _StrictRead bool + _StrictWrite bool + _ReadLength int + _CheckReadLength bool +} + +type TBinaryProtocolFactory struct { + _StrictRead bool + _StrictWrite bool +} + +func NewTBinaryProtocolTransport(t TTransport) *TBinaryProtocol { + return NewTBinaryProtocol(t, false, true) +} + +func NewTBinaryProtocol(t TTransport, strictRead, strictWrite bool) *TBinaryProtocol { + //return &TBinaryProtocol{TProtocolBase:TProtocolBase{trans:t}, _StrictRead:strictRead, _StrictWrite:strictWrite, _ReadLength:0, _CheckReadLength:false}; + return &TBinaryProtocol{trans: t, _StrictRead: strictRead, _StrictWrite: strictWrite, _ReadLength: 0, _CheckReadLength: false} +} + +func NewTBinaryProtocolFactoryDefault() *TBinaryProtocolFactory { + return NewTBinaryProtocolFactory(false, true) +} + +func NewTBinaryProtocolFactory(strictRead, strictWrite bool) *TBinaryProtocolFactory { + return &TBinaryProtocolFactory{_StrictRead: strictRead, _StrictWrite: strictWrite} +} + +func (p *TBinaryProtocolFactory) GetProtocol(t TTransport) TProtocol { + return NewTBinaryProtocol(t, p._StrictRead, p._StrictWrite) +} + +/** + * Writing Methods + */ + +func (p *TBinaryProtocol) WriteMessageBegin(name string, typeId TMessageType, seqId int32) TProtocolException { + if p._StrictWrite { + version := uint32(VERSION_1) | uint32(typeId) + e := p.WriteI32(int32(version)) + if e != nil { + return e + } + e = p.WriteString(name) + if e != nil { + return e + } + e = p.WriteI32(seqId) + return e + } else { + e := p.WriteString(name) + if e != nil { + return e + } + e = p.WriteByte(byte(typeId)) + if e != nil { + return e + } + e = p.WriteI32(seqId) + return e + } + return nil +} + +func (p *TBinaryProtocol) WriteMessageEnd() TProtocolException { + return nil +} + +func (p *TBinaryProtocol) WriteStructBegin(name string) TProtocolException { + return nil +} + +func (p *TBinaryProtocol) WriteStructEnd() TProtocolException { + return nil +} + +func (p *TBinaryProtocol) WriteFieldBegin(name string, typeId TType, id int16) TProtocolException { + e := p.WriteByte(byte(typeId)) + if e != nil { + return e + } + e = p.WriteI16(id) + return e +} + +func (p *TBinaryProtocol) WriteFieldEnd() TProtocolException { + return nil +} + +func (p *TBinaryProtocol) WriteFieldStop() TProtocolException { + e := p.WriteByte(STOP) + return e +} + +func (p *TBinaryProtocol) WriteMapBegin(keyType TType, valueType TType, size int) TProtocolException { + e := p.WriteByte(byte(keyType)) + if e != nil { + return e + } + e = p.WriteByte(byte(valueType)) + if e != nil { + return e + } + e = p.WriteI32(int32(size)) + return e +} + +func (p *TBinaryProtocol) WriteMapEnd() TProtocolException { + return nil +} + +func (p *TBinaryProtocol) WriteListBegin(elemType TType, size int) TProtocolException { + e := p.WriteByte(byte(elemType)) + if e != nil { + return e + } + e = p.WriteI32(int32(size)) + return e +} + +func (p *TBinaryProtocol) WriteListEnd() TProtocolException { + return nil +} + +func (p *TBinaryProtocol) WriteSetBegin(elemType TType, size int) TProtocolException { + e := p.WriteByte(byte(elemType)) + if e != nil { + return e + } + e = p.WriteI32(int32(size)) + return e +} + +func (p *TBinaryProtocol) WriteSetEnd() TProtocolException { + return nil +} + +func (p *TBinaryProtocol) WriteBool(value bool) TProtocolException { + if value { + return p.WriteByte(1) + } + return p.WriteByte(0) +} + +func (p *TBinaryProtocol) WriteByte(value byte) TProtocolException { + v := []byte{value} + _, e := p.trans.Write(v) + return NewTProtocolExceptionFromOsError(e) +} + +func (p *TBinaryProtocol) WriteI16(value int16) TProtocolException { + h := byte(0xff & (value >> 8)) + l := byte(0xff & value) + v := []byte{h, l} + _, e := p.trans.Write(v) + return NewTProtocolExceptionFromOsError(e) +} + +func (p *TBinaryProtocol) WriteI32(value int32) TProtocolException { + a := byte(0xff & (value >> 24)) + b := byte(0xff & (value >> 16)) + c := byte(0xff & (value >> 8)) + d := byte(0xff & value) + v := []byte{a, b, c, d} + _, e := p.trans.Write(v) + return NewTProtocolExceptionFromOsError(e) +} + +func (p *TBinaryProtocol) WriteI64(value int64) TProtocolException { + a := byte(0xff & (value >> 56)) + b := byte(0xff & (value >> 48)) + c := byte(0xff & (value >> 40)) + d := byte(0xff & (value >> 32)) + e := byte(0xff & (value >> 24)) + f := byte(0xff & (value >> 16)) + g := byte(0xff & (value >> 8)) + h := byte(0xff & value) + v := []byte{a, b, c, d, e, f, g, h} + _, err := p.trans.Write(v) + return NewTProtocolExceptionFromOsError(err) +} + +func (p *TBinaryProtocol) WriteDouble(value float64) TProtocolException { + return p.WriteI64(int64(math.Float64bits(value))) +} + +func (p *TBinaryProtocol) WriteString(value string) TProtocolException { + return p.WriteBinaryFromReader(strings.NewReader(value), len(value)) +} + +func (p *TBinaryProtocol) WriteBinary(value []byte) TProtocolException { + e := p.WriteI32(int32(len(value))) + if e != nil { + return e + } + _, err := p.trans.Write(value) + return NewTProtocolExceptionFromOsError(err) +} + +func (p *TBinaryProtocol) WriteBinaryFromReader(reader io.Reader, size int) TProtocolException { + e := p.WriteI32(int32(size)) + if e != nil { + return e + } + _, err := io.Copyn(p.trans, reader, int64(size)) + return NewTProtocolExceptionFromOsError(err) +} + + +/** + * Reading methods + */ + +func (p *TBinaryProtocol) ReadMessageBegin() (name string, typeId TMessageType, seqId int32, err TProtocolException) { + size, e := p.ReadI32() + if e != nil { + return "", typeId, 0, NewTProtocolExceptionFromOsError(e) + } + if size < 0 { + typeId = TMessageType(size & 0x0ff) + version := int64(int64(size) & VERSION_MASK) + if version != VERSION_1 { + return name, typeId, seqId, NewTProtocolException(BAD_VERSION, "Bad version in ReadMessageBegin") + } + name, e = p.ReadString() + if e != nil { + return name, typeId, seqId, NewTProtocolExceptionFromOsError(e) + } + seqId, e = p.ReadI32() + if e != nil { + return name, typeId, seqId, NewTProtocolExceptionFromOsError(e) + } + return name, typeId, seqId, nil + } + if p._StrictRead { + return name, typeId, seqId, NewTProtocolException(BAD_VERSION, "Missing version in ReadMessageBegin") + } + name, e2 := p.readStringBody(int(size)) + if e2 != nil { + return name, typeId, seqId, e2 + } + b, e3 := p.ReadByte() + if e3 != nil { + return name, typeId, seqId, e3 + } + typeId = TMessageType(b) + seqId, e4 := p.ReadI32() + if e4 != nil { + return name, typeId, seqId, e4 + } + return name, typeId, seqId, nil +} + +func (p *TBinaryProtocol) ReadMessageEnd() TProtocolException { + return nil +} + +func (p *TBinaryProtocol) ReadStructBegin() (name string, err TProtocolException) { + return +} + +func (p *TBinaryProtocol) ReadStructEnd() TProtocolException { + return nil +} + +func (p *TBinaryProtocol) ReadFieldBegin() (name string, typeId TType, seqId int16, err TProtocolException) { + t, err := p.ReadByte() + typeId = TType(t) + if err != nil { + return name, typeId, seqId, err + } + if t != STOP { + seqId, err = p.ReadI16() + } + return name, typeId, seqId, err +} + +func (p *TBinaryProtocol) ReadFieldEnd() TProtocolException { + return nil +} + +func (p *TBinaryProtocol) ReadMapBegin() (kType, vType TType, size int, err TProtocolException) { + k, e := p.ReadByte() + if e != nil { + err = NewTProtocolExceptionFromOsError(e) + return + } + kType = TType(k) + v, e := p.ReadByte() + if e != nil { + err = NewTProtocolExceptionFromOsError(e) + return + } + vType = TType(v) + size32, e := p.ReadI32() + size = int(size32) + if e != nil { + err = NewTProtocolExceptionFromOsError(e) + return + } + return kType, vType, size, nil +} + +func (p *TBinaryProtocol) ReadMapEnd() TProtocolException { + return nil +} + +func (p *TBinaryProtocol) ReadListBegin() (elemType TType, size int, err TProtocolException) { + b, e := p.ReadByte() + if e != nil { + err = NewTProtocolExceptionFromOsError(e) + return + } + elemType = TType(b) + size32, e := p.ReadI32() + size = int(size32) + if e != nil { + err = NewTProtocolExceptionFromOsError(e) + return + } + return elemType, size, nil +} + +func (p *TBinaryProtocol) ReadListEnd() TProtocolException { + return nil +} + +func (p *TBinaryProtocol) ReadSetBegin() (elemType TType, size int, err TProtocolException) { + b, e := p.ReadByte() + if e != nil { + err = NewTProtocolExceptionFromOsError(e) + return + } + elemType = TType(b) + size32, e := p.ReadI32() + size = int(size32) + if e != nil { + err = NewTProtocolExceptionFromOsError(e) + return + } + return elemType, size, nil +} + +func (p *TBinaryProtocol) ReadSetEnd() TProtocolException { + return nil +} + +func (p *TBinaryProtocol) ReadBool() (bool, TProtocolException) { + b, e := p.ReadByte() + v := true + if b != 1 { + v = false + } + return v, e +} + +func (p *TBinaryProtocol) ReadByte() (value byte, err TProtocolException) { + buf := []byte{0} + err = p.readAll(buf) + return buf[0], err +} + +func (p *TBinaryProtocol) ReadI16() (value int16, err TProtocolException) { + buf := []byte{0, 0} + err = p.readAll(buf) + value = int16(binary.BigEndian.Uint16(buf)) + return value, err +} + +func (p *TBinaryProtocol) ReadI32() (value int32, err TProtocolException) { + buf := []byte{0, 0, 0, 0} + err = p.readAll(buf) + value = int32(binary.BigEndian.Uint32(buf)) + return value, err +} + +func (p *TBinaryProtocol) ReadI64() (value int64, err TProtocolException) { + buf := []byte{0, 0, 0, 0, 0, 0, 0, 0} + err = p.readAll(buf) + value = int64(binary.BigEndian.Uint64(buf)) + return value, err +} + +func (p *TBinaryProtocol) ReadDouble() (value float64, err TProtocolException) { + buf := []byte{0, 0, 0, 0, 0, 0, 0, 0} + err = p.readAll(buf) + value = math.Float64frombits(binary.BigEndian.Uint64(buf)) + return value, err +} + +func (p *TBinaryProtocol) ReadString() (value string, err TProtocolException) { + size, e := p.ReadI32() + if e != nil { + return "", e + } + return p.readStringBody(int(size)) +} + +func (p *TBinaryProtocol) ReadBinary() ([]byte, TProtocolException) { + size, e := p.ReadI32() + if e != nil { + return nil, e + } + isize := int(size) + e = p.checkReadLength(isize) + if e != nil { + return nil, e + } + buf := make([]byte, isize) + _, err := p.trans.ReadAll(buf) + return buf, NewTProtocolExceptionFromOsError(err) +} + +func (p *TBinaryProtocol) Flush() (err TProtocolException) { + return NewTProtocolExceptionFromOsError(p.trans.Flush()) +} + +func (p *TBinaryProtocol) Skip(fieldType TType) (err TProtocolException) { + return SkipDefaultDepth(p, fieldType) +} + +func (p *TBinaryProtocol) Transport() TTransport { + return p.trans +} + +func (p *TBinaryProtocol) readAll(buf []byte) TProtocolException { + e := p.checkReadLength(len(buf)) + if e != nil { + return e + } + _, err := p.trans.ReadAll(buf) + return NewTProtocolExceptionFromOsError(err) +} + +func (p *TBinaryProtocol) setReadLength(readLength int) { + p._ReadLength = readLength + p._CheckReadLength = true +} + +func (p *TBinaryProtocol) checkReadLength(length int) TProtocolException { + if p._CheckReadLength { + p._ReadLength = p._ReadLength - length + if p._ReadLength < 0 { + return NewTProtocolException(UNKNOWN_PROTOCOL_EXCEPTION, "Message length exceeded: "+string(length)) + } + } + return nil +} + +func (p *TBinaryProtocol) readStringBody(size int) (value string, err TProtocolException) { + if size < 0 { + return "", nil + } + err = p.checkReadLength(size) + if err != nil { + return "", err + } + isize := int(size) + buf := make([]byte, isize) + _, e := p.trans.ReadAll(buf) + return string(buf), NewTProtocolExceptionFromOsError(e) +} diff --git a/lib/go/thrift/tbinary_protocol_test.go b/lib/go/thrift/tbinary_protocol_test.go new file mode 100644 index 00000000..b21b2483 --- /dev/null +++ b/lib/go/thrift/tbinary_protocol_test.go @@ -0,0 +1,31 @@ +/* + * 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. + */ + +package thrift_test + +import ( + . "thrift" + "testing" + //"bytes"; +) + + +func TestReadWriteBinaryProtocol(t *testing.T) { + ReadWriteProtocolTest(t, NewTBinaryProtocolFactoryDefault()) +} diff --git a/lib/go/thrift/tcompact_protocol.go b/lib/go/thrift/tcompact_protocol.go new file mode 100644 index 00000000..9b780f7d --- /dev/null +++ b/lib/go/thrift/tcompact_protocol.go @@ -0,0 +1,856 @@ +/* + * 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. + */ + +package thrift + +import ( + "container/vector" + "encoding/binary" + "fmt" + "math" + "os" + "strings" +) + +const ( + COMPACT_PROTOCOL_ID = 0x082 + COMPACT_VERSION = 1 + COMPACT_VERSION_MASK = 0x1f + COMPACT_TYPE_MASK = 0x0E0 + COMPACT_TYPE_SHIFT_AMOUNT = 5 +) + +type TCompactType byte + +const ( + COMPACT_BOOLEAN_TRUE = 0x01 + COMPACT_BOOLEAN_FALSE = 0x02 + COMPACT_BYTE = 0x03 + COMPACT_I16 = 0x04 + COMPACT_I32 = 0x05 + COMPACT_I64 = 0x06 + COMPACT_DOUBLE = 0x07 + COMPACT_BINARY = 0x08 + COMPACT_LIST = 0x09 + COMPACT_SET = 0x0A + COMPACT_MAP = 0x0B + COMPACT_STRUCT = 0x0C +) + +var ( + _TTypeToCompactType []TCompactType + _TSTOP TField +) + +func init() { + _TSTOP = NewTField("", STOP, 0) + _TTypeToCompactType = make([]TCompactType, int(UTF16)+1) + _TTypeToCompactType[int(STOP)] = STOP + _TTypeToCompactType[int(BOOL)] = COMPACT_BOOLEAN_TRUE + _TTypeToCompactType[int(BYTE)] = COMPACT_BYTE + _TTypeToCompactType[int(I16)] = COMPACT_I16 + _TTypeToCompactType[int(I32)] = COMPACT_I32 + _TTypeToCompactType[int(I64)] = COMPACT_I64 + _TTypeToCompactType[int(DOUBLE)] = COMPACT_DOUBLE + _TTypeToCompactType[int(STRING)] = COMPACT_BINARY + _TTypeToCompactType[int(LIST)] = COMPACT_LIST + _TTypeToCompactType[int(SET)] = COMPACT_SET + _TTypeToCompactType[int(MAP)] = COMPACT_MAP + _TTypeToCompactType[int(STRUCT)] = COMPACT_STRUCT +} + +type TCompactProtocolFactory struct{} + +func NewTCompactProtocolFactory() *TCompactProtocolFactory { + return &TCompactProtocolFactory{} +} + +func (p *TCompactProtocolFactory) GetProtocol(trans TTransport) TProtocol { + return NewTCompactProtocol(trans) +} + +type TCompactProtocol struct { + trans TTransport + + /** + * Used to keep track of the last field for the current and previous structs, + * so we can do the delta stuff. + */ + lastField *vector.IntVector + lastFieldId int + + /** + * If we encounter a boolean field begin, save the TField here so it can + * have the value incorporated. + */ + booleanField TField + + /** + * If we read a field header, and it's a boolean field, save the boolean + * value here so that readBool can use it. + */ + boolValue bool + boolValueIsNotNull bool +} + +/** + * Create a TCompactProtocol. + * + * @param transport the TTransport object to read from or write to. + */ +func NewTCompactProtocol(trans TTransport) *TCompactProtocol { + return &TCompactProtocol{trans: trans, lastField: &vector.IntVector{}} +} + + +// +// Public Writing methods. +// + +/** + * Write a message header to the wire. Compact Protocol messages contain the + * protocol version so we can migrate forwards in the future if need be. + */ +func (p *TCompactProtocol) WriteMessageBegin(name string, typeId TMessageType, seqid int32) TProtocolException { + _, err := p.writeByteDirect(COMPACT_PROTOCOL_ID) + if err != nil { + return NewTProtocolExceptionFromOsError(err) + } + _, err = p.writeByteDirect((COMPACT_VERSION & COMPACT_VERSION_MASK) | ((byte(typeId) << COMPACT_TYPE_SHIFT_AMOUNT) & COMPACT_TYPE_MASK)) + if err != nil { + return NewTProtocolExceptionFromOsError(err) + } + _, err = p.writeVarint32(seqid) + if err != nil { + return NewTProtocolExceptionFromOsError(err) + } + e := p.WriteString(name) + return e + +} + +func (p *TCompactProtocol) WriteMessageEnd() TProtocolException { return nil } + +/** + * Write a struct begin. This doesn't actually put anything on the wire. We + * use it as an opportunity to put special placeholder markers on the field + * stack so we can get the field id deltas correct. + */ +func (p *TCompactProtocol) WriteStructBegin(name string) TProtocolException { + p.lastField.Push(p.lastFieldId) + p.lastFieldId = 0 + return nil +} + +/** + * Write a struct end. This doesn't actually put anything on the wire. We use + * this as an opportunity to pop the last field from the current struct off + * of the field stack. + */ +func (p *TCompactProtocol) WriteStructEnd() TProtocolException { + p.lastFieldId = p.lastField.Pop() + return nil +} + +func (p *TCompactProtocol) WriteFieldBegin(name string, typeId TType, id int16) TProtocolException { + if typeId == BOOL { + // we want to possibly include the value, so we'll wait. + p.booleanField = NewTField(name, typeId, int(id)) + return nil + } + _, err := p.writeFieldBeginInternal(name, typeId, id, 0xFF) + return NewTProtocolExceptionFromOsError(err) +} + + +/** + * The workhorse of writeFieldBegin. It has the option of doing a + * 'type override' of the type header. This is used specifically in the + * boolean field case. + */ +func (p *TCompactProtocol) writeFieldBeginInternal(name string, typeId TType, id int16, typeOverride byte) (int, os.Error) { + // short lastField = lastField_.pop(); + + // if there's a type override, use that. + var typeToWrite byte + if typeOverride == 0xFF { + typeToWrite = byte(p.getCompactType(typeId)) + } else { + typeToWrite = typeOverride + } + // check if we can use delta encoding for the field id + fieldId := int(id) + written := 0 + if fieldId > p.lastFieldId && fieldId-p.lastFieldId <= 15 { + // write them together + written, err := p.writeByteDirect(byte((fieldId-p.lastFieldId)<<4) | typeToWrite) + if err != nil { + return written, err + } + } else { + // write them separate + n, err := p.writeByteDirect(typeToWrite) + if err != nil { + return n, err + } + err = p.WriteI16(id) + written = n + 2 + if err != nil { + return written, err + } + } + + p.lastFieldId = fieldId + // p.lastField.Push(field.id); + return written, nil +} + + +func (p *TCompactProtocol) WriteFieldEnd() TProtocolException { return nil } + +func (p *TCompactProtocol) WriteFieldStop() TProtocolException { + _, err := p.writeByteDirect(STOP) + return NewTProtocolExceptionFromOsError(err) +} + +func (p *TCompactProtocol) WriteMapBegin(keyType TType, valueType TType, size int) TProtocolException { + if size == 0 { + _, err := p.writeByteDirect(0) + return NewTProtocolExceptionFromOsError(err) + } + _, err := p.writeVarint32(int32(size)) + if err != nil { + return NewTProtocolExceptionFromOsError(err) + } + _, err = p.writeByteDirect(byte(p.getCompactType(keyType))<<4 | byte(p.getCompactType(valueType))) + return NewTProtocolExceptionFromOsError(err) +} + +func (p *TCompactProtocol) WriteMapEnd() TProtocolException { return nil } + +/** + * Write a list header. + */ +func (p *TCompactProtocol) WriteListBegin(elemType TType, size int) TProtocolException { + _, err := p.writeCollectionBegin(elemType, size) + return NewTProtocolExceptionFromOsError(err) +} + +func (p *TCompactProtocol) WriteListEnd() TProtocolException { return nil } + +/** + * Write a set header. + */ +func (p *TCompactProtocol) WriteSetBegin(elemType TType, size int) TProtocolException { + _, err := p.writeCollectionBegin(elemType, size) + return NewTProtocolExceptionFromOsError(err) +} + +func (p *TCompactProtocol) WriteSetEnd() TProtocolException { return nil } + +func (p *TCompactProtocol) WriteBool(value bool) TProtocolException { + v := byte(COMPACT_BOOLEAN_FALSE) + if value { + v = byte(COMPACT_BOOLEAN_TRUE) + } + if p.booleanField != nil { + // we haven't written the field header yet + _, err := p.writeFieldBeginInternal(p.booleanField.Name(), p.booleanField.TypeId(), int16(p.booleanField.Id()), v) + p.booleanField = nil + return NewTProtocolExceptionFromOsError(err) + } + // we're not part of a field, so just write the value. + _, err := p.writeByteDirect(v) + return NewTProtocolExceptionFromOsError(err) +} + +/** + * Write a byte. Nothing to see here! + */ +func (p *TCompactProtocol) WriteByte(value byte) TProtocolException { + _, err := p.writeByteDirect(value) + return NewTProtocolExceptionFromOsError(err) +} + +/** + * Write an I16 as a zigzag varint. + */ +func (p *TCompactProtocol) WriteI16(value int16) TProtocolException { + _, err := p.writeVarint32(p.int32ToZigzag(int32(value))) + return NewTProtocolExceptionFromOsError(err) +} + +/** + * Write an i32 as a zigzag varint. + */ +func (p *TCompactProtocol) WriteI32(value int32) TProtocolException { + _, err := p.writeVarint32(p.int32ToZigzag(value)) + return NewTProtocolExceptionFromOsError(err) +} + +/** + * Write an i64 as a zigzag varint. + */ +func (p *TCompactProtocol) WriteI64(value int64) TProtocolException { + _, err := p.writeVarint64(p.int64ToZigzag(value)) + return NewTProtocolExceptionFromOsError(err) +} + +/** + * Write a double to the wire as 8 bytes. + */ +func (p *TCompactProtocol) WriteDouble(value float64) TProtocolException { + buf := make([]byte, 8) + binary.LittleEndian.PutUint64(buf, math.Float64bits(value)) + _, err := p.trans.Write(buf) + return NewTProtocolExceptionFromOsError(err) +} + +/** + * Write a string to the wire with a varint size preceeding. + */ +func (p *TCompactProtocol) WriteString(value string) TProtocolException { + buf := make([]byte, len(value)) + strings.NewReader(value).Read(buf) + return p.WriteBinary(buf) +} + +/** + * Write a byte array, using a varint for the size. + */ +func (p *TCompactProtocol) WriteBinary(bin []byte) TProtocolException { + _, e := p.writeVarint32(int32(len(bin))) + if e != nil { + return NewTProtocolExceptionFromOsError(e) + } + if len(bin) > 0 { + _, e = p.trans.Write(bin) + return NewTProtocolExceptionFromOsError(e) + } + return nil +} + + +// +// Reading methods. +// + +/** + * Read a message header. + */ +func (p *TCompactProtocol) ReadMessageBegin() (name string, typeId TMessageType, seqId int32, err TProtocolException) { + protocolId, err := p.ReadByte() + if protocolId != COMPACT_PROTOCOL_ID { + s := fmt.Sprintf("Expected protocol id %02x but got %02x", COMPACT_PROTOCOL_ID, protocolId) + return "", typeId, seqId, NewTProtocolException(BAD_VERSION, s) + } + versionAndType, err := p.ReadByte() + version := versionAndType & COMPACT_VERSION_MASK + typeId = TMessageType((versionAndType >> COMPACT_TYPE_SHIFT_AMOUNT) & 0x03) + if err != nil { + return + } + if version != COMPACT_VERSION { + s := fmt.Sprintf("Expected version %02x but got %02x", COMPACT_VERSION, version) + err = NewTProtocolException(BAD_VERSION, s) + return + } + seqId, e := p.readVarint32() + if e != nil { + err = NewTProtocolExceptionFromOsError(e) + return + } + name, err = p.ReadString() + return +} + +func (p *TCompactProtocol) ReadMessageEnd() TProtocolException { return nil } + +/** + * Read a struct begin. There's nothing on the wire for this, but it is our + * opportunity to push a new struct begin marker onto the field stack. + */ +func (p *TCompactProtocol) ReadStructBegin() (name string, err TProtocolException) { + p.lastField.Push(p.lastFieldId) + p.lastFieldId = 0 + return +} + +/** + * Doesn't actually consume any wire data, just removes the last field for + * this struct from the field stack. + */ +func (p *TCompactProtocol) ReadStructEnd() TProtocolException { + // consume the last field we read off the wire. + p.lastFieldId = p.lastField.Pop() + return nil +} + +/** + * Read a field header off the wire. + */ +func (p *TCompactProtocol) ReadFieldBegin() (name string, typeId TType, id int16, err TProtocolException) { + t, err := p.ReadByte() + if err != nil { + return + } + + // if it's a stop, then we can return immediately, as the struct is over. + if (t & 0x0f) == STOP { + return _TSTOP.Name(), _TSTOP.TypeId(), int16(_TSTOP.Id()), nil + } + + // mask off the 4 MSB of the type header. it could contain a field id delta. + modifier := int16((t & 0xf0) >> 4) + if modifier == 0 { + // not a delta. look ahead for the zigzag varint field id. + id, err = p.ReadI16() + if err != nil { + return + } + } else { + // has a delta. add the delta to the last read field id. + id = int16(p.lastFieldId) + modifier + } + typeId, e := p.getTType(TCompactType(t & 0x0f)) + if e != nil { + err = NewTProtocolExceptionFromOsError(e) + return + } + + // if this happens to be a boolean field, the value is encoded in the type + if p.isBoolType(t) { + // save the boolean value in a special instance variable. + p.boolValue = (byte(t)&0x0f == COMPACT_BOOLEAN_TRUE) + p.boolValueIsNotNull = true + } + + // push the new field onto the field stack so we can keep the deltas going. + p.lastFieldId = int(id) + return +} + +func (p *TCompactProtocol) ReadFieldEnd() TProtocolException { return nil } + +/** + * Read a map header off the wire. If the size is zero, skip reading the key + * and value type. This means that 0-length maps will yield TMaps without the + * "correct" types. + */ +func (p *TCompactProtocol) ReadMapBegin() (keyType TType, valueType TType, size int, err TProtocolException) { + size32, e := p.readVarint32() + size = int(size32) + if e != nil { + err = NewTProtocolExceptionFromOsError(e) + return + } + keyAndValueType := byte(STOP) + if size != 0 { + keyAndValueType, err = p.ReadByte() + if err != nil { + return + } + } + keyType, _ = p.getTType(TCompactType(keyAndValueType >> 4)) + valueType, _ = p.getTType(TCompactType(keyAndValueType & 0xf)) + return +} + +func (p *TCompactProtocol) ReadMapEnd() TProtocolException { return nil } + +/** + * Read a list header off the wire. If the list size is 0-14, the size will + * be packed into the element type header. If it's a longer list, the 4 MSB + * of the element type header will be 0xF, and a varint will follow with the + * true size. + */ +func (p *TCompactProtocol) ReadListBegin() (elemType TType, size int, err TProtocolException) { + size_and_type, err := p.ReadByte() + if err != nil { + return + } + size = int((size_and_type >> 4) & 0x0f) + if size == 15 { + size2, e := p.readVarint32() + if e != nil { + err = NewTProtocolExceptionFromOsError(e) + return + } + size = int(size2) + } + elemType, e := p.getTType(TCompactType(size_and_type)) + if e != nil { + err = NewTProtocolExceptionFromOsError(e) + return + } + return +} + +func (p *TCompactProtocol) ReadListEnd() TProtocolException { return nil } + +/** + * Read a set header off the wire. If the set size is 0-14, the size will + * be packed into the element type header. If it's a longer set, the 4 MSB + * of the element type header will be 0xF, and a varint will follow with the + * true size. + */ +func (p *TCompactProtocol) ReadSetBegin() (elemType TType, size int, err TProtocolException) { + return p.ReadListBegin() +} + +func (p *TCompactProtocol) ReadSetEnd() TProtocolException { return nil } + +/** + * Read a boolean off the wire. If this is a boolean field, the value should + * already have been read during readFieldBegin, so we'll just consume the + * pre-stored value. Otherwise, read a byte. + */ +func (p *TCompactProtocol) ReadBool() (value bool, err TProtocolException) { + if p.boolValueIsNotNull { + p.boolValueIsNotNull = false + return p.boolValue, nil + } + v, err := p.ReadByte() + return v == COMPACT_BOOLEAN_TRUE, err +} + +/** + * Read a single byte off the wire. Nothing interesting here. + */ +func (p *TCompactProtocol) ReadByte() (value byte, err TProtocolException) { + buf := []byte{0} + _, e := p.trans.ReadAll(buf) + if e != nil { + return 0, NewTProtocolExceptionFromOsError(e) + } + return buf[0], nil +} + +/** + * Read an i16 from the wire as a zigzag varint. + */ +func (p *TCompactProtocol) ReadI16() (value int16, err TProtocolException) { + v, err := p.ReadI32() + return int16(v), err +} + +/** + * Read an i32 from the wire as a zigzag varint. + */ +func (p *TCompactProtocol) ReadI32() (value int32, err TProtocolException) { + v, e := p.readVarint32() + if e != nil { + return 0, NewTProtocolExceptionFromOsError(e) + } + value = p.zigzagToInt32(v) + return value, nil +} + +/** + * Read an i64 from the wire as a zigzag varint. + */ +func (p *TCompactProtocol) ReadI64() (value int64, err TProtocolException) { + v, e := p.readVarint64() + if e != nil { + return 0, NewTProtocolExceptionFromOsError(e) + } + value = p.zigzagToInt64(v) + return value, nil +} + +/** + * No magic here - just read a double off the wire. + */ +func (p *TCompactProtocol) ReadDouble() (value float64, err TProtocolException) { + longBits := make([]byte, 8) + _, e := p.trans.ReadAll(longBits) + if e != nil { + return 0.0, NewTProtocolExceptionFromOsError(e) + } + return math.Float64frombits(p.bytesToUint64(longBits)), nil +} + +/** + * Reads a []byte (via readBinary), and then UTF-8 decodes it. + */ +func (p *TCompactProtocol) ReadString() (value string, err TProtocolException) { + v, e := p.ReadBinary() + return string(v), NewTProtocolExceptionFromOsError(e) +} + +/** + * Read a []byte from the wire. + */ +func (p *TCompactProtocol) ReadBinary() (value []byte, err TProtocolException) { + length, e := p.readVarint32() + if e != nil { + return []byte{}, NewTProtocolExceptionFromOsError(e) + } + if length == 0 { + return []byte{}, nil + } + + buf := make([]byte, length) + p.trans.ReadAll(buf) + return buf, nil +} + +func (p *TCompactProtocol) Flush() (err TProtocolException) { + return NewTProtocolExceptionFromOsError(p.trans.Flush()) +} + +func (p *TCompactProtocol) Skip(fieldType TType) (err TProtocolException) { + return SkipDefaultDepth(p, fieldType) +} + +func (p *TCompactProtocol) Transport() TTransport { + return p.trans +} + +// +// Internal writing methods +// + +/** + * Abstract method for writing the start of lists and sets. List and sets on + * the wire differ only by the type indicator. + */ +func (p *TCompactProtocol) writeCollectionBegin(elemType TType, size int) (int, os.Error) { + if size <= 14 { + return p.writeByteDirect(byte(int32(size<<4) | int32(p.getCompactType(elemType)))) + } + n, err := p.writeByteDirect(0xf0 | byte(p.getCompactType(elemType))) + if err != nil { + return n, err + } + m, err := p.writeVarint32(int32(size)) + return n + m, err +} + +/** + * Write an i32 as a varint. Results in 1-5 bytes on the wire. + * TODO(pomack): make a permanent buffer like writeVarint64? + */ +func (p *TCompactProtocol) writeVarint32(n int32) (int, os.Error) { + i32buf := make([]byte, 5) + idx := 0 + for { + if (n & ^0x7F) == 0 { + i32buf[idx] = byte(n) + idx++ + // p.writeByteDirect(byte(n)); + break + // return; + } else { + i32buf[idx] = byte((n & 0x7F) | 0x80) + idx++ + // p.writeByteDirect(byte(((n & 0x7F) | 0x80))); + u := uint32(n) + n = int32(u >> 7) + } + } + return p.trans.Write(i32buf[0:idx]) +} + +/** + * Write an i64 as a varint. Results in 1-10 bytes on the wire. + */ +func (p *TCompactProtocol) writeVarint64(n int64) (int, os.Error) { + varint64out := make([]byte, 10) + idx := 0 + for { + if (n & ^0x7F) == 0 { + varint64out[idx] = byte(n) + idx++ + break + } else { + varint64out[idx] = byte((n & 0x7F) | 0x80) + idx++ + u := uint64(n) + n = int64(u >> 7) + } + } + return p.trans.Write(varint64out[0:idx]) +} + +/** + * Convert l into a zigzag long. This allows negative numbers to be + * represented compactly as a varint. + */ +func (p *TCompactProtocol) int64ToZigzag(l int64) int64 { + return (l << 1) ^ (l >> 63) +} + +/** + * Convert l into a zigzag long. This allows negative numbers to be + * represented compactly as a varint. + */ +func (p *TCompactProtocol) int32ToZigzag(n int32) int32 { + return (n << 1) ^ (n >> 31) +} + +func (p *TCompactProtocol) fixedUint64ToBytes(n uint64, buf []byte) { + binary.LittleEndian.PutUint64(buf, n) +} + +func (p *TCompactProtocol) fixedInt64ToBytes(n int64, buf []byte) { + binary.LittleEndian.PutUint64(buf, uint64(n)) +} + +/** + * Writes a byte without any possiblity of all that field header nonsense. + * Used internally by other writing methods that know they need to write a byte. + */ +func (p *TCompactProtocol) writeByteDirect(b byte) (int, os.Error) { + return p.trans.Write([]byte{b}) +} + +/** + * Writes a byte without any possiblity of all that field header nonsense. + */ +func (p *TCompactProtocol) writeIntAsByteDirect(n int) (int, os.Error) { + return p.writeByteDirect(byte(n)) +} + + +// +// Internal reading methods +// + +/** + * Read an i32 from the wire as a varint. The MSB of each byte is set + * if there is another byte to follow. This can read up to 5 bytes. + */ +func (p *TCompactProtocol) readVarint32() (int32, os.Error) { + // if the wire contains the right stuff, this will just truncate the i64 we + // read and get us the right sign. + v, err := p.readVarint64() + return int32(v), err +} + + +/** + * Read an i64 from the wire as a proper varint. The MSB of each byte is set + * if there is another byte to follow. This can read up to 10 bytes. + */ +func (p *TCompactProtocol) readVarint64() (int64, os.Error) { + shift := uint(0) + result := int64(0) + for { + b, err := p.ReadByte() + if err != nil { + return 0, err + } + result |= int64(b&0x7f) << shift + if (b & 0x80) != 0x80 { + break + } + shift += 7 + } + return result, nil +} + + +// +// encoding helpers +// + +/** + * Convert from zigzag int to int. + */ +func (p *TCompactProtocol) zigzagToInt32(n int32) int32 { + u := uint32(n) + return int32(u>>1) ^ -(n & 1) +} + +/** + * Convert from zigzag long to long. + */ +func (p *TCompactProtocol) zigzagToInt64(n int64) int64 { + u := uint64(n) + return int64(u>>1) ^ -(n & 1) +} + +/** + * Note that it's important that the mask bytes are long literals, + * otherwise they'll default to ints, and when you shift an int left 56 bits, + * you just get a messed up int. + */ +func (p *TCompactProtocol) bytesToInt64(b []byte) int64 { + return int64(binary.LittleEndian.Uint64(b)) +} + +/** + * Note that it's important that the mask bytes are long literals, + * otherwise they'll default to ints, and when you shift an int left 56 bits, + * you just get a messed up int. + */ +func (p *TCompactProtocol) bytesToUint64(b []byte) uint64 { + return binary.LittleEndian.Uint64(b) +} + +// +// type testing and converting +// + +func (p *TCompactProtocol) isBoolType(b byte) bool { + return (b&0x0f) == COMPACT_BOOLEAN_TRUE || (b&0x0f) == COMPACT_BOOLEAN_FALSE +} + +/** + * Given a TCompactType constant, convert it to its corresponding + * TType value. + */ +func (p *TCompactProtocol) getTType(t TCompactType) (TType, os.Error) { + switch byte(t) & 0x0f { + case STOP: + return STOP, nil + case COMPACT_BOOLEAN_FALSE: + case COMPACT_BOOLEAN_TRUE: + return BOOL, nil + case COMPACT_BYTE: + return BYTE, nil + case COMPACT_I16: + return I16, nil + case COMPACT_I32: + return I32, nil + case COMPACT_I64: + return I64, nil + case COMPACT_DOUBLE: + return DOUBLE, nil + case COMPACT_BINARY: + return STRING, nil + case COMPACT_LIST: + return LIST, nil + case COMPACT_SET: + return SET, nil + case COMPACT_MAP: + return MAP, nil + case COMPACT_STRUCT: + return STRUCT, nil + } + return STOP, NewTException("don't know what type: " + string(t&0x0f)) +} + +/** + * Given a TType value, find the appropriate TCompactProtocol.Types constant. + */ +func (p *TCompactProtocol) getCompactType(t TType) TCompactType { + return _TTypeToCompactType[int(t)] +} diff --git a/lib/go/thrift/tcompact_protocol_test.go b/lib/go/thrift/tcompact_protocol_test.go new file mode 100644 index 00000000..da0e39cc --- /dev/null +++ b/lib/go/thrift/tcompact_protocol_test.go @@ -0,0 +1,56 @@ +/* + * 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. + */ + +package thrift_test + +import ( + . "thrift" + "testing" + //"bytes"; +) + +func TestReadWriteCompactProtocol(t *testing.T) { + ReadWriteProtocolTest(t, NewTCompactProtocolFactory()) + /* + transports := []TTransport{ + NewTMemoryBuffer(), + NewTIOStreamTransportRW(bytes.NewBuffer(make([]byte, 0, 16384))), + NewTFramedTransport(NewTMemoryBuffer()), + } + for _, trans := range transports { + p := NewTCompactProtocol(trans); + ReadWriteBool(t, p, trans); + p = NewTCompactProtocol(trans); + ReadWriteByte(t, p, trans); + p = NewTCompactProtocol(trans); + ReadWriteI16(t, p, trans); + p = NewTCompactProtocol(trans); + ReadWriteI32(t, p, trans); + p = NewTCompactProtocol(trans); + ReadWriteI64(t, p, trans); + p = NewTCompactProtocol(trans); + ReadWriteDouble(t, p, trans); + p = NewTCompactProtocol(trans); + ReadWriteString(t, p, trans); + p = NewTCompactProtocol(trans); + ReadWriteBinary(t, p, trans); + trans.Close(); + } + */ +} diff --git a/lib/go/thrift/tcompare.go b/lib/go/thrift/tcompare.go new file mode 100644 index 00000000..01fef447 --- /dev/null +++ b/lib/go/thrift/tcompare.go @@ -0,0 +1,127 @@ +/* + * 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. + */ + + +package thrift + +func CompareInt(i, j int) int { + if i > j { + return 1 + } + if i < j { + return -1 + } + return 0 +} + +func CompareInt16(i, j int16) int { + if i > j { + return 1 + } + if i < j { + return -1 + } + return 0 +} + +func CompareInt32(i, j int32) int { + if i > j { + return 1 + } + if i < j { + return -1 + } + return 0 +} + +func CompareInt64(i, j int32) int { + if i > j { + return 1 + } + if i < j { + return -1 + } + return 0 +} + +func CompareStringArray(i, j []string) int { + if cmp := CompareInt(len(i), len(j)); cmp != 0 { + return cmp + } + size := len(i) + for k := 0; k < size; k++ { + if cmp := CompareString(i[k], j[k]); cmp != 0 { + return cmp + } + } + return 0 +} + +func CompareString(i, j string) int { + if i > j { + return 1 + } + if i < j { + return -1 + } + return 0 +} + +func CompareFloat(i, j float32) int { + if i > j { + return 1 + } + if i < j { + return -1 + } + return 0 +} + +func CompareDouble(i, j float64) int { + if i > j { + return 1 + } + if i < j { + return -1 + } + return 0 +} + +func CompareByte(i, j byte) int { + if i > j { + return 1 + } + if i < j { + return -1 + } + return 0 +} + +func CompareBool(i, j bool) int { + if i { + if j { + return 0 + } + return 1 + } + if j { + return -1 + } + return 0 +} diff --git a/lib/go/thrift/tcontainer.go b/lib/go/thrift/tcontainer.go new file mode 100644 index 00000000..9ea3cba0 --- /dev/null +++ b/lib/go/thrift/tcontainer.go @@ -0,0 +1,28 @@ +/* + * 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. + */ + + +package thrift + +type TContainer interface { + Len() int + Contains(data interface{}) bool + Equals(other interface{}) bool + CompareTo(other interface{}) (int, bool) +} diff --git a/lib/go/thrift/texception.go b/lib/go/thrift/texception.go new file mode 100644 index 00000000..be6cbd5c --- /dev/null +++ b/lib/go/thrift/texception.go @@ -0,0 +1,56 @@ +/* + * 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. + */ + +package thrift + +import ( + "os" +) + +/** + * Generic exception class for Thrift. + * + */ + +type TException interface { + String() string +} + +type tException struct { + message string +} + +func (p *tException) String() string { + return p.message +} + +func NewTException(m string) TException { + return &tException{message: m} +} + +func NewTExceptionFromOsError(e os.Error) TException { + if e == nil { + return nil + } + t, ok := e.(TException) + if ok { + return t + } + return NewTException(e.String()) +} diff --git a/lib/go/thrift/texception_test.go b/lib/go/thrift/texception_test.go new file mode 100644 index 00000000..50b3cddb --- /dev/null +++ b/lib/go/thrift/texception_test.go @@ -0,0 +1,38 @@ +/* + * 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. + */ + +package thrift_test + +import ( + . "thrift" + "os" + "testing" +) + + +func TestTException(t *testing.T) { + exc := NewTException("") + if exc.String() != "" { + t.Fatalf("Expected empty string for exception but found '%s'", exc.String()) + } + exc = NewTExceptionFromOsError(os.EOF) + if exc.String() != os.EOF.String() { + t.Fatalf("Expected '%s', but found '%s'", os.EOF.String(), exc.String()) + } +} diff --git a/lib/go/thrift/tfield.go b/lib/go/thrift/tfield.go new file mode 100644 index 00000000..31d9b891 --- /dev/null +++ b/lib/go/thrift/tfield.go @@ -0,0 +1,281 @@ +/* + * 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. + */ + +package thrift + +import ( + "sort" +) + +/** + * Helper class that encapsulates field metadata. + * + */ +type TField interface { + Name() string + TypeId() TType + Id() int + String() string + CompareTo(other interface{}) (int, bool) + Equals(other interface{}) bool +} + +type tField struct { + name string + typeId TType + id int +} + +func NewTFieldDefault() TField { + return ANONYMOUS_FIELD +} + +func NewTField(n string, t TType, i int) TField { + return &tField{name: n, typeId: t, id: i} +} + +func (p *tField) Name() string { + if p == nil { + return "" + } + return p.name +} + +func (p *tField) TypeId() TType { + if p == nil { + return TType(VOID) + } + return p.typeId +} + +func (p *tField) Id() int { + if p == nil { + return -1 + } + return p.id +} + +func (p *tField) String() string { + if p == nil { + return "" + } + return "" +} + +func (p *tField) CompareTo(other interface{}) (int, bool) { + if other == nil { + return 1, true + } + if data, ok := other.(TField); ok { + if p.Id() != data.Id() { + return CompareInt(p.Id(), data.Id()), true + } + if p.TypeId() != data.TypeId() { + return CompareByte(byte(p.TypeId()), byte(data.TypeId())), true + } + return CompareString(p.Name(), data.Name()), true + } + return 0, false +} + +func (p *tField) Equals(other interface{}) bool { + if p == nil { + return other == nil + } + if other == nil { + return false + } + if data, ok := other.(TField); ok { + return p.TypeId() == data.TypeId() && p.Id() == data.Id() + } + return false +} + +var ANONYMOUS_FIELD TField + +type tFieldArray []TField + +func (p tFieldArray) Len() int { + return len(p) +} + +func (p tFieldArray) Less(i, j int) bool { + return p[i].Id() < p[j].Id() +} + +func (p tFieldArray) Swap(i, j int) { + p[i], p[j] = p[j], p[i] +} + +type TFieldContainer interface { + TContainer + FieldNameFromFieldId(id int) string + FieldIdFromFieldName(name string) int + FieldFromFieldId(id int) TField + FieldFromFieldName(name string) TField + At(i int) TField + Iter() <-chan TField +} + +type tFieldContainer struct { + fields []TField + nameToFieldMap map[string]TField + idToFieldMap map[int]TField +} + +func NewTFieldContainer(fields []TField) TFieldContainer { + sortedFields := make([]TField, len(fields)) + nameToFieldMap := make(map[string]TField) + idToFieldMap := make(map[int]TField) + for i, field := range fields { + sortedFields[i] = field + idToFieldMap[field.Id()] = field + if field.Name() != "" { + nameToFieldMap[field.Name()] = field + } + } + sort.Sort(tFieldArray(sortedFields)) + return &tFieldContainer{ + fields: fields, + nameToFieldMap: nameToFieldMap, + idToFieldMap: idToFieldMap, + } +} + +func (p *tFieldContainer) FieldNameFromFieldId(id int) string { + if field, ok := p.idToFieldMap[id]; ok { + return field.Name() + } + return "" +} + +func (p *tFieldContainer) FieldIdFromFieldName(name string) int { + if field, ok := p.nameToFieldMap[name]; ok { + return field.Id() + } + return -1 +} + +func (p *tFieldContainer) FieldFromFieldId(id int) TField { + if field, ok := p.idToFieldMap[id]; ok { + return field + } + return ANONYMOUS_FIELD +} + +func (p *tFieldContainer) FieldFromFieldName(name string) TField { + if field, ok := p.nameToFieldMap[name]; ok { + return field + } + return ANONYMOUS_FIELD +} + +func (p *tFieldContainer) Len() int { + return len(p.fields) +} + +func (p *tFieldContainer) At(i int) TField { + return p.FieldFromFieldId(i) +} + +func (p *tFieldContainer) Contains(data interface{}) bool { + if i, ok := data.(int); ok { + for _, field := range p.fields { + if field.Id() == i { + return true + } + } + } else if i, ok := data.(int16); ok { + for _, field := range p.fields { + if field.Id() == int(i) { + return true + } + } + } else if s, ok := data.(string); ok { + for _, field := range p.fields { + if field.Name() == s { + return true + } + } + } else if f, ok := data.(TField); ok { + for _, field := range p.fields { + if field.Equals(f) { + return true + } + } + } + return false +} + +func (p *tFieldContainer) Equals(other interface{}) bool { + if other == nil { + return false + } + if data, ok := other.(TFieldContainer); ok { + if p.Len() != data.Len() { + return false + } + for _, field := range p.fields { + if !data.Contains(field) { + return false + } + } + return true + } + return false +} + +func (p *tFieldContainer) CompareTo(other interface{}) (int, bool) { + if other == nil { + return 1, true + } + if data, ok := other.(TFieldContainer); ok { + cont, ok2 := data.(*tFieldContainer) + if ok2 && p == cont { + return 0, true + } + if cmp := CompareInt(p.Len(), data.Len()); cmp != 0 { + return cmp, true + } + for _, field := range p.fields { + if cmp, ok3 := field.CompareTo(data.At(field.Id())); !ok3 || cmp != 0 { + return cmp, ok3 + } + } + return 0, true + } + return 0, false +} + +func (p *tFieldContainer) Iter() <-chan TField { + c := make(chan TField) + go p.iterate(c) + return c +} + +func (p *tFieldContainer) iterate(c chan<- TField) { + for _, v := range p.fields { + c <- v + } + close(c) +} + +func init() { + ANONYMOUS_FIELD = NewTField("", STOP, 0) +} diff --git a/lib/go/thrift/tframed_transport.go b/lib/go/thrift/tframed_transport.go new file mode 100644 index 00000000..52049cbc --- /dev/null +++ b/lib/go/thrift/tframed_transport.go @@ -0,0 +1,133 @@ +/* + * 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. + */ + +package thrift + +import ( + "encoding/binary" + "bytes" + "os" +) + + +type TFramedTransport struct { + transport TTransport + writeBuffer *bytes.Buffer + readBuffer *bytes.Buffer +} + +type tFramedTransportFactory struct { + factory TTransportFactory +} + +func NewTFramedTransportFactory(factory TTransportFactory) TTransportFactory { + return &tFramedTransportFactory{factory: factory} +} + +func (p *tFramedTransportFactory) GetTransport(base TTransport) TTransport { + return NewTFramedTransport(p.factory.GetTransport(base)) +} + +func NewTFramedTransport(transport TTransport) *TFramedTransport { + writeBuf := make([]byte, 0, 1024) + readBuf := make([]byte, 0, 1024) + return &TFramedTransport{transport: transport, writeBuffer: bytes.NewBuffer(writeBuf), readBuffer: bytes.NewBuffer(readBuf)} +} + +func (p *TFramedTransport) Open() os.Error { + return p.transport.Open() +} + +func (p *TFramedTransport) IsOpen() bool { + return p.transport.IsOpen() +} + +func (p *TFramedTransport) Peek() bool { + return p.transport.Peek() +} + +func (p *TFramedTransport) Close() os.Error { + return p.transport.Close() +} + +func (p *TFramedTransport) Read(buf []byte) (int, os.Error) { + if p.readBuffer.Len() > 0 { + got, err := p.readBuffer.Read(buf) + if got > 0 { + return got, NewTTransportExceptionFromOsError(err) + } + } + + // Read another frame of data + p.readFrame() + + got, err := p.readBuffer.Read(buf) + return got, NewTTransportExceptionFromOsError(err) +} + +func (p *TFramedTransport) ReadAll(buf []byte) (int, os.Error) { + return ReadAllTransport(p, buf) +} + +func (p *TFramedTransport) Write(buf []byte) (int, os.Error) { + n, err := p.writeBuffer.Write(buf) + return n, NewTTransportExceptionFromOsError(err) +} + +func (p *TFramedTransport) Flush() os.Error { + size := p.writeBuffer.Len() + buf := []byte{0, 0, 0, 0} + binary.BigEndian.PutUint32(buf, uint32(size)) + _, err := p.transport.Write(buf) + if err != nil { + return NewTTransportExceptionFromOsError(err) + } + if size > 0 { + n, err := p.writeBuffer.WriteTo(p.transport) + if err != nil { + print("Error while flushing write buffer of size ", size, " to transport, only wrote ", n, " bytes: ", err.String(), "\n") + return NewTTransportExceptionFromOsError(err) + } + } + err = p.transport.Flush() + return NewTTransportExceptionFromOsError(err) +} + +func (p *TFramedTransport) readFrame() (int, os.Error) { + buf := []byte{0, 0, 0, 0} + _, err := p.transport.ReadAll(buf) + if err != nil { + return 0, err + } + size := int(binary.BigEndian.Uint32(buf)) + if size < 0 { + // TODO(pomack) log error + return 0, NewTTransportException(UNKNOWN_TRANSPORT_EXCEPTION, "Read a negative frame size ("+string(size)+")") + } + if size == 0 { + return 0, nil + } + buf2 := make([]byte, size) + n, err := p.transport.ReadAll(buf2) + if err != nil { + return n, err + } + p.readBuffer = bytes.NewBuffer(buf2) + return size, nil +} diff --git a/lib/go/thrift/tframed_transport_test.go b/lib/go/thrift/tframed_transport_test.go new file mode 100644 index 00000000..566318cc --- /dev/null +++ b/lib/go/thrift/tframed_transport_test.go @@ -0,0 +1,30 @@ +/* + * 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. + */ + +package thrift_test + +import ( + . "thrift" + "testing" +) + +func TestFramedTransport(t *testing.T) { + trans := NewTFramedTransport(NewTMemoryBuffer()) + TransportTest(t, trans, trans) +} diff --git a/lib/go/thrift/thttp_client.go b/lib/go/thrift/thttp_client.go new file mode 100644 index 00000000..e09ecdfe --- /dev/null +++ b/lib/go/thrift/thttp_client.go @@ -0,0 +1,146 @@ +/* + * 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. + */ + +package thrift + +import ( + "bytes" + "http" + "os" +) + + +type THttpClient struct { + response *http.Response + url *http.URL + requestBuffer *bytes.Buffer + nsecConnectTimeout int64 + nsecReadTimeout int64 +} + +type THttpClientTransportFactory struct { + url string + isPost bool +} + +func (p *THttpClientTransportFactory) GetTransport(trans TTransport) TTransport { + if trans != nil { + t, ok := trans.(*THttpClient) + if ok && t.url != nil { + if t.requestBuffer != nil { + t2, _ := NewTHttpPostClient(t.url.String()) + return t2 + } + t2, _ := NewTHttpClient(t.url.String()) + return t2 + } + } + if p.isPost { + s, _ := NewTHttpPostClient(p.url) + return s + } + s, _ := NewTHttpClient(p.url) + return s +} + +func NewTHttpClientTransportFactory(url string) *THttpClientTransportFactory { + return &THttpClientTransportFactory{url: url, isPost: false} +} + +func NewTHttpPostClientTransportFactory(url string) *THttpClientTransportFactory { + return &THttpClientTransportFactory{url: url, isPost: true} +} + + +func NewTHttpClient(url string) (TTransport, os.Error) { + response, finalUrl, err := http.Get(url) + if err != nil { + return nil, err + } + parsedURL, err := http.ParseURL(finalUrl) + if err != nil { + return nil, err + } + return &THttpClient{response: response, url: parsedURL}, nil +} + +func NewTHttpPostClient(url string) (TTransport, os.Error) { + parsedURL, err := http.ParseURL(url) + if err != nil { + return nil, err + } + buf := make([]byte, 0, 1024) + return &THttpClient{url: parsedURL, requestBuffer: bytes.NewBuffer(buf)}, nil +} + +func (p *THttpClient) Open() os.Error { + // do nothing + return nil +} + +func (p *THttpClient) IsOpen() bool { + return p.response != nil || p.requestBuffer != nil +} + +func (p *THttpClient) Peek() bool { + return p.IsOpen() +} + +func (p *THttpClient) Close() os.Error { + if p.response != nil && p.response.Body != nil { + err := p.response.Body.Close() + p.response = nil + return err + } + if p.requestBuffer != nil { + p.requestBuffer.Reset() + p.requestBuffer = nil + } + return nil +} + +func (p *THttpClient) Read(buf []byte) (int, os.Error) { + if p.response == nil { + return 0, NewTTransportException(NOT_OPEN, "Response buffer is empty, no request.") + } + n, err := p.response.Body.Read(buf) + return n, NewTTransportExceptionFromOsError(err) +} + +func (p *THttpClient) ReadAll(buf []byte) (int, os.Error) { + return ReadAllTransport(p, buf) +} + +func (p *THttpClient) Write(buf []byte) (int, os.Error) { + n, err := p.requestBuffer.Write(buf) + return n, err +} + +func (p *THttpClient) Flush() os.Error { + response, err := http.Post(p.url.String(), "application/x-thrift", p.requestBuffer) + if err != nil { + return NewTTransportExceptionFromOsError(err) + } + if response.StatusCode != http.StatusOK { + // TODO(pomack) log bad response + return NewTTransportException(UNKNOWN_TRANSPORT_EXCEPTION, "HTTP Response code: "+string(response.StatusCode)) + } + p.response = response + return nil +} diff --git a/lib/go/thrift/thttp_client_test.go b/lib/go/thrift/thttp_client_test.go new file mode 100644 index 00000000..1af8cd31 --- /dev/null +++ b/lib/go/thrift/thttp_client_test.go @@ -0,0 +1,45 @@ +/* + * 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. + */ + +package thrift_test + +import ( + . "thrift" + "testing" + "http" + "net" +) + +func TestHttpClient(t *testing.T) { + addr, err := FindAvailableTCPServerPort(40000) + if err != nil { + t.Fatalf("Unable to find available tcp port addr: %s", err) + } + l, err := net.Listen(addr.Network(), addr.String()) + if err != nil { + t.Fatalf("Unable to setup tcp listener on %s: %s", addr.String(), err) + } + go http.Serve(l, &HTTPEchoServer{}) + trans, err := NewTHttpPostClient("http://" + addr.String()) + if err != nil { + l.Close() + t.Fatalf("Unable to connect to %s: %s", addr.String(), err) + } + TransportTest(t, trans, trans) +} diff --git a/lib/go/thrift/tiostream_transport.go b/lib/go/thrift/tiostream_transport.go new file mode 100644 index 00000000..5c4beeac --- /dev/null +++ b/lib/go/thrift/tiostream_transport.go @@ -0,0 +1,231 @@ +/* + * 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. + */ + +package thrift + +import ( + "bufio" + "io" + "os" +) + +/** + * This is the most commonly used base transport. It takes an InputStream + * and an OutputStream and uses those to perform all transport operations. + * This allows for compatibility with all the nice constructs Java already + * has to provide a variety of types of streams. + * + */ +type TIOStreamTransport struct { + Reader io.Reader + Writer io.Writer + IsReadWriter bool +} + +type TIOStreamTransportFactory struct { + Reader io.Reader + Writer io.Writer + IsReadWriter bool +} + +func (p *TIOStreamTransportFactory) GetTransport(trans TTransport) TTransport { + if trans != nil { + t, ok := trans.(*TIOStreamTransport) + if ok { + if t.IsReadWriter { + return NewTIOStreamTransportRW(t.Reader.(io.ReadWriter)) + } + if t.Reader != nil && t.Writer != nil { + return NewTIOStreamTransportRAndW(t.Reader, t.Writer) + } + if t.Reader != nil && t.Writer == nil { + return NewTIOStreamTransportR(t.Reader) + } + if t.Reader == nil && t.Writer != nil { + return NewTIOStreamTransportW(t.Writer) + } + return NewTIOStreamTransportDefault() + } + } + if p.IsReadWriter { + return NewTIOStreamTransportRW(p.Reader.(io.ReadWriter)) + } + if p.Reader != nil && p.Writer != nil { + return NewTIOStreamTransportRAndW(p.Reader, p.Writer) + } + if p.Reader != nil && p.Writer == nil { + return NewTIOStreamTransportR(p.Reader) + } + if p.Reader == nil && p.Writer != nil { + return NewTIOStreamTransportW(p.Writer) + } + return NewTIOStreamTransportDefault() +} + +func NewTIOStreamTransportFactory(reader io.Reader, writer io.Writer, isReadWriter bool) *TIOStreamTransportFactory { + return &TIOStreamTransportFactory{Reader: reader, Writer: writer, IsReadWriter: isReadWriter} +} + + +/** + * Subclasses can invoke the default constructor and then assign the input + * streams in the open method. + */ +func NewTIOStreamTransportDefault() *TIOStreamTransport { + return &TIOStreamTransport{} +} + +/** + * Input stream constructor. + * + * @param is Input stream to read from + */ +func NewTIOStreamTransportR(r io.Reader) *TIOStreamTransport { + return &TIOStreamTransport{Reader: bufio.NewReader(r)} +} + +/** + * Output stream constructor. + * + * @param os Output stream to read from + */ +func NewTIOStreamTransportW(w io.Writer) *TIOStreamTransport { + return &TIOStreamTransport{Writer: bufio.NewWriter(w)} +} + +/** + * Two-way stream constructor. + * + * @param is Input stream to read from + * @param os Output stream to read from + */ +func NewTIOStreamTransportRAndW(r io.Reader, w io.Writer) *TIOStreamTransport { + return &TIOStreamTransport{Reader: bufio.NewReader(r), Writer: bufio.NewWriter(w)} +} + +/** + * Two-way stream constructor. + * + * @param is Input stream to read from + * @param os Output stream to read from + */ +func NewTIOStreamTransportRW(rw io.ReadWriter) *TIOStreamTransport { + // bufio has a bug where once a Reader hits EOF, a new Write never brings the reader out of EOF + // even if reader and writer use the same underlier + //bufrw := bufio.NewReadWriter(bufio.NewReader(rw), bufio.NewWriter(rw)); + return &TIOStreamTransport{Reader: rw, Writer: rw, IsReadWriter: true} +} + +/** + * The streams must already be open at construction time, so this should + * always return true. + * + * @return true + */ +func (p *TIOStreamTransport) IsOpen() bool { + return true +} + +/** + * The streams must already be open. This method does nothing. + */ +func (p *TIOStreamTransport) Open() os.Error { + return nil +} + +func (p *TIOStreamTransport) Peek() bool { + return p.IsOpen() +} + +/** + * Closes both the input and output streams. + */ +func (p *TIOStreamTransport) Close() os.Error { + closedReader := false + if p.Reader != nil { + c, ok := p.Reader.(io.Closer) + if ok { + e := c.Close() + closedReader = true + if e != nil { + LOGGER.Print("Error closing input stream.", e) + } + } + p.Reader = nil + } + if p.Writer != nil && (!closedReader || !p.IsReadWriter) { + c, ok := p.Writer.(io.Closer) + if ok { + e := c.Close() + if e != nil { + LOGGER.Print("Error closing output stream.", e) + } + } + p.Writer = nil + } + return nil +} + +/** + * Reads from the underlying input stream if not null. + */ +func (p *TIOStreamTransport) Read(buf []byte) (int, os.Error) { + if p.Reader == nil { + return 0, NewTTransportException(NOT_OPEN, "Cannot read from null inputStream") + } + n, err := p.Reader.Read(buf) + return n, NewTTransportExceptionFromOsError(err) +} + +func (p *TIOStreamTransport) ReadAll(buf []byte) (int, os.Error) { + return ReadAllTransport(p, buf) +} + + +/** + * Writes to the underlying output stream if not null. + */ +func (p *TIOStreamTransport) Write(buf []byte) (int, os.Error) { + if p.Writer == nil { + LOGGER.Print("Could not write to iostream as Writer is null\n") + return 0, NewTTransportException(NOT_OPEN, "Cannot write to null outputStream") + } + n, err := p.Writer.Write(buf) + if n == 0 || err != nil { + LOGGER.Print("Error writing to iostream, only wrote ", n, " bytes: ", err.String(), "\n") + } + return n, NewTTransportExceptionFromOsError(err) +} + +/** + * Flushes the underlying output stream if not null. + */ +func (p *TIOStreamTransport) Flush() os.Error { + if p.Writer == nil { + return NewTTransportException(NOT_OPEN, "Cannot flush null outputStream") + } + f, ok := p.Writer.(Flusher) + if ok { + err := f.Flush() + if err != nil { + return NewTTransportExceptionFromOsError(err) + } + } + return nil +} diff --git a/lib/go/thrift/tiostream_transport_test.go b/lib/go/thrift/tiostream_transport_test.go new file mode 100644 index 00000000..a7425c75 --- /dev/null +++ b/lib/go/thrift/tiostream_transport_test.go @@ -0,0 +1,31 @@ +/* + * 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. + */ + +package thrift_test + +import ( + . "thrift" + "testing" + "bytes" +) + +func TestIOStreamTransport(t *testing.T) { + trans := NewTIOStreamTransportRW(bytes.NewBuffer(make([]byte, 0, 1024))) + TransportTest(t, trans, trans) +} diff --git a/lib/go/thrift/tjson_protocol.go b/lib/go/thrift/tjson_protocol.go new file mode 100644 index 00000000..45ab7000 --- /dev/null +++ b/lib/go/thrift/tjson_protocol.go @@ -0,0 +1,537 @@ +/* + * 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. + */ + +package thrift + +import ( + "encoding/base64" + "fmt" +) + +const ( + THRIFT_JSON_PROTOCOL_VERSION = 1 +) + +// for references to _ParseContext see tsimplejson_protocol.go + +/** + * JSON protocol implementation for thrift. + * + * This protocol produces/consumes a simple output format + * suitable for parsing by scripting languages. It should not be + * confused with the full-featured TJSONProtocol. + * + */ +type TJSONProtocol struct { + *TSimpleJSONProtocol +} + +/** + * Constructor + */ +func NewTJSONProtocol(t TTransport) *TJSONProtocol { + v := &TJSONProtocol{TSimpleJSONProtocol: NewTSimpleJSONProtocol(t)} + v.parseContextStack.Push(int(_CONTEXT_IN_TOPLEVEL)) + v.dumpContext.Push(int(_CONTEXT_IN_TOPLEVEL)) + return v +} + +/** + * Factory + */ +type TJSONProtocolFactory struct{} + +func (p *TJSONProtocolFactory) GetProtocol(trans TTransport) TProtocol { + return NewTJSONProtocol(trans) +} + +func NewTJSONProtocolFactory() *TJSONProtocolFactory { + return &TJSONProtocolFactory{} +} + + +func (p *TJSONProtocol) WriteMessageBegin(name string, typeId TMessageType, seqId int32) TProtocolException { + if e := p.OutputListBegin(); e != nil { + return e + } + if e := p.WriteI32(THRIFT_JSON_PROTOCOL_VERSION); e != nil { + return e + } + if e := p.WriteString(name); e != nil { + return e + } + if e := p.WriteByte(byte(typeId)); e != nil { + return e + } + if e := p.WriteI32(seqId); e != nil { + return e + } + return nil +} + +func (p *TJSONProtocol) WriteMessageEnd() TProtocolException { + return p.OutputListEnd() +} + +func (p *TJSONProtocol) WriteStructBegin(name string) TProtocolException { + if e := p.OutputObjectBegin(); e != nil { + return e + } + return nil +} + +func (p *TJSONProtocol) WriteStructEnd() TProtocolException { + return p.OutputObjectEnd() +} + +func (p *TJSONProtocol) WriteFieldBegin(name string, typeId TType, id int16) TProtocolException { + if e := p.WriteI16(id); e != nil { + return e + } + if e := p.OutputObjectBegin(); e != nil { + return e + } + if e := p.WriteString(p.TypeIdToString(typeId)); e != nil { + return e + } + return nil +} + +func (p *TJSONProtocol) WriteFieldEnd() TProtocolException { + return p.OutputObjectEnd() +} + +func (p *TJSONProtocol) WriteFieldStop() TProtocolException { return nil } + +func (p *TJSONProtocol) WriteMapBegin(keyType TType, valueType TType, size int) TProtocolException { + if e := p.OutputListBegin(); e != nil { + return e + } + if e := p.WriteString(p.TypeIdToString(keyType)); e != nil { + return e + } + if e := p.WriteString(p.TypeIdToString(valueType)); e != nil { + return e + } + return p.WriteI64(int64(size)) +} + +func (p *TJSONProtocol) WriteMapEnd() TProtocolException { + return p.OutputListEnd() +} + +func (p *TJSONProtocol) WriteListBegin(elemType TType, size int) TProtocolException { + return p.OutputElemListBegin(elemType, size) +} + +func (p *TJSONProtocol) WriteListEnd() TProtocolException { + return p.OutputListEnd() +} + +func (p *TJSONProtocol) WriteSetBegin(elemType TType, size int) TProtocolException { + return p.OutputElemListBegin(elemType, size) +} + +func (p *TJSONProtocol) WriteSetEnd() TProtocolException { + return p.OutputListEnd() +} + +func (p *TJSONProtocol) WriteBool(b bool) TProtocolException { + return p.OutputBool(b) +} + +func (p *TJSONProtocol) WriteByte(b byte) TProtocolException { + return p.WriteI32(int32(b)) +} + +func (p *TJSONProtocol) WriteI16(v int16) TProtocolException { + return p.WriteI32(int32(v)) +} + +func (p *TJSONProtocol) WriteI32(v int32) TProtocolException { + return p.OutputI64(int64(v)) +} + +func (p *TJSONProtocol) WriteI64(v int64) TProtocolException { + return p.OutputI64(int64(v)) +} + +func (p *TJSONProtocol) WriteDouble(v float64) TProtocolException { + return p.OutputF64(v) +} + +func (p *TJSONProtocol) WriteString(v string) TProtocolException { + return p.OutputString(v) +} + +func (p *TJSONProtocol) WriteBinary(v []byte) TProtocolException { + // JSON library only takes in a string, + // not an arbitrary byte array, to ensure bytes are transmitted + // efficiently we must convert this into a valid JSON string + // therefore we use base64 encoding to avoid excessive escaping/quoting + if e := p.OutputPreValue(); e != nil { + return e + } + p.writer.Write(JSON_QUOTE_BYTES) + writer := base64.NewEncoder(base64.StdEncoding, p.writer) + if _, e := writer.Write(v); e != nil { + return NewTProtocolExceptionFromOsError(e) + } + writer.Close() + p.writer.Write(JSON_QUOTE_BYTES) + return p.OutputPostValue() +} + +/** + * Reading methods. + */ + +func (p *TJSONProtocol) ReadMessageBegin() (name string, typeId TMessageType, seqId int32, err TProtocolException) { + if isNull, err := p.ParseListBegin(); isNull || err != nil { + return name, typeId, seqId, err + } + version, err := p.ReadI32() + if err != nil { + return name, typeId, seqId, err + } + if version != THRIFT_JSON_PROTOCOL_VERSION { + return name, typeId, seqId, NewTProtocolException(INVALID_DATA, fmt.Sprint("Unknown Protocol version ", version, ", expected version ", THRIFT_JSON_PROTOCOL_VERSION, "\n")) + } + if name, err = p.ReadString(); err != nil { + return name, typeId, seqId, err + } + bTypeId, err := p.ReadByte() + typeId = TMessageType(bTypeId) + if err != nil { + return name, typeId, seqId, err + } + if seqId, err = p.ReadI32(); err != nil { + return name, typeId, seqId, err + } + return name, typeId, seqId, nil +} + +func (p *TJSONProtocol) ReadMessageEnd() TProtocolException { + err := p.ParseListEnd() + return err +} + +func (p *TJSONProtocol) ReadStructBegin() (name string, err TProtocolException) { + _, err = p.ParseObjectStart() + return "", err +} + +func (p *TJSONProtocol) ReadStructEnd() TProtocolException { + return p.ParseObjectEnd() +} + +func (p *TJSONProtocol) ReadFieldBegin() (string, TType, int16, TProtocolException) { + if p.reader.Buffered() < 1 { + return "", STOP, -1, nil + } + b, _ := p.reader.Peek(1) + if len(b) < 1 || b[0] == JSON_RBRACE[0] || b[0] == JSON_RBRACKET[0] { + return "", STOP, -1, nil + } + fieldId, err := p.ReadI16() + if err != nil { + return "", STOP, fieldId, err + } + if _, err = p.ParseObjectStart(); err != nil { + return "", STOP, fieldId, err + } + sType, err := p.ReadString() + fType := p.StringToTypeId(sType) + return "", fType, fieldId, err +} + +func (p *TJSONProtocol) ReadFieldEnd() TProtocolException { + return p.ParseObjectEnd() +} + +func (p *TJSONProtocol) ReadMapBegin() (keyType TType, valueType TType, size int, e TProtocolException) { + if isNull, e := p.ParseListBegin(); isNull || e != nil { + return VOID, VOID, 0, e + } + + // read keyType + sKeyType, e := p.ReadString() + keyType = p.StringToTypeId(sKeyType) + if e != nil { + return keyType, valueType, size, e + } + + // read valueType + sValueType, e := p.ReadString() + valueType = p.StringToTypeId(sValueType) + if e != nil { + return keyType, valueType, size, e + } + + // read size + iSize, err := p.ReadI64() + size = int(iSize) + return keyType, valueType, size, err +} + +func (p *TJSONProtocol) ReadMapEnd() TProtocolException { + return p.ParseListEnd() +} + +func (p *TJSONProtocol) ReadListBegin() (elemType TType, size int, e TProtocolException) { + return p.ParseElemListBegin() +} + +func (p *TJSONProtocol) ReadListEnd() TProtocolException { + return p.ParseListEnd() +} + +func (p *TJSONProtocol) ReadSetBegin() (elemType TType, size int, e TProtocolException) { + return p.ParseElemListBegin() +} + +func (p *TJSONProtocol) ReadSetEnd() TProtocolException { + return p.ParseListEnd() +} + +func (p *TJSONProtocol) ReadBool() (bool, TProtocolException) { + var value bool + if err := p.ParsePreValue(); err != nil { + return value, err + } + b, _ := p.reader.Peek(len(JSON_FALSE)) + if len(b) > 0 { + switch b[0] { + case JSON_TRUE[0]: + if string(b[0:len(JSON_TRUE)]) == string(JSON_TRUE) { + p.reader.Read(b[0:len(JSON_TRUE)]) + value = true + } else { + return value, NewTProtocolException(INVALID_DATA, "Expected \"true\" but found: "+string(b)) + } + break + case JSON_FALSE[0]: + if string(b[0:len(JSON_FALSE)]) == string(JSON_FALSE) { + p.reader.Read(b[0:len(JSON_FALSE)]) + value = false + } else { + return value, NewTProtocolException(INVALID_DATA, "Expected \"false\" but found: "+string(b)) + } + break + case JSON_NULL[0]: + if string(b[0:len(JSON_NULL)]) == string(JSON_NULL) { + p.reader.Read(b[0:len(JSON_NULL)]) + value = false + } else { + return value, NewTProtocolException(INVALID_DATA, "Expected \"null\" but found: "+string(b)) + } + default: + return value, NewTProtocolException(INVALID_DATA, "Expected \"true\", \"false\", or \"null\" but found: "+string(b)) + } + } + return value, p.ParsePostValue() +} + +func (p *TJSONProtocol) ReadByte() (byte, TProtocolException) { + v, err := p.ReadI64() + return byte(v), err +} + +func (p *TJSONProtocol) ReadI16() (int16, TProtocolException) { + v, err := p.ReadI64() + return int16(v), err +} + +func (p *TJSONProtocol) ReadI32() (int32, TProtocolException) { + v, err := p.ReadI64() + return int32(v), err +} + +func (p *TJSONProtocol) ReadI64() (int64, TProtocolException) { + v, _, err := p.ParseI64() + return v, err +} + +func (p *TJSONProtocol) ReadDouble() (float64, TProtocolException) { + v, _, err := p.ParseF64() + return v, err +} + +func (p *TJSONProtocol) ReadString() (string, TProtocolException) { + var v string + if err := p.ParsePreValue(); err != nil { + return v, err + } + b, _ := p.reader.Peek(len(JSON_NULL)) + if len(b) > 0 && b[0] == JSON_QUOTE { + p.reader.ReadByte() + value, err := p.ParseStringBody() + v = value + if err != nil { + return v, err + } + } else if len(b) >= len(JSON_NULL) && string(b[0:len(JSON_NULL)]) == string(JSON_NULL) { + _, err := p.reader.Read(b[0:len(JSON_NULL)]) + if err != nil { + return v, NewTProtocolExceptionFromOsError(err) + } + } else { + return v, NewTProtocolException(INVALID_DATA, fmt.Sprint("Expected a JSON string, found ", string(b))) + } + return v, p.ParsePostValue() +} + +func (p *TJSONProtocol) ReadBinary() ([]byte, TProtocolException) { + var v []byte + if err := p.ParsePreValue(); err != nil { + return nil, err + } + b, _ := p.reader.Peek(len(JSON_NULL)) + if len(b) > 0 && b[0] == JSON_QUOTE { + p.reader.ReadByte() + value, err := p.ParseBase64EncodedBody() + v = value + if err != nil { + return v, err + } + } else if len(b) >= len(JSON_NULL) && string(b[0:len(JSON_NULL)]) == string(JSON_NULL) { + _, err := p.reader.Read(b[0:len(JSON_NULL)]) + if err != nil { + return v, NewTProtocolExceptionFromOsError(err) + } + } else { + return v, NewTProtocolException(INVALID_DATA, fmt.Sprint("Expected a JSON string, found ", string(b))) + } + return v, p.ParsePostValue() +} + +func (p *TJSONProtocol) Flush() (err TProtocolException) { + return NewTProtocolExceptionFromOsError(p.writer.Flush()) +} + +func (p *TJSONProtocol) Skip(fieldType TType) (err TProtocolException) { + return SkipDefaultDepth(p, fieldType) +} + +func (p *TJSONProtocol) Transport() TTransport { + return p.trans +} + +func (p *TJSONProtocol) readElemListBegin() (elemType TType, size int, e TProtocolException) { + if isNull, e := p.ParseListBegin(); isNull || e != nil { + return VOID, 0, e + } + sElemType, err := p.ReadString() + elemType = p.StringToTypeId(sElemType) + if err != nil { + return elemType, size, err + } + nSize, err2 := p.ReadI64() + size = int(nSize) + return elemType, size, err2 +} + +func (p *TJSONProtocol) writeElemListBegin(elemType TType, size int) TProtocolException { + if e := p.OutputListBegin(); e != nil { + return e + } + if e := p.OutputString(p.TypeIdToString(elemType)); e != nil { + return e + } + if e := p.OutputI64(int64(size)); e != nil { + return e + } + return nil +} + +func (p *TJSONProtocol) TypeIdToString(fieldType TType) string { + switch byte(fieldType) { + case STOP: + return "stp" + case VOID: + return "v" + case BOOL: + return "tf" + case BYTE: + return "i8" + case DOUBLE: + return "dbl" + case I16: + return "i16" + case I32: + return "i32" + case I64: + return "i64" + case STRING: + return "str" + case STRUCT: + return "rec" + case MAP: + return "map" + case SET: + return "set" + case LIST: + return "lst" + case ENUM: + return "i32" + case UTF16: + return "str" + case GENERIC: + return "gen" + } + return "" +} + +func (p *TJSONProtocol) StringToTypeId(fieldType string) TType { + switch fieldType { + case "stp": + return TType(STOP) + case "v": + return TType(VOID) + case "tf": + return TType(BOOL) + case "i8": + return TType(BYTE) + case "dbl": + return TType(DOUBLE) + case "16": + return TType(I16) + case "i32": + return TType(I32) + case "i64": + return TType(I64) + case "str": + return TType(STRING) + case "rec": + return TType(STRUCT) + case "map": + return TType(MAP) + case "set": + return TType(SET) + case "lst": + return TType(LIST) + case "enm": + return TType(ENUM) + case "u16": + return TType(UTF16) + case "gen": + return TType(GENERIC) + } + return TType(STOP) +} diff --git a/lib/go/thrift/tjson_protocol_test.go b/lib/go/thrift/tjson_protocol_test.go new file mode 100644 index 00000000..60f0f2e6 --- /dev/null +++ b/lib/go/thrift/tjson_protocol_test.go @@ -0,0 +1,674 @@ +/* + * 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. + */ + +package thrift_test + +import ( + . "thrift" + "encoding/base64" + "fmt" + "json" + "math" + "strconv" + "testing" +) + +func TestWriteJSONProtocolBool(t *testing.T) { + thetype := "boolean" + trans := NewTMemoryBuffer() + p := NewTJSONProtocol(trans) + for _, value := range BOOL_VALUES { + if e := p.WriteBool(value); e != nil { + t.Fatalf("Unable to write %s value %v due to error: %s", thetype, value, e.String()) + } + if e := p.Flush(); e != nil { + t.Fatalf("Unable to write %s value %v due to error flushing: %s", thetype, value, e.String()) + } + s := trans.String() + if s != fmt.Sprint(value) { + t.Fatalf("Bad value for %s %v: %s", thetype, value, s) + } + v := false + if err := json.Unmarshal([]byte(s), &v); err != nil || v != value { + t.Fatalf("Bad json-decoded value for %s %v, wrote: '%s', expected: '%v'", thetype, value, s, v) + } + trans.Reset() + } + trans.Close() +} + +func TestReadJSONProtocolBool(t *testing.T) { + thetype := "boolean" + for _, value := range BOOL_VALUES { + trans := NewTMemoryBuffer() + p := NewTJSONProtocol(trans) + if value { + trans.Write(JSON_TRUE) + } else { + trans.Write(JSON_FALSE) + } + trans.Flush() + s := trans.String() + v, e := p.ReadBool() + if e != nil { + t.Fatalf("Unable to read %s value %v due to error: %s", thetype, value, e.String()) + } + if v != value { + t.Fatalf("Bad value for %s value %v, wrote: %v, received: %v", thetype, value, s, v) + } + if err := json.Unmarshal([]byte(s), &v); err != nil || v != value { + t.Fatalf("Bad json-decoded value for %s %v, wrote: '%s', expected: '%v'", thetype, value, s, v) + } + trans.Reset() + trans.Close() + } +} + +func TestWriteJSONProtocolByte(t *testing.T) { + thetype := "byte" + trans := NewTMemoryBuffer() + p := NewTJSONProtocol(trans) + for _, value := range BYTE_VALUES { + if e := p.WriteByte(value); e != nil { + t.Fatalf("Unable to write %s value %v due to error: %s", thetype, value, e.String()) + } + if e := p.Flush(); e != nil { + t.Fatalf("Unable to write %s value %v due to error flushing: %s", thetype, value, e.String()) + } + s := trans.String() + if s != fmt.Sprint(value) { + t.Fatalf("Bad value for %s %v: %s", thetype, value, s) + } + v := byte(0) + if err := json.Unmarshal([]byte(s), &v); err != nil || v != value { + t.Fatalf("Bad json-decoded value for %s %v, wrote: '%s', expected: '%v'", thetype, value, s, v) + } + trans.Reset() + } + trans.Close() +} + +func TestReadJSONProtocolByte(t *testing.T) { + thetype := "byte" + for _, value := range BYTE_VALUES { + trans := NewTMemoryBuffer() + p := NewTJSONProtocol(trans) + trans.WriteString(strconv.Itoa(int(value))) + trans.Flush() + s := trans.String() + v, e := p.ReadByte() + if e != nil { + t.Fatalf("Unable to read %s value %v due to error: %s", thetype, value, e.String()) + } + if v != value { + t.Fatalf("Bad value for %s value %v, wrote: %v, received: %v", thetype, value, s, v) + } + if err := json.Unmarshal([]byte(s), &v); err != nil || v != value { + t.Fatalf("Bad json-decoded value for %s %v, wrote: '%s', expected: '%v'", thetype, value, s, v) + } + trans.Reset() + trans.Close() + } +} + +func TestWriteJSONProtocolI16(t *testing.T) { + thetype := "int16" + trans := NewTMemoryBuffer() + p := NewTJSONProtocol(trans) + for _, value := range INT16_VALUES { + if e := p.WriteI16(value); e != nil { + t.Fatalf("Unable to write %s value %v due to error: %s", thetype, value, e.String()) + } + if e := p.Flush(); e != nil { + t.Fatalf("Unable to write %s value %v due to error flushing: %s", thetype, value, e.String()) + } + s := trans.String() + if s != fmt.Sprint(value) { + t.Fatalf("Bad value for %s %v: %s", thetype, value, s) + } + v := int16(0) + if err := json.Unmarshal([]byte(s), &v); err != nil || v != value { + t.Fatalf("Bad json-decoded value for %s %v, wrote: '%s', expected: '%v'", thetype, value, s, v) + } + trans.Reset() + } + trans.Close() +} + +func TestReadJSONProtocolI16(t *testing.T) { + thetype := "int16" + for _, value := range INT16_VALUES { + trans := NewTMemoryBuffer() + p := NewTJSONProtocol(trans) + trans.WriteString(strconv.Itoa(int(value))) + trans.Flush() + s := trans.String() + v, e := p.ReadI16() + if e != nil { + t.Fatalf("Unable to read %s value %v due to error: %s", thetype, value, e.String()) + } + if v != value { + t.Fatalf("Bad value for %s value %v, wrote: %v, received: %v", thetype, value, s, v) + } + if err := json.Unmarshal([]byte(s), &v); err != nil || v != value { + t.Fatalf("Bad json-decoded value for %s %v, wrote: '%s', expected: '%v'", thetype, value, s, v) + } + trans.Reset() + trans.Close() + } +} + +func TestWriteJSONProtocolI32(t *testing.T) { + thetype := "int32" + trans := NewTMemoryBuffer() + p := NewTJSONProtocol(trans) + for _, value := range INT32_VALUES { + if e := p.WriteI32(value); e != nil { + t.Fatalf("Unable to write %s value %v due to error: %s", thetype, value, e.String()) + } + if e := p.Flush(); e != nil { + t.Fatalf("Unable to write %s value %v due to error flushing: %s", thetype, value, e.String()) + } + s := trans.String() + if s != fmt.Sprint(value) { + t.Fatalf("Bad value for %s %v: %s", thetype, value, s) + } + v := int32(0) + if err := json.Unmarshal([]byte(s), &v); err != nil || v != value { + t.Fatalf("Bad json-decoded value for %s %v, wrote: '%s', expected: '%v'", thetype, value, s, v) + } + trans.Reset() + } + trans.Close() +} + +func TestReadJSONProtocolI32(t *testing.T) { + thetype := "int32" + for _, value := range INT32_VALUES { + trans := NewTMemoryBuffer() + p := NewTJSONProtocol(trans) + trans.WriteString(strconv.Itoa(int(value))) + trans.Flush() + s := trans.String() + v, e := p.ReadI32() + if e != nil { + t.Fatalf("Unable to read %s value %v due to error: %s", thetype, value, e.String()) + } + if v != value { + t.Fatalf("Bad value for %s value %v, wrote: %v, received: %v", thetype, value, s, v) + } + if err := json.Unmarshal([]byte(s), &v); err != nil || v != value { + t.Fatalf("Bad json-decoded value for %s %v, wrote: '%s', expected: '%v'", thetype, value, s, v) + } + trans.Reset() + trans.Close() + } +} + +func TestWriteJSONProtocolI64(t *testing.T) { + thetype := "int64" + trans := NewTMemoryBuffer() + p := NewTJSONProtocol(trans) + for _, value := range INT64_VALUES { + if e := p.WriteI64(value); e != nil { + t.Fatalf("Unable to write %s value %v due to error: %s", thetype, value, e.String()) + } + if e := p.Flush(); e != nil { + t.Fatalf("Unable to write %s value %v due to error flushing: %s", thetype, value, e.String()) + } + s := trans.String() + if s != fmt.Sprint(value) { + t.Fatalf("Bad value for %s %v: %s", thetype, value, s) + } + v := int64(0) + if err := json.Unmarshal([]byte(s), &v); err != nil || v != value { + t.Fatalf("Bad json-decoded value for %s %v, wrote: '%s', expected: '%v'", thetype, value, s, v) + } + trans.Reset() + } + trans.Close() +} + +func TestReadJSONProtocolI64(t *testing.T) { + thetype := "int64" + for _, value := range INT64_VALUES { + trans := NewTMemoryBuffer() + p := NewTJSONProtocol(trans) + trans.WriteString(strconv.Itoa64(value)) + trans.Flush() + s := trans.String() + v, e := p.ReadI64() + if e != nil { + t.Fatalf("Unable to read %s value %v due to error: %s", thetype, value, e.String()) + } + if v != value { + t.Fatalf("Bad value for %s value %v, wrote: %v, received: %v", thetype, value, s, v) + } + if err := json.Unmarshal([]byte(s), &v); err != nil || v != value { + t.Fatalf("Bad json-decoded value for %s %v, wrote: '%s', expected: '%v'", thetype, value, s, v) + } + trans.Reset() + trans.Close() + } +} + +func TestWriteJSONProtocolDouble(t *testing.T) { + thetype := "double" + trans := NewTMemoryBuffer() + p := NewTJSONProtocol(trans) + for _, value := range DOUBLE_VALUES { + if e := p.WriteDouble(value); e != nil { + t.Fatalf("Unable to write %s value %v due to error: %s", thetype, value, e.String()) + } + if e := p.Flush(); e != nil { + t.Fatalf("Unable to write %s value %v due to error flushing: %s", thetype, value, e.String()) + } + s := trans.String() + if math.IsInf(value, 1) { + if s != JsonQuote(JSON_INFINITY) { + t.Fatalf("Bad value for %s %v, wrote: %v, expected: %v", thetype, value, s, JsonQuote(JSON_INFINITY)) + } + } else if math.IsInf(value, -1) { + if s != JsonQuote(JSON_NEGATIVE_INFINITY) { + t.Fatalf("Bad value for %s %v, wrote: %v, expected: %v", thetype, value, s, JsonQuote(JSON_NEGATIVE_INFINITY)) + } + } else if math.IsNaN(value) { + if s != JsonQuote(JSON_NAN) { + t.Fatalf("Bad value for %s %v, wrote: %v, expected: %v", thetype, value, s, JsonQuote(JSON_NAN)) + } + } else { + if s != fmt.Sprint(value) { + t.Fatalf("Bad value for %s %v: %s", thetype, value, s) + } + v := float64(0) + if err := json.Unmarshal([]byte(s), &v); err != nil || v != value { + t.Fatalf("Bad json-decoded value for %s %v, wrote: '%s', expected: '%v'", thetype, value, s, v) + } + } + trans.Reset() + } + trans.Close() +} + +func TestReadJSONProtocolDouble(t *testing.T) { + thetype := "double" + for _, value := range DOUBLE_VALUES { + trans := NewTMemoryBuffer() + p := NewTJSONProtocol(trans) + n := NewNumericFromDouble(value) + trans.WriteString(n.String()) + trans.Flush() + s := trans.String() + v, e := p.ReadDouble() + if e != nil { + t.Fatalf("Unable to read %s value %v due to error: %s", thetype, value, e.String()) + } + if math.IsInf(value, 1) { + if !math.IsInf(v, 1) { + t.Fatalf("Bad value for %s %v, wrote: %v, received: %v", thetype, value, s, v) + } + } else if math.IsInf(value, -1) { + if !math.IsInf(v, -1) { + t.Fatalf("Bad value for %s %v, wrote: %v, received: %v", thetype, value, s, v) + } + } else if math.IsNaN(value) { + if !math.IsNaN(v) { + t.Fatalf("Bad value for %s %v, wrote: %v, received: %v", thetype, value, s, v) + } + } else { + if v != value { + t.Fatalf("Bad value for %s value %v, wrote: %v, received: %v", thetype, value, s, v) + } + if err := json.Unmarshal([]byte(s), &v); err != nil || v != value { + t.Fatalf("Bad json-decoded value for %s %v, wrote: '%s', expected: '%v'", thetype, value, s, v) + } + } + trans.Reset() + trans.Close() + } +} + +func TestWriteJSONProtocolString(t *testing.T) { + thetype := "string" + trans := NewTMemoryBuffer() + p := NewTJSONProtocol(trans) + for _, value := range STRING_VALUES { + if e := p.WriteString(value); e != nil { + t.Fatalf("Unable to write %s value %v due to error: %s", thetype, value, e.String()) + } + if e := p.Flush(); e != nil { + t.Fatalf("Unable to write %s value %v due to error flushing: %s", thetype, value, e.String()) + } + s := trans.String() + if s[0] != '"' || s[len(s)-1] != '"' { + t.Fatalf("Bad value for %s '%v', wrote '%v', expected: %v", thetype, value, s, fmt.Sprint("\"", value, "\"")) + } + v := new(string) + if err := json.Unmarshal([]byte(s), v); err != nil || *v != value { + t.Fatalf("Bad json-decoded value for %s %v, wrote: '%s', expected: '%v'", thetype, value, s, *v) + } + trans.Reset() + } + trans.Close() +} + +func TestReadJSONProtocolString(t *testing.T) { + thetype := "string" + for _, value := range STRING_VALUES { + trans := NewTMemoryBuffer() + p := NewTJSONProtocol(trans) + trans.WriteString(JsonQuote(value)) + trans.Flush() + s := trans.String() + v, e := p.ReadString() + if e != nil { + t.Fatalf("Unable to read %s value %v due to error: %s", thetype, value, e.String()) + } + if v != value { + t.Fatalf("Bad value for %s value %v, wrote: %v, received: %v", thetype, value, s, v) + } + v1 := new(string) + if err := json.Unmarshal([]byte(s), v1); err != nil || *v1 != value { + t.Fatalf("Bad json-decoded value for %s %v, wrote: '%s', expected: '%v'", thetype, value, s, *v1) + } + trans.Reset() + trans.Close() + } +} + +func TestWriteJSONProtocolBinary(t *testing.T) { + thetype := "binary" + value := protocol_bdata + b64value := make([]byte, base64.StdEncoding.EncodedLen(len(protocol_bdata))) + base64.StdEncoding.Encode(b64value, value) + b64String := string(b64value) + trans := NewTMemoryBuffer() + p := NewTJSONProtocol(trans) + if e := p.WriteBinary(value); e != nil { + t.Fatalf("Unable to write %s value %v due to error: %s", thetype, value, e.String()) + } + if e := p.Flush(); e != nil { + t.Fatalf("Unable to write %s value %v due to error flushing: %s", thetype, value, e.String()) + } + s := trans.String() + expectedString := fmt.Sprint("\"", b64String, "\"") + if s != expectedString { + t.Fatalf("Bad value for %s %v\n wrote: \"%v\"\nexpected: \"%v\"", thetype, value, s, expectedString) + } + v1, err := p.ReadBinary() + if err != nil { + t.Fatalf("Unable to read binary: %s", err.String()) + } + if len(v1) != len(value) { + t.Fatalf("Invalid value for binary\nexpected: \"%v\"\n read: \"%v\"", value, v1) + } + for k, v := range value { + if v1[k] != v { + t.Fatalf("Invalid value for binary at %v\nexpected: \"%v\"\n read: \"%v\"", k, v, v1[k]) + } + } + trans.Close() +} + +func TestReadJSONProtocolBinary(t *testing.T) { + thetype := "binary" + value := protocol_bdata + b64value := make([]byte, base64.StdEncoding.EncodedLen(len(protocol_bdata))) + base64.StdEncoding.Encode(b64value, value) + b64String := string(b64value) + trans := NewTMemoryBuffer() + p := NewTJSONProtocol(trans) + trans.WriteString(JsonQuote(b64String)) + trans.Flush() + s := trans.String() + v, e := p.ReadBinary() + if e != nil { + t.Fatalf("Unable to read %s value %v due to error: %s", thetype, value, e.String()) + } + if len(v) != len(value) { + t.Fatalf("Bad value for %s value length %v, wrote: %v, received length: %v", thetype, len(value), s, len(v)) + } + for i := 0; i < len(v); i++ { + if v[i] != value[i] { + t.Fatalf("Bad value for %s at index %d value %v, wrote: %v, received: %v", thetype, i, value[i], s, v[i]) + } + } + v1 := new(string) + if err := json.Unmarshal([]byte(s), v1); err != nil || *v1 != b64String { + t.Fatalf("Bad json-decoded value for %s %v, wrote: '%s', expected: '%v'", thetype, value, s, *v1) + } + trans.Reset() + trans.Close() +} + +func TestWriteJSONProtocolList(t *testing.T) { + thetype := "list" + trans := NewTMemoryBuffer() + p := NewTJSONProtocol(trans) + p.WriteListBegin(TType(DOUBLE), len(DOUBLE_VALUES)) + for _, value := range DOUBLE_VALUES { + if e := p.WriteDouble(value); e != nil { + t.Fatalf("Unable to write %s value %v due to error: %s", thetype, value, e.String()) + } + } + p.WriteListEnd() + if e := p.Flush(); e != nil { + t.Fatalf("Unable to write %s due to error flushing: %s", thetype, e.String()) + } + str := trans.String() + str1 := new([]interface{}) + err := json.Unmarshal([]byte(str), str1) + if err != nil { + t.Fatalf("Unable to decode %s, wrote: %s", thetype, str) + } + l := *str1 + if len(l) < 2 { + t.Fatalf("List must be at least of length two to include metadata") + } + if int(l[0].(float64)) != DOUBLE { + t.Fatal("Invalid type for list, expected: ", DOUBLE, ", but was: ", l[0]) + } + if int(l[1].(float64)) != len(DOUBLE_VALUES) { + t.Fatal("Invalid length for list, expected: ", len(DOUBLE_VALUES), ", but was: ", l[1]) + } + for k, value := range DOUBLE_VALUES { + s := l[k+2] + if math.IsInf(value, 1) { + if s.(string) != JSON_INFINITY { + t.Fatalf("Bad value for %s at index %v %v, wrote: %q, expected: %q, originally wrote: %q", thetype, k, value, s, JsonQuote(JSON_INFINITY), str) + } + } else if math.IsInf(value, 0) { + if s.(string) != JSON_NEGATIVE_INFINITY { + t.Fatalf("Bad value for %s at index %v %v, wrote: %q, expected: %q, originally wrote: %q", thetype, k, value, s, JsonQuote(JSON_NEGATIVE_INFINITY), str) + } + } else if math.IsNaN(value) { + if s.(string) != JSON_NAN { + t.Fatalf("Bad value for %s at index %v %v, wrote: %q, expected: %q, originally wrote: %q", thetype, k, value, s, JsonQuote(JSON_NAN), str) + } + } else { + if s.(float64) != value { + t.Fatalf("Bad json-decoded value for %s %v, wrote: '%s'", thetype, value, s) + } + } + trans.Reset() + } + trans.Close() +} + +func TestWriteJSONProtocolSet(t *testing.T) { + thetype := "set" + trans := NewTMemoryBuffer() + p := NewTJSONProtocol(trans) + p.WriteSetBegin(TType(DOUBLE), len(DOUBLE_VALUES)) + for _, value := range DOUBLE_VALUES { + if e := p.WriteDouble(value); e != nil { + t.Fatalf("Unable to write %s value %v due to error: %s", thetype, value, e.String()) + } + } + p.WriteSetEnd() + if e := p.Flush(); e != nil { + t.Fatalf("Unable to write %s due to error flushing: %s", thetype, e.String()) + } + str := trans.String() + str1 := new([]interface{}) + err := json.Unmarshal([]byte(str), str1) + if err != nil { + t.Fatalf("Unable to decode %s, wrote: %s", thetype, str) + } + l := *str1 + if len(l) < 2 { + t.Fatalf("Set must be at least of length two to include metadata") + } + if int(l[0].(float64)) != DOUBLE { + t.Fatal("Invalid type for set, expected: ", DOUBLE, ", but was: ", l[0]) + } + if int(l[1].(float64)) != len(DOUBLE_VALUES) { + t.Fatal("Invalid length for set, expected: ", len(DOUBLE_VALUES), ", but was: ", l[1]) + } + for k, value := range DOUBLE_VALUES { + s := l[k+2] + if math.IsInf(value, 1) { + if s.(string) != JSON_INFINITY { + t.Fatalf("Bad value for %s at index %v %v, wrote: %q, expected: %q, originally wrote: %q", thetype, k, value, s, JsonQuote(JSON_INFINITY), str) + } + } else if math.IsInf(value, 0) { + if s.(string) != JSON_NEGATIVE_INFINITY { + t.Fatalf("Bad value for %s at index %v %v, wrote: %q, expected: %q, originally wrote: %q", thetype, k, value, s, JsonQuote(JSON_NEGATIVE_INFINITY), str) + } + } else if math.IsNaN(value) { + if s.(string) != JSON_NAN { + t.Fatalf("Bad value for %s at index %v %v, wrote: %q, expected: %q, originally wrote: %q", thetype, k, value, s, JsonQuote(JSON_NAN), str) + } + } else { + if s.(float64) != value { + t.Fatalf("Bad json-decoded value for %s %v, wrote: '%s'", thetype, value, s) + } + } + trans.Reset() + } + trans.Close() +} + +func TestWriteJSONProtocolMap(t *testing.T) { + thetype := "map" + trans := NewTMemoryBuffer() + p := NewTJSONProtocol(trans) + p.WriteMapBegin(TType(I32), TType(DOUBLE), len(DOUBLE_VALUES)) + for k, value := range DOUBLE_VALUES { + if e := p.WriteI32(int32(k)); e != nil { + t.Fatalf("Unable to write %s key int32 value %v due to error: %s", thetype, k, e.String()) + } + if e := p.WriteDouble(value); e != nil { + t.Fatalf("Unable to write %s value float64 value %v due to error: %s", thetype, value, e.String()) + } + } + p.WriteMapEnd() + if e := p.Flush(); e != nil { + t.Fatalf("Unable to write %s due to error flushing: %s", thetype, e.String()) + } + str := trans.String() + if str[0] != '[' || str[len(str)-1] != ']' { + t.Fatalf("Bad value for %s, wrote: %q, in go: %q", thetype, str, DOUBLE_VALUES) + } + expectedKeyType, expectedValueType, expectedSize, err := p.ReadMapBegin() + if err != nil { + t.Fatalf("Error while reading map begin: %s", err.String()) + } + if expectedKeyType != I32 { + t.Fatal("Expected map key type ", I32, ", but was ", expectedKeyType) + } + if expectedValueType != DOUBLE { + t.Fatal("Expected map value type ", DOUBLE, ", but was ", expectedValueType) + } + if expectedSize != len(DOUBLE_VALUES) { + t.Fatal("Expected map size of ", len(DOUBLE_VALUES), ", but was ", expectedSize) + } + for k, value := range DOUBLE_VALUES { + ik, err := p.ReadI32() + if err != nil { + t.Fatalf("Bad key for %s index %v, wrote: %v, expected: %v, error: %s", thetype, k, ik, string(k), err.String()) + } + if int(ik) != k { + t.Fatalf("Bad key for %s index %v, wrote: %v, expected: %v", thetype, k, ik, k) + } + dv, err := p.ReadDouble() + if err != nil { + t.Fatalf("Bad value for %s index %v, wrote: %v, expected: %v, error: %s", thetype, k, dv, value, err.String()) + } + s := strconv.Ftoa64(dv, 'g', 10) + if math.IsInf(value, 1) { + if !math.IsInf(dv, 1) { + t.Fatalf("Bad value for %s at index %v %v, wrote: %v, expected: %v", thetype, k, value, s, JsonQuote(JSON_INFINITY)) + } + } else if math.IsInf(value, 0) { + if !math.IsInf(dv, 0) { + t.Fatalf("Bad value for %s at index %v %v, wrote: %v, expected: %v", thetype, k, value, s, JsonQuote(JSON_NEGATIVE_INFINITY)) + } + } else if math.IsNaN(value) { + if !math.IsNaN(dv) { + t.Fatalf("Bad value for %s at index %v %v, wrote: %v, expected: %v", thetype, k, value, s, JsonQuote(JSON_NAN)) + } + } else { + expected := strconv.Ftoa64(value, 'g', 10) + if s != expected { + t.Fatalf("Bad value for %s at index %v %v, wrote: %v, expected %v", thetype, k, value, s, expected) + } + v := float64(0) + if err := json.Unmarshal([]byte(s), &v); err != nil || v != value { + t.Fatalf("Bad json-decoded value for %s %v, wrote: '%s', expected: '%v'", thetype, value, s, v) + } + } + trans.Reset() + } + trans.Close() +} + + +func TestReadWriteJSONStruct(t *testing.T) { + thetype := "struct" + trans := NewTMemoryBuffer() + p := NewTJSONProtocol(trans) + orig := NewWork() + orig.Num1 = 25 + orig.Num2 = 102 + orig.Op = ADD + orig.Comment = "Add: 25 + 102" + if e := orig.Write(p); e != nil { + t.Fatalf("Unable to write %s value %#v due to error: %s", thetype, orig, e.String()) + } + p.Flush() + t.Log("Memory buffer contents: ", trans.String()) + expectedString := "{\"1\":{\"i32\":25},\"2\":{\"i32\":102},\"3\":{\"i32\":1},\"4\":{\"str\":\"Add: 25 + 102\"}}" + if expectedString != trans.String() { + t.Fatalf("Expected JSON Struct with value %#v but have %#v", expectedString, trans.String()) + } + read := NewWork() + e := read.Read(p) + t.Logf("Read %s value: %#v", thetype, read) + if e != nil { + t.Fatalf("Unable to read %s due to error: %s", thetype, e.String()) + } + if !orig.Equals(read) { + t.Fatalf("Original Write != Read: %#v != %#v ", orig, read) + } +} + +func TestReadWriteJSONProtocol(t *testing.T) { + ReadWriteProtocolTest(t, NewTJSONProtocolFactory()) +} diff --git a/lib/go/thrift/tlist.go b/lib/go/thrift/tlist.go new file mode 100644 index 00000000..778fc3b6 --- /dev/null +++ b/lib/go/thrift/tlist.go @@ -0,0 +1,222 @@ +/* + * 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. + */ + +package thrift + +import ( + "container/vector" +) + +/** + * Helper class that encapsulates list metadata. + * + */ +type TList interface { + TContainer + ElemType() TType + At(i int) interface{} + Set(i int, data interface{}) + Push(data interface{}) + Pop() interface{} + Swap(i, j int) + Insert(i int, data interface{}) + Delete(i int) + Less(i, j int) bool + Iter() <-chan interface{} +} + +type tList struct { + elemType TType + l *vector.Vector +} + +func NewTList(t TType, s int) TList { + var v vector.Vector + return &tList{elemType: t, l: v.Resize(s, s)} +} + +func NewTListDefault() TList { + var v vector.Vector + return &tList{elemType: TType(STOP), l: &v} +} + +func (p *tList) ElemType() TType { + return p.elemType +} + +func (p *tList) Len() int { + return p.l.Len() +} + +func (p *tList) At(i int) interface{} { + return p.l.At(i) +} + +func (p *tList) Set(i int, data interface{}) { + if p.elemType.IsEmptyType() { + p.elemType = TypeFromValue(data) + } + if data, ok := p.elemType.CoerceData(data); ok { + p.l.Set(i, data) + } +} + +func (p *tList) Push(data interface{}) { + if p.elemType.IsEmptyType() { + p.elemType = TypeFromValue(data) + } + data, ok := p.elemType.CoerceData(data) + if ok { + p.l.Push(data) + } +} + +func (p *tList) Pop() interface{} { + return p.l.Pop() +} + +func (p *tList) Swap(i, j int) { + p.l.Swap(i, j) +} + +func (p *tList) Insert(i int, data interface{}) { + p.l.Insert(i, data) +} + +func (p *tList) Delete(i int) { + p.l.Delete(i) +} + +func (p *tList) Contains(data interface{}) bool { + return p.indexOf(data) >= 0 +} + +func (p *tList) Less(i, j int) bool { + return p.l.Less(i, j) +} + +func (p *tList) Iter() <-chan interface{} { + c := make(chan interface{}) + go p.iterate(c) + return c +} + +func (p *tList) iterate(c chan<- interface{}) { + for _, elem := range *p.l { + c <- elem + } + close(c) +} + +func (p *tList) indexOf(data interface{}) int { + if data == nil { + size := p.l.Len() + for i := 0; i < size; i++ { + if p.l.At(i) == nil { + return i + } + } + return -1 + } + data, ok := p.elemType.CoerceData(data) + if data == nil || !ok { + return -1 + } + size := p.l.Len() + if p.elemType.IsBaseType() || p.elemType.IsEnum() { + for i := 0; i < size; i++ { + if data == p.l.At(i) { + return i + } + } + return -1 + } + if cmp, ok := data.(EqualsOtherInterface); ok { + for i := 0; i < size; i++ { + if cmp.Equals(p.l.At(i)) { + return i + } + } + return -1 + } + switch p.elemType { + case MAP: + if cmp, ok := data.(EqualsMap); ok { + for i := 0; i < size; i++ { + v := p.l.At(i) + if v == nil { + continue + } + if cmp.Equals(v.(TMap)) { + return i + } + } + return -1 + } + case SET: + if cmp, ok := data.(EqualsSet); ok { + for i := 0; i < size; i++ { + v := p.l.At(i) + if v == nil { + continue + } + if cmp.Equals(v.(TSet)) { + return i + } + } + return -1 + } + case LIST: + if cmp, ok := data.(EqualsList); ok { + for i := 0; i < size; i++ { + v := p.l.At(i) + if v == nil { + continue + } + if cmp.Equals(v.(TList)) { + return i + } + } + return -1 + } + case STRUCT: + if cmp, ok := data.(EqualsStruct); ok { + for i := 0; i < size; i++ { + v := p.l.At(i) + if v == nil { + continue + } + if cmp.Equals(v.(TStruct)) { + return i + } + } + return -1 + } + } + return -1 +} + +func (p *tList) Equals(other interface{}) bool { + c, cok := p.CompareTo(other) + return cok && c == 0 +} + +func (p *tList) CompareTo(other interface{}) (int, bool) { + return TType(LIST).Compare(p, other) +} diff --git a/lib/go/thrift/tmap.go b/lib/go/thrift/tmap.go new file mode 100644 index 00000000..d042f05e --- /dev/null +++ b/lib/go/thrift/tmap.go @@ -0,0 +1,763 @@ +/* + * 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. + */ + +package thrift + +import ( + "container/list" + "reflect" +) + +/** + * Helper class that encapsulates map metadata. + * + */ +type TMap interface { + KeyType() TType + ValueType() TType + Len() int + Set(key, value interface{}) + Get(key interface{}) (interface{}, bool) + Contains(key interface{}) bool + Iter() <-chan TMapElem + KeyIter() <-chan interface{} + ValueIter() <-chan interface{} + Keys() []interface{} + Values() []interface{} + Less(other interface{}) bool + Equals(other interface{}) bool + CompareTo(other interface{}) (int, bool) +} + +type TMapElem interface { + Key() interface{} + Value() interface{} +} + +type tMap struct { + keyType TType + valueType TType + size int + l *list.List + b map[bool]interface{} + i08 map[byte]interface{} + i16 map[int16]interface{} + i32 map[int32]interface{} + i64 map[int64]interface{} + f64 map[float64]interface{} + s map[string]interface{} +} + +type tMapElem struct { + key interface{} + value interface{} +} + +func (p *tMapElem) Key() interface{} { + return p.key +} + +func (p *tMapElem) Value() interface{} { + return p.value +} + +func NewTMapElem(k, v interface{}) TMapElem { + return &tMapElem{key: k, value: v} +} + +func NewTMap(k, v TType, s int) TMap { + return &tMap{keyType: k, valueType: v, size: s, l: list.New()} +} + +func NewTMapDefault() TMap { + return NewTMap(STOP, STOP, 0) +} + +func (p *tMap) KeyType() TType { + return p.keyType +} + +func (p *tMap) ValueType() TType { + return p.valueType +} + +func (p *tMap) Len() int { + if p.l.Len() != 0 { + return p.l.Len() + } + switch p.KeyType() { + case STOP, VOID: + return 0 + case BOOL: + return len(p.b) + case BYTE: + return len(p.i08) + case I16: + return len(p.i16) + case I32: + return len(p.i32) + case I64: + return len(p.i64) + case DOUBLE: + return len(p.f64) + case STRING, UTF8, UTF16: + return len(p.s) + default: + return p.size + } + return p.size +} + +func (p *tMap) Get(key interface{}) (interface{}, bool) { + if p.KeyType().IsEmptyType() { + return nil, false + } + if key == nil { + for elem := p.l.Front(); elem != nil; elem = elem.Next() { + e := elem.Value.(TMapElem) + k := e.Key() + if k == nil { + return e.Value(), true + } + } + return nil, false + } + useKey, ok := p.KeyType().CoerceData(key) + if !ok { + return nil, false + } + switch p.KeyType() { + case STOP, VOID: + // if here, then we don't have a key type yet and key is not nil + // so this is pretty much an empty map + return nil, false + case BOOL: + m := p.b + if m == nil { + return nil, false + } + if v, ok := m[useKey.(bool)]; ok { + return v, true + } + return nil, true + case BYTE: + m := p.i08 + if v, ok := m[useKey.(byte)]; ok { + return v, true + } + return nil, false + case DOUBLE: + m := p.f64 + if m == nil { + return nil, false + } + if v, ok := m[useKey.(float64)]; ok { + return v, true + } + return nil, false + case I16: + m := p.i16 + if m == nil { + return nil, false + } + if v, ok := m[useKey.(int16)]; ok { + return v, true + } + return nil, false + case I32: + m := p.i32 + if m == nil { + return nil, false + } + if v, ok := m[useKey.(int32)]; ok { + return v, true + } + return nil, false + case I64: + m := p.i64 + if m == nil { + return nil, false + } + if v, ok := m[useKey.(int64)]; ok { + return v, true + } + return nil, false + case STRING, UTF8, UTF16: + // TODO(pomack) properly handle ENUM + m := p.s + if m == nil { + return nil, false + } + if v, ok := m[useKey.(string)]; ok { + return v, true + } + return nil, false + case STRUCT: + for elem := p.l.Front(); elem != nil; elem = elem.Next() { + e := elem.Value.(TMapElem) + k := e.Key() + if k == nil { + continue + } + structkey, ok := k.(TStruct) + if ok { + if structkey.Equals(useKey.(TStruct)) { + return e.Value(), true + } + continue + } + if reflect.DeepEqual(useKey, k) { + return e.Value(), true + } + } + return nil, false + case MAP: + for elem := p.l.Front(); elem != nil; elem = elem.Next() { + e := elem.Value.(TMapElem) + k := e.Key() + if k == nil { + continue + } + mapkey, ok := k.(TMap) + if ok { + if mapkey.Equals(useKey.(TMap)) { + return e.Value(), true + } + continue + } + if reflect.DeepEqual(useKey, k) { + return e.Value(), true + } + } + return nil, false + case SET: + for elem := p.l.Front(); elem != nil; elem = elem.Next() { + e := elem.Value.(TMapElem) + k := e.Key() + if k == nil { + continue + } + setkey, ok := k.(TSet) + if ok { + if setkey.Equals(useKey.(TSet)) { + return e.Value(), true + } + continue + } + if reflect.DeepEqual(useKey, k) { + return e.Value(), true + } + } + return nil, false + case LIST: + for elem := p.l.Front(); elem != nil; elem = elem.Next() { + e := elem.Value.(TMapElem) + k := e.Key() + if k == nil { + continue + } + listkey, ok := k.(TList) + if ok { + if listkey.Equals(useKey.(TList)) { + return e.Value(), true + } + continue + } + if reflect.DeepEqual(useKey, k) { + return e.Value(), true + } + } + return nil, false + default: + panic("Invalid Thrift element type") + } + return nil, false +} + + +func (p *tMap) Set(key, value interface{}) { + if p.KeyType() == STOP || p.KeyType() == VOID { + p.keyType = TypeFromValue(key) + } + coercedKey, ok := p.KeyType().CoerceData(key) + if !ok { + return + } + if p.ValueType() == STOP || p.ValueType() == VOID { + p.valueType = TypeFromValue(value) + } + coercedValue, ok := p.ValueType().CoerceData(value) + if !ok { + return + } + newElem := NewTMapElem(coercedKey, coercedValue) + if !p.KeyType().IsBaseType() { + for elem := p.l.Front(); elem != nil; elem = elem.Next() { + k := elem.Value.(TMapElem).Key() + if cmp, ok := p.KeyType().Compare(coercedKey, k); ok && cmp >= 0 { + if cmp == 0 { + p.l.InsertAfter(newElem, elem) + p.l.Remove(elem) + return + } + p.l.InsertBefore(newElem, elem) + return + } + } + p.l.PushBack(newElem) + return + } + if key == nil { + return + } + switch p.KeyType() { + case STOP, VOID: + // if here, then we don't have a key type yet and key is not nil + // so this is pretty much an empty map + return + case BOOL: + if p.b == nil { + p.b = make(map[bool]interface{}) + } + b := coercedKey.(bool) + p.b[b] = value + case BYTE: + if p.i08 == nil { + p.i08 = make(map[byte]interface{}) + } + b := coercedKey.(byte) + p.i08[b] = value + case DOUBLE: + if p.f64 == nil { + p.f64 = make(map[float64]interface{}) + } + b := coercedKey.(float64) + p.f64[b] = value + case I16: + if p.i16 == nil { + p.i16 = make(map[int16]interface{}) + } + b := coercedKey.(int16) + p.i16[b] = value + case I32: + if p.i32 == nil { + p.i32 = make(map[int32]interface{}) + } + b := coercedKey.(int32) + p.i32[b] = value + case I64: + if p.i64 == nil { + p.i64 = make(map[int64]interface{}) + } + b := coercedKey.(int64) + p.i64[b] = value + case STRING, UTF8, UTF16: + if p.s == nil { + p.s = make(map[string]interface{}) + } + b := coercedKey.(string) + p.s[b] = value + case STRUCT, MAP, SET, LIST: + panic("Should never be here") + default: + panic("Should never be here") + } +} + +func (p *tMap) Contains(key interface{}) bool { + coercedKey, ok := p.KeyType().CoerceData(key) + if !ok { + return false + } + if coercedKey == nil { + for elem := p.l.Front(); elem != nil; elem = elem.Next() { + k := elem.Value.(TMapElem).Key() + if k == nil { + return true + } + } + return false + } + if !ok { + return false + } + switch p.KeyType() { + case STOP: + // if here, then we don't have a key type yet and key is not nil + // so this is pretty much an empty map + return false + case VOID: + // if here, then we don't have a key type yet and key is not nil + // so this is pretty much an empty map + return false + case BOOL: + m := p.b + if m == nil { + return false + } + _, ok := m[coercedKey.(bool)] + return ok + case BYTE: + m := p.i08 + _, ok := m[coercedKey.(byte)] + return ok + case DOUBLE: + m := p.f64 + if m == nil { + return false + } + _, ok := m[coercedKey.(float64)] + return ok + case I16: + m := p.i16 + if m == nil { + return false + } + _, ok := m[coercedKey.(int16)] + return ok + case I32: + m := p.i32 + if m == nil { + return false + } + _, ok := m[coercedKey.(int32)] + return ok + case I64: + m := p.i64 + if m == nil { + return false + } + _, ok := m[coercedKey.(int64)] + return ok + case STRING, UTF8, UTF16: + // TODO(pomack) properly handle ENUM + m := p.s + if m == nil { + return false + } + _, ok := m[coercedKey.(string)] + return ok + case STRUCT: + for elem := p.l.Front(); elem != nil; elem = elem.Next() { + e := elem.Value.(TMapElem) + k := e.Key() + if k == nil { + continue + } + structkey, ok := k.(TStruct) + if ok { + if structkey.Equals(coercedKey.(TStruct)) { + return true + } + continue + } + if reflect.DeepEqual(coercedKey, k) { + return true + } + } + return false + case MAP: + for elem := p.l.Front(); elem != nil; elem = elem.Next() { + e := elem.Value.(TMapElem) + k := e.Key() + if k == nil { + continue + } + mapkey, ok := k.(TMap) + if ok { + if mapkey.Equals(coercedKey.(TMap)) { + return true + } + continue + } + } + return false + case SET: + for elem := p.l.Front(); elem != nil; elem = elem.Next() { + e := elem.Value.(TMapElem) + k := e.Key() + if k == nil { + continue + } + setkey, ok := k.(TSet) + if ok { + if setkey.Equals(coercedKey.(TSet)) { + return true + } + continue + } + } + return false + case LIST: + for elem := p.l.Front(); elem != nil; elem = elem.Next() { + e := elem.Value.(TMapElem) + k := e.Key() + if k == nil { + continue + } + listkey, ok := k.(TList) + if ok { + if listkey.Equals(coercedKey.(TList)) { + return true + } + continue + } + } + return false + default: + panic("Invalid Thrift element type") + } + return false +} + +// Iterate over all elements; driver for range +func (p *tMap) iterate(c chan<- TMapElem) { + switch p.KeyType() { + case STOP, VOID: + close(c) + case BOOL: + for k, v := range p.b { + c <- NewTMapElem(k, v) + } + close(c) + case BYTE: + for k, v := range p.i08 { + c <- NewTMapElem(k, v) + } + close(c) + case I16: + for k, v := range p.i16 { + c <- NewTMapElem(k, v) + } + close(c) + case I32: + for k, v := range p.i32 { + c <- NewTMapElem(k, v) + } + close(c) + case I64: + for k, v := range p.i64 { + c <- NewTMapElem(k, v) + } + close(c) + case DOUBLE: + for k, v := range p.f64 { + c <- NewTMapElem(k, v) + } + close(c) + case STRING, UTF8, UTF16: + for k, v := range p.s { + c <- NewTMapElem(k, v) + } + close(c) + case STRUCT: + for v := p.l.Front(); v != nil; v = v.Next() { + c <- v.Value.(TMapElem) + } + close(c) + case LIST: + for v := p.l.Front(); v != nil; v = v.Next() { + c <- v.Value.(TMapElem) + } + close(c) + case SET: + for v := p.l.Front(); v != nil; v = v.Next() { + c <- v.Value.(TMapElem) + } + close(c) + default: + panic("Invalid Thrift type") + } +} + +// Channel iterator for range. +func (p *tMap) Iter() <-chan TMapElem { + c := make(chan TMapElem) + go p.iterate(c) + return c +} + +// Iterate over all keys; driver for range +func (p *tMap) iterateKeys(c chan<- interface{}) { + switch p.KeyType() { + case STOP, VOID: + close(c) + case BOOL: + for k, _ := range p.b { + c <- k + } + close(c) + case BYTE: + for k, _ := range p.i08 { + c <- k + } + close(c) + case I16: + for k, _ := range p.i16 { + c <- k + } + close(c) + case I32: + for k, _ := range p.i32 { + c <- k + } + close(c) + case I64: + for k, _ := range p.i64 { + c <- k + } + close(c) + case DOUBLE: + for k, _ := range p.f64 { + c <- k + } + close(c) + case STRING, UTF8, UTF16: + for k, _ := range p.s { + c <- k + } + close(c) + case STRUCT: + for v := p.l.Front(); v != nil; v = v.Next() { + c <- v.Value.(TMapElem).Key() + } + close(c) + case LIST: + for v := p.l.Front(); v != nil; v = v.Next() { + c <- v.Value.(TMapElem).Key() + } + close(c) + case SET: + for v := p.l.Front(); v != nil; v = v.Next() { + c <- v.Value.(TMapElem).Key() + } + close(c) + default: + panic("Invalid Thrift type") + } +} + +func (p *tMap) KeyIter() <-chan interface{} { + c := make(chan interface{}) + go p.iterateKeys(c) + return c +} + +// Iterate over all values; driver for range +func (p *tMap) iterateValues(c chan<- interface{}) { + switch p.KeyType() { + case STOP, VOID: + close(c) + case BOOL: + for _, v := range p.b { + c <- v + } + close(c) + case BYTE: + for _, v := range p.i08 { + c <- v + } + close(c) + case I16: + for _, v := range p.i16 { + c <- v + } + close(c) + case I32: + for _, v := range p.i32 { + c <- v + } + close(c) + case I64: + for _, v := range p.i64 { + c <- v + } + close(c) + case DOUBLE: + for _, v := range p.f64 { + c <- v + } + close(c) + case STRING, UTF8, UTF16: + for _, v := range p.s { + c <- v + } + close(c) + case STRUCT: + for v := p.l.Front(); v != nil; v = v.Next() { + c <- v.Value.(TMapElem).Value() + } + close(c) + case LIST: + for v := p.l.Front(); v != nil; v = v.Next() { + c <- v.Value.(TMapElem).Value() + } + close(c) + case SET: + for v := p.l.Front(); v != nil; v = v.Next() { + c <- v.Value.(TMapElem).Value() + } + close(c) + default: + panic("Invalid Thrift type") + } +} + +func (p *tMap) ValueIter() <-chan interface{} { + c := make(chan interface{}) + go p.iterateValues(c) + return c +} + + +func (p *tMap) Less(other interface{}) bool { + cmp, ok := p.CompareTo(other) + return ok && cmp > 0 +} + +func (p *tMap) Equals(other interface{}) bool { + c, cok := p.CompareTo(other) + return cok && c == 0 +} + +func (p *tMap) CompareTo(other interface{}) (int, bool) { + return TType(MAP).Compare(p, other) +} + +func (p *tMap) Keys() []interface{} { + size := p.Len() + values := make([]interface{}, size, size) + i := 0 + for k := range p.KeyIter() { + values[i] = k + i++ + } + return values +} + +func (p *tMap) Values() []interface{} { + size := p.Len() + values := make([]interface{}, size, size) + i := 0 + for v := range p.ValueIter() { + values[i] = v + i++ + } + return values +} diff --git a/lib/go/thrift/tmemory_buffer.go b/lib/go/thrift/tmemory_buffer.go new file mode 100644 index 00000000..69b794e2 --- /dev/null +++ b/lib/go/thrift/tmemory_buffer.go @@ -0,0 +1,127 @@ +/* + * 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. + */ + +package thrift + +import ( + "bytes" + "io" + "os" +) + +/** + * Memory buffer-based implementation of the TTransport interface. + * + */ +type TMemoryBuffer struct { + buf *bytes.Buffer + size int +} + +type TMemoryBufferTransportFactory struct { + size int +} + +func (p *TMemoryBufferTransportFactory) GetTransport(trans TTransport) TTransport { + if trans != nil { + t, ok := trans.(*TMemoryBuffer) + if ok && t.size > 0 { + return NewTMemoryBufferLen(t.size) + } + } + return NewTMemoryBufferLen(p.size) +} + +func NewTMemoryBufferTransportFactory(size int) *TMemoryBufferTransportFactory { + return &TMemoryBufferTransportFactory{size: size} +} + +func NewTMemoryBuffer() *TMemoryBuffer { + return &TMemoryBuffer{buf: &bytes.Buffer{}, size: 0} +} + +func NewTMemoryBufferLen(size int) *TMemoryBuffer { + buf := make([]byte, 0, size) + return &TMemoryBuffer{buf: bytes.NewBuffer(buf), size: size} +} + +func (p *TMemoryBuffer) IsOpen() bool { + return true +} + +func (p *TMemoryBuffer) Open() os.Error { + return nil +} + +func (p *TMemoryBuffer) Peek() bool { + return p.IsOpen() +} + +func (p *TMemoryBuffer) Close() os.Error { + p.buf.Reset() + return nil +} + +func (p *TMemoryBuffer) Read(buf []byte) (int, os.Error) { + return p.buf.Read(buf) +} + +func (p *TMemoryBuffer) ReadAll(buf []byte) (int, os.Error) { + return ReadAllTransport(p, buf) +} + +func (p *TMemoryBuffer) ReadByte() (byte, os.Error) { + return p.buf.ReadByte() +} + +func (p *TMemoryBuffer) ReadFrom(r io.Reader) (int64, os.Error) { + return p.buf.ReadFrom(r) +} + +func (p *TMemoryBuffer) Write(buf []byte) (int, os.Error) { + return p.buf.Write(buf) +} + +func (p *TMemoryBuffer) WriteString(buf string) (int, os.Error) { + return p.buf.WriteString(buf) +} + +func (p *TMemoryBuffer) WriteTo(w io.Writer) (int64, os.Error) { + return p.buf.WriteTo(w) +} + +func (p *TMemoryBuffer) Flush() os.Error { + return nil +} + +func (p *TMemoryBuffer) Reset() { + p.buf.Reset() +} + +func (p *TMemoryBuffer) Bytes() []byte { + return p.buf.Bytes() +} + +func (p *TMemoryBuffer) Len() int { + return p.buf.Len() +} + +func (p *TMemoryBuffer) String() string { + return p.buf.String() +} diff --git a/lib/go/thrift/tmemory_buffer_test.go b/lib/go/thrift/tmemory_buffer_test.go new file mode 100644 index 00000000..5d17864b --- /dev/null +++ b/lib/go/thrift/tmemory_buffer_test.go @@ -0,0 +1,30 @@ +/* + * 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. + */ + +package thrift_test + +import ( + . "thrift" + "testing" +) + +func TestMemoryBuffer(t *testing.T) { + trans := NewTMemoryBufferLen(1024) + TransportTest(t, trans, trans) +} diff --git a/lib/go/thrift/tmessage.go b/lib/go/thrift/tmessage.go new file mode 100644 index 00000000..c768cb4b --- /dev/null +++ b/lib/go/thrift/tmessage.go @@ -0,0 +1,70 @@ +/* + * 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. + */ + +package thrift + +/** + * Helper class that encapsulates struct metadata. + * + */ +type TMessage interface { + Name() string + TypeId() TMessageType + SeqId() int + Equals(other TMessage) bool +} +type tMessage struct { + name string + typeId TMessageType + seqid int +} + +func NewTMessageDefault() TMessage { + return NewTMessage("", STOP, 0) +} + +func NewTMessage(n string, t TMessageType, s int) TMessage { + return &tMessage{name: n, typeId: t, seqid: s} +} + +func (p *tMessage) Name() string { + return p.name +} + +func (p *tMessage) TypeId() TMessageType { + return p.typeId +} + +func (p *tMessage) SeqId() int { + return p.seqid +} + +func (p *tMessage) String() string { + return "" +} + +func (p *tMessage) Equals(other TMessage) bool { + return p.name == other.Name() && p.typeId == other.TypeId() && p.seqid == other.SeqId() +} + +var EMPTY_MESSAGE TMessage + +func init() { + EMPTY_MESSAGE = NewTMessageDefault() +} diff --git a/lib/go/thrift/tmessagetype.go b/lib/go/thrift/tmessagetype.go new file mode 100644 index 00000000..b31c66cb --- /dev/null +++ b/lib/go/thrift/tmessagetype.go @@ -0,0 +1,34 @@ +/* + * 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. + */ + +package thrift + +/** + * Message type constants in the Thrift protocol. + * + */ +type TMessageType int32 + +const ( + INVALID_TMESSAGE_TYPE TMessageType = 0 + CALL TMessageType = 1 + REPLY TMessageType = 2 + EXCEPTION TMessageType = 3 + ONEWAY TMessageType = 4 +) diff --git a/lib/go/thrift/tnonblocking_server.go b/lib/go/thrift/tnonblocking_server.go new file mode 100644 index 00000000..e234c5a5 --- /dev/null +++ b/lib/go/thrift/tnonblocking_server.go @@ -0,0 +1,178 @@ +/* + * 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. + */ + +package thrift + +import ( + "os" +) + +/** + * A nonblocking TServer implementation. This allows for fairness amongst all + * connected clients in terms of invocations. + * + * This server is inherently single-threaded. If you want a limited thread pool + * coupled with invocation-fairness, see THsHaServer. + * + * To use this server, you MUST use a TFramedTransport at the outermost + * transport, otherwise this server will be unable to determine when a whole + * method call has been read off the wire. Clients must also use TFramedTransport. + */ +type TNonblockingServer struct { + /** Flag for stopping the server */ + stopped bool + + processorFactory TProcessorFactory + serverTransport TServerTransport + inputTransportFactory TTransportFactory + outputTransportFactory TTransportFactory + inputProtocolFactory TProtocolFactory + outputProtocolFactory TProtocolFactory +} + + +func NewTNonblockingServer2(processor TProcessor, serverTransport TServerTransport) *TNonblockingServer { + return NewTNonblockingServerFactory2(NewTProcessorFactory(processor), serverTransport) +} + +func NewTNonblockingServer4(processor TProcessor, serverTransport TServerTransport, transportFactory TTransportFactory, protocolFactory TProtocolFactory) *TNonblockingServer { + return NewTNonblockingServerFactory4(NewTProcessorFactory(processor), + serverTransport, + transportFactory, + protocolFactory, + ) +} + +func NewTNonblockingServer6(processor TProcessor, serverTransport TServerTransport, inputTransportFactory TTransportFactory, outputTransportFactory TTransportFactory, inputProtocolFactory TProtocolFactory, outputProtocolFactory TProtocolFactory) *TNonblockingServer { + return NewTNonblockingServerFactory6(NewTProcessorFactory(processor), + serverTransport, + inputTransportFactory, + outputTransportFactory, + inputProtocolFactory, + outputProtocolFactory, + ) +} + +func NewTNonblockingServerFactory2(processorFactory TProcessorFactory, serverTransport TServerTransport) *TNonblockingServer { + return NewTNonblockingServerFactory6(processorFactory, + serverTransport, + NewTTransportFactory(), + NewTTransportFactory(), + NewTBinaryProtocolFactoryDefault(), + NewTBinaryProtocolFactoryDefault(), + ) +} + +func NewTNonblockingServerFactory4(processorFactory TProcessorFactory, serverTransport TServerTransport, transportFactory TTransportFactory, protocolFactory TProtocolFactory) *TNonblockingServer { + return NewTNonblockingServerFactory6(processorFactory, + serverTransport, + transportFactory, + transportFactory, + protocolFactory, + protocolFactory, + ) +} + +func NewTNonblockingServerFactory6(processorFactory TProcessorFactory, serverTransport TServerTransport, inputTransportFactory TTransportFactory, outputTransportFactory TTransportFactory, inputProtocolFactory TProtocolFactory, outputProtocolFactory TProtocolFactory) *TNonblockingServer { + return &TNonblockingServer{processorFactory: processorFactory, + serverTransport: serverTransport, + inputTransportFactory: inputTransportFactory, + outputTransportFactory: outputTransportFactory, + inputProtocolFactory: inputProtocolFactory, + outputProtocolFactory: outputProtocolFactory, + } +} + +func (p *TNonblockingServer) ProcessorFactory() TProcessorFactory { + return p.processorFactory +} + +func (p *TNonblockingServer) ServerTransport() TServerTransport { + return p.serverTransport +} + +func (p *TNonblockingServer) InputTransportFactory() TTransportFactory { + return p.inputTransportFactory +} + +func (p *TNonblockingServer) OutputTransportFactory() TTransportFactory { + return p.outputTransportFactory +} + +func (p *TNonblockingServer) InputProtocolFactory() TProtocolFactory { + return p.inputProtocolFactory +} + +func (p *TNonblockingServer) OutputProtocolFactory() TProtocolFactory { + return p.outputProtocolFactory +} + +func (p *TNonblockingServer) Serve() os.Error { + p.stopped = false + err := p.serverTransport.Listen() + if err != nil { + return err + } + for !p.stopped { + client, err := p.serverTransport.Accept() + if err != nil { + return err + } + if client != nil { + go p.processRequest(client) + } + } + return nil +} + +func (p *TNonblockingServer) Stop() os.Error { + p.stopped = true + p.serverTransport.Interrupt() + return nil +} + +func (p *TNonblockingServer) IsStopped() bool { + return p.stopped +} + +func (p *TNonblockingServer) processRequest(client TTransport) { + processor := p.processorFactory.GetProcessor(client) + inputTransport := p.inputTransportFactory.GetTransport(client) + outputTransport := p.outputTransportFactory.GetTransport(client) + inputProtocol := p.inputProtocolFactory.GetProtocol(inputTransport) + outputProtocol := p.outputProtocolFactory.GetProtocol(outputTransport) + if inputTransport != nil { + defer inputTransport.Close() + } + if outputTransport != nil { + defer outputTransport.Close() + } + for { + ok, e := processor.Process(inputProtocol, outputProtocol) + if e != nil { + if !p.stopped { + // TODO(pomack) log error + break + } + } + if !ok { + break + } + } +} diff --git a/lib/go/thrift/tnonblocking_server_socket.go b/lib/go/thrift/tnonblocking_server_socket.go new file mode 100644 index 00000000..3c9dbaa9 --- /dev/null +++ b/lib/go/thrift/tnonblocking_server_socket.go @@ -0,0 +1,169 @@ +/* + * 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. + */ + +package thrift + +import ( + "net" + "os" +) + +/** + * Socket implementation of the TTransport interface. To be commented soon! + */ +type TNonblockingServerSocket struct { + listener net.Listener + addr net.Addr + /** + * Socket timeout + */ + nsecTimeout int64 +} + +type TNonblockingServerSocketTransportFactory struct { + addr net.Addr +} + +func (p *TNonblockingServerSocketTransportFactory) GetTransport(trans TTransport) TTransport { + if trans != nil { + t, ok := trans.(*TNonblockingServerSocket) + if ok && t.addr != nil { + s, _ := NewTNonblockingServerSocketAddr(t.addr) + s.SetTimeout(t.nsecTimeout) + return s + } + } + s, _ := NewTNonblockingServerSocketAddr(p.addr) + return s +} + +func NewTNonblockingServerSocketTransportFactory(addr net.Addr) *TNonblockingServerSocketTransportFactory { + return &TNonblockingServerSocketTransportFactory{addr: addr} +} + + +func NewTNonblockingServerSocketListener(listener net.Listener) (*TNonblockingServerSocket, TTransportException) { + s := &TNonblockingServerSocket{listener: listener, addr: listener.Addr()} + return s, nil +} + +func NewTNonblockingServerSocketAddr(addr net.Addr) (*TNonblockingServerSocket, TTransportException) { + s := &TNonblockingServerSocket{addr: addr} + return s, nil +} + +func (p *TNonblockingServerSocket) Listen() os.Error { + return p.Open() +} + +/** + * Sets the socket timeout + * + * @param timeout Nanoseconds timeout + */ +func (p *TNonblockingServerSocket) SetTimeout(nsecTimeout int64) os.Error { + p.nsecTimeout = nsecTimeout + return nil +} + +/** + * Checks whether the socket is connected. + */ +func (p *TNonblockingServerSocket) IsOpen() bool { + return p.listener != nil +} + +/** + * Connects the socket, creating a new socket object if necessary. + */ +func (p *TNonblockingServerSocket) Open() os.Error { + if !p.IsOpen() { + l, err := net.Listen(p.addr.Network(), p.addr.String()) + if err != nil { + return err + } + p.listener = l + return nil + } + return NewTTransportException(ALREADY_OPEN, "Server socket already open") +} + +/** + * Perform a nonblocking read into buffer. + */ +func (p *TNonblockingServerSocket) Read(buf []byte) (int, os.Error) { + return 0, NewTTransportException(UNKNOWN_TRANSPORT_EXCEPTION, "TNonblockingServerSocket.Read([]byte) is not implemented") +} + +func (p *TNonblockingServerSocket) ReadAll(buf []byte) (int, os.Error) { + return ReadAllTransport(p, buf) +} + +/** + * Perform a nonblocking write of the data in buffer; + */ +func (p *TNonblockingServerSocket) Write(buf []byte) (int, os.Error) { + return 0, NewTTransportException(UNKNOWN_TRANSPORT_EXCEPTION, "TNonblockingServerSocket.Write([]byte) is not implemented") +} + +/** + * Flushes the underlying output stream if not null. + */ +func (p *TNonblockingServerSocket) Flush() os.Error { + return NewTTransportException(UNKNOWN_TRANSPORT_EXCEPTION, "TNonblockingServerSocket.Flush() is not implemented") +} + +func (p *TNonblockingServerSocket) Addr() net.Addr { + return p.addr +} + +func (p *TNonblockingServerSocket) Accept() (TTransport, os.Error) { + if !p.IsOpen() { + return nil, NewTTransportException(NOT_OPEN, "No underlying server socket") + } + conn, err := p.listener.Accept() + if err != nil { + return nil, NewTTransportExceptionFromOsError(err) + } + conn.SetTimeout(p.nsecTimeout) + return NewTSocketConn(conn) +} + +func (p *TNonblockingServerSocket) Peek() bool { + return p.IsOpen() +} + +/** + * Closes the socket. + */ +func (p *TNonblockingServerSocket) Close() (err os.Error) { + if p.IsOpen() { + err := p.listener.Close() + if err != nil { + return NewTTransportExceptionFromOsError(err) + } + p.listener = nil + } + return nil +} + +func (p *TNonblockingServerSocket) Interrupt() os.Error { + // probably not right + return p.Close() +} diff --git a/lib/go/thrift/tnonblocking_socket.go b/lib/go/thrift/tnonblocking_socket.go new file mode 100644 index 00000000..a1c03107 --- /dev/null +++ b/lib/go/thrift/tnonblocking_socket.go @@ -0,0 +1,192 @@ +/* + * 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. + */ + +package thrift + +import ( + "net" + "os" +) + +/** + * Socket implementation of the TTransport interface. To be commented soon! + */ +type TNonblockingSocket struct { + conn net.Conn + addr net.Addr + /** + * Socket timeout + */ + nsecTimeout int64 +} + +type TNonblockingSocketTransportFactory struct { + addr net.Addr +} + +func (p *TNonblockingSocketTransportFactory) GetTransport(trans TTransport) TTransport { + if trans != nil { + t, ok := trans.(*TNonblockingSocket) + if ok { + s, _ := NewTNonblockingSocketAddr(t.addr) + s.SetTimeout(t.nsecTimeout) + return s + } + } + s, _ := NewTNonblockingSocketAddr(p.addr) + return s +} + +func NewTNonblockingSocketTransportFactory(addr net.Addr) *TNonblockingSocketTransportFactory { + return &TNonblockingSocketTransportFactory{addr: addr} +} + +func NewTNonblockingSocketConn(conn net.Conn) (*TNonblockingSocket, TTransportException) { + s := &TNonblockingSocket{conn: conn, addr: conn.RemoteAddr()} + return s, nil +} + +func NewTNonblockingSocketAddr(addr net.Addr) (*TNonblockingSocket, TTransportException) { + s := &TNonblockingSocket{addr: addr} + return s, nil +} + +/** + * Sets the socket timeout + * + * @param nsecTimeout Nanoseconds timeout + */ +func (p *TNonblockingSocket) SetTimeout(nsecTimeout int64) os.Error { + p.nsecTimeout = nsecTimeout + if p.IsOpen() { + if err := p.conn.SetTimeout(nsecTimeout); err != nil { + LOGGER.Print("Could not set socket timeout.", err) + return err + } + } + return nil +} + +/** + * Checks whether the socket is connected. + */ +func (p *TNonblockingSocket) IsOpen() bool { + return p.conn != nil +} + +/** + * Connects the socket, creating a new socket object if necessary. + */ +func (p *TNonblockingSocket) Open() os.Error { + if p.IsOpen() { + return NewTTransportException(ALREADY_OPEN, "Socket already connected.") + } + if p.addr == nil { + return NewTTransportException(NOT_OPEN, "Cannot open nil address.") + } + if len(p.addr.Network()) == 0 { + return NewTTransportException(NOT_OPEN, "Cannot open bad network name.") + } + if len(p.addr.String()) == 0 { + return NewTTransportException(NOT_OPEN, "Cannot open bad address.") + } + + var err os.Error + if p.conn, err = net.Dial(p.addr.Network(), "", p.addr.String()); err != nil { + LOGGER.Print("Could not open socket", err.String()) + return NewTTransportException(NOT_OPEN, err.String()) + } + if p.conn != nil { + p.conn.SetTimeout(p.nsecTimeout) + } + return nil +} + +/** + * Perform a nonblocking read into buffer. + */ +func (p *TNonblockingSocket) Read(buf []byte) (int, os.Error) { + if !p.IsOpen() { + return 0, NewTTransportException(NOT_OPEN, "Connection not open") + } + n, err := p.conn.Read(buf) + return n, NewTTransportExceptionFromOsError(err) +} + + +func (p *TNonblockingSocket) ReadAll(buf []byte) (int, os.Error) { + return ReadAllTransport(p, buf) +} + +/** + * Perform a nonblocking write of the data in buffer; + */ +func (p *TNonblockingSocket) Write(buf []byte) (int, os.Error) { + if !p.IsOpen() { + return 0, NewTTransportException(NOT_OPEN, "Connection not open") + } + return p.conn.Write(buf) +} + +/** + * Flushes the underlying output stream if not null. + */ +func (p *TNonblockingSocket) Flush() os.Error { + if !p.IsOpen() { + return NewTTransportException(NOT_OPEN, "Connection not open") + } + f, ok := p.conn.(Flusher) + if ok { + err := f.Flush() + if err != nil { + return NewTTransportExceptionFromOsError(err) + } + } + return nil +} + +func (p *TNonblockingSocket) Addr() net.Addr { + return p.addr +} + +func (p *TNonblockingSocket) Peek() bool { + return p.IsOpen() +} + +/** + * Closes the socket. + */ +func (p *TNonblockingSocket) Close() os.Error { + if p.conn != nil { + if err := p.conn.Close(); err != nil { + LOGGER.Print("Could not close socket.", err.String()) + return err + } + p.conn = nil + } + return nil +} + +func (p *TNonblockingSocket) Interrupt() os.Error { + if !p.IsOpen() { + return nil + } + // TODO(pomack) fix Interrupt as it is probably not right + return p.Close() +} diff --git a/lib/go/thrift/tnonblocking_transport.go b/lib/go/thrift/tnonblocking_transport.go new file mode 100644 index 00000000..da9c26dc --- /dev/null +++ b/lib/go/thrift/tnonblocking_transport.go @@ -0,0 +1,24 @@ +/* + * 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. + */ + +package thrift + +type TNonblockingTransport interface { + TTransport +} diff --git a/lib/go/thrift/tnonblocking_transport_test.go b/lib/go/thrift/tnonblocking_transport_test.go new file mode 100644 index 00000000..7d019dbd --- /dev/null +++ b/lib/go/thrift/tnonblocking_transport_test.go @@ -0,0 +1,88 @@ +/* + * 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. + */ + +package thrift_test + +import ( + . "thrift" + "testing" + "net" +) + +func TestNonblockingTransportServerToClient(t *testing.T) { + + addr, err := FindAvailableTCPServerPort(40000) + if err != nil { + t.Fatalf("Unable to find available tcp port addr: %s", err) + } + trans1, err := NewTNonblockingServerSocketAddr(addr) + if err != nil { + t.Fatalf("Unable to setup server socket listener: %s", err) + } + trans1.Open() + trans2, err := NewTNonblockingSocketAddr(addr) + if err != nil { + t.Fatalf("Unable to setup client socket: %s", err) + } + trans1.SetTimeout(10) + trans2.SetTimeout(10) + err = trans2.Open() + if err != nil { + t.Fatalf("Unable to connect client to server: %s", err) + } + s, err := trans1.Accept() + if err != nil { + t.Fatalf("Unable to accept client connection from server: %s", err) + } + //s.SetTimeout(10) + TransportTest(t, NewTFramedTransport(s), NewTFramedTransport(trans2)) + trans1.Close() +} + +func TestNonblockingTransportClientToServer(t *testing.T) { + addr, err := FindAvailableTCPServerPort(40000) + if err != nil { + t.Fatalf("Unable to find available tcp port addr: %s", err) + } + l, err := net.Listen(addr.Network(), addr.String()) + if err != nil { + t.Fatalf("Unable to setup listener: %s", err) + } + trans1, err := NewTNonblockingServerSocketListener(l) + if err != nil { + t.Fatalf("Unable to setup server socket listener: %s", err) + } + trans2, err := NewTNonblockingSocketAddr(l.Addr()) + if err != nil { + t.Fatalf("Unable to setup client socket: %s", err) + } + trans1.SetTimeout(10) + trans2.SetTimeout(10) + err = trans2.Open() + if err != nil { + t.Fatalf("Unable to connect client to server: %s", err) + } + s, err := trans1.Accept() + if err != nil { + t.Fatalf("Unable to accept client connection from server: %s", err) + } + //s.SetTimeout(10) + TransportTest(t, NewTFramedTransport(trans2), NewTFramedTransport(s)) + trans1.Close() +} diff --git a/lib/go/thrift/tnumeric.go b/lib/go/thrift/tnumeric.go new file mode 100644 index 00000000..b1f65084 --- /dev/null +++ b/lib/go/thrift/tnumeric.go @@ -0,0 +1,165 @@ +/* + * 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. + */ + + +package thrift + +import ( + "math" + "strconv" +) + +type Numeric interface { + Int64() int64 + Int32() int32 + Int16() int16 + Byte() byte + Int() int + Float64() float64 + Float32() float32 + String() string + isNull() bool +} + +type numeric struct { + iValue int64 + dValue float64 + sValue string + isNil bool +} + +var ( + INFINITY Numeric + NEGATIVE_INFINITY Numeric + NAN Numeric + ZERO Numeric + NUMERIC_NULL Numeric +) + +func NewNumericFromDouble(dValue float64) Numeric { + if math.IsInf(dValue, 1) { + return INFINITY + } + if math.IsInf(dValue, -1) { + return NEGATIVE_INFINITY + } + if math.IsNaN(dValue) { + return NAN + } + iValue := int64(dValue) + sValue := strconv.Ftoa64(dValue, 'g', 10) + isNil := false + return &numeric{iValue: iValue, dValue: dValue, sValue: sValue, isNil: isNil} +} + +func NewNumericFromI64(iValue int64) Numeric { + dValue := float64(iValue) + sValue := string(iValue) + isNil := false + return &numeric{iValue: iValue, dValue: dValue, sValue: sValue, isNil: isNil} +} + +func NewNumericFromI32(iValue int32) Numeric { + dValue := float64(iValue) + sValue := string(iValue) + isNil := false + return &numeric{iValue: int64(iValue), dValue: dValue, sValue: sValue, isNil: isNil} +} + +func NewNumericFromString(sValue string) Numeric { + if sValue == INFINITY.String() { + return INFINITY + } + if sValue == NEGATIVE_INFINITY.String() { + return NEGATIVE_INFINITY + } + if sValue == NAN.String() { + return NAN + } + iValue, _ := strconv.Atoi64(sValue) + dValue, _ := strconv.Atof64(sValue) + isNil := len(sValue) == 0 + return &numeric{iValue: iValue, dValue: dValue, sValue: sValue, isNil: isNil} +} + +func NewNumericFromJSONString(sValue string, isNull bool) Numeric { + if isNull { + return NewNullNumeric() + } + if sValue == JSON_INFINITY { + return INFINITY + } + if sValue == JSON_NEGATIVE_INFINITY { + return NEGATIVE_INFINITY + } + if sValue == JSON_NAN { + return NAN + } + iValue, _ := strconv.Atoi64(sValue) + dValue, _ := strconv.Atof64(sValue) + return &numeric{iValue: iValue, dValue: dValue, sValue: sValue, isNil: isNull} +} + +func NewNullNumeric() Numeric { + return &numeric{iValue: 0, dValue: 0.0, sValue: "", isNil: true} +} + +func (p *numeric) Int64() int64 { + return p.iValue +} + +func (p *numeric) Int32() int32 { + return int32(p.iValue) +} + +func (p *numeric) Int16() int16 { + return int16(p.iValue) +} + +func (p *numeric) Byte() byte { + return byte(p.iValue) +} + +func (p *numeric) Int() int { + return int(p.iValue) +} + +func (p *numeric) Float64() float64 { + return p.dValue +} + +func (p *numeric) Float32() float32 { + return float32(p.dValue) +} + +func (p *numeric) String() string { + return p.sValue +} + +func (p *numeric) isNull() bool { + return p.isNil +} + +func init() { + INFINITY = &numeric{iValue: 0, dValue: math.Inf(1), sValue: "Infinity", isNil: false} + NEGATIVE_INFINITY = &numeric{iValue: 0, dValue: math.Inf(-1), sValue: "-Infinity", isNil: false} + NAN = &numeric{iValue: 0, dValue: math.NaN(), sValue: "NaN", isNil: false} + ZERO = &numeric{iValue: 0, dValue: 0, sValue: "0", isNil: false} + NUMERIC_NULL = &numeric{iValue: 0, dValue: 0, sValue: "0", isNil: true} +} diff --git a/lib/go/thrift/tprocessor.go b/lib/go/thrift/tprocessor.go new file mode 100644 index 00000000..d2bb5e4b --- /dev/null +++ b/lib/go/thrift/tprocessor.go @@ -0,0 +1,33 @@ +/* + * 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. + */ + +package thrift + +/** + * A processor is a generic object which operates upon an input stream and + * writes to some output stream. + * + */ +type TProcessor interface { + Process(in, out TProtocol) (bool, TException) +} + +type TProcessorFunction interface { + Process(seqId int32, in, out TProtocol) (bool, TException) +} diff --git a/lib/go/thrift/tprocessor_factory.go b/lib/go/thrift/tprocessor_factory.go new file mode 100644 index 00000000..72681ab6 --- /dev/null +++ b/lib/go/thrift/tprocessor_factory.go @@ -0,0 +1,62 @@ +/* + * 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. + */ + +package thrift + + +/** + * The default processor factory just returns a singleton + * instance. + */ +type TProcessorFactory interface { + GetProcessor(trans TTransport) TProcessor +} + +type tProcessorFactory struct { + processor TProcessor +} + +func NewTProcessorFactory(p TProcessor) TProcessorFactory { + return &tProcessorFactory{processor: p} +} + +func (p *tProcessorFactory) GetProcessor(trans TTransport) TProcessor { + return p.processor +} + + +/** + * The default processor factory just returns a singleton + * instance. + */ +type TProcessorFunctionFactory interface { + GetProcessorFunction(trans TTransport) TProcessorFunction +} + +type tProcessorFunctionFactory struct { + processor TProcessorFunction +} + +func NewTProcessorFunctionFactory(p TProcessorFunction) TProcessorFunctionFactory { + return &tProcessorFunctionFactory{processor: p} +} + +func (p *tProcessorFunctionFactory) GetProcessorFunction(trans TTransport) TProcessorFunction { + return p.processor +} diff --git a/lib/go/thrift/tprotocol.go b/lib/go/thrift/tprotocol.go new file mode 100644 index 00000000..dcb27a89 --- /dev/null +++ b/lib/go/thrift/tprotocol.go @@ -0,0 +1,201 @@ +/* + * 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. + */ + +package thrift + + +const ( + VERSION_MASK = 0xffff0000 + VERSION_1 = 0x80010000 +) + +type EmptyInterface interface{} + +type TProtocol interface { + WriteMessageBegin(name string, typeId TMessageType, seqid int32) TProtocolException + WriteMessageEnd() TProtocolException + WriteStructBegin(name string) TProtocolException + WriteStructEnd() TProtocolException + WriteFieldBegin(name string, typeId TType, id int16) TProtocolException + WriteFieldEnd() TProtocolException + WriteFieldStop() TProtocolException + WriteMapBegin(keyType TType, valueType TType, size int) TProtocolException + WriteMapEnd() TProtocolException + WriteListBegin(elemType TType, size int) TProtocolException + WriteListEnd() TProtocolException + WriteSetBegin(elemType TType, size int) TProtocolException + WriteSetEnd() TProtocolException + WriteBool(value bool) TProtocolException + WriteByte(value byte) TProtocolException + WriteI16(value int16) TProtocolException + WriteI32(value int32) TProtocolException + WriteI64(value int64) TProtocolException + WriteDouble(value float64) TProtocolException + WriteString(value string) TProtocolException + WriteBinary(value []byte) TProtocolException + + ReadMessageBegin() (name string, typeId TMessageType, seqid int32, err TProtocolException) + ReadMessageEnd() TProtocolException + ReadStructBegin() (name string, err TProtocolException) + ReadStructEnd() TProtocolException + ReadFieldBegin() (name string, typeId TType, id int16, err TProtocolException) + ReadFieldEnd() TProtocolException + ReadMapBegin() (keyType TType, valueType TType, size int, err TProtocolException) + ReadMapEnd() TProtocolException + ReadListBegin() (elemType TType, size int, err TProtocolException) + ReadListEnd() TProtocolException + ReadSetBegin() (elemType TType, size int, err TProtocolException) + ReadSetEnd() TProtocolException + ReadBool() (value bool, err TProtocolException) + ReadByte() (value byte, err TProtocolException) + ReadI16() (value int16, err TProtocolException) + ReadI32() (value int32, err TProtocolException) + ReadI64() (value int64, err TProtocolException) + ReadDouble() (value float64, err TProtocolException) + ReadString() (value string, err TProtocolException) + ReadBinary() (value []byte, err TProtocolException) + + Skip(fieldType TType) (err TProtocolException) + Flush() (err TProtocolException) + + Transport() TTransport +} + +/** + * The maximum recursive depth the skip() function will traverse before + * throwing a TException. + */ +var ( + MaxSkipDepth = 1<<31 - 1 +) + +/** + * Specifies the maximum recursive depth that the skip function will + * traverse before throwing a TException. This is a global setting, so + * any call to skip in this JVM will enforce this value. + * + * @param depth the maximum recursive depth. A value of 2 would allow + * the skip function to skip a structure or collection with basic children, + * but it would not permit skipping a struct that had a field containing + * a child struct. A value of 1 would only allow skipping of simple + * types and empty structs/collections. + */ +func SetMaxSkipDepth(depth int) { + MaxSkipDepth = depth +} + +/** + * Skips over the next data element from the provided input TProtocol object. + * + * @param prot the protocol object to read from + * @param type the next value will be intepreted as this TType value. + */ +func SkipDefaultDepth(prot TProtocol, typeId TType) (err TProtocolException) { + return Skip(prot, typeId, MaxSkipDepth) +} + +/** + * Skips over the next data element from the provided input TProtocol object. + * + * @param prot the protocol object to read from + * @param type the next value will be intepreted as this TType value. + * @param maxDepth this function will only skip complex objects to this + * recursive depth, to prevent Java stack overflow. + */ +func Skip(self TProtocol, fieldType TType, maxDepth int) (err TProtocolException) { + switch fieldType { + case STOP: + return + case BOOL: + _, err = self.ReadBool() + return + case BYTE: + _, err = self.ReadByte() + return + case I16: + _, err = self.ReadI16() + return + case I32: + _, err = self.ReadI32() + return + case I64: + _, err = self.ReadI64() + return + case DOUBLE: + _, err = self.ReadDouble() + return + case STRING: + _, err = self.ReadString() + return + case STRUCT: + { + _, err = self.ReadStructBegin() + if err != nil { + return + } + for { + _, typeId, _, _ := self.ReadFieldBegin() + if typeId == STOP { + break + } + Skip(self, typeId, maxDepth-1) + self.ReadFieldEnd() + } + return self.ReadStructEnd() + } + case MAP: + { + keyType, valueType, l, err := self.ReadMapBegin() + if err != nil { + return err + } + size := int(l) + for i := 0; i < size; i++ { + Skip(self, keyType, maxDepth-1) + self.Skip(valueType) + } + return self.ReadMapEnd() + } + case SET: + { + elemType, l, err := self.ReadSetBegin() + if err != nil { + return err + } + size := int(l) + for i := 0; i < size; i++ { + Skip(self, elemType, maxDepth-1) + } + return self.ReadSetEnd() + } + case LIST: + { + elemType, l, err := self.ReadListBegin() + if err != nil { + return err + } + size := int(l) + for i := 0; i < size; i++ { + Skip(self, elemType, maxDepth-1) + } + return self.ReadListEnd() + } + } + return nil +} diff --git a/lib/go/thrift/tprotocol_exception.go b/lib/go/thrift/tprotocol_exception.go new file mode 100644 index 00000000..2dac97e2 --- /dev/null +++ b/lib/go/thrift/tprotocol_exception.go @@ -0,0 +1,130 @@ +/* + * 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. + */ + +package thrift + +import ( + "encoding/base64" + "os" +) + +/** + * Protocol exceptions. + * + */ +type TProtocolException interface { + TException + TypeId() int +} + +const ( + UNKNOWN_PROTOCOL_EXCEPTION = 0 + INVALID_DATA = 1 + NEGATIVE_SIZE = 2 + SIZE_LIMIT = 3 + BAD_VERSION = 4 + NOT_IMPLEMENTED = 5 +) + +type tProtocolException struct { + typeId int + message string +} + +func (p *tProtocolException) TypeId() int { + return p.typeId +} + +func (p *tProtocolException) String() string { + return p.message +} + +func NewTProtocolExceptionDefault() TProtocolException { + return NewTProtocolExceptionDefaultType(UNKNOWN_PROTOCOL_EXCEPTION) +} + +func NewTProtocolExceptionDefaultType(t int) TProtocolException { + return NewTProtocolException(t, "") +} + +func NewTProtocolExceptionDefaultString(m string) TProtocolException { + return NewTProtocolException(UNKNOWN_PROTOCOL_EXCEPTION, m) +} + +func NewTProtocolException(t int, m string) TProtocolException { + return &tProtocolException{typeId: t, message: m} +} + +func NewTProtocolExceptionReadField(fieldId int, fieldName string, structName string, e TProtocolException) TProtocolException { + t := e.TypeId() + if t == UNKNOWN_PROTOCOL_EXCEPTION { + t = INVALID_DATA + } + return NewTProtocolException(t, "Unable to read field "+string(fieldId)+" ("+fieldName+") in "+structName+" due to: "+e.String()) +} + +func NewTProtocolExceptionWriteField(fieldId int, fieldName string, structName string, e TProtocolException) TProtocolException { + t := e.TypeId() + if t == UNKNOWN_PROTOCOL_EXCEPTION { + t = INVALID_DATA + } + return NewTProtocolException(t, "Unable to write field "+string(fieldId)+" ("+fieldName+") in "+structName+" due to: "+e.String()) +} + +func NewTProtocolExceptionReadStruct(structName string, e TProtocolException) TProtocolException { + t := e.TypeId() + if t == UNKNOWN_PROTOCOL_EXCEPTION { + t = INVALID_DATA + } + return NewTProtocolException(t, "Unable to read struct "+structName+" due to: "+e.String()) +} + +func NewTProtocolExceptionWriteStruct(structName string, e TProtocolException) TProtocolException { + t := e.TypeId() + if t == UNKNOWN_PROTOCOL_EXCEPTION { + t = INVALID_DATA + } + return NewTProtocolException(t, "Unable to write struct "+structName+" due to: "+e.String()) +} + +func NewTProtocolExceptionFromOsError(e os.Error) TProtocolException { + if e == nil { + return nil + } + if t, ok := e.(TProtocolException); ok { + return t + } + if te, ok := e.(TTransportException); ok { + return NewTProtocolExceptionFromTransportException(te) + } + if _, ok := e.(base64.CorruptInputError); ok { + return NewTProtocolException(INVALID_DATA, e.String()) + } + return NewTProtocolExceptionDefaultString(e.String()) +} + +func NewTProtocolExceptionFromTransportException(e TTransportException) TProtocolException { + if e == nil { + return nil + } + if t, ok := e.(TProtocolException); ok { + return t + } + return NewTProtocolExceptionDefaultString(e.String()) +} diff --git a/lib/go/thrift/tprotocol_factory.go b/lib/go/thrift/tprotocol_factory.go new file mode 100644 index 00000000..2eed2c2c --- /dev/null +++ b/lib/go/thrift/tprotocol_factory.go @@ -0,0 +1,28 @@ +/* + * 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. + */ + +package thrift + +/** + * Factory interface for constructing protocol instances. + * + */ +type TProtocolFactory interface { + GetProtocol(trans TTransport) TProtocol +} diff --git a/lib/go/thrift/tprotocol_test.go b/lib/go/thrift/tprotocol_test.go new file mode 100644 index 00000000..22d6ad63 --- /dev/null +++ b/lib/go/thrift/tprotocol_test.go @@ -0,0 +1,1817 @@ +/* + * 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. + */ + +package thrift_test + +import ( + . "thrift" + "testing" + "http" + "math" + "net" + "io" + "os" + "bytes" + "fmt" +) + +const PROTOCOL_BINARY_DATA_SIZE = 155 + +var ( + data string // test data for writing + protocol_bdata []byte // test data for writing; same as data + BOOL_VALUES []bool + BYTE_VALUES []byte + INT16_VALUES []int16 + INT32_VALUES []int32 + INT64_VALUES []int64 + DOUBLE_VALUES []float64 + STRING_VALUES []string +) + + +func init() { + protocol_bdata = make([]byte, PROTOCOL_BINARY_DATA_SIZE) + for i := 0; i < PROTOCOL_BINARY_DATA_SIZE; i++ { + protocol_bdata[i] = byte((i + 'a') % 255) + } + data = string(protocol_bdata) + BOOL_VALUES = []bool{false, true, false, false, true} + BYTE_VALUES = []byte{117, 0, 1, 32, 127, 128, 255} + INT16_VALUES = []int16{459, 0, 1, -1, -128, 127, 32767, -32768} + INT32_VALUES = []int32{459, 0, 1, -1, -128, 127, 32767, 2147483647, -2147483535} + INT64_VALUES = []int64{459, 0, 1, -1, -128, 127, 32767, 2147483647, -2147483535, 34359738481, -35184372088719, -9223372036854775808, 9223372036854775807} + DOUBLE_VALUES = []float64{459.3, 0.0, -1.0, 1.0, 0.5, 0.3333, 3.14159, 1.537e-38, 1.673e25, 6.02214179e23, -6.02214179e23, INFINITY.Float64(), NEGATIVE_INFINITY.Float64(), NAN.Float64()} + STRING_VALUES = []string{"", "a", "st[uf]f", "st,u:ff with spaces", "stuff\twith\nescape\\characters'...\"lots{of}fun"} +} + +type HTTPEchoServer struct{} + +func (p *HTTPEchoServer) ServeHTTP(w http.ResponseWriter, req *http.Request) { + w.WriteHeader(http.StatusOK) + io.Copy(w, req.Body) +} + +func HttpClientSetupForTest(t *testing.T) (net.Listener, net.Addr) { + addr, err := FindAvailableTCPServerPort(40000) + if err != nil { + t.Fatalf("Unable to find available tcp port addr: %s", err) + } + l, err := net.Listen(addr.Network(), addr.String()) + if err != nil { + t.Fatalf("Unable to setup tcp listener on %s: %s", addr.String(), err) + } + go http.Serve(l, &HTTPEchoServer{}) + return l, addr +} + + +func ReadWriteProtocolTest(t *testing.T, protocolFactory TProtocolFactory) { + buf := bytes.NewBuffer(make([]byte, 0, 1024)) + l, addr := HttpClientSetupForTest(t) + transports := []TTransportFactory{ + NewTMemoryBufferTransportFactory(1024), + NewTIOStreamTransportFactory(buf, buf, true), + NewTFramedTransportFactory(NewTMemoryBufferTransportFactory(1024)), + NewTHttpPostClientTransportFactory("http://" + addr.String()), + } + for _, tf := range transports { + trans := tf.GetTransport(nil) + p := protocolFactory.GetProtocol(trans) + ReadWriteBool(t, p, trans) + trans.Close() + } + for _, tf := range transports { + trans := tf.GetTransport(nil) + p := protocolFactory.GetProtocol(trans) + ReadWriteByte(t, p, trans) + trans.Close() + } + for _, tf := range transports { + trans := tf.GetTransport(nil) + p := protocolFactory.GetProtocol(trans) + ReadWriteI16(t, p, trans) + trans.Close() + } + for _, tf := range transports { + trans := tf.GetTransport(nil) + p := protocolFactory.GetProtocol(trans) + ReadWriteI32(t, p, trans) + trans.Close() + } + for _, tf := range transports { + trans := tf.GetTransport(nil) + p := protocolFactory.GetProtocol(trans) + ReadWriteI64(t, p, trans) + trans.Close() + } + for _, tf := range transports { + trans := tf.GetTransport(nil) + p := protocolFactory.GetProtocol(trans) + ReadWriteDouble(t, p, trans) + trans.Close() + } + for _, tf := range transports { + trans := tf.GetTransport(nil) + p := protocolFactory.GetProtocol(trans) + ReadWriteString(t, p, trans) + trans.Close() + } + for _, tf := range transports { + trans := tf.GetTransport(nil) + p := protocolFactory.GetProtocol(trans) + ReadWriteBinary(t, p, trans) + trans.Close() + } + for _, tf := range transports { + trans := tf.GetTransport(nil) + p := protocolFactory.GetProtocol(trans) + ReadWriteWork(t, p, trans) + trans.Close() + } + for _, tf := range transports { + trans := tf.GetTransport(nil) + p := protocolFactory.GetProtocol(trans) + ReadWriteCalculate(t, p, trans) + trans.Close() + } + + // this test doesn't work in all cases due to EOF issues between + // buffer read and buffer write when using the same bufio for both + //for _, tf := range transports { + // trans := tf.GetTransport(nil) + // p := GetProtocol(trans); + // ReadWriteI64(t, p, trans); + // ReadWriteDouble(t, p, trans); + // ReadWriteBinary(t, p, trans); + // ReadWriteByte(t, p, trans); + // trans.Close() + //} + + l.Close() +} + +func ReadWriteBool(t *testing.T, p TProtocol, trans TTransport) { + thetype := TType(BOOL) + thelen := len(BOOL_VALUES) + err := p.WriteListBegin(thetype, thelen) + if err != nil { + t.Errorf("%s: %T %T %q Error writing list begin: %q", "ReadWriteBool", p, trans, err, thetype) + } + for k, v := range BOOL_VALUES { + err = p.WriteBool(v) + if err != nil { + t.Errorf("%s: %T %T %q Error writing bool in list at index %d: %q", "ReadWriteBool", p, trans, err, k, v) + } + } + p.WriteListEnd() + if err != nil { + t.Errorf("%s: %T %T %q Error writing list end: %q", "ReadWriteBool", p, trans, err, BOOL_VALUES) + } + p.Flush() + thetype2, thelen2, err := p.ReadListBegin() + if err != nil { + t.Errorf("%s: %T %T %q Error reading list: %q", "ReadWriteBool", p, trans, err, BOOL_VALUES) + } + _, ok := p.(*TSimpleJSONProtocol) + if !ok { + if thetype != thetype2 { + t.Errorf("%s: %T %T type %s != type %s", "ReadWriteBool", p, trans, thetype, thetype2) + } + if thelen != thelen2 { + t.Errorf("%s: %T %T len %s != len %s", "ReadWriteBool", p, trans, thelen, thelen2) + } + } + for k, v := range BOOL_VALUES { + value, err := p.ReadBool() + if err != nil { + t.Errorf("%s: %T %T %q Error reading bool at index %d: %q", "ReadWriteBool", p, trans, err, k, v) + } + if v != value { + t.Errorf("%s: index %d %q %q %q != %q", "ReadWriteBool", k, p, trans, v, value) + } + } + err = p.ReadListEnd() + if err != nil { + t.Errorf("%s: %T %T Unable to read list end: %q", "ReadWriteBool", p, trans, err) + } +} + +func ReadWriteByte(t *testing.T, p TProtocol, trans TTransport) { + thetype := TType(BYTE) + thelen := len(BYTE_VALUES) + err := p.WriteListBegin(thetype, thelen) + if err != nil { + t.Errorf("%s: %T %T %q Error writing list begin: %q", "ReadWriteByte", p, trans, err, thetype) + } + for k, v := range BYTE_VALUES { + err = p.WriteByte(v) + if err != nil { + t.Errorf("%s: %T %T %q Error writing byte in list at index %d: %q", "ReadWriteByte", p, trans, err, k, v) + } + } + err = p.WriteListEnd() + if err != nil { + t.Errorf("%s: %T %T %q Error writing list end: %q", "ReadWriteByte", p, trans, err, BYTE_VALUES) + } + err = p.Flush() + if err != nil { + t.Errorf("%s: %T %T %q Error flushing list of bytes: %q", "ReadWriteByte", p, trans, err, BYTE_VALUES) + } + thetype2, thelen2, err := p.ReadListBegin() + if err != nil { + t.Errorf("%s: %T %T %q Error reading list: %q", "ReadWriteByte", p, trans, err, BYTE_VALUES) + } + _, ok := p.(*TSimpleJSONProtocol) + if !ok { + if thetype != thetype2 { + t.Errorf("%s: %T %T type %s != type %s", "ReadWriteByte", p, trans, thetype, thetype2) + } + if thelen != thelen2 { + t.Errorf("%s: %T %T len %s != len %s", "ReadWriteByte", p, trans, thelen, thelen2) + } + } + for k, v := range BYTE_VALUES { + value, err := p.ReadByte() + if err != nil { + t.Errorf("%s: %T %T %q Error reading byte at index %d: %q", "ReadWriteByte", p, trans, err, k, v) + } + if v != value { + t.Errorf("%s: %T %T %d != %d", "ReadWriteByte", p, trans, v, value) + } + } + err = p.ReadListEnd() + if err != nil { + t.Errorf("%s: %T %T Unable to read list end: %q", "ReadWriteByte", p, trans, err) + } +} + +func ReadWriteI16(t *testing.T, p TProtocol, trans TTransport) { + thetype := TType(I16) + thelen := len(INT16_VALUES) + p.WriteListBegin(thetype, thelen) + for _, v := range INT16_VALUES { + p.WriteI16(v) + } + p.WriteListEnd() + p.Flush() + thetype2, thelen2, err := p.ReadListBegin() + if err != nil { + t.Errorf("%s: %T %T %q Error reading list: %q", "ReadWriteI16", p, trans, err, INT16_VALUES) + } + _, ok := p.(*TSimpleJSONProtocol) + if !ok { + if thetype != thetype2 { + t.Errorf("%s: %T %T type %s != type %s", "ReadWriteI16", p, trans, thetype, thetype2) + } + if thelen != thelen2 { + t.Errorf("%s: %T %T len %s != len %s", "ReadWriteI16", p, trans, thelen, thelen2) + } + } + for k, v := range INT16_VALUES { + value, err := p.ReadI16() + if err != nil { + t.Errorf("%s: %T %T %q Error reading int16 at index %d: %q", "ReadWriteI16", p, trans, err, k, v) + } + if v != value { + t.Errorf("%s: %T %T %d != %d", "ReadWriteI16", p, trans, v, value) + } + } + err = p.ReadListEnd() + if err != nil { + t.Errorf("%s: %T %T Unable to read list end: %q", "ReadWriteI16", p, trans, err) + } +} + +func ReadWriteI32(t *testing.T, p TProtocol, trans TTransport) { + thetype := TType(I32) + thelen := len(INT32_VALUES) + p.WriteListBegin(thetype, thelen) + for _, v := range INT32_VALUES { + p.WriteI32(v) + } + p.WriteListEnd() + p.Flush() + thetype2, thelen2, err := p.ReadListBegin() + if err != nil { + t.Errorf("%s: %T %T %q Error reading list: %q", "ReadWriteI32", p, trans, err, INT32_VALUES) + } + _, ok := p.(*TSimpleJSONProtocol) + if !ok { + if thetype != thetype2 { + t.Errorf("%s: %T %T type %s != type %s", "ReadWriteI32", p, trans, thetype, thetype2) + } + if thelen != thelen2 { + t.Errorf("%s: %T %T len %s != len %s", "ReadWriteI32", p, trans, thelen, thelen2) + } + } + for k, v := range INT32_VALUES { + value, err := p.ReadI32() + if err != nil { + t.Errorf("%s: %T %T %q Error reading int32 at index %d: %q", "ReadWriteI32", p, trans, err, k, v) + } + if v != value { + t.Errorf("%s: %T %T %d != %d", "ReadWriteI32", p, trans, v, value) + } + } + if err != nil { + t.Errorf("%s: %T %T Unable to read list end: %q", "ReadWriteI32", p, trans, err) + } +} + +func ReadWriteI64(t *testing.T, p TProtocol, trans TTransport) { + thetype := TType(I64) + thelen := len(INT64_VALUES) + p.WriteListBegin(thetype, thelen) + for _, v := range INT64_VALUES { + p.WriteI64(v) + } + p.WriteListEnd() + p.Flush() + thetype2, thelen2, err := p.ReadListBegin() + if err != nil { + t.Errorf("%s: %T %T %q Error reading list: %q", "ReadWriteI64", p, trans, err, INT64_VALUES) + } + _, ok := p.(*TSimpleJSONProtocol) + if !ok { + if thetype != thetype2 { + t.Errorf("%s: %T %T type %s != type %s", "ReadWriteI64", p, trans, thetype, thetype2) + } + if thelen != thelen2 { + t.Errorf("%s: %T %T len %s != len %s", "ReadWriteI64", p, trans, thelen, thelen2) + } + } + for k, v := range INT64_VALUES { + value, err := p.ReadI64() + if err != nil { + t.Errorf("%s: %T %T %q Error reading int64 at index %d: %q", "ReadWriteI64", p, trans, err, k, v) + } + if v != value { + t.Errorf("%s: %T %T %q != %q", "ReadWriteI64", p, trans, v, value) + } + } + if err != nil { + t.Errorf("%s: %T %T Unable to read list end: %q", "ReadWriteI64", p, trans, err) + } +} + +func ReadWriteDouble(t *testing.T, p TProtocol, trans TTransport) { + thetype := TType(DOUBLE) + thelen := len(DOUBLE_VALUES) + p.WriteListBegin(thetype, thelen) + for _, v := range DOUBLE_VALUES { + p.WriteDouble(v) + } + p.WriteListEnd() + p.Flush() + wrotebuffer := "" + if memtrans, ok := trans.(*TMemoryBuffer); ok { + wrotebuffer = memtrans.String() + } + thetype2, thelen2, err := p.ReadListBegin() + if err != nil { + t.Errorf("%s: %T %T %q Error reading list: %q, wrote: %v", "ReadWriteDouble", p, trans, err, DOUBLE_VALUES, wrotebuffer) + } + if thetype != thetype2 { + t.Errorf("%s: %T %T type %s != type %s", "ReadWriteDouble", p, trans, thetype, thetype2) + } + if thelen != thelen2 { + t.Errorf("%s: %T %T len %s != len %s", "ReadWriteDouble", p, trans, thelen, thelen2) + } + for k, v := range DOUBLE_VALUES { + value, err := p.ReadDouble() + if err != nil { + t.Errorf("%s: %T %T %q Error reading double at index %d: %q", "ReadWriteDouble", p, trans, err, k, v) + } + if math.IsNaN(v) { + if !math.IsNaN(value) { + t.Errorf("%s: %T %T math.IsNaN(%q) != math.IsNaN(%q)", "ReadWriteDouble", p, trans, v, value) + } + } else if v != value { + t.Errorf("%s: %T %T %v != %q", "ReadWriteDouble", p, trans, v, value) + } + } + err = p.ReadListEnd() + if err != nil { + t.Errorf("%s: %T %T Unable to read list end: %q", "ReadWriteDouble", p, trans, err) + } +} + +func ReadWriteString(t *testing.T, p TProtocol, trans TTransport) { + thetype := TType(STRING) + thelen := len(STRING_VALUES) + p.WriteListBegin(thetype, thelen) + for _, v := range STRING_VALUES { + p.WriteString(v) + } + p.WriteListEnd() + p.Flush() + thetype2, thelen2, err := p.ReadListBegin() + if err != nil { + t.Errorf("%s: %T %T %q Error reading list: %q", "ReadWriteString", p, trans, err, STRING_VALUES) + } + _, ok := p.(*TSimpleJSONProtocol) + if !ok { + if thetype != thetype2 { + t.Errorf("%s: %T %T type %s != type %s", "ReadWriteString", p, trans, thetype, thetype2) + } + if thelen != thelen2 { + t.Errorf("%s: %T %T len %s != len %s", "ReadWriteString", p, trans, thelen, thelen2) + } + } + for k, v := range STRING_VALUES { + value, err := p.ReadString() + if err != nil { + t.Errorf("%s: %T %T %q Error reading string at index %d: %q", "ReadWriteString", p, trans, err, k, v) + } + if v != value { + t.Errorf("%s: %T %T %d != %d", "ReadWriteString", p, trans, v, value) + } + } + if err != nil { + t.Errorf("%s: %T %T Unable to read list end: %q", "ReadWriteString", p, trans, err) + } +} + + +func ReadWriteBinary(t *testing.T, p TProtocol, trans TTransport) { + v := protocol_bdata + p.WriteBinary(v) + p.Flush() + value, err := p.ReadBinary() + if err != nil { + t.Errorf("%s: %T %T Unable to read binary: %s", "ReadWriteBinary", p, trans, err.String()) + } + if len(v) != len(value) { + t.Errorf("%s: %T %T len(v) != len(value)... %d != %d", "ReadWriteBinary", p, trans, len(v), len(value)) + } else { + for i := 0; i < len(v); i++ { + if v[i] != value[i] { + t.Errorf("%s: %T %T %s != %s", "ReadWriteBinary", p, trans, v, value) + } + } + } +} + + +func ReadWriteWork(t *testing.T, p TProtocol, trans TTransport) { + thetype := "struct" + orig := NewWork() + orig.Num1 = 25 + orig.Num2 = 102 + orig.Op = ADD + orig.Comment = "Add: 25 + 102" + return + if e := orig.Write(p); e != nil { + t.Fatalf("Unable to write %s value %#v due to error: %s", thetype, orig, e.String()) + } + read := NewWork() + e := read.Read(p) + if e != nil { + t.Fatalf("Unable to read %s due to error: %s", thetype, e.String()) + } + if !orig.Equals(read) { + t.Fatalf("Original Write != Read: %#v != %#v ", orig, read) + } +} + + +func ReadWriteCalculate(t *testing.T, p TProtocol, trans TTransport) { + messageName := "calculate" + logid := int32(12) + seqId := int32(35) + w := NewWork() + w.Num1 = 25 + w.Num2 = 102 + w.Op = ADD + w.Comment = "Add: 25 + 102" + + args31 := NewCalculateArgs() + args31.Logid = logid + args31.W = w + p.WriteMessageBegin(messageName, CALL, seqId) + if err := args31.Write(p); err != nil { + t.Fatalf("%s: %T %T Unable to write message: %s", messageName, p, trans, err.String()) + } + p.WriteMessageEnd() + p.Transport().Flush() + + name, ttype, seqid, err1 := p.ReadMessageBegin() + if err1 != nil { + t.Fatalf("%s: %T %T Unable to read message begin: %s", messageName, p, trans, err1.String()) + } + if name != messageName { + t.Errorf("%s: %T %T Expected message named \"%s\", but was: \"%s\"", messageName, p, trans, messageName, name) + } + if ttype != CALL { + t.Errorf("%s: %T %T Expected message type \"%s\", but was: \"%s\"", messageName, p, trans, CALL, ttype) + } + if seqid != seqId { + t.Errorf("%s: %T %T Expected message type \"%s\", but was: \"%s\"", messageName, p, trans, seqId, seqid) + } + calcArgs := NewCalculateArgs() + err2 := calcArgs.Read(p) + if !args31.Equals(calcArgs) { + //cmp1, _ := args31.W.CompareTo(calcArgs.W) + cmp2, ok := args31.CompareTo(calcArgs) + t.Errorf("%s: %T %T Calculate args not as expected, %T vs %T, cmp: %#v, ok: %#v, equals: %#v", messageName, p, trans, args31, calcArgs, cmp2, ok, args31.Equals(calcArgs)) + } + if err2 != nil { + t.Fatalf("%s: %T %T Unable to read message end: %s", messageName, p, trans, err2.String()) + } + err3 := p.ReadMessageEnd() + if err3 != nil { + t.Fatalf("%s: %T %T Unable to read message end: %s", messageName, p, trans, err3.String()) + } +} + + +/** + *You can define enums, which are just 32 bit integers. Values are optional + *and start at 1 if not supplied, C style again. + */ +type Operation int + +const ( + ADD Operation = 1 + SUBTRACT Operation = 2 + MULTIPLY Operation = 3 + DIVIDE Operation = 4 +) + +func (p Operation) String() string { + switch p { + case ADD: + return "ADD" + case SUBTRACT: + return "SUBTRACT" + case MULTIPLY: + return "MULTIPLY" + case DIVIDE: + return "DIVIDE" + } + return "" +} + +func FromOperationString(s string) Operation { + switch s { + case "ADD": + return ADD + case "SUBTRACT": + return SUBTRACT + case "MULTIPLY": + return MULTIPLY + case "DIVIDE": + return DIVIDE + } + return Operation(-10000) +} + +func (p Operation) Value() int { + return int(p) +} + +func (p Operation) IsEnum() bool { + return true +} + +/** + *Thrift lets you do typedefs to get pretty names for your types. Standard + *C style here. + */ +type MyInteger int32 + +const INT32CONSTANT = 9853 + +var MAPCONSTANT TMap +/** + * Structs are the basic complex data structures. They are comprised of fields + * which each have an integer identifier, a type, a symbolic name, and an + * optional default value. + * + * Fields can be declared "optional", which ensures they will not be included + * in the serialized output if they aren't set. Note that this requires some + * manual management in some languages. + * + * Attributes: + * - Num1 + * - Num2 + * - Op + * - Comment + */ +type Work struct { + TStruct + _ interface{} "num1" // nil # 0 + Num1 int32 "num1" // 1 + Num2 int32 "num2" // 2 + Op Operation "op" // 3 + Comment string "comment" // 4 +} + +func NewWork() *Work { + output := &Work{ + TStruct: NewTStruct("Work", []TField{ + NewTField("num1", I32, 1), + NewTField("num2", I32, 2), + NewTField("op", I32, 3), + NewTField("comment", STRING, 4), + }), + } + { + output.Num1 = 0 + } + return output +} + +func (p *Work) Read(iprot TProtocol) (err TProtocolException) { + _, err = iprot.ReadStructBegin() + if err != nil { + return NewTProtocolExceptionReadStruct(p.ThriftName(), err) + } + for { + fieldName, fieldTypeId, fieldId, err := iprot.ReadFieldBegin() + if fieldId < 0 { + fieldId = int16(p.FieldIdFromFieldName(fieldName)) + } else if fieldName == "" { + fieldName = p.FieldNameFromFieldId(int(fieldId)) + } + if fieldTypeId == GENERIC { + fieldTypeId = p.FieldFromFieldId(int(fieldId)).TypeId() + } + if err != nil { + return NewTProtocolExceptionReadField(int(fieldId), fieldName, p.ThriftName(), err) + } + if fieldTypeId == STOP { + break + } + if fieldId == 1 || fieldName == "num1" { + if fieldTypeId == I32 { + err = p.ReadField1(iprot) + if err != nil { + return NewTProtocolExceptionReadField(int(fieldId), fieldName, p.ThriftName(), err) + } + } else if fieldTypeId == VOID { + err = iprot.Skip(fieldTypeId) + if err != nil { + return NewTProtocolExceptionReadField(int(fieldId), fieldName, p.ThriftName(), err) + } + } else { + err = p.ReadField1(iprot) + if err != nil { + return NewTProtocolExceptionReadField(int(fieldId), fieldName, p.ThriftName(), err) + } + } + } else if fieldId == 2 || fieldName == "num2" { + if fieldTypeId == I32 { + err = p.ReadField2(iprot) + if err != nil { + return NewTProtocolExceptionReadField(int(fieldId), fieldName, p.ThriftName(), err) + } + } else if fieldTypeId == VOID { + err = iprot.Skip(fieldTypeId) + if err != nil { + return NewTProtocolExceptionReadField(int(fieldId), fieldName, p.ThriftName(), err) + } + } else { + err = p.ReadField2(iprot) + if err != nil { + return NewTProtocolExceptionReadField(int(fieldId), fieldName, p.ThriftName(), err) + } + } + } else if fieldId == 3 || fieldName == "op" { + if fieldTypeId == I32 { + err = p.ReadField3(iprot) + if err != nil { + return NewTProtocolExceptionReadField(int(fieldId), fieldName, p.ThriftName(), err) + } + } else if fieldTypeId == VOID { + err = iprot.Skip(fieldTypeId) + if err != nil { + return NewTProtocolExceptionReadField(int(fieldId), fieldName, p.ThriftName(), err) + } + } else { + err = p.ReadField3(iprot) + if err != nil { + return NewTProtocolExceptionReadField(int(fieldId), fieldName, p.ThriftName(), err) + } + } + } else if fieldId == 4 || fieldName == "comment" { + if fieldTypeId == STRING { + err = p.ReadField4(iprot) + if err != nil { + return NewTProtocolExceptionReadField(int(fieldId), fieldName, p.ThriftName(), err) + } + } else if fieldTypeId == VOID { + err = iprot.Skip(fieldTypeId) + if err != nil { + return NewTProtocolExceptionReadField(int(fieldId), fieldName, p.ThriftName(), err) + } + } else { + err = p.ReadField4(iprot) + if err != nil { + return NewTProtocolExceptionReadField(int(fieldId), fieldName, p.ThriftName(), err) + } + } + } else { + err = iprot.Skip(fieldTypeId) + if err != nil { + return NewTProtocolExceptionReadField(int(fieldId), fieldName, p.ThriftName(), err) + } + } + err = iprot.ReadFieldEnd() + if err != nil { + return NewTProtocolExceptionReadField(int(fieldId), fieldName, p.ThriftName(), err) + } + } + err = iprot.ReadStructEnd() + if err != nil { + return NewTProtocolExceptionReadStruct(p.ThriftName(), err) + } + return err +} + +func (p *Work) ReadField1(iprot TProtocol) (err TProtocolException) { + v4, err5 := iprot.ReadI32() + if err5 != nil { + return NewTProtocolExceptionReadField(1, "num1", p.ThriftName(), err5) + } + p.Num1 = v4 + return err +} + +func (p *Work) ReadFieldNum1(iprot TProtocol) TProtocolException { + return p.ReadField1(iprot) +} + +func (p *Work) ReadField2(iprot TProtocol) (err TProtocolException) { + v6, err7 := iprot.ReadI32() + if err7 != nil { + return NewTProtocolExceptionReadField(2, "num2", p.ThriftName(), err7) + } + p.Num2 = v6 + return err +} + +func (p *Work) ReadFieldNum2(iprot TProtocol) TProtocolException { + return p.ReadField2(iprot) +} + +func (p *Work) ReadField3(iprot TProtocol) (err TProtocolException) { + v8, err9 := iprot.ReadI32() + if err9 != nil { + return NewTProtocolExceptionReadField(3, "op", p.ThriftName(), err9) + } + p.Op = Operation(v8) + return err +} + +func (p *Work) ReadFieldOp(iprot TProtocol) TProtocolException { + return p.ReadField3(iprot) +} + +func (p *Work) ReadField4(iprot TProtocol) (err TProtocolException) { + v10, err11 := iprot.ReadString() + if err11 != nil { + return NewTProtocolExceptionReadField(4, "comment", p.ThriftName(), err11) + } + p.Comment = v10 + return err +} + +func (p *Work) ReadFieldComment(iprot TProtocol) TProtocolException { + return p.ReadField4(iprot) +} + +func (p *Work) Write(oprot TProtocol) (err TProtocolException) { + err = oprot.WriteStructBegin("Work") + if err != nil { + return NewTProtocolExceptionWriteStruct(p.ThriftName(), err) + } + err = p.WriteField1(oprot) + if err != nil { + return err + } + err = p.WriteField2(oprot) + if err != nil { + return err + } + err = p.WriteField3(oprot) + if err != nil { + return err + } + err = p.WriteField4(oprot) + if err != nil { + return err + } + err = oprot.WriteFieldStop() + if err != nil { + return NewTProtocolExceptionWriteField(-1, "STOP", p.ThriftName(), err) + } + err = oprot.WriteStructEnd() + if err != nil { + return NewTProtocolExceptionWriteStruct(p.ThriftName(), err) + } + return err +} + +func (p *Work) WriteField1(oprot TProtocol) (err TProtocolException) { + err = oprot.WriteFieldBegin("num1", I32, 1) + if err != nil { + return NewTProtocolExceptionWriteField(1, "num1", p.ThriftName(), err) + } + err = oprot.WriteI32(int32(p.Num1)) + if err != nil { + return NewTProtocolExceptionWriteField(1, "num1", p.ThriftName(), err) + } + err = oprot.WriteFieldEnd() + if err != nil { + return NewTProtocolExceptionWriteField(1, "num1", p.ThriftName(), err) + } + return err +} + +func (p *Work) WriteFieldNum1(oprot TProtocol) TProtocolException { + return p.WriteField1(oprot) +} + +func (p *Work) WriteField2(oprot TProtocol) (err TProtocolException) { + err = oprot.WriteFieldBegin("num2", I32, 2) + if err != nil { + return NewTProtocolExceptionWriteField(2, "num2", p.ThriftName(), err) + } + err = oprot.WriteI32(int32(p.Num2)) + if err != nil { + return NewTProtocolExceptionWriteField(2, "num2", p.ThriftName(), err) + } + err = oprot.WriteFieldEnd() + if err != nil { + return NewTProtocolExceptionWriteField(2, "num2", p.ThriftName(), err) + } + return err +} + +func (p *Work) WriteFieldNum2(oprot TProtocol) TProtocolException { + return p.WriteField2(oprot) +} + +func (p *Work) WriteField3(oprot TProtocol) (err TProtocolException) { + err = oprot.WriteFieldBegin("op", I32, 3) + if err != nil { + return NewTProtocolExceptionWriteField(3, "op", p.ThriftName(), err) + } + err = oprot.WriteI32(int32(p.Op)) + if err != nil { + return NewTProtocolExceptionWriteField(3, "op", p.ThriftName(), err) + } + err = oprot.WriteFieldEnd() + if err != nil { + return NewTProtocolExceptionWriteField(3, "op", p.ThriftName(), err) + } + return err +} + +func (p *Work) WriteFieldOp(oprot TProtocol) TProtocolException { + return p.WriteField3(oprot) +} + +func (p *Work) WriteField4(oprot TProtocol) (err TProtocolException) { + err = oprot.WriteFieldBegin("comment", STRING, 4) + if err != nil { + return NewTProtocolExceptionWriteField(4, "comment", p.ThriftName(), err) + } + err = oprot.WriteString(string(p.Comment)) + if err != nil { + return NewTProtocolExceptionWriteField(4, "comment", p.ThriftName(), err) + } + err = oprot.WriteFieldEnd() + if err != nil { + return NewTProtocolExceptionWriteField(4, "comment", p.ThriftName(), err) + } + return err +} + +func (p *Work) WriteFieldComment(oprot TProtocol) TProtocolException { + return p.WriteField4(oprot) +} + +func (p *Work) TStructName() string { + return "Work" +} + +func (p *Work) ThriftName() string { + return "Work" +} + +func (p *Work) String() string { + if p == nil { + return "" + } + return fmt.Sprintf("Work(%+v)", *p) +} + +func (p *Work) CompareTo(other interface{}) (int, bool) { + if other == nil { + return 1, true + } + data, ok := other.(*Work) + if !ok { + return 0, false + } + if p.Num1 != data.Num1 { + if p.Num1 < data.Num1 { + return -1, true + } + return 1, true + } + if p.Num2 != data.Num2 { + if p.Num2 < data.Num2 { + return -1, true + } + return 1, true + } + if p.Op != data.Op { + if p.Op < data.Op { + return -1, true + } + return 1, true + } + if p.Comment != data.Comment { + if p.Comment < data.Comment { + return -1, true + } + return 1, true + } + return 0, true +} + +func (p *Work) AttributeByFieldId(id int) interface{} { + switch id { + default: + return nil + case 1: + return p.Num1 + case 2: + return p.Num2 + case 3: + return p.Op + case 4: + return p.Comment + } + return nil +} + +func (p *Work) TStructFields() TFieldContainer { + return NewTFieldContainer([]TField{ + NewTField("num1", I32, 1), + NewTField("num2", I32, 2), + NewTField("op", I32, 3), + NewTField("comment", STRING, 4), + }) +} + + +type ICalculator interface { + /** + * Parameters: + * - Key + */ + Calculate(logid int32, w *Work) (retval30 int32, ouch *InvalidOperation, err os.Error) +} + +type CalculatorClient struct { + Transport TTransport + ProtocolFactory TProtocolFactory + InputProtocol TProtocol + OutputProtocol TProtocol + SeqId int32 +} + +func NewCalculatorClientFactory(t TTransport, f TProtocolFactory) *CalculatorClient { + return &CalculatorClient{Transport: t, + ProtocolFactory: f, + InputProtocol: f.GetProtocol(t), + OutputProtocol: f.GetProtocol(t), + SeqId: 0, + } +} + +func NewCalculatorClientProtocol(t TTransport, iprot TProtocol, oprot TProtocol) *CalculatorClient { + return &CalculatorClient{Transport: t, + ProtocolFactory: nil, + InputProtocol: iprot, + OutputProtocol: oprot, + SeqId: 0, + } +} + + +/** + * Parameters: + * - Logid + * - W + */ +func (p *CalculatorClient) Calculate(logid int32, w *Work) (retval30 int32, ouch *InvalidOperation, err os.Error) { + err = p.SendCalculate(logid, w) + if err != nil { + return + } + return p.RecvCalculate() +} + +func (p *CalculatorClient) SendCalculate(logid int32, w *Work) (err os.Error) { + oprot := p.OutputProtocol + if oprot != nil { + oprot = p.ProtocolFactory.GetProtocol(p.Transport) + p.OutputProtocol = oprot + } + oprot.WriteMessageBegin("calculate", CALL, p.SeqId) + args31 := NewCalculateArgs() + args31.Logid = logid + args31.W = w + err = args31.Write(oprot) + oprot.WriteMessageEnd() + oprot.Transport().Flush() + return +} + + +func (p *CalculatorClient) RecvCalculate() (value int32, ouch *InvalidOperation, err os.Error) { + iprot := p.InputProtocol + if iprot == nil { + iprot = p.ProtocolFactory.GetProtocol(p.Transport) + p.InputProtocol = iprot + } + _, mTypeId, _, err := iprot.ReadMessageBegin() + if err != nil { + return + } + if mTypeId == EXCEPTION { + error33 := NewTApplicationExceptionDefault() + error34, err := error33.Read(iprot) + if err != nil { + return + } + if err = iprot.ReadMessageEnd(); err != nil { + return + } + err = error34 + return + } + result32 := NewCalculateResult() + err = result32.Read(iprot) + iprot.ReadMessageEnd() + value = result32.Success + if result32.Ouch != nil { + ouch = result32.Ouch + } + return +} + + +/** + * Attributes: + * - Logid + * - W + */ +type CalculateArgs struct { + TStruct + _ interface{} "logid" // nil # 0 + Logid int32 "logid" // 1 + W *Work "w" // 2 +} + +func NewCalculateArgs() *CalculateArgs { + output := &CalculateArgs{ + TStruct: NewTStruct("calculate_args", []TField{ + NewTField("logid", I32, 1), + NewTField("w", STRUCT, 2), + }), + } + { + } + return output +} + +func (p *CalculateArgs) Read(iprot TProtocol) (err TProtocolException) { + _, err = iprot.ReadStructBegin() + if err != nil { + return NewTProtocolExceptionReadStruct(p.ThriftName(), err) + } + for { + fieldName, fieldTypeId, fieldId, err := iprot.ReadFieldBegin() + if fieldId < 0 { + fieldId = int16(p.FieldIdFromFieldName(fieldName)) + } else if fieldName == "" { + fieldName = p.FieldNameFromFieldId(int(fieldId)) + } + if fieldTypeId == GENERIC { + fieldTypeId = p.FieldFromFieldId(int(fieldId)).TypeId() + } + if err != nil { + return NewTProtocolExceptionReadField(int(fieldId), fieldName, p.ThriftName(), err) + } + if fieldTypeId == STOP { + break + } + if fieldId == 1 || fieldName == "logid" { + if fieldTypeId == I32 { + err = p.ReadField1(iprot) + if err != nil { + return NewTProtocolExceptionReadField(int(fieldId), fieldName, p.ThriftName(), err) + } + } else if fieldTypeId == VOID { + err = iprot.Skip(fieldTypeId) + if err != nil { + return NewTProtocolExceptionReadField(int(fieldId), fieldName, p.ThriftName(), err) + } + } else { + err = p.ReadField1(iprot) + if err != nil { + return NewTProtocolExceptionReadField(int(fieldId), fieldName, p.ThriftName(), err) + } + } + } else if fieldId == 2 || fieldName == "w" { + if fieldTypeId == STRUCT { + err = p.ReadField2(iprot) + if err != nil { + return NewTProtocolExceptionReadField(int(fieldId), fieldName, p.ThriftName(), err) + } + } else if fieldTypeId == VOID { + err = iprot.Skip(fieldTypeId) + if err != nil { + return NewTProtocolExceptionReadField(int(fieldId), fieldName, p.ThriftName(), err) + } + } else { + err = p.ReadField2(iprot) + if err != nil { + return NewTProtocolExceptionReadField(int(fieldId), fieldName, p.ThriftName(), err) + } + } + } else { + err = iprot.Skip(fieldTypeId) + if err != nil { + return NewTProtocolExceptionReadField(int(fieldId), fieldName, p.ThriftName(), err) + } + } + err = iprot.ReadFieldEnd() + if err != nil { + return NewTProtocolExceptionReadField(int(fieldId), fieldName, p.ThriftName(), err) + } + } + err = iprot.ReadStructEnd() + if err != nil { + return NewTProtocolExceptionReadStruct(p.ThriftName(), err) + } + return err +} + +func (p *CalculateArgs) ReadField1(iprot TProtocol) (err TProtocolException) { + v47, err48 := iprot.ReadI32() + if err48 != nil { + return NewTProtocolExceptionReadField(1, "logid", p.ThriftName(), err48) + } + p.Logid = v47 + return err +} + +func (p *CalculateArgs) ReadFieldLogid(iprot TProtocol) TProtocolException { + return p.ReadField1(iprot) +} + +func (p *CalculateArgs) ReadField2(iprot TProtocol) (err TProtocolException) { + p.W = NewWork() + err51 := p.W.Read(iprot) + if err51 != nil { + return NewTProtocolExceptionReadStruct("p.WWork", err51) + } + return err +} + +func (p *CalculateArgs) ReadFieldW(iprot TProtocol) TProtocolException { + return p.ReadField2(iprot) +} + +func (p *CalculateArgs) Write(oprot TProtocol) (err TProtocolException) { + err = oprot.WriteStructBegin("calculate_args") + if err != nil { + return NewTProtocolExceptionWriteStruct(p.ThriftName(), err) + } + err = p.WriteField1(oprot) + if err != nil { + return err + } + err = p.WriteField2(oprot) + if err != nil { + return err + } + err = oprot.WriteFieldStop() + if err != nil { + return NewTProtocolExceptionWriteField(-1, "STOP", p.ThriftName(), err) + } + err = oprot.WriteStructEnd() + if err != nil { + return NewTProtocolExceptionWriteStruct(p.ThriftName(), err) + } + return err +} + +func (p *CalculateArgs) WriteField1(oprot TProtocol) (err TProtocolException) { + err = oprot.WriteFieldBegin("logid", I32, 1) + if err != nil { + return NewTProtocolExceptionWriteField(1, "logid", p.ThriftName(), err) + } + err = oprot.WriteI32(int32(p.Logid)) + if err != nil { + return NewTProtocolExceptionWriteField(1, "logid", p.ThriftName(), err) + } + err = oprot.WriteFieldEnd() + if err != nil { + return NewTProtocolExceptionWriteField(1, "logid", p.ThriftName(), err) + } + return err +} + +func (p *CalculateArgs) WriteFieldLogid(oprot TProtocol) TProtocolException { + return p.WriteField1(oprot) +} + +func (p *CalculateArgs) WriteField2(oprot TProtocol) (err TProtocolException) { + if p.W != nil { + err = oprot.WriteFieldBegin("w", STRUCT, 2) + if err != nil { + return NewTProtocolExceptionWriteField(2, "w", p.ThriftName(), err) + } + err = p.W.Write(oprot) + if err != nil { + return NewTProtocolExceptionWriteStruct("Work", err) + } + err = oprot.WriteFieldEnd() + if err != nil { + return NewTProtocolExceptionWriteField(2, "w", p.ThriftName(), err) + } + } + return err +} + +func (p *CalculateArgs) WriteFieldW(oprot TProtocol) TProtocolException { + return p.WriteField2(oprot) +} + +func (p *CalculateArgs) TStructName() string { + return "CalculateArgs" +} + +func (p *CalculateArgs) ThriftName() string { + return "calculate_args" +} + +func (p *CalculateArgs) String() string { + if p == nil { + return "" + } + return fmt.Sprintf("CalculateArgs(%+v)", *p) +} + +func (p *CalculateArgs) CompareTo(other interface{}) (int, bool) { + if other == nil { + return 1, true + } + data, ok := other.(*CalculateArgs) + if !ok { + return 0, false + } + if p.Logid != data.Logid { + if p.Logid < data.Logid { + return -1, true + } + return 1, true + } + if cmp, ok := p.W.CompareTo(data.W); !ok || cmp != 0 { + return cmp, ok + } + return 0, true +} + +func (p *CalculateArgs) AttributeByFieldId(id int) interface{} { + switch id { + default: + return nil + case 1: + return p.Logid + case 2: + return p.W + } + return nil +} + +func (p *CalculateArgs) TStructFields() TFieldContainer { + return NewTFieldContainer([]TField{ + NewTField("logid", I32, 1), + NewTField("w", STRUCT, 2), + }) +} + +/** + * Attributes: + * - Success + * - Ouch + */ +type CalculateResult struct { + TStruct + Success int32 "success" // 0 + Ouch *InvalidOperation "ouch" // 1 +} + +func NewCalculateResult() *CalculateResult { + output := &CalculateResult{ + TStruct: NewTStruct("calculate_result", []TField{ + NewTField("success", I32, 0), + NewTField("ouch", STRUCT, 1), + }), + } + { + } + return output +} + +func (p *CalculateResult) Read(iprot TProtocol) (err TProtocolException) { + _, err = iprot.ReadStructBegin() + if err != nil { + return NewTProtocolExceptionReadStruct(p.ThriftName(), err) + } + for { + fieldName, fieldTypeId, fieldId, err := iprot.ReadFieldBegin() + if fieldId < 0 { + fieldId = int16(p.FieldIdFromFieldName(fieldName)) + } else if fieldName == "" { + fieldName = p.FieldNameFromFieldId(int(fieldId)) + } + if fieldTypeId == GENERIC { + fieldTypeId = p.FieldFromFieldId(int(fieldId)).TypeId() + } + if err != nil { + return NewTProtocolExceptionReadField(int(fieldId), fieldName, p.ThriftName(), err) + } + if fieldTypeId == STOP { + break + } + if fieldId == 0 || fieldName == "success" { + if fieldTypeId == I32 { + err = p.ReadField0(iprot) + if err != nil { + return NewTProtocolExceptionReadField(int(fieldId), fieldName, p.ThriftName(), err) + } + } else if fieldTypeId == VOID { + err = iprot.Skip(fieldTypeId) + if err != nil { + return NewTProtocolExceptionReadField(int(fieldId), fieldName, p.ThriftName(), err) + } + } else { + err = p.ReadField0(iprot) + if err != nil { + return NewTProtocolExceptionReadField(int(fieldId), fieldName, p.ThriftName(), err) + } + } + } else if fieldId == 1 || fieldName == "ouch" { + if fieldTypeId == STRUCT { + err = p.ReadField1(iprot) + if err != nil { + return NewTProtocolExceptionReadField(int(fieldId), fieldName, p.ThriftName(), err) + } + } else if fieldTypeId == VOID { + err = iprot.Skip(fieldTypeId) + if err != nil { + return NewTProtocolExceptionReadField(int(fieldId), fieldName, p.ThriftName(), err) + } + } else { + err = p.ReadField1(iprot) + if err != nil { + return NewTProtocolExceptionReadField(int(fieldId), fieldName, p.ThriftName(), err) + } + } + } else { + err = iprot.Skip(fieldTypeId) + if err != nil { + return NewTProtocolExceptionReadField(int(fieldId), fieldName, p.ThriftName(), err) + } + } + err = iprot.ReadFieldEnd() + if err != nil { + return NewTProtocolExceptionReadField(int(fieldId), fieldName, p.ThriftName(), err) + } + } + err = iprot.ReadStructEnd() + if err != nil { + return NewTProtocolExceptionReadStruct(p.ThriftName(), err) + } + return err +} + +func (p *CalculateResult) ReadField0(iprot TProtocol) (err TProtocolException) { + v52, err53 := iprot.ReadI32() + if err53 != nil { + return NewTProtocolExceptionReadField(0, "success", p.ThriftName(), err53) + } + p.Success = v52 + return err +} + +func (p *CalculateResult) ReadFieldSuccess(iprot TProtocol) TProtocolException { + return p.ReadField0(iprot) +} + +func (p *CalculateResult) ReadField1(iprot TProtocol) (err TProtocolException) { + p.Ouch = NewInvalidOperation() + err56 := p.Ouch.Read(iprot) + if err56 != nil { + return NewTProtocolExceptionReadStruct("p.OuchInvalidOperation", err56) + } + return err +} + +func (p *CalculateResult) ReadFieldOuch(iprot TProtocol) TProtocolException { + return p.ReadField1(iprot) +} + +func (p *CalculateResult) Write(oprot TProtocol) (err TProtocolException) { + err = oprot.WriteStructBegin("calculate_result") + if err != nil { + return NewTProtocolExceptionWriteStruct(p.ThriftName(), err) + } + err = p.WriteField0(oprot) + if err != nil { + return err + } + err = p.WriteField1(oprot) + if err != nil { + return err + } + err = oprot.WriteFieldStop() + if err != nil { + return NewTProtocolExceptionWriteField(-1, "STOP", p.ThriftName(), err) + } + err = oprot.WriteStructEnd() + if err != nil { + return NewTProtocolExceptionWriteStruct(p.ThriftName(), err) + } + return err +} + +func (p *CalculateResult) WriteField0(oprot TProtocol) (err TProtocolException) { + err = oprot.WriteFieldBegin("success", I32, 0) + if err != nil { + return NewTProtocolExceptionWriteField(0, "success", p.ThriftName(), err) + } + err = oprot.WriteI32(int32(p.Success)) + if err != nil { + return NewTProtocolExceptionWriteField(0, "success", p.ThriftName(), err) + } + err = oprot.WriteFieldEnd() + if err != nil { + return NewTProtocolExceptionWriteField(0, "success", p.ThriftName(), err) + } + return err +} + +func (p *CalculateResult) WriteFieldSuccess(oprot TProtocol) TProtocolException { + return p.WriteField0(oprot) +} + +func (p *CalculateResult) WriteField1(oprot TProtocol) (err TProtocolException) { + if p.Ouch != nil { + err = oprot.WriteFieldBegin("ouch", STRUCT, 1) + if err != nil { + return NewTProtocolExceptionWriteField(1, "ouch", p.ThriftName(), err) + } + err = p.Ouch.Write(oprot) + if err != nil { + return NewTProtocolExceptionWriteStruct("InvalidOperation", err) + } + err = oprot.WriteFieldEnd() + if err != nil { + return NewTProtocolExceptionWriteField(1, "ouch", p.ThriftName(), err) + } + } + return err +} + +func (p *CalculateResult) WriteFieldOuch(oprot TProtocol) TProtocolException { + return p.WriteField1(oprot) +} + +func (p *CalculateResult) TStructName() string { + return "CalculateResult" +} + +func (p *CalculateResult) ThriftName() string { + return "calculate_result" +} + +func (p *CalculateResult) String() string { + if p == nil { + return "" + } + return fmt.Sprintf("CalculateResult(%+v)", *p) +} + +func (p *CalculateResult) CompareTo(other interface{}) (int, bool) { + if other == nil { + return 1, true + } + data, ok := other.(*CalculateResult) + if !ok { + return 0, false + } + if p.Success != data.Success { + if p.Success < data.Success { + return -1, true + } + return 1, true + } + if cmp, ok := p.Ouch.CompareTo(data.Ouch); !ok || cmp != 0 { + return cmp, ok + } + return 0, true +} + +func (p *CalculateResult) AttributeByFieldId(id int) interface{} { + switch id { + default: + return nil + case 0: + return p.Success + case 1: + return p.Ouch + } + return nil +} + +func (p *CalculateResult) TStructFields() TFieldContainer { + return NewTFieldContainer([]TField{ + NewTField("success", I32, 0), + NewTField("ouch", STRUCT, 1), + }) +} + + +/** + * Structs can also be exceptions, if they are nasty. + * + * Attributes: + * - What + * - Why + */ +type InvalidOperation struct { + TStruct + _ interface{} "what" // nil # 0 + What int32 "what" // 1 + Why string "why" // 2 +} + +func NewInvalidOperation() *InvalidOperation { + output := &InvalidOperation{ + TStruct: NewTStruct("InvalidOperation", []TField{ + NewTField("what", I32, 1), + NewTField("why", STRING, 2), + }), + } + { + } + return output +} + +func (p *InvalidOperation) Read(iprot TProtocol) (err TProtocolException) { + _, err = iprot.ReadStructBegin() + if err != nil { + return NewTProtocolExceptionReadStruct(p.ThriftName(), err) + } + for { + fieldName, fieldTypeId, fieldId, err := iprot.ReadFieldBegin() + if fieldId < 0 { + fieldId = int16(p.FieldIdFromFieldName(fieldName)) + } else if fieldName == "" { + fieldName = p.FieldNameFromFieldId(int(fieldId)) + } + if fieldTypeId == GENERIC { + fieldTypeId = p.FieldFromFieldId(int(fieldId)).TypeId() + } + if err != nil { + return NewTProtocolExceptionReadField(int(fieldId), fieldName, p.ThriftName(), err) + } + if fieldTypeId == STOP { + break + } + if fieldId == 1 || fieldName == "what" { + if fieldTypeId == I32 { + err = p.ReadField1(iprot) + if err != nil { + return NewTProtocolExceptionReadField(int(fieldId), fieldName, p.ThriftName(), err) + } + } else if fieldTypeId == VOID { + err = iprot.Skip(fieldTypeId) + if err != nil { + return NewTProtocolExceptionReadField(int(fieldId), fieldName, p.ThriftName(), err) + } + } else { + err = p.ReadField1(iprot) + if err != nil { + return NewTProtocolExceptionReadField(int(fieldId), fieldName, p.ThriftName(), err) + } + } + } else if fieldId == 2 || fieldName == "why" { + if fieldTypeId == STRING { + err = p.ReadField2(iprot) + if err != nil { + return NewTProtocolExceptionReadField(int(fieldId), fieldName, p.ThriftName(), err) + } + } else if fieldTypeId == VOID { + err = iprot.Skip(fieldTypeId) + if err != nil { + return NewTProtocolExceptionReadField(int(fieldId), fieldName, p.ThriftName(), err) + } + } else { + err = p.ReadField2(iprot) + if err != nil { + return NewTProtocolExceptionReadField(int(fieldId), fieldName, p.ThriftName(), err) + } + } + } else { + err = iprot.Skip(fieldTypeId) + if err != nil { + return NewTProtocolExceptionReadField(int(fieldId), fieldName, p.ThriftName(), err) + } + } + err = iprot.ReadFieldEnd() + if err != nil { + return NewTProtocolExceptionReadField(int(fieldId), fieldName, p.ThriftName(), err) + } + } + err = iprot.ReadStructEnd() + if err != nil { + return NewTProtocolExceptionReadStruct(p.ThriftName(), err) + } + return err +} + +func (p *InvalidOperation) ReadField1(iprot TProtocol) (err TProtocolException) { + v12, err13 := iprot.ReadI32() + if err13 != nil { + return NewTProtocolExceptionReadField(1, "what", p.ThriftName(), err13) + } + p.What = v12 + return err +} + +func (p *InvalidOperation) ReadFieldWhat(iprot TProtocol) TProtocolException { + return p.ReadField1(iprot) +} + +func (p *InvalidOperation) ReadField2(iprot TProtocol) (err TProtocolException) { + v14, err15 := iprot.ReadString() + if err15 != nil { + return NewTProtocolExceptionReadField(2, "why", p.ThriftName(), err15) + } + p.Why = v14 + return err +} + +func (p *InvalidOperation) ReadFieldWhy(iprot TProtocol) TProtocolException { + return p.ReadField2(iprot) +} + +func (p *InvalidOperation) Write(oprot TProtocol) (err TProtocolException) { + err = oprot.WriteStructBegin("InvalidOperation") + if err != nil { + return NewTProtocolExceptionWriteStruct(p.ThriftName(), err) + } + err = p.WriteField1(oprot) + if err != nil { + return err + } + err = p.WriteField2(oprot) + if err != nil { + return err + } + err = oprot.WriteFieldStop() + if err != nil { + return NewTProtocolExceptionWriteField(-1, "STOP", p.ThriftName(), err) + } + err = oprot.WriteStructEnd() + if err != nil { + return NewTProtocolExceptionWriteStruct(p.ThriftName(), err) + } + return err +} + +func (p *InvalidOperation) WriteField1(oprot TProtocol) (err TProtocolException) { + err = oprot.WriteFieldBegin("what", I32, 1) + if err != nil { + return NewTProtocolExceptionWriteField(1, "what", p.ThriftName(), err) + } + err = oprot.WriteI32(int32(p.What)) + if err != nil { + return NewTProtocolExceptionWriteField(1, "what", p.ThriftName(), err) + } + err = oprot.WriteFieldEnd() + if err != nil { + return NewTProtocolExceptionWriteField(1, "what", p.ThriftName(), err) + } + return err +} + +func (p *InvalidOperation) WriteFieldWhat(oprot TProtocol) TProtocolException { + return p.WriteField1(oprot) +} + +func (p *InvalidOperation) WriteField2(oprot TProtocol) (err TProtocolException) { + err = oprot.WriteFieldBegin("why", STRING, 2) + if err != nil { + return NewTProtocolExceptionWriteField(2, "why", p.ThriftName(), err) + } + err = oprot.WriteString(string(p.Why)) + if err != nil { + return NewTProtocolExceptionWriteField(2, "why", p.ThriftName(), err) + } + err = oprot.WriteFieldEnd() + if err != nil { + return NewTProtocolExceptionWriteField(2, "why", p.ThriftName(), err) + } + return err +} + +func (p *InvalidOperation) WriteFieldWhy(oprot TProtocol) TProtocolException { + return p.WriteField2(oprot) +} + +func (p *InvalidOperation) TStructName() string { + return "InvalidOperation" +} + +func (p *InvalidOperation) ThriftName() string { + return "InvalidOperation" +} + +func (p *InvalidOperation) String() string { + if p == nil { + return "" + } + return fmt.Sprintf("InvalidOperation(%+v)", *p) +} + +func (p *InvalidOperation) CompareTo(other interface{}) (int, bool) { + if other == nil { + return 1, true + } + data, ok := other.(*InvalidOperation) + if !ok { + return 0, false + } + if p.What != data.What { + if p.What < data.What { + return -1, true + } + return 1, true + } + if p.Why != data.Why { + if p.Why < data.Why { + return -1, true + } + return 1, true + } + return 0, true +} + +func (p *InvalidOperation) AttributeByFieldId(id int) interface{} { + switch id { + default: + return nil + case 1: + return p.What + case 2: + return p.Why + } + return nil +} + +func (p *InvalidOperation) TStructFields() TFieldContainer { + return NewTFieldContainer([]TField{ + NewTField("what", I32, 1), + NewTField("why", STRING, 2), + }) +} diff --git a/lib/go/thrift/tserver.go b/lib/go/thrift/tserver.go new file mode 100644 index 00000000..6bc3167e --- /dev/null +++ b/lib/go/thrift/tserver.go @@ -0,0 +1,61 @@ +/* + * 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. + */ + +package thrift + +import ( + "os" +) + +type TServer interface { + /** + * Core processor + */ + ProcessorFactory() TProcessorFactory + /** + * Server transport + */ + ServerTransport() TServerTransport + /** + * Input Transport Factory + */ + InputTransportFactory() TTransportFactory + /** + * Output Transport Factory + */ + OutputTransportFactory() TTransportFactory + /** + * Input Protocol Factory + */ + InputProtocolFactory() TProtocolFactory + /** + * Output Protocol Factory + */ + OutputProtocolFactory() TProtocolFactory + + /** + * The run method fires up the server and gets things going. + */ + Serve() os.Error + /** + * Stop the server. This is optional on a per-implementation basis. Not + * all servers are required to be cleanly stoppable. + */ + Stop() os.Error +} diff --git a/lib/go/thrift/tserver_socket.go b/lib/go/thrift/tserver_socket.go new file mode 100644 index 00000000..dc3e7489 --- /dev/null +++ b/lib/go/thrift/tserver_socket.go @@ -0,0 +1,194 @@ +/* + * 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. + */ + +package thrift + +import ( + "net" + "os" +) + + +type TServerSocket struct { + /** + * Underlying socket conection object + */ + conn net.Conn + /** + * Underlying socket conection object + */ + listener net.Listener + + /** + * Address to listen on + */ + addr net.Addr + + /** + * Client timeout in nanoseconds + */ + nsecClientTimeout int64 +} + +type TServerSocketTransportFactory struct { + addr net.Addr + nsecClientTimeout int64 +} + +func (p *TServerSocketTransportFactory) GetTransport(trans TTransport) TTransport { + if trans != nil { + t, ok := trans.(*TServerSocket) + if ok && t.addr != nil { + s, _ := NewTServerSocketAddrTimeout(t.addr, t.nsecClientTimeout) + return s + } + } + s, _ := NewTServerSocketAddrTimeout(p.addr, p.nsecClientTimeout) + return s +} + +func NewTServerSocketTransportFactory(addr net.Addr, nsecClientTimeout int64) *TServerSocketTransportFactory { + return &TServerSocketTransportFactory{addr: addr, nsecClientTimeout: nsecClientTimeout} +} + +func NewTServerSocketConn(conn net.Conn) *TServerSocket { + return NewTServerSocketConnTimeout(conn, 0) +} + +func NewTServerSocketConnTimeout(conn net.Conn, nsecClientTimeout int64) *TServerSocket { + v := &TServerSocket{conn: conn, addr: conn.LocalAddr(), nsecClientTimeout: nsecClientTimeout} + conn.SetTimeout(nsecClientTimeout) + return v +} + +func NewTServerSocketAddr(addr net.Addr) (*TServerSocket, TTransportException) { + return NewTServerSocketAddrTimeout(addr, 0) +} + +func NewTServerSocketAddrTimeout(addr net.Addr, nsecClientTimeout int64) (*TServerSocket, TTransportException) { + s := &TServerSocket{addr: addr, nsecClientTimeout: nsecClientTimeout} + return s, nil +} + +func (p *TServerSocket) Listen() (err os.Error) { + if p.listener == nil { + if p.listener, err = net.Listen("tcp", p.addr.String()); err != nil { + return err + } + } + return nil +} + +func (p *TServerSocket) Accept() (TTransport, os.Error) { + if p.listener == nil { + if err := p.Listen(); err != nil { + return nil, NewTTransportExceptionFromOsError(err) + } + if p.listener == nil { + return nil, NewTTransportException(NOT_OPEN, "No underlying server socket") + } + } + conn, err := p.listener.Accept() + if err != nil { + return nil, NewTTransportExceptionFromOsError(err) + } + conn.SetTimeout(p.nsecClientTimeout) + return NewTSocketConn(conn) +} + +/** + * Checks whether the socket is connected. + */ +func (p *TServerSocket) IsOpen() bool { + return p.listener != nil +} + +/** + * Connects the socket, creating a new socket object if necessary. + */ +func (p *TServerSocket) Open() os.Error { + if !p.IsOpen() { + l, err := net.Listen(p.addr.Network(), p.addr.String()) + if err != nil { + return err + } + p.listener = l + return nil + } + return NewTTransportException(ALREADY_OPEN, "Server socket already open") +} + +/** + * Perform a nonblocking read into buffer. + */ +func (p *TServerSocket) Read(buf []byte) (int, os.Error) { + return 0, NewTTransportException(UNKNOWN_TRANSPORT_EXCEPTION, "TServerSocket.Read([]byte) is not implemented") +} + +func (p *TServerSocket) ReadAll(buf []byte) (int, os.Error) { + return ReadAllTransport(p, buf) +} + +/** + * Perform a nonblocking write of the data in buffer; + */ +func (p *TServerSocket) Write(buf []byte) (int, os.Error) { + return 0, NewTTransportException(UNKNOWN_TRANSPORT_EXCEPTION, "TServerSocket.Write([]byte) is not implemented") +} + +/** + * Flushes the underlying output stream if not null. + */ +func (p *TServerSocket) Flush() os.Error { + return NewTTransportException(UNKNOWN_TRANSPORT_EXCEPTION, "TServerSocket.Flush() is not implemented") +} + +func (p *TServerSocket) Addr() net.Addr { + return p.addr +} + +func (p *TServerSocket) Peek() bool { + return p.IsOpen() +} + +/** + * Closes the socket. + */ +func (p *TServerSocket) Close() (err os.Error) { + if p.IsOpen() { + err := p.listener.Close() + if err != nil { + return NewTTransportExceptionFromOsError(err) + } + p.listener = nil + } + if p.conn != nil { + err := p.conn.Close() + if err != nil { + return NewTTransportExceptionFromOsError(err) + } + p.conn = nil + } + return nil +} + +func (p *TServerSocket) Interrupt() os.Error { + // TODO(pomack) fix Interrupt as it is probably not right + return NewTTransportExceptionFromOsError(p.Close()) +} diff --git a/lib/go/thrift/tserver_test.go b/lib/go/thrift/tserver_test.go new file mode 100644 index 00000000..3cbe8798 --- /dev/null +++ b/lib/go/thrift/tserver_test.go @@ -0,0 +1,28 @@ +/* + * 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. + */ + +package thrift_test + +import ( + "testing" +) + +func TestNothing(t *testing.T) { + +} diff --git a/lib/go/thrift/tserver_transport.go b/lib/go/thrift/tserver_transport.go new file mode 100644 index 00000000..ef3a462e --- /dev/null +++ b/lib/go/thrift/tserver_transport.go @@ -0,0 +1,41 @@ +/* + * 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. + */ + +package thrift + +import "os" + +/** + * Server transport. Object which provides client transports. + * + */ +type TServerTransport interface { + Listen() os.Error + Accept() (TTransport, os.Error) + Close() os.Error + + /** + * Optional method implementation. This signals to the server transport + * that it should break out of any accept() or listen() that it is currently + * blocked on. This method, if implemented, MUST be thread safe, as it may + * be called from a different thread context than the other TServerTransport + * methods. + */ + Interrupt() os.Error +} diff --git a/lib/go/thrift/tset.go b/lib/go/thrift/tset.go new file mode 100644 index 00000000..f8681098 --- /dev/null +++ b/lib/go/thrift/tset.go @@ -0,0 +1,207 @@ +/* + * 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. + */ + +package thrift + +import ( + "container/list" +) + +/** + * Helper class that encapsulates set metadata. + * + */ +type TSet interface { + TContainer + ElemType() TType + Add(data interface{}) + Remove(data interface{}) + Less(other interface{}) bool + Front() *list.Element + Back() *list.Element + Values() []interface{} +} + +type tSet struct { + elemType TType + size int + l *list.List +} + +func NewTSet(t TType, s int) TSet { + return &tSet{elemType: t, size: s, l: list.New()} +} + +func NewTSetDefault() TSet { + return NewTSet(STOP, 0) +} + +func (p *tSet) ElemType() TType { + return p.elemType +} + +func (p *tSet) Front() *list.Element { + return p.l.Front() +} + +func (p *tSet) Back() *list.Element { + return p.l.Back() +} + +func (p *tSet) Len() int { + if p.l.Len() != 0 { + return p.l.Len() + } + return p.size +} + +func (p *tSet) Contains(data interface{}) bool { + return p.find(data) != nil +} + +func (p *tSet) Add(other interface{}) { + if data, ok := p.elemType.CoerceData(other); ok { + for elem := p.l.Front(); elem != nil; elem = elem.Next() { + if cmp, ok := p.elemType.Compare(data, elem.Value); ok && cmp >= 0 { + if cmp > 0 { + p.l.InsertBefore(data, elem) + } + return + } + } + } +} + +func (p *tSet) Remove(data interface{}) { + elem := p.find(data) + if elem != nil { + p.l.Remove(elem) + } +} + +func (p *tSet) Less(other interface{}) bool { + cmp, ok := p.CompareTo(other) + return ok && cmp > 0 +} + +func (p *tSet) Equals(other interface{}) bool { + c, cok := p.CompareTo(other) + return cok && c == 0 +} + +func (p *tSet) CompareTo(other interface{}) (int, bool) { + return TType(SET).Compare(p, other) +} + +func (p *tSet) find(data interface{}) *list.Element { + if data == nil { + for elem := p.l.Front(); elem != nil; elem = elem.Next() { + if elem.Value == nil { + return elem + } + } + return nil + } + data, ok := p.elemType.CoerceData(data) + if data == nil || !ok { + return nil + } + if p.elemType.IsBaseType() || p.elemType.IsEnum() { + for elem := p.l.Front(); elem != nil; elem = elem.Next() { + if data == elem.Value { + return elem + } + } + return nil + } + if cmp, ok := data.(EqualsOtherInterface); ok { + for elem := p.l.Front(); elem != nil; elem = elem.Next() { + if cmp.Equals(elem.Value) { + return elem + } + } + return nil + } + switch p.elemType { + case MAP: + if cmp, ok := data.(EqualsMap); ok { + for elem := p.l.Front(); elem != nil; elem = elem.Next() { + v := elem.Value + if v == nil { + continue + } + if cmp.Equals(v.(TMap)) { + return elem + } + } + return nil + } + case SET: + if cmp, ok := data.(EqualsSet); ok { + for elem := p.l.Front(); elem != nil; elem = elem.Next() { + v := elem.Value + if v == nil { + continue + } + if cmp.Equals(v.(TSet)) { + return elem + } + } + return nil + } + case LIST: + if cmp, ok := data.(EqualsList); ok { + for elem := p.l.Front(); elem != nil; elem = elem.Next() { + v := elem.Value + if v == nil { + continue + } + if cmp.Equals(v.(TList)) { + return elem + } + } + return nil + } + case STRUCT: + if cmp, ok := data.(EqualsStruct); ok { + for elem := p.l.Front(); elem != nil; elem = elem.Next() { + v := elem.Value + if v == nil { + continue + } + if cmp.Equals(v.(TStruct)) { + return elem + } + } + return nil + } + } + return nil +} + +func (p *tSet) Values() []interface{} { + size := p.l.Len() + values := make([]interface{}, size, size) + i := 0 + for v := p.l.Front(); v != nil; v = v.Next() { + values[i] = v.Value + i++ + } + return values +} diff --git a/lib/go/thrift/tsimple_json_protocol.go b/lib/go/thrift/tsimple_json_protocol.go new file mode 100644 index 00000000..569dd9cc --- /dev/null +++ b/lib/go/thrift/tsimple_json_protocol.go @@ -0,0 +1,1281 @@ +/* + * 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. + */ + +package thrift + +import ( + "bufio" + "bytes" + "container/vector" + "encoding/base64" + "fmt" + "io" + "json" + "math" + "os" + "strconv" + "strings" +) + +type _ParseContext int + +const ( + _CONTEXT_IN_TOPLEVEL _ParseContext = 1 + _CONTEXT_IN_LIST_FIRST _ParseContext = 2 + _CONTEXT_IN_LIST _ParseContext = 3 + _CONTEXT_IN_OBJECT_FIRST _ParseContext = 4 + _CONTEXT_IN_OBJECT_NEXT_KEY _ParseContext = 5 + _CONTEXT_IN_OBJECT_NEXT_VALUE _ParseContext = 6 +) + +func (p _ParseContext) String() string { + switch p { + case _CONTEXT_IN_TOPLEVEL: + return "TOPLEVEL" + case _CONTEXT_IN_LIST_FIRST: + return "LIST-FIRST" + case _CONTEXT_IN_LIST: + return "LIST" + case _CONTEXT_IN_OBJECT_FIRST: + return "OBJECT-FIRST" + case _CONTEXT_IN_OBJECT_NEXT_KEY: + return "OBJECT-NEXT-KEY" + case _CONTEXT_IN_OBJECT_NEXT_VALUE: + return "OBJECT-NEXT-VALUE" + } + return "UNKNOWN-PARSE-CONTEXT" +} + +/** + * JSON protocol implementation for thrift. + * + * This protocol produces/consumes a simple output format + * suitable for parsing by scripting languages. It should not be + * confused with the full-featured TJSONProtocol. + * + */ +type TSimpleJSONProtocol struct { + //TProtocolBase; + trans TTransport + + /** + * Stack of nested contexts that we may be in. + */ + parseContextStack vector.IntVector + /** + * Stack of nested contexts that we may be in. + */ + dumpContext vector.IntVector + + /** + * Current context that we are in + */ + writer TTransport + reader *bufio.Reader +} + +/** + * Constructor + */ +func NewTSimpleJSONProtocol(t TTransport) *TSimpleJSONProtocol { + v := &TSimpleJSONProtocol{trans: t, + writer: t, + reader: bufio.NewReader(t), + } + v.parseContextStack.Push(int(_CONTEXT_IN_TOPLEVEL)) + v.dumpContext.Push(int(_CONTEXT_IN_TOPLEVEL)) + return v +} + +/** + * Factory + */ +type TSimpleJSONProtocolFactory struct{} + +func (p *TSimpleJSONProtocolFactory) GetProtocol(trans TTransport) TProtocol { + return NewTSimpleJSONProtocol(trans) +} + +func NewTSimpleJSONProtocolFactory() *TSimpleJSONProtocolFactory { + return &TSimpleJSONProtocolFactory{} +} + +var ( + JSON_COMMA []byte + JSON_COLON []byte + JSON_LBRACE []byte + JSON_RBRACE []byte + JSON_LBRACKET []byte + JSON_RBRACKET []byte + JSON_QUOTE byte + JSON_QUOTE_BYTES []byte + JSON_NULL []byte + JSON_TRUE []byte + JSON_FALSE []byte + JSON_INFINITY string + JSON_NEGATIVE_INFINITY string + JSON_NAN string + JSON_INFINITY_BYTES []byte + JSON_NEGATIVE_INFINITY_BYTES []byte + JSON_NAN_BYTES []byte + json_nonbase_map_elem_bytes []byte +) + +func init() { + JSON_COMMA = []byte{','} + JSON_COLON = []byte{':'} + JSON_LBRACE = []byte{'{'} + JSON_RBRACE = []byte{'}'} + JSON_LBRACKET = []byte{'['} + JSON_RBRACKET = []byte{']'} + JSON_QUOTE = '"' + JSON_QUOTE_BYTES = []byte{'"'} + JSON_NULL = []byte{'n', 'u', 'l', 'l'} + JSON_TRUE = []byte{'t', 'r', 'u', 'e'} + JSON_FALSE = []byte{'f', 'a', 'l', 's', 'e'} + JSON_INFINITY = "Infinity" + JSON_NEGATIVE_INFINITY = "-Infinity" + JSON_NAN = "NaN" + JSON_INFINITY_BYTES = []byte{'I', 'n', 'f', 'i', 'n', 'i', 't', 'y'} + JSON_NEGATIVE_INFINITY_BYTES = []byte{'-', 'I', 'n', 'f', 'i', 'n', 'i', 't', 'y'} + JSON_NAN_BYTES = []byte{'N', 'a', 'N'} + json_nonbase_map_elem_bytes = []byte{']', ',', '['} +} + +func JsonQuote(s string) string { + b, _ := json.Marshal(s) + s1 := string(b) + return s1 +} + +func JsonUnquote(s string) (string, bool) { + s1 := new(string) + err := json.Unmarshal([]byte(s), s1) + return *s1, err == nil +} + + +func (p *TSimpleJSONProtocol) WriteMessageBegin(name string, typeId TMessageType, seqId int32) TProtocolException { + if e := p.OutputListBegin(); e != nil { + return e + } + if e := p.WriteString(name); e != nil { + return e + } + if e := p.WriteByte(byte(typeId)); e != nil { + return e + } + if e := p.WriteI32(seqId); e != nil { + return e + } + return nil +} + +func (p *TSimpleJSONProtocol) WriteMessageEnd() TProtocolException { + return p.OutputListEnd() +} + +func (p *TSimpleJSONProtocol) WriteStructBegin(name string) TProtocolException { + if e := p.OutputObjectBegin(); e != nil { + return e + } + return nil +} + +func (p *TSimpleJSONProtocol) WriteStructEnd() TProtocolException { + return p.OutputObjectEnd() +} + +func (p *TSimpleJSONProtocol) WriteFieldBegin(name string, typeId TType, id int16) TProtocolException { + if e := p.WriteString(name); e != nil { + return e + } + return nil + /* + if e := p.OutputListBegin(); e != nil { + return e + } + if e := p.WriteByte(byte(typeId)); e != nil { + return e + } + return p.WriteI16(id) + */ +} + +func (p *TSimpleJSONProtocol) WriteFieldEnd() TProtocolException { + //return p.OutputListEnd() + return nil +} + +func (p *TSimpleJSONProtocol) WriteFieldStop() TProtocolException { return nil } + +func (p *TSimpleJSONProtocol) WriteMapBegin(keyType TType, valueType TType, size int) TProtocolException { + if e := p.OutputListBegin(); e != nil { + return e + } + if e := p.WriteByte(byte(keyType)); e != nil { + return e + } + if e := p.WriteByte(byte(valueType)); e != nil { + return e + } + return p.WriteI32(int32(size)) +} + +func (p *TSimpleJSONProtocol) WriteMapEnd() TProtocolException { + return p.OutputListEnd() +} + +func (p *TSimpleJSONProtocol) WriteListBegin(elemType TType, size int) TProtocolException { + return p.OutputElemListBegin(elemType, size) +} + +func (p *TSimpleJSONProtocol) WriteListEnd() TProtocolException { + return p.OutputListEnd() +} + +func (p *TSimpleJSONProtocol) WriteSetBegin(elemType TType, size int) TProtocolException { + return p.OutputElemListBegin(elemType, size) +} + +func (p *TSimpleJSONProtocol) WriteSetEnd() TProtocolException { + return p.OutputListEnd() +} + +func (p *TSimpleJSONProtocol) WriteBool(b bool) TProtocolException { + return p.OutputBool(b) +} + +func (p *TSimpleJSONProtocol) WriteByte(b byte) TProtocolException { + return p.WriteI32(int32(b)) +} + +func (p *TSimpleJSONProtocol) WriteI16(v int16) TProtocolException { + return p.WriteI32(int32(v)) +} + +func (p *TSimpleJSONProtocol) WriteI32(v int32) TProtocolException { + return p.OutputI64(int64(v)) +} + +func (p *TSimpleJSONProtocol) WriteI64(v int64) TProtocolException { + return p.OutputI64(int64(v)) +} + +func (p *TSimpleJSONProtocol) WriteDouble(v float64) TProtocolException { + return p.OutputF64(v) +} + +func (p *TSimpleJSONProtocol) WriteString(v string) TProtocolException { + return p.OutputString(v) +} + +func (p *TSimpleJSONProtocol) WriteBinary(v []byte) TProtocolException { + // JSON library only takes in a string, + // not an arbitrary byte array, to ensure bytes are transmitted + // efficiently we must convert this into a valid JSON string + // therefore we use base64 encoding to avoid excessive escaping/quoting + if e := p.OutputPreValue(); e != nil { + return e + } + if _, e := p.writer.Write(JSON_QUOTE_BYTES); e != nil { + return NewTProtocolExceptionFromOsError(e) + } + writer := base64.NewEncoder(base64.StdEncoding, p.writer) + if _, e := writer.Write(v); e != nil { + return NewTProtocolExceptionFromOsError(e) + } + if e := writer.Close(); e != nil { + return NewTProtocolExceptionFromOsError(e) + } + if _, e := p.writer.Write(JSON_QUOTE_BYTES); e != nil { + return NewTProtocolExceptionFromOsError(e) + } + return p.OutputPostValue() +} + +/** + * Reading methods. + */ + +func (p *TSimpleJSONProtocol) ReadMessageBegin() (name string, typeId TMessageType, seqId int32, err TProtocolException) { + if isNull, err := p.ParseListBegin(); isNull || err != nil { + return name, typeId, seqId, err + } + if name, err = p.ReadString(); err != nil { + return name, typeId, seqId, err + } + bTypeId, err := p.ReadByte() + typeId = TMessageType(bTypeId) + if err != nil { + return name, typeId, seqId, err + } + if seqId, err = p.ReadI32(); err != nil { + return name, typeId, seqId, err + } + return name, typeId, seqId, nil +} + +func (p *TSimpleJSONProtocol) ReadMessageEnd() TProtocolException { + return p.ParseListEnd() +} + +func (p *TSimpleJSONProtocol) ReadStructBegin() (name string, err TProtocolException) { + _, err = p.ParseObjectStart() + return "", err +} + +func (p *TSimpleJSONProtocol) ReadStructEnd() TProtocolException { + return p.ParseObjectEnd() +} + +func (p *TSimpleJSONProtocol) ReadFieldBegin() (string, TType, int16, TProtocolException) { + if err := p.ParsePreValue(); err != nil { + return "", STOP, 0, err + } + if p.reader.Buffered() < 1 { + return "", STOP, 0, nil + } + b, _ := p.reader.Peek(1) + if len(b) > 0 { + switch b[0] { + case JSON_RBRACE[0]: + return "", STOP, 0, nil + case JSON_QUOTE: + p.reader.ReadByte() + name, err := p.ParseStringBody() + if err != nil { + return name, STOP, 0, err + } + return name, GENERIC, -1, p.ParsePostValue() + /* + if err = p.ParsePostValue(); err != nil { + return name, STOP, 0, err + } + if isNull, err := p.ParseListBegin(); isNull || err != nil { + return name, STOP, 0, err + } + bType, err := p.ReadByte() + thetype := TType(bType) + if err != nil { + return name, thetype, 0, err + } + id, err := p.ReadI16() + return name, thetype, id, err + */ + } + return "", STOP, 0, NewTProtocolException(INVALID_DATA, fmt.Sprint("Expected \"}\" or '\"', but found: '", string(b), "'")) + } + return "", STOP, 0, NewTProtocolExceptionFromOsError(os.EOF) +} + +func (p *TSimpleJSONProtocol) ReadFieldEnd() TProtocolException { + return nil + //return p.ParseListEnd() +} + +func (p *TSimpleJSONProtocol) ReadMapBegin() (keyType TType, valueType TType, size int, e TProtocolException) { + if isNull, e := p.ParseListBegin(); isNull || e != nil { + return VOID, VOID, 0, e + } + + // read keyType + bKeyType, e := p.ReadByte() + keyType = TType(bKeyType) + if e != nil { + return keyType, valueType, size, e + } + + // read valueType + bValueType, e := p.ReadByte() + valueType = TType(bValueType) + if e != nil { + return keyType, valueType, size, e + } + + // read size + iSize, err := p.ReadI64() + size = int(iSize) + return keyType, valueType, size, err +} + +func (p *TSimpleJSONProtocol) ReadMapEnd() TProtocolException { + return p.ParseListEnd() +} + +func (p *TSimpleJSONProtocol) ReadListBegin() (elemType TType, size int, e TProtocolException) { + return p.ParseElemListBegin() +} + +func (p *TSimpleJSONProtocol) ReadListEnd() TProtocolException { + return p.ParseListEnd() +} + +func (p *TSimpleJSONProtocol) ReadSetBegin() (elemType TType, size int, e TProtocolException) { + return p.ParseElemListBegin() +} + +func (p *TSimpleJSONProtocol) ReadSetEnd() TProtocolException { + return p.ParseListEnd() +} + +func (p *TSimpleJSONProtocol) ReadBool() (bool, TProtocolException) { + var value bool + if err := p.ParsePreValue(); err != nil { + return value, err + } + b, _ := p.reader.Peek(len(JSON_FALSE)) + if len(b) > 0 { + switch b[0] { + case JSON_TRUE[0]: + if string(b[0:len(JSON_TRUE)]) == string(JSON_TRUE) { + p.reader.Read(b[0:len(JSON_TRUE)]) + value = true + } else { + return value, NewTProtocolException(INVALID_DATA, "Expected \"true\" but found: "+string(b)) + } + break + case JSON_FALSE[0]: + if string(b[0:len(JSON_FALSE)]) == string(JSON_FALSE) { + p.reader.Read(b[0:len(JSON_FALSE)]) + value = false + } else { + return value, NewTProtocolException(INVALID_DATA, "Expected \"false\" but found: "+string(b)) + } + break + case JSON_NULL[0]: + if string(b[0:len(JSON_NULL)]) == string(JSON_NULL) { + p.reader.Read(b[0:len(JSON_NULL)]) + value = false + } else { + return value, NewTProtocolException(INVALID_DATA, "Expected \"null\" but found: "+string(b)) + } + default: + return value, NewTProtocolException(INVALID_DATA, "Expected \"true\", \"false\", or \"null\" but found: "+string(b)) + } + } + return value, p.ParsePostValue() +} + +func (p *TSimpleJSONProtocol) ReadByte() (byte, TProtocolException) { + v, err := p.ReadI64() + return byte(v), err +} + +func (p *TSimpleJSONProtocol) ReadI16() (int16, TProtocolException) { + v, err := p.ReadI64() + return int16(v), err +} + +func (p *TSimpleJSONProtocol) ReadI32() (int32, TProtocolException) { + v, err := p.ReadI64() + return int32(v), err +} + +func (p *TSimpleJSONProtocol) ReadI64() (int64, TProtocolException) { + v, _, err := p.ParseI64() + return v, err +} + +func (p *TSimpleJSONProtocol) ReadDouble() (float64, TProtocolException) { + v, _, err := p.ParseF64() + return v, err +} + +func (p *TSimpleJSONProtocol) ReadString() (string, TProtocolException) { + var v string + if err := p.ParsePreValue(); err != nil { + return v, err + } + b, _ := p.reader.Peek(len(JSON_NULL)) + if len(b) > 0 && b[0] == JSON_QUOTE { + p.reader.ReadByte() + value, err := p.ParseStringBody() + v = value + if err != nil { + return v, err + } + } else if len(b) >= len(JSON_NULL) && string(b[0:len(JSON_NULL)]) == string(JSON_NULL) { + _, err := p.reader.Read(b[0:len(JSON_NULL)]) + if err != nil { + return v, NewTProtocolExceptionFromOsError(err) + } + } else { + return v, NewTProtocolException(INVALID_DATA, fmt.Sprint("Expected a JSON string, found ", string(b))) + } + return v, p.ParsePostValue() +} + +func (p *TSimpleJSONProtocol) ReadBinary() ([]byte, TProtocolException) { + var v []byte + if err := p.ParsePreValue(); err != nil { + return nil, err + } + b, _ := p.reader.Peek(len(JSON_NULL)) + if len(b) > 0 && b[0] == JSON_QUOTE { + p.reader.ReadByte() + value, err := p.ParseBase64EncodedBody() + v = value + if err != nil { + return v, err + } + } else if len(b) >= len(JSON_NULL) && string(b[0:len(JSON_NULL)]) == string(JSON_NULL) { + _, err := p.reader.Read(b[0:len(JSON_NULL)]) + if err != nil { + return v, NewTProtocolExceptionFromOsError(err) + } + } else { + return v, NewTProtocolException(INVALID_DATA, fmt.Sprint("Expected a JSON string, found ", string(b))) + } + return v, p.ParsePostValue() +} + +func (p *TSimpleJSONProtocol) Flush() (err TProtocolException) { + return NewTProtocolExceptionFromOsError(p.writer.Flush()) +} + +func (p *TSimpleJSONProtocol) Skip(fieldType TType) (err TProtocolException) { + return SkipDefaultDepth(p, fieldType) +} + +func (p *TSimpleJSONProtocol) Transport() TTransport { + return p.trans +} + + +func (p *TSimpleJSONProtocol) OutputPreValue() TProtocolException { + cxt := _ParseContext(p.dumpContext.Last()) + switch cxt { + case _CONTEXT_IN_LIST, _CONTEXT_IN_OBJECT_NEXT_KEY: + if _, e := p.writer.Write(JSON_COMMA); e != nil { + return NewTProtocolExceptionFromOsError(e) + } + break + case _CONTEXT_IN_OBJECT_NEXT_VALUE: + if _, e := p.writer.Write(JSON_COLON); e != nil { + return NewTProtocolExceptionFromOsError(e) + } + break + } + return nil +} + +func (p *TSimpleJSONProtocol) OutputPostValue() TProtocolException { + cxt := _ParseContext(p.dumpContext.Last()) + switch cxt { + case _CONTEXT_IN_LIST_FIRST: + p.dumpContext.Pop() + p.dumpContext.Push(int(_CONTEXT_IN_LIST)) + break + case _CONTEXT_IN_OBJECT_FIRST: + p.dumpContext.Pop() + p.dumpContext.Push(int(_CONTEXT_IN_OBJECT_NEXT_VALUE)) + break + case _CONTEXT_IN_OBJECT_NEXT_KEY: + p.dumpContext.Pop() + p.dumpContext.Push(int(_CONTEXT_IN_OBJECT_NEXT_VALUE)) + break + case _CONTEXT_IN_OBJECT_NEXT_VALUE: + p.dumpContext.Pop() + p.dumpContext.Push(int(_CONTEXT_IN_OBJECT_NEXT_KEY)) + break + } + return nil +} + +func (p *TSimpleJSONProtocol) OutputBool(value bool) TProtocolException { + if e := p.OutputPreValue(); e != nil { + return e + } + var v string + if value { + v = string(JSON_TRUE) + } else { + v = string(JSON_FALSE) + } + switch _ParseContext(p.dumpContext.Last()) { + case _CONTEXT_IN_OBJECT_FIRST, _CONTEXT_IN_OBJECT_NEXT_KEY: + v = JsonQuote(v) + default: + } + if e := p.OutputStringData(v); e != nil { + return e + } + return p.OutputPostValue() +} + +func (p *TSimpleJSONProtocol) OutputNull() TProtocolException { + if e := p.OutputPreValue(); e != nil { + return e + } + if _, e := p.writer.Write(JSON_NULL); e != nil { + return NewTProtocolExceptionFromOsError(e) + } + return p.OutputPostValue() +} + +func (p *TSimpleJSONProtocol) OutputF64(value float64) TProtocolException { + if e := p.OutputPreValue(); e != nil { + return e + } + var v string + if math.IsNaN(value) { + v = string(JSON_QUOTE) + JSON_NAN + string(JSON_QUOTE) + } else if math.IsInf(value, 1) { + v = string(JSON_QUOTE) + JSON_INFINITY + string(JSON_QUOTE) + } else if math.IsInf(value, -1) { + v = string(JSON_QUOTE) + JSON_NEGATIVE_INFINITY + string(JSON_QUOTE) + } else { + v = strconv.Ftoa64(value, 'g', -1) + switch _ParseContext(p.dumpContext.Last()) { + case _CONTEXT_IN_OBJECT_FIRST, _CONTEXT_IN_OBJECT_NEXT_KEY: + v = string(JSON_QUOTE) + v + string(JSON_QUOTE) + default: + } + } + if e := p.OutputStringData(v); e != nil { + return e + } + return p.OutputPostValue() +} + +func (p *TSimpleJSONProtocol) OutputI64(value int64) TProtocolException { + if e := p.OutputPreValue(); e != nil { + return e + } + v := strconv.Itoa64(value) + switch _ParseContext(p.dumpContext.Last()) { + case _CONTEXT_IN_OBJECT_FIRST, _CONTEXT_IN_OBJECT_NEXT_KEY: + v = JsonQuote(v) + default: + } + if e := p.OutputStringData(v); e != nil { + return e + } + return p.OutputPostValue() +} + +func (p *TSimpleJSONProtocol) OutputString(s string) TProtocolException { + if e := p.OutputPreValue(); e != nil { + return e + } + if e := p.OutputStringData(JsonQuote(s)); e != nil { + return e + } + return p.OutputPostValue() +} + +func (p *TSimpleJSONProtocol) OutputStringData(s string) TProtocolException { + _, e := io.Copyn(p.writer, strings.NewReader(s), int64(len(s))) + return NewTProtocolExceptionFromOsError(e) +} + +func (p *TSimpleJSONProtocol) OutputObjectBegin() TProtocolException { + if e := p.OutputPreValue(); e != nil { + return e + } + if _, e := p.writer.Write(JSON_LBRACE); e != nil { + return NewTProtocolExceptionFromOsError(e) + } + p.dumpContext.Push(int(_CONTEXT_IN_OBJECT_FIRST)) + return nil +} + +func (p *TSimpleJSONProtocol) OutputObjectEnd() TProtocolException { + if _, e := p.writer.Write(JSON_RBRACE); e != nil { + return NewTProtocolExceptionFromOsError(e) + } + p.dumpContext.Pop() + if e := p.OutputPostValue(); e != nil { + return e + } + return nil +} + +func (p *TSimpleJSONProtocol) OutputListBegin() TProtocolException { + if e := p.OutputPreValue(); e != nil { + return e + } + if _, e := p.writer.Write(JSON_LBRACKET); e != nil { + return NewTProtocolExceptionFromOsError(e) + } + p.dumpContext.Push(int(_CONTEXT_IN_LIST_FIRST)) + return nil +} + +func (p *TSimpleJSONProtocol) OutputListEnd() TProtocolException { + if _, e := p.writer.Write(JSON_RBRACKET); e != nil { + return NewTProtocolExceptionFromOsError(e) + } + p.dumpContext.Pop() + if e := p.OutputPostValue(); e != nil { + return e + } + return nil +} + +func (p *TSimpleJSONProtocol) OutputElemListBegin(elemType TType, size int) TProtocolException { + if e := p.OutputListBegin(); e != nil { + return e + } + if e := p.WriteByte(byte(elemType)); e != nil { + return e + } + if e := p.WriteI64(int64(size)); e != nil { + return e + } + return nil +} + +func (p *TSimpleJSONProtocol) ParsePreValue() TProtocolException { + if e := p.readNonSignificantWhitespace(); e != nil { + return NewTProtocolExceptionFromOsError(e) + } + cxt := _ParseContext(p.parseContextStack.Last()) + if p.reader.Buffered() < 1 { + return nil + } + b, _ := p.reader.Peek(1) + switch cxt { + case _CONTEXT_IN_LIST: + if len(b) > 0 { + switch b[0] { + case JSON_RBRACKET[0]: + return nil + case JSON_COMMA[0]: + p.reader.ReadByte() + if e := p.readNonSignificantWhitespace(); e != nil { + return NewTProtocolExceptionFromOsError(e) + } + return nil + default: + return NewTProtocolException(INVALID_DATA, fmt.Sprint("Expected \"]\" or \",\" in list context, but found \"", string(b), "\"")) + } + } + break + case _CONTEXT_IN_OBJECT_NEXT_KEY: + if len(b) > 0 { + switch b[0] { + case JSON_RBRACE[0]: + return nil + case JSON_COMMA[0]: + p.reader.ReadByte() + if e := p.readNonSignificantWhitespace(); e != nil { + return NewTProtocolExceptionFromOsError(e) + } + return nil + default: + return NewTProtocolException(INVALID_DATA, fmt.Sprint("Expected \"}\" or \",\" in object context, but found \"", string(b), "\"")) + } + } + break + case _CONTEXT_IN_OBJECT_NEXT_VALUE: + if len(b) > 0 { + switch b[0] { + case JSON_COLON[0]: + p.reader.ReadByte() + if e := p.readNonSignificantWhitespace(); e != nil { + return NewTProtocolExceptionFromOsError(e) + } + return nil + default: + return NewTProtocolException(INVALID_DATA, fmt.Sprint("Expected \":\" in object context, but found \"", string(b), "\"")) + } + } + break + } + return nil +} + +func (p *TSimpleJSONProtocol) ParsePostValue() TProtocolException { + if e := p.readNonSignificantWhitespace(); e != nil { + return NewTProtocolExceptionFromOsError(e) + } + cxt := _ParseContext(p.parseContextStack.Last()) + switch cxt { + case _CONTEXT_IN_LIST_FIRST: + p.parseContextStack.Pop() + p.parseContextStack.Push(int(_CONTEXT_IN_LIST)) + break + case _CONTEXT_IN_OBJECT_FIRST, _CONTEXT_IN_OBJECT_NEXT_KEY: + p.parseContextStack.Pop() + p.parseContextStack.Push(int(_CONTEXT_IN_OBJECT_NEXT_VALUE)) + break + case _CONTEXT_IN_OBJECT_NEXT_VALUE: + p.parseContextStack.Pop() + p.parseContextStack.Push(int(_CONTEXT_IN_OBJECT_NEXT_KEY)) + break + } + return nil +} + +func (p *TSimpleJSONProtocol) readNonSignificantWhitespace() os.Error { + for p.reader.Buffered() > 0 { + b, _ := p.reader.Peek(1) + if len(b) < 1 { + return nil + } + switch b[0] { + case ' ', '\r', '\n', '\t': + p.reader.ReadByte() + continue + default: + break + } + break + } + return nil +} + +func (p *TSimpleJSONProtocol) ParseStringBody() (string, TProtocolException) { + line, err := p.reader.ReadString(JSON_QUOTE) + if err != nil { + return "", NewTProtocolExceptionFromOsError(err) + } + l := len(line) + // count number of escapes to see if we need to keep going + i := 1 + for ; i < l; i++ { + if line[l-i-1] != '\\' { + break + } + } + if i&0x01 == 1 { + v, ok := JsonUnquote(string(JSON_QUOTE) + line) + if !ok { + return "", NewTProtocolExceptionFromOsError(err) + } + return v, nil + } + s, err := p.ParseQuotedStringBody() + if err != nil { + return "", NewTProtocolExceptionFromOsError(err) + } + str := string(JSON_QUOTE) + line + s + v, ok := JsonUnquote(str) + if !ok { + return "", NewTProtocolException(INVALID_DATA, "Unable to parse as JSON string "+str) + } + return v, nil +} + +func (p *TSimpleJSONProtocol) ParseQuotedStringBody() (string, TProtocolException) { + line, err := p.reader.ReadString(JSON_QUOTE) + if err != nil { + return "", NewTProtocolExceptionFromOsError(err) + } + l := len(line) + // count number of escapes to see if we need to keep going + i := 1 + for ; i < l; i++ { + if line[l-i-1] != '\\' { + break + } + } + if i&0x01 == 1 { + return line, nil + } + s, err := p.ParseQuotedStringBody() + if err != nil { + return "", NewTProtocolExceptionFromOsError(err) + } + v := line + s + return v, nil +} + +func (p *TSimpleJSONProtocol) ParseBase64EncodedBody() ([]byte, TProtocolException) { + line, err := p.reader.ReadBytes(JSON_QUOTE) + if err != nil { + return line, NewTProtocolExceptionFromOsError(err) + } + line2 := line[0 : len(line)-1] + l := len(line2) + output := make([]byte, base64.StdEncoding.DecodedLen(l)) + n, err := base64.StdEncoding.Decode(output, line2) + return output[0:n], NewTProtocolExceptionFromOsError(err) +} + +func (p *TSimpleJSONProtocol) ParseI64() (int64, bool, TProtocolException) { + if err := p.ParsePreValue(); err != nil { + return 0, false, err + } + var value int64 + var isnull bool + b, _ := p.reader.Peek(len(JSON_NULL)) + if len(b) >= len(JSON_NULL) && string(b) == string(JSON_NULL) { + p.reader.Read(b[0:len(JSON_NULL)]) + isnull = true + } else { + num, err := p.readNumeric() + isnull = (num == nil) + if !isnull { + value = num.Int64() + } + if err != nil { + return value, isnull, err + } + } + return value, isnull, p.ParsePostValue() +} + +func (p *TSimpleJSONProtocol) ParseF64() (float64, bool, TProtocolException) { + if err := p.ParsePreValue(); err != nil { + return 0, false, err + } + var value float64 + var isnull bool + b, _ := p.reader.Peek(len(JSON_NULL)) + if len(b) >= len(JSON_NULL) && string(b) == string(JSON_NULL) { + p.reader.Read(b[0:len(JSON_NULL)]) + isnull = true + } else { + num, err := p.readNumeric() + isnull = (num == nil) + if !isnull { + value = num.Float64() + } + if err != nil { + return value, isnull, err + } + } + return value, isnull, p.ParsePostValue() +} + +func (p *TSimpleJSONProtocol) ParseObjectStart() (bool, TProtocolException) { + if err := p.ParsePreValue(); err != nil { + return false, err + } + b, _ := p.reader.Peek(len(JSON_NULL)) + if len(b) > 0 && b[0] == JSON_LBRACE[0] { + p.reader.ReadByte() + p.parseContextStack.Push(int(_CONTEXT_IN_OBJECT_FIRST)) + return false, nil + } else if len(b) >= len(JSON_NULL) && string(b[0:len(JSON_NULL)]) == string(JSON_NULL) { + return true, nil + } + return false, NewTProtocolException(INVALID_DATA, fmt.Sprint("Expected '{' or null, but found '", string(b), "'")) +} + +func (p *TSimpleJSONProtocol) ParseObjectEnd() TProtocolException { + if isNull, err := p.readIfNull(); isNull || err != nil { + return err + } + cxt := _ParseContext(p.parseContextStack.Last()) + if cxt != _CONTEXT_IN_OBJECT_FIRST && cxt != _CONTEXT_IN_OBJECT_NEXT_KEY { + return NewTProtocolException(INVALID_DATA, fmt.Sprint("Expected to be in the Object Context, but not in Object Context")) + } + line, err := p.reader.ReadString(JSON_RBRACE[0]) + if err != nil { + return NewTProtocolExceptionFromOsError(err) + } + for _, char := range line { + switch char { + default: + return NewTProtocolException(INVALID_DATA, fmt.Sprint("Expecting end of object \"}\", but found: \"", line, "\"")) + case ' ', '\n', '\r', '\t', '}': + break + } + } + p.parseContextStack.Pop() + return p.ParsePostValue() +} + +func (p *TSimpleJSONProtocol) ParseListBegin() (bool, TProtocolException) { + if e := p.ParsePreValue(); e != nil { + return false, e + } + b, e := p.reader.Peek(len(JSON_NULL)) + if e == nil && len(b) >= 1 && b[0] == JSON_LBRACKET[0] { + p.parseContextStack.Push(int(_CONTEXT_IN_LIST_FIRST)) + p.reader.ReadByte() + return false, nil + } else if e == nil && len(b) >= len(JSON_NULL) && string(b) == string(JSON_NULL) { + return true, nil + } + return false, NewTProtocolException(INVALID_DATA, fmt.Sprintf("Expected 'null' or '{', received '%q'", b)) +} + +func (p *TSimpleJSONProtocol) ParseElemListBegin() (elemType TType, size int, e TProtocolException) { + if isNull, e := p.ParseListBegin(); isNull || e != nil { + return VOID, 0, e + } + bElemType, err := p.ReadByte() + elemType = TType(bElemType) + if err != nil { + return elemType, size, err + } + nSize, err2 := p.ReadI64() + size = int(nSize) + return elemType, size, err2 +} + +func (p *TSimpleJSONProtocol) ParseListEnd() TProtocolException { + if isNull, err := p.readIfNull(); isNull || err != nil { + return err + } + if _ParseContext(p.parseContextStack.Last()) != _CONTEXT_IN_LIST { + return NewTProtocolException(INVALID_DATA, "Expected to be in the List Context, but not in List Context") + } + line, err := p.reader.ReadString(JSON_RBRACKET[0]) + if err != nil { + return NewTProtocolExceptionFromOsError(err) + } + for _, char := range line { + switch char { + default: + return NewTProtocolException(INVALID_DATA, fmt.Sprint("Expecting end of list \"]\", but found: \"", line, "\"")) + case ' ', '\n', '\r', '\t', int(JSON_RBRACKET[0]): + break + } + } + p.parseContextStack.Pop() + return p.ParsePostValue() +} + +func (p *TSimpleJSONProtocol) readSingleValue() (interface{}, TType, TProtocolException) { + e := p.readNonSignificantWhitespace() + if e != nil { + return nil, VOID, NewTProtocolExceptionFromOsError(e) + } + b, e := p.reader.Peek(10) + if len(b) > 0 { + c := b[0] + switch c { + case JSON_NULL[0]: + buf := make([]byte, len(JSON_NULL)) + _, e := p.reader.Read(buf) + if e != nil { + return nil, VOID, NewTProtocolExceptionFromOsError(e) + } + if string(JSON_NULL) != string(buf) { + e := NewTProtocolException(INVALID_DATA, "Expected '"+string(JSON_NULL)+"' but found '"+string(buf)+"' while parsing JSON.") + return nil, VOID, e + } + return nil, VOID, nil + case JSON_QUOTE: + p.reader.ReadByte() + v, e := p.ParseStringBody() + if e != nil { + return v, UTF8, NewTProtocolExceptionFromOsError(e) + } + if v == JSON_INFINITY { + return INFINITY, DOUBLE, nil + } else if v == JSON_NEGATIVE_INFINITY { + return NEGATIVE_INFINITY, DOUBLE, nil + } else if v == JSON_NAN { + return NAN, DOUBLE, nil + } + return v, UTF8, nil + case JSON_TRUE[0]: + buf := make([]byte, len(JSON_TRUE)) + _, e := p.reader.Read(buf) + if e != nil { + return true, BOOL, NewTProtocolExceptionFromOsError(e) + } + if string(JSON_TRUE) != string(buf) { + e := NewTProtocolException(INVALID_DATA, "Expected '"+string(JSON_TRUE)+"' but found '"+string(buf)+"' while parsing JSON.") + return true, BOOL, NewTProtocolExceptionFromOsError(e) + } + return true, BOOL, nil + case JSON_FALSE[0]: + buf := make([]byte, len(JSON_FALSE)) + _, e := p.reader.Read(buf) + if e != nil { + return false, BOOL, NewTProtocolExceptionFromOsError(e) + } + if string(JSON_FALSE) != string(buf) { + e := NewTProtocolException(INVALID_DATA, "Expected '"+string(JSON_FALSE)+"' but found '"+string(buf)+"' while parsing JSON.") + return false, BOOL, NewTProtocolExceptionFromOsError(e) + } + return false, BOOL, nil + case JSON_LBRACKET[0]: + _, e := p.reader.ReadByte() + return make([]interface{}, 0), LIST, NewTProtocolExceptionFromOsError(e) + case JSON_LBRACE[0]: + _, e := p.reader.ReadByte() + return make(map[string]interface{}), STRUCT, NewTProtocolExceptionFromOsError(e) + case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'e', 'E', '.', '+', '-', JSON_INFINITY[0], JSON_NAN[0]: + // assume numeric + v, e := p.readNumeric() + return v, DOUBLE, e + default: + return nil, VOID, NewTProtocolException(INVALID_DATA, "Expected element in list but found '"+string(c)+"' while parsing JSON.") + } + } + return nil, VOID, NewTProtocolException(INVALID_DATA, "Cannot read a single element while parsing JSON.") + +} + + +func (p *TSimpleJSONProtocol) readIfNull() (bool, TProtocolException) { + cont := true + for p.reader.Buffered() > 0 && cont { + b, _ := p.reader.Peek(1) + if len(b) < 1 { + return false, nil + } + switch b[0] { + default: + return false, nil + case JSON_NULL[0]: + cont = false + break + case ' ', '\n', '\r', '\t': + p.reader.ReadByte() + break + } + } + if p.reader.Buffered() == 0 { + return false, nil + } + b, _ := p.reader.Peek(len(JSON_NULL)) + if string(b) == string(JSON_NULL) { + p.reader.Read(b[0:len(JSON_NULL)]) + return true, nil + } + return false, nil +} + +func (p *TSimpleJSONProtocol) readQuoteIfNext() { + if p.reader.Buffered() < 1 { + return + } + b, _ := p.reader.Peek(1) + if len(b) > 0 && b[0] == JSON_QUOTE { + p.reader.ReadByte() + } +} + +func (p *TSimpleJSONProtocol) readNumeric() (Numeric, TProtocolException) { + isNull, err := p.readIfNull() + if isNull || err != nil { + return NUMERIC_NULL, err + } + hasDecimalPoint := false + nextCanBeSign := true + hasE := false + MAX_LEN := 40 + buf := bytes.NewBuffer(make([]byte, 0, MAX_LEN)) + continueFor := true + inQuotes := false + for continueFor { + c, err := p.reader.ReadByte() + if err != nil { + if err == os.EOF { + break + } + return NUMERIC_NULL, NewTProtocolExceptionFromOsError(err) + } + switch c { + case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': + buf.WriteByte(c) + nextCanBeSign = false + case '.': + if hasDecimalPoint { + return NUMERIC_NULL, NewTProtocolException(INVALID_DATA, fmt.Sprintf("Unable to parse number with multiple decimal points '%s.'", buf.String())) + } + if hasE { + return NUMERIC_NULL, NewTProtocolException(INVALID_DATA, fmt.Sprintf("Unable to parse number with decimal points in the exponent '%s.'", buf.String())) + } + buf.WriteByte(c) + hasDecimalPoint, nextCanBeSign = true, false + case 'e', 'E': + if hasE { + return NUMERIC_NULL, NewTProtocolException(INVALID_DATA, fmt.Sprintf("Unable to parse number with multiple exponents '%s%c'", buf.String(), c)) + } + buf.WriteByte(c) + hasE, nextCanBeSign = true, true + case '-', '+': + if !nextCanBeSign { + return NUMERIC_NULL, NewTProtocolException(INVALID_DATA, fmt.Sprint("Negative sign within number")) + } + buf.WriteByte(c) + nextCanBeSign = false + case ' ', 0, '\t', '\n', '\r', JSON_RBRACE[0], JSON_RBRACKET[0], JSON_COMMA[0], JSON_COLON[0]: + p.reader.UnreadByte() + continueFor = false + case JSON_NAN[0]: + if buf.Len() == 0 { + buffer := make([]byte, len(JSON_NAN)) + buffer[0] = c + _, e := p.reader.Read(buffer[1:]) + if e != nil { + return NUMERIC_NULL, NewTProtocolExceptionFromOsError(e) + } + if JSON_NAN != string(buffer) { + e := NewTProtocolException(INVALID_DATA, "Expected '"+JSON_NAN+"' but found '"+string(buffer)+"' while parsing JSON.") + return NUMERIC_NULL, e + } + if inQuotes { + p.readQuoteIfNext() + } + return NAN, nil + } else { + return NUMERIC_NULL, NewTProtocolException(INVALID_DATA, fmt.Sprintf("Unable to parse number starting with character '%c'", c)) + } + case JSON_INFINITY[0]: + if buf.Len() == 0 || (buf.Len() == 1 && buf.Bytes()[0] == '+') { + buffer := make([]byte, len(JSON_INFINITY)) + buffer[0] = c + _, e := p.reader.Read(buffer[1:]) + if e != nil { + return NUMERIC_NULL, NewTProtocolExceptionFromOsError(e) + } + if JSON_INFINITY != string(buffer) { + e := NewTProtocolException(INVALID_DATA, "Expected '"+JSON_INFINITY+"' but found '"+string(buffer)+"' while parsing JSON.") + return NUMERIC_NULL, e + } + if inQuotes { + p.readQuoteIfNext() + } + return INFINITY, nil + } else if buf.Len() == 1 && buf.Bytes()[0] == JSON_NEGATIVE_INFINITY[0] { + buffer := make([]byte, len(JSON_NEGATIVE_INFINITY)) + buffer[0] = JSON_NEGATIVE_INFINITY[0] + buffer[1] = c + _, e := p.reader.Read(buffer[2:]) + if e != nil { + return NUMERIC_NULL, NewTProtocolExceptionFromOsError(e) + } + if JSON_NEGATIVE_INFINITY != string(buffer) { + e := NewTProtocolException(INVALID_DATA, "Expected '"+JSON_NEGATIVE_INFINITY+"' but found '"+string(buffer)+"' while parsing JSON.") + return NUMERIC_NULL, e + } + if inQuotes { + p.readQuoteIfNext() + } + return NEGATIVE_INFINITY, nil + } else { + return NUMERIC_NULL, NewTProtocolException(INVALID_DATA, fmt.Sprintf("Unable to parse number starting with character '%c' due to existing buffer %s", c, buf.String())) + } + case JSON_QUOTE: + if !inQuotes { + inQuotes = true + } else { + break + } + default: + return NUMERIC_NULL, NewTProtocolException(INVALID_DATA, fmt.Sprintf("Unable to parse number starting with character '%c'", c)) + } + } + if buf.Len() == 0 { + return NUMERIC_NULL, NewTProtocolException(INVALID_DATA, fmt.Sprint("Unable to parse number from empty string ''")) + } + return NewNumericFromJSONString(buf.String(), false), nil +} diff --git a/lib/go/thrift/tsimple_json_protocol_test.go b/lib/go/thrift/tsimple_json_protocol_test.go new file mode 100644 index 00000000..e57d55bb --- /dev/null +++ b/lib/go/thrift/tsimple_json_protocol_test.go @@ -0,0 +1,662 @@ +/* + * 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. + */ + +package thrift_test + +import ( + . "thrift" + "encoding/base64" + "fmt" + "json" + "math" + "strconv" + "strings" + "testing" +) + +func TestWriteSimpleJSONProtocolBool(t *testing.T) { + thetype := "boolean" + trans := NewTMemoryBuffer() + p := NewTSimpleJSONProtocol(trans) + for _, value := range BOOL_VALUES { + if e := p.WriteBool(value); e != nil { + t.Fatalf("Unable to write %s value %v due to error: %s", thetype, value, e.String()) + } + if e := p.Flush(); e != nil { + t.Fatalf("Unable to write %s value %v due to error flushing: %s", thetype, value, e.String()) + } + s := trans.String() + if s != fmt.Sprint(value) { + t.Fatalf("Bad value for %s %v: %s", thetype, value, s) + } + v := false + if err := json.Unmarshal([]byte(s), &v); err != nil || v != value { + t.Fatalf("Bad json-decoded value for %s %v, wrote: '%s', expected: '%v'", thetype, value, s, v) + } + trans.Reset() + } + trans.Close() +} + +func TestReadSimpleJSONProtocolBool(t *testing.T) { + thetype := "boolean" + for _, value := range BOOL_VALUES { + trans := NewTMemoryBuffer() + p := NewTSimpleJSONProtocol(trans) + if value { + trans.Write(JSON_TRUE) + } else { + trans.Write(JSON_FALSE) + } + trans.Flush() + s := trans.String() + v, e := p.ReadBool() + if e != nil { + t.Fatalf("Unable to read %s value %v due to error: %s", thetype, value, e.String()) + } + if v != value { + t.Fatalf("Bad value for %s value %v, wrote: %v, received: %v", thetype, value, s, v) + } + if err := json.Unmarshal([]byte(s), &v); err != nil || v != value { + t.Fatalf("Bad json-decoded value for %s %v, wrote: '%s', expected: '%v'", thetype, value, s, v) + } + trans.Reset() + trans.Close() + } +} + +func TestWriteSimpleJSONProtocolByte(t *testing.T) { + thetype := "byte" + trans := NewTMemoryBuffer() + p := NewTSimpleJSONProtocol(trans) + for _, value := range BYTE_VALUES { + if e := p.WriteByte(value); e != nil { + t.Fatalf("Unable to write %s value %v due to error: %s", thetype, value, e.String()) + } + if e := p.Flush(); e != nil { + t.Fatalf("Unable to write %s value %v due to error flushing: %s", thetype, value, e.String()) + } + s := trans.String() + if s != fmt.Sprint(value) { + t.Fatalf("Bad value for %s %v: %s", thetype, value, s) + } + v := byte(0) + if err := json.Unmarshal([]byte(s), &v); err != nil || v != value { + t.Fatalf("Bad json-decoded value for %s %v, wrote: '%s', expected: '%v'", thetype, value, s, v) + } + trans.Reset() + } + trans.Close() +} + +func TestReadSimpleJSONProtocolByte(t *testing.T) { + thetype := "byte" + for _, value := range BYTE_VALUES { + trans := NewTMemoryBuffer() + p := NewTSimpleJSONProtocol(trans) + trans.WriteString(strconv.Itoa(int(value))) + trans.Flush() + s := trans.String() + v, e := p.ReadByte() + if e != nil { + t.Fatalf("Unable to read %s value %v due to error: %s", thetype, value, e.String()) + } + if v != value { + t.Fatalf("Bad value for %s value %v, wrote: %v, received: %v", thetype, value, s, v) + } + if err := json.Unmarshal([]byte(s), &v); err != nil || v != value { + t.Fatalf("Bad json-decoded value for %s %v, wrote: '%s', expected: '%v'", thetype, value, s, v) + } + trans.Reset() + trans.Close() + } +} + +func TestWriteSimpleJSONProtocolI16(t *testing.T) { + thetype := "int16" + trans := NewTMemoryBuffer() + p := NewTSimpleJSONProtocol(trans) + for _, value := range INT16_VALUES { + if e := p.WriteI16(value); e != nil { + t.Fatalf("Unable to write %s value %v due to error: %s", thetype, value, e.String()) + } + if e := p.Flush(); e != nil { + t.Fatalf("Unable to write %s value %v due to error flushing: %s", thetype, value, e.String()) + } + s := trans.String() + if s != fmt.Sprint(value) { + t.Fatalf("Bad value for %s %v: %s", thetype, value, s) + } + v := int16(0) + if err := json.Unmarshal([]byte(s), &v); err != nil || v != value { + t.Fatalf("Bad json-decoded value for %s %v, wrote: '%s', expected: '%v'", thetype, value, s, v) + } + trans.Reset() + } + trans.Close() +} + +func TestReadSimpleJSONProtocolI16(t *testing.T) { + thetype := "int16" + for _, value := range INT16_VALUES { + trans := NewTMemoryBuffer() + p := NewTSimpleJSONProtocol(trans) + trans.WriteString(strconv.Itoa(int(value))) + trans.Flush() + s := trans.String() + v, e := p.ReadI16() + if e != nil { + t.Fatalf("Unable to read %s value %v due to error: %s", thetype, value, e.String()) + } + if v != value { + t.Fatalf("Bad value for %s value %v, wrote: %v, received: %v", thetype, value, s, v) + } + if err := json.Unmarshal([]byte(s), &v); err != nil || v != value { + t.Fatalf("Bad json-decoded value for %s %v, wrote: '%s', expected: '%v'", thetype, value, s, v) + } + trans.Reset() + trans.Close() + } +} + +func TestWriteSimpleJSONProtocolI32(t *testing.T) { + thetype := "int32" + trans := NewTMemoryBuffer() + p := NewTSimpleJSONProtocol(trans) + for _, value := range INT32_VALUES { + if e := p.WriteI32(value); e != nil { + t.Fatalf("Unable to write %s value %v due to error: %s", thetype, value, e.String()) + } + if e := p.Flush(); e != nil { + t.Fatalf("Unable to write %s value %v due to error flushing: %s", thetype, value, e.String()) + } + s := trans.String() + if s != fmt.Sprint(value) { + t.Fatalf("Bad value for %s %v: %s", thetype, value, s) + } + v := int32(0) + if err := json.Unmarshal([]byte(s), &v); err != nil || v != value { + t.Fatalf("Bad json-decoded value for %s %v, wrote: '%s', expected: '%v'", thetype, value, s, v) + } + trans.Reset() + } + trans.Close() +} + +func TestReadSimpleJSONProtocolI32(t *testing.T) { + thetype := "int32" + for _, value := range INT32_VALUES { + trans := NewTMemoryBuffer() + p := NewTSimpleJSONProtocol(trans) + trans.WriteString(strconv.Itoa(int(value))) + trans.Flush() + s := trans.String() + v, e := p.ReadI32() + if e != nil { + t.Fatalf("Unable to read %s value %v due to error: %s", thetype, value, e.String()) + } + if v != value { + t.Fatalf("Bad value for %s value %v, wrote: %v, received: %v", thetype, value, s, v) + } + if err := json.Unmarshal([]byte(s), &v); err != nil || v != value { + t.Fatalf("Bad json-decoded value for %s %v, wrote: '%s', expected: '%v'", thetype, value, s, v) + } + trans.Reset() + trans.Close() + } +} + +func TestWriteSimpleJSONProtocolI64(t *testing.T) { + thetype := "int64" + trans := NewTMemoryBuffer() + p := NewTSimpleJSONProtocol(trans) + for _, value := range INT64_VALUES { + if e := p.WriteI64(value); e != nil { + t.Fatalf("Unable to write %s value %v due to error: %s", thetype, value, e.String()) + } + if e := p.Flush(); e != nil { + t.Fatalf("Unable to write %s value %v due to error flushing: %s", thetype, value, e.String()) + } + s := trans.String() + if s != fmt.Sprint(value) { + t.Fatalf("Bad value for %s %v: %s", thetype, value, s) + } + v := int64(0) + if err := json.Unmarshal([]byte(s), &v); err != nil || v != value { + t.Fatalf("Bad json-decoded value for %s %v, wrote: '%s', expected: '%v'", thetype, value, s, v) + } + trans.Reset() + } + trans.Close() +} + +func TestReadSimpleJSONProtocolI64(t *testing.T) { + thetype := "int64" + for _, value := range INT64_VALUES { + trans := NewTMemoryBuffer() + p := NewTSimpleJSONProtocol(trans) + trans.WriteString(strconv.Itoa64(value)) + trans.Flush() + s := trans.String() + v, e := p.ReadI64() + if e != nil { + t.Fatalf("Unable to read %s value %v due to error: %s", thetype, value, e.String()) + } + if v != value { + t.Fatalf("Bad value for %s value %v, wrote: %v, received: %v", thetype, value, s, v) + } + if err := json.Unmarshal([]byte(s), &v); err != nil || v != value { + t.Fatalf("Bad json-decoded value for %s %v, wrote: '%s', expected: '%v'", thetype, value, s, v) + } + trans.Reset() + trans.Close() + } +} + +func TestWriteSimpleJSONProtocolDouble(t *testing.T) { + thetype := "double" + trans := NewTMemoryBuffer() + p := NewTSimpleJSONProtocol(trans) + for _, value := range DOUBLE_VALUES { + if e := p.WriteDouble(value); e != nil { + t.Fatalf("Unable to write %s value %v due to error: %s", thetype, value, e.String()) + } + if e := p.Flush(); e != nil { + t.Fatalf("Unable to write %s value %v due to error flushing: %s", thetype, value, e.String()) + } + s := trans.String() + if math.IsInf(value, 1) { + if s != JsonQuote(JSON_INFINITY) { + t.Fatalf("Bad value for %s %v, wrote: %v, expected: %v", thetype, value, s, JsonQuote(JSON_INFINITY)) + } + } else if math.IsInf(value, -1) { + if s != JsonQuote(JSON_NEGATIVE_INFINITY) { + t.Fatalf("Bad value for %s %v, wrote: %v, expected: %v", thetype, value, s, JsonQuote(JSON_NEGATIVE_INFINITY)) + } + } else if math.IsNaN(value) { + if s != JsonQuote(JSON_NAN) { + t.Fatalf("Bad value for %s %v, wrote: %v, expected: %v", thetype, value, s, JsonQuote(JSON_NAN)) + } + } else { + if s != fmt.Sprint(value) { + t.Fatalf("Bad value for %s %v: %s", thetype, value, s) + } + v := float64(0) + if err := json.Unmarshal([]byte(s), &v); err != nil || v != value { + t.Fatalf("Bad json-decoded value for %s %v, wrote: '%s', expected: '%v'", thetype, value, s, v) + } + } + trans.Reset() + } + trans.Close() +} + +func TestReadSimpleJSONProtocolDouble(t *testing.T) { + thetype := "double" + for _, value := range DOUBLE_VALUES { + trans := NewTMemoryBuffer() + p := NewTSimpleJSONProtocol(trans) + n := NewNumericFromDouble(value) + trans.WriteString(n.String()) + trans.Flush() + s := trans.String() + v, e := p.ReadDouble() + if e != nil { + t.Fatalf("Unable to read %s value %v due to error: %s", thetype, value, e.String()) + } + if math.IsInf(value, 1) { + if !math.IsInf(v, 1) { + t.Fatalf("Bad value for %s %v, wrote: %v, received: %v", thetype, value, s, v) + } + } else if math.IsInf(value, -1) { + if !math.IsInf(v, -1) { + t.Fatalf("Bad value for %s %v, wrote: %v, received: %v", thetype, value, s, v) + } + } else if math.IsNaN(value) { + if !math.IsNaN(v) { + t.Fatalf("Bad value for %s %v, wrote: %v, received: %v", thetype, value, s, v) + } + } else { + if v != value { + t.Fatalf("Bad value for %s value %v, wrote: %v, received: %v", thetype, value, s, v) + } + if err := json.Unmarshal([]byte(s), &v); err != nil || v != value { + t.Fatalf("Bad json-decoded value for %s %v, wrote: '%s', expected: '%v'", thetype, value, s, v) + } + } + trans.Reset() + trans.Close() + } +} + +func TestWriteSimpleJSONProtocolString(t *testing.T) { + thetype := "string" + trans := NewTMemoryBuffer() + p := NewTSimpleJSONProtocol(trans) + for _, value := range STRING_VALUES { + if e := p.WriteString(value); e != nil { + t.Fatalf("Unable to write %s value %v due to error: %s", thetype, value, e.String()) + } + if e := p.Flush(); e != nil { + t.Fatalf("Unable to write %s value %v due to error flushing: %s", thetype, value, e.String()) + } + s := trans.String() + if s[0] != '"' || s[len(s)-1] != '"' { + t.Fatalf("Bad value for %s '%v', wrote '%v', expected: %v", thetype, value, s, fmt.Sprint("\"", value, "\"")) + } + v := new(string) + if err := json.Unmarshal([]byte(s), v); err != nil || *v != value { + t.Fatalf("Bad json-decoded value for %s %v, wrote: '%s', expected: '%v'", thetype, value, s, *v) + } + trans.Reset() + } + trans.Close() +} + +func TestReadSimpleJSONProtocolString(t *testing.T) { + thetype := "string" + for _, value := range STRING_VALUES { + trans := NewTMemoryBuffer() + p := NewTSimpleJSONProtocol(trans) + trans.WriteString(JsonQuote(value)) + trans.Flush() + s := trans.String() + v, e := p.ReadString() + if e != nil { + t.Fatalf("Unable to read %s value %v due to error: %s", thetype, value, e.String()) + } + if v != value { + t.Fatalf("Bad value for %s value %v, wrote: %v, received: %v", thetype, value, s, v) + } + v1 := new(string) + if err := json.Unmarshal([]byte(s), v1); err != nil || *v1 != value { + t.Fatalf("Bad json-decoded value for %s %v, wrote: '%s', expected: '%v'", thetype, value, s, *v1) + } + trans.Reset() + trans.Close() + } +} + +func TestWriteSimpleJSONProtocolBinary(t *testing.T) { + thetype := "binary" + value := protocol_bdata + b64value := make([]byte, base64.StdEncoding.EncodedLen(len(protocol_bdata))) + base64.StdEncoding.Encode(b64value, value) + b64String := string(b64value) + trans := NewTMemoryBuffer() + p := NewTSimpleJSONProtocol(trans) + if e := p.WriteBinary(value); e != nil { + t.Fatalf("Unable to write %s value %v due to error: %s", thetype, value, e.String()) + } + if e := p.Flush(); e != nil { + t.Fatalf("Unable to write %s value %v due to error flushing: %s", thetype, value, e.String()) + } + s := trans.String() + if s != fmt.Sprint("\"", b64String, "\"") { + t.Fatalf("Bad value for %s %v\n wrote: %v\nexpected: %v", thetype, value, s, "\""+b64String+"\"") + } + v1 := new(string) + if err := json.Unmarshal([]byte(s), v1); err != nil || *v1 != b64String { + t.Fatalf("Bad json-decoded value for %s %v, wrote: '%s', expected: '%v'", thetype, value, s, *v1) + } + trans.Close() +} + +func TestReadSimpleJSONProtocolBinary(t *testing.T) { + thetype := "binary" + value := protocol_bdata + b64value := make([]byte, base64.StdEncoding.EncodedLen(len(protocol_bdata))) + base64.StdEncoding.Encode(b64value, value) + b64String := string(b64value) + trans := NewTMemoryBuffer() + p := NewTSimpleJSONProtocol(trans) + trans.WriteString(JsonQuote(b64String)) + trans.Flush() + s := trans.String() + v, e := p.ReadBinary() + if e != nil { + t.Fatalf("Unable to read %s value %v due to error: %s", thetype, value, e.String()) + } + if len(v) != len(value) { + t.Fatalf("Bad value for %s value length %v, wrote: %v, received length: %v", thetype, len(value), s, len(v)) + } + for i := 0; i < len(v); i++ { + if v[i] != value[i] { + t.Fatalf("Bad value for %s at index %d value %v, wrote: %v, received: %v", thetype, i, value[i], s, v[i]) + } + } + v1 := new(string) + if err := json.Unmarshal([]byte(s), v1); err != nil || *v1 != b64String { + t.Fatalf("Bad json-decoded value for %s %v, wrote: '%s', expected: '%v'", thetype, value, s, *v1) + } + trans.Reset() + trans.Close() +} + +func TestWriteSimpleJSONProtocolList(t *testing.T) { + thetype := "list" + trans := NewTMemoryBuffer() + p := NewTSimpleJSONProtocol(trans) + p.WriteListBegin(TType(DOUBLE), len(DOUBLE_VALUES)) + for _, value := range DOUBLE_VALUES { + if e := p.WriteDouble(value); e != nil { + t.Fatalf("Unable to write %s value %v due to error: %s", thetype, value, e.String()) + } + } + p.WriteListEnd() + if e := p.Flush(); e != nil { + t.Fatalf("Unable to write %s due to error flushing: %s", thetype, e.String()) + } + str := trans.String() + str1 := new([]interface{}) + err := json.Unmarshal([]byte(str), str1) + if err != nil { + t.Fatalf("Unable to decode %s, wrote: %s", thetype, str) + } + l := *str1 + if len(l) < 2 { + t.Fatalf("List must be at least of length two to include metadata") + } + if int(l[0].(float64)) != DOUBLE { + t.Fatal("Invalid type for list, expected: ", DOUBLE, ", but was: ", l[0]) + } + if int(l[1].(float64)) != len(DOUBLE_VALUES) { + t.Fatal("Invalid length for list, expected: ", len(DOUBLE_VALUES), ", but was: ", l[1]) + } + for k, value := range DOUBLE_VALUES { + s := l[k+2] + if math.IsInf(value, 1) { + if s.(string) != JSON_INFINITY { + t.Fatalf("Bad value for %s at index %v %v, wrote: %q, expected: %q, originally wrote: %q", thetype, k, value, s, JsonQuote(JSON_INFINITY), str) + } + } else if math.IsInf(value, 0) { + if s.(string) != JSON_NEGATIVE_INFINITY { + t.Fatalf("Bad value for %s at index %v %v, wrote: %q, expected: %q, originally wrote: %q", thetype, k, value, s, JsonQuote(JSON_NEGATIVE_INFINITY), str) + } + } else if math.IsNaN(value) { + if s.(string) != JSON_NAN { + t.Fatalf("Bad value for %s at index %v %v, wrote: %q, expected: %q, originally wrote: %q", thetype, k, value, s, JsonQuote(JSON_NAN), str) + } + } else { + if s.(float64) != value { + t.Fatalf("Bad json-decoded value for %s %v, wrote: '%s'", thetype, value, s) + } + } + trans.Reset() + } + trans.Close() +} + +func TestWriteSimpleJSONProtocolSet(t *testing.T) { + thetype := "set" + trans := NewTMemoryBuffer() + p := NewTSimpleJSONProtocol(trans) + p.WriteSetBegin(TType(DOUBLE), len(DOUBLE_VALUES)) + for _, value := range DOUBLE_VALUES { + if e := p.WriteDouble(value); e != nil { + t.Fatalf("Unable to write %s value %v due to error: %s", thetype, value, e.String()) + } + } + p.WriteSetEnd() + if e := p.Flush(); e != nil { + t.Fatalf("Unable to write %s due to error flushing: %s", thetype, e.String()) + } + str := trans.String() + str1 := new([]interface{}) + err := json.Unmarshal([]byte(str), str1) + if err != nil { + t.Fatalf("Unable to decode %s, wrote: %s", thetype, str) + } + l := *str1 + if len(l) < 2 { + t.Fatalf("Set must be at least of length two to include metadata") + } + if int(l[0].(float64)) != DOUBLE { + t.Fatal("Invalid type for set, expected: ", DOUBLE, ", but was: ", l[0]) + } + if int(l[1].(float64)) != len(DOUBLE_VALUES) { + t.Fatal("Invalid length for set, expected: ", len(DOUBLE_VALUES), ", but was: ", l[1]) + } + for k, value := range DOUBLE_VALUES { + s := l[k+2] + if math.IsInf(value, 1) { + if s.(string) != JSON_INFINITY { + t.Fatalf("Bad value for %s at index %v %v, wrote: %q, expected: %q, originally wrote: %q", thetype, k, value, s, JsonQuote(JSON_INFINITY), str) + } + } else if math.IsInf(value, 0) { + if s.(string) != JSON_NEGATIVE_INFINITY { + t.Fatalf("Bad value for %s at index %v %v, wrote: %q, expected: %q, originally wrote: %q", thetype, k, value, s, JsonQuote(JSON_NEGATIVE_INFINITY), str) + } + } else if math.IsNaN(value) { + if s.(string) != JSON_NAN { + t.Fatalf("Bad value for %s at index %v %v, wrote: %q, expected: %q, originally wrote: %q", thetype, k, value, s, JsonQuote(JSON_NAN), str) + } + } else { + if s.(float64) != value { + t.Fatalf("Bad json-decoded value for %s %v, wrote: '%s'", thetype, value, s) + } + } + trans.Reset() + } + trans.Close() +} + +func TestWriteSimpleJSONProtocolMap(t *testing.T) { + thetype := "map" + trans := NewTMemoryBuffer() + p := NewTSimpleJSONProtocol(trans) + p.WriteMapBegin(TType(I32), TType(DOUBLE), len(DOUBLE_VALUES)) + for k, value := range DOUBLE_VALUES { + if e := p.WriteI32(int32(k)); e != nil { + t.Fatalf("Unable to write %s key int32 value %v due to error: %s", thetype, k, e.String()) + } + if e := p.WriteDouble(value); e != nil { + t.Fatalf("Unable to write %s value float64 value %v due to error: %s", thetype, value, e.String()) + } + } + p.WriteMapEnd() + if e := p.Flush(); e != nil { + t.Fatalf("Unable to write %s due to error flushing: %s", thetype, e.String()) + } + str := trans.String() + if str[0] != '[' || str[len(str)-1] != ']' { + t.Fatalf("Bad value for %s, wrote: %q, in go: %q", thetype, str, DOUBLE_VALUES) + } + l := strings.Split(str[1:len(str)-1], ",", -1) + if len(l) < 3 { + t.Fatal("Expected list of at least length 3 for map for metadata, but was of length ", len(l)) + } + expectedKeyType, _ := strconv.Atoi(l[0]) + expectedValueType, _ := strconv.Atoi(l[1]) + expectedSize, _ := strconv.Atoi(l[2]) + if expectedKeyType != I32 { + t.Fatal("Expected map key type ", I32, ", but was ", l[0]) + } + if expectedValueType != DOUBLE { + t.Fatal("Expected map value type ", DOUBLE, ", but was ", l[1]) + } + if expectedSize != len(DOUBLE_VALUES) { + t.Fatal("Expected map size of ", len(DOUBLE_VALUES), ", but was ", l[2]) + } + for k, value := range DOUBLE_VALUES { + strk := l[k*2+3] + strv := l[k*2+4] + ik, err := strconv.Atoi(strk) + if err != nil { + t.Fatalf("Bad value for %s index %v, wrote: %v, expected: %v, error: %s", thetype, k, strk, string(k), err.String()) + } + if ik != k { + t.Fatalf("Bad value for %s index %v, wrote: %v, expected: %v", thetype, k, strk, k) + } + s := strv + if math.IsInf(value, 1) { + if s != JsonQuote(JSON_INFINITY) { + t.Fatalf("Bad value for %s at index %v %v, wrote: %v, expected: %v", thetype, k, value, s, JsonQuote(JSON_INFINITY)) + } + } else if math.IsInf(value, 0) { + if s != JsonQuote(JSON_NEGATIVE_INFINITY) { + t.Fatalf("Bad value for %s at index %v %v, wrote: %v, expected: %v", thetype, k, value, s, JsonQuote(JSON_NEGATIVE_INFINITY)) + } + } else if math.IsNaN(value) { + if s != JsonQuote(JSON_NAN) { + t.Fatalf("Bad value for %s at index %v %v, wrote: %v, expected: %v", thetype, k, value, s, JsonQuote(JSON_NAN)) + } + } else { + expected := strconv.Ftoa64(value, 'g', 10) + if s != expected { + t.Fatalf("Bad value for %s at index %v %v, wrote: %v, expected %v", thetype, k, value, s, expected) + } + v := float64(0) + if err := json.Unmarshal([]byte(s), &v); err != nil || v != value { + t.Fatalf("Bad json-decoded value for %s %v, wrote: '%s', expected: '%v'", thetype, value, s, v) + } + } + trans.Reset() + } + trans.Close() +} + + +func TestReadWriteSimpleJSONStruct(t *testing.T) { + thetype := "struct" + trans := NewTMemoryBuffer() + p := NewTSimpleJSONProtocol(trans) + orig := NewWork() + orig.Num1 = 25 + orig.Num2 = 102 + orig.Op = ADD + orig.Comment = "Add: 25 + 102" + if e := orig.Write(p); e != nil { + t.Fatalf("Unable to write %s value %#v due to error: %s", thetype, orig, e.String()) + } + t.Log("Memory buffer contents: ", trans.String()) + read := NewWork() + e := read.Read(p) + t.Logf("Read %s value: %#v", thetype, read) + if e != nil { + t.Fatalf("Unable to read %s due to error: %s", thetype, e.String()) + } + if !orig.Equals(read) { + t.Fatalf("Original Write != Read: %#v != %#v ", orig, read) + } +} + +func TestReadWriteSimpleJSONProtocol(t *testing.T) { + ReadWriteProtocolTest(t, NewTSimpleJSONProtocolFactory()) +} diff --git a/lib/go/thrift/tsimple_server.go b/lib/go/thrift/tsimple_server.go new file mode 100644 index 00000000..6c7d656e --- /dev/null +++ b/lib/go/thrift/tsimple_server.go @@ -0,0 +1,166 @@ +/* + * 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. + */ + +package thrift + +import ( + "os" +) + + +/** + * Simple singlethreaded server for testing. + * + */ +type TSimpleServer struct { + stopped bool + + processorFactory TProcessorFactory + serverTransport TServerTransport + inputTransportFactory TTransportFactory + outputTransportFactory TTransportFactory + inputProtocolFactory TProtocolFactory + outputProtocolFactory TProtocolFactory +} + +func NewTSimpleServer2(processor TProcessor, serverTransport TServerTransport) *TSimpleServer { + return NewTSimpleServerFactory2(NewTProcessorFactory(processor), serverTransport) +} + +func NewTSimpleServer4(processor TProcessor, serverTransport TServerTransport, transportFactory TTransportFactory, protocolFactory TProtocolFactory) *TSimpleServer { + return NewTSimpleServerFactory4(NewTProcessorFactory(processor), + serverTransport, + transportFactory, + protocolFactory, + ) +} + +func NewTSimpleServer6(processor TProcessor, serverTransport TServerTransport, inputTransportFactory TTransportFactory, outputTransportFactory TTransportFactory, inputProtocolFactory TProtocolFactory, outputProtocolFactory TProtocolFactory) *TSimpleServer { + return NewTSimpleServerFactory6(NewTProcessorFactory(processor), + serverTransport, + inputTransportFactory, + outputTransportFactory, + inputProtocolFactory, + outputProtocolFactory, + ) +} + +func NewTSimpleServerFactory2(processorFactory TProcessorFactory, serverTransport TServerTransport) *TSimpleServer { + return NewTSimpleServerFactory6(processorFactory, + serverTransport, + NewTTransportFactory(), + NewTTransportFactory(), + NewTBinaryProtocolFactoryDefault(), + NewTBinaryProtocolFactoryDefault(), + ) +} + +func NewTSimpleServerFactory4(processorFactory TProcessorFactory, serverTransport TServerTransport, transportFactory TTransportFactory, protocolFactory TProtocolFactory) *TSimpleServer { + return NewTSimpleServerFactory6(processorFactory, + serverTransport, + transportFactory, + transportFactory, + protocolFactory, + protocolFactory, + ) +} + +func NewTSimpleServerFactory6(processorFactory TProcessorFactory, serverTransport TServerTransport, inputTransportFactory TTransportFactory, outputTransportFactory TTransportFactory, inputProtocolFactory TProtocolFactory, outputProtocolFactory TProtocolFactory) *TSimpleServer { + return &TSimpleServer{processorFactory: processorFactory, + serverTransport: serverTransport, + inputTransportFactory: inputTransportFactory, + outputTransportFactory: outputTransportFactory, + inputProtocolFactory: inputProtocolFactory, + outputProtocolFactory: outputProtocolFactory, + } +} + +func (p *TSimpleServer) ProcessorFactory() TProcessorFactory { + return p.processorFactory +} + +func (p *TSimpleServer) ServerTransport() TServerTransport { + return p.serverTransport +} + +func (p *TSimpleServer) InputTransportFactory() TTransportFactory { + return p.inputTransportFactory +} + +func (p *TSimpleServer) OutputTransportFactory() TTransportFactory { + return p.outputTransportFactory +} + +func (p *TSimpleServer) InputProtocolFactory() TProtocolFactory { + return p.inputProtocolFactory +} + +func (p *TSimpleServer) OutputProtocolFactory() TProtocolFactory { + return p.outputProtocolFactory +} + +func (p *TSimpleServer) Serve() os.Error { + p.stopped = false + err := p.serverTransport.Listen() + if err != nil { + return err + } + for !p.stopped { + client, err := p.serverTransport.Accept() + if err != nil { + return err + } + if client != nil { + p.processRequest(client) + } + } + return nil +} + +func (p *TSimpleServer) Stop() os.Error { + p.stopped = true + p.serverTransport.Interrupt() + return nil +} + +func (p *TSimpleServer) processRequest(client TTransport) { + processor := p.processorFactory.GetProcessor(client) + inputTransport := p.inputTransportFactory.GetTransport(client) + outputTransport := p.outputTransportFactory.GetTransport(client) + inputProtocol := p.inputProtocolFactory.GetProtocol(inputTransport) + outputProtocol := p.outputProtocolFactory.GetProtocol(outputTransport) + if inputTransport != nil { + defer inputTransport.Close() + } + if outputTransport != nil { + defer outputTransport.Close() + } + for { + ok, e := processor.Process(inputProtocol, outputProtocol) + if e != nil { + if !p.stopped { + // TODO(pomack) log error + break + } + } + if !ok { + break + } + } +} diff --git a/lib/go/thrift/tsocket.go b/lib/go/thrift/tsocket.go new file mode 100644 index 00000000..758c1326 --- /dev/null +++ b/lib/go/thrift/tsocket.go @@ -0,0 +1,203 @@ +/* + * 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. + */ + +package thrift + +import ( + "net" + "os" + "bytes" +) + +/** + * Socket implementation of the TTransport interface. To be commented soon! + * + */ +type TSocket struct { + writeBuffer *bytes.Buffer + /** + * Wrapped Socket object + */ + conn net.Conn + /** + * Remote Addr + */ + addr net.Addr + /** + * Socket timeout in nanoseconds + */ + nsecTimeout int64 +} + +/** + * Constructor that takes an already created socket. + * + * @param socket Already created socket object + * @throws TTransportException if there is an error setting up the streams + */ +func NewTSocketConn(connection net.Conn) (*TSocket, TTransportException) { + address := connection.RemoteAddr() + if address == nil { + address = connection.LocalAddr() + } + p := &TSocket{conn: connection, addr: address, nsecTimeout: 0, writeBuffer: bytes.NewBuffer(make([]byte, 0, 4096))} + return p, nil +} + +/** + * Creates a new unconnected socket that will connect to the given host + * on the given port. + * + * @param host Remote host + * @param port Remote port + */ +func NewTSocketAddr(address net.Addr) *TSocket { + return NewTSocket(address, 0) +} + +/** + * Creates a new unconnected socket that will connect to the given host + * on the given port. + * + * @param host Remote host + * @param port Remote port + * @param nsecTimeout Socket timeout + */ +func NewTSocket(address net.Addr, nsecTimeout int64) *TSocket { + sock := &TSocket{addr: address, nsecTimeout: nsecTimeout, writeBuffer: bytes.NewBuffer(make([]byte, 0, 4096))} + return sock +} + +/** + * Sets the socket timeout + * + * @param timeout Nanoseconds timeout + */ +func (p *TSocket) SetTimeout(nsecTimeout int64) os.Error { + p.nsecTimeout = nsecTimeout + if p.IsOpen() { + if err := p.conn.SetTimeout(nsecTimeout); err != nil { + LOGGER.Print("Could not set socket timeout.", err) + return err + } + } + return nil +} + +/** + * Returns a reference to the underlying socket. + */ +func (p *TSocket) Conn() net.Conn { + return p.conn +} + +/** + * Checks whether the socket is connected. + */ +func (p *TSocket) IsOpen() bool { + if p.conn == nil { + return false + } + return true +} + +/** + * Connects the socket, creating a new socket object if necessary. + */ +func (p *TSocket) Open() os.Error { + if p.IsOpen() { + return NewTTransportException(ALREADY_OPEN, "Socket already connected.") + } + if p.addr == nil { + return NewTTransportException(NOT_OPEN, "Cannot open nil address.") + } + if len(p.addr.Network()) == 0 { + return NewTTransportException(NOT_OPEN, "Cannot open bad network name.") + } + if len(p.addr.String()) == 0 { + return NewTTransportException(NOT_OPEN, "Cannot open bad address.") + } + var err os.Error + if p.conn, err = net.Dial(p.addr.Network(), "", p.addr.String()); err != nil { + LOGGER.Print("Could not open socket", err.String()) + return NewTTransportException(NOT_OPEN, err.String()) + } + if p.conn != nil { + p.conn.SetTimeout(p.nsecTimeout) + } + return nil +} + +/** + * Closes the socket. + */ +func (p *TSocket) Close() os.Error { + // Close the socket + if p.conn != nil { + err := p.conn.Close() + if err != nil { + LOGGER.Print("Could not close socket. ", err.String()) + return err + } + p.conn = nil + } + return nil +} + + +func (p *TSocket) Read(buf []byte) (int, os.Error) { + if !p.IsOpen() { + return 0, NewTTransportException(NOT_OPEN, "Connection not open") + } + n, err := p.conn.Read(buf) + return n, NewTTransportExceptionFromOsError(err) +} + + +func (p *TSocket) ReadAll(buf []byte) (int, os.Error) { + return ReadAllTransport(p, buf) +} + +func (p *TSocket) Write(buf []byte) (int, os.Error) { + if !p.IsOpen() { + return 0, NewTTransportException(NOT_OPEN, "Connection not open") + } + p.writeBuffer.Write(buf) + return len(buf), nil +} + +func (p *TSocket) Peek() bool { + return p.IsOpen() +} + +func (p *TSocket) Flush() os.Error { + if !p.IsOpen() { + return NewTTransportException(NOT_OPEN, "Connection not open") + } + _, err := p.writeBuffer.WriteTo(p.conn) + return NewTTransportExceptionFromOsError(err) +} + +func (p *TSocket) Interrupt() os.Error { + if !p.IsOpen() { + return nil + } + // TODO(pomack) fix Interrupt as this is probably wrong + return p.conn.Close() +} diff --git a/lib/go/thrift/tstruct.go b/lib/go/thrift/tstruct.go new file mode 100644 index 00000000..5eaffae0 --- /dev/null +++ b/lib/go/thrift/tstruct.go @@ -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. + */ + +package thrift + +/** + * Helper class that encapsulates struct metadata. + * + */ +type TStruct interface { + TFieldContainer + TStructName() string + ThriftName() string + TStructFields() TFieldContainer + String() string + AttributeFromFieldId(fieldId int) interface{} + AttributeFromFieldName(fieldName string) interface{} +} + +type tStruct struct { + TFieldContainer + name string +} + +func NewTStructEmpty(name string) TStruct { + return &tStruct{ + name: name, + TFieldContainer: NewTFieldContainer(make([]TField, 0, 0)), + } +} + +func NewTStruct(name string, fields []TField) TStruct { + return &tStruct{ + name: name, + TFieldContainer: NewTFieldContainer(fields), + } +} + +func (p *tStruct) TStructName() string { + return p.name +} + +func (p *tStruct) ThriftName() string { + return p.name +} + +func (p *tStruct) TStructFields() TFieldContainer { + return p.TFieldContainer +} + +func (p *tStruct) String() string { + return p.name +} + +func (p *tStruct) Equals(other interface{}) bool { + cmp, ok := p.CompareTo(other) + return ok && cmp == 0 +} + +func (p *tStruct) CompareTo(other interface{}) (int, bool) { + return TType(STRUCT).Compare(p, other) +} + +func (p *tStruct) AttributeFromFieldId(fieldId int) interface{} { + return nil +} + +func (p *tStruct) AttributeFromFieldName(fieldName string) interface{} { + return p.AttributeFromFieldId(p.FieldIdFromFieldName(fieldName)) +} + + +var ANONYMOUS_STRUCT TStruct + +func init() { + ANONYMOUS_STRUCT = NewTStructEmpty("") +} diff --git a/lib/go/thrift/ttransport.go b/lib/go/thrift/ttransport.go new file mode 100644 index 00000000..45557f85 --- /dev/null +++ b/lib/go/thrift/ttransport.go @@ -0,0 +1,181 @@ +/* + * 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. + */ + +package thrift + +import ( + "os" + "log" +) + +type Flusher interface { + Flush() (err os.Error) +} + +/** + * Generic class that encapsulates the I/O layer. This is basically a thin + * wrapper around the combined functionality of Java input/output streams. + * + */ +type TTransport interface { + /** + * Queries whether the transport is open. + * + * @return True if the transport is open. + */ + IsOpen() bool + + /** + * Opens the transport for reading/writing. + * + * @returns TTransportException if the transport could not be opened + */ + Open() (err os.Error) + + /** + * Closes the transport. + */ + Close() (err os.Error) + + /** + * Reads up to len bytes into buffer buf, starting att offset off. + * + * @param buf Array to read into + * @param off Index to start reading at + * @param len Maximum number of bytes to read + * @return The number of bytes actually read + * @return TTransportException if there was an error reading data + */ + Read(buf []byte) (n int, err os.Error) + + /** + * Guarantees that all of len bytes are actually read off the transport. + * + * @param buf Array to read into + * @param off Index to start reading at + * @param len Maximum number of bytes to read + * @return The number of bytes actually read, which must be equal to len + * @return TTransportException if there was an error reading data + */ + ReadAll(buf []byte) (n int, err os.Error) + + /** + * Writes the buffer to the output + * + * @param buf The output data buffer + * @return Number of bytes written + * @return TTransportException if an error occurs writing data + */ + Write(buf []byte) (n int, err os.Error) + + /** + * Flush any pending data out of a transport buffer. + * + * @return TTransportException if there was an error writing out data. + */ + Flush() (err os.Error) + + /** + * Is there more data to be read? + * + * @return True if the remote side is still alive and feeding us + */ + Peek() bool +} +/* +type TTransportBase struct { +} + +func (p* TTransportBase) IsOpen() bool { + return false; +}; + +func (p* TTransportBase) Peek() bool { + return p.IsOpen(); +} + +func (p* TTransportBase) Open() os.Error { + return NewTTransportException(UNKNOWN, "Subclasses must implement TTransportBase.Open()"); +} + +func (p* TTransportBase) Close() os.Error { + return NewTTransportException(UNKNOWN, "Subclasses must implement TTransportBase.Close()"); +} + +func (p* TTransportBase) Read(buf []byte) (int, os.Error) { + return 0, NewTTransportExceptionDefaultString("Subclasses must implement TTransportBase.Read()"); +} + +func (p* TTransportBase) ReadAll(buf []byte) (n int, err os.Error){ + ret := 0; + size := len(buf); + for (n < size) { + ret, err = p.Read(buf[n:]); + if ret <= 0 { + if err != nil { + err = NewTTransportExceptionDefaultString("Cannot read. Remote side has closed. Tried to read " + string(size) + " bytes, but only got " + string(n) + " bytes."); + } + return ret, err; + } + n += ret; + } + return n, err; +} + +func (p* TTransportBase) Write(buf []byte) (int, os.Error) { + return 0, NewTTransportExceptionDefaultString("Subclasses must implement TTransportBase.Write()"); +} + +func (p* TTransportBase) Flush() os.Error { + return nil; +} +*/ +/** + * Guarantees that all of len bytes are actually read off the transport. + * + * @param buf Array to read into + * @param off Index to start reading at + * @param len Maximum number of bytes to read + * @return The number of bytes actually read, which must be equal to len + * @return TTransportException if there was an error reading data + */ +func ReadAllTransport(p TTransport, buf []byte) (n int, err os.Error) { + ret := 0 + size := len(buf) + for n < size { + ret, err = p.Read(buf[n:]) + if ret <= 0 { + if err != nil { + err = NewTTransportExceptionDefaultString("Cannot read. Remote side has closed. Tried to read " + string(size) + " bytes, but only got " + string(n) + " bytes.") + } + return ret, err + } + n += ret + } + return n, err +} + + +var ( + LOGGER *log.Logger +) + +func init() { + LOGGER = log.New(os.Stderr, "", log.Ldate|log.Ltime|log.Lshortfile) +} diff --git a/lib/go/thrift/ttransport_exception.go b/lib/go/thrift/ttransport_exception.go new file mode 100644 index 00000000..b9fae17b --- /dev/null +++ b/lib/go/thrift/ttransport_exception.go @@ -0,0 +1,84 @@ +/* + * 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. + */ + +package thrift + +import ( + "os" +) + +/** + * Transport exceptions. + * + */ +type TTransportException interface { + TException + TypeId() int +} + +const ( + UNKNOWN_TRANSPORT_EXCEPTION = 0 + NOT_OPEN = 1 + ALREADY_OPEN = 2 + TIMED_OUT = 3 + END_OF_FILE = 4 +) + +type tTransportException struct { + typeId int + message string +} + +func (p *tTransportException) TypeId() int { + return p.typeId +} + +func (p *tTransportException) String() string { + return p.message +} + +func NewTTransportExceptionDefault() TTransportException { + return NewTTransportExceptionDefaultType(UNKNOWN_TRANSPORT_EXCEPTION) +} + +func NewTTransportExceptionDefaultType(t int) TTransportException { + return NewTTransportException(t, "") +} + +func NewTTransportExceptionDefaultString(m string) TTransportException { + return NewTTransportException(UNKNOWN_TRANSPORT_EXCEPTION, m) +} + +func NewTTransportException(t int, m string) TTransportException { + return &tTransportException{typeId: t, message: m} +} + +func NewTTransportExceptionFromOsError(e os.Error) TTransportException { + if e == nil { + return nil + } + t, ok := e.(TTransportException) + if ok { + return t + } + if e == os.EOF { + return NewTTransportException(END_OF_FILE, e.String()) + } + return NewTTransportExceptionDefaultString(e.String()) +} diff --git a/lib/go/thrift/ttransport_factory.go b/lib/go/thrift/ttransport_factory.go new file mode 100644 index 00000000..4ab4dbea --- /dev/null +++ b/lib/go/thrift/ttransport_factory.go @@ -0,0 +1,47 @@ +/* + * 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. + */ + +package thrift + +/** + * Factory class used to create wrapped instance of Transports. + * This is used primarily in servers, which get Transports from + * a ServerTransport and then may want to mutate them (i.e. create + * a BufferedTransport from the underlying base transport) + * + */ +type TTransportFactory interface { + GetTransport(trans TTransport) TTransport +} + +type tTransportFactory struct{} + +/** + * Return a wrapped instance of the base Transport. + * + * @param trans The base transport + * @return Wrapped Transport + */ +func (p *tTransportFactory) GetTransport(trans TTransport) TTransport { + return trans +} + +func NewTTransportFactory() TTransportFactory { + return &tTransportFactory{} +} diff --git a/lib/go/thrift/ttransport_test.go b/lib/go/thrift/ttransport_test.go new file mode 100644 index 00000000..b9630dea --- /dev/null +++ b/lib/go/thrift/ttransport_test.go @@ -0,0 +1,131 @@ +/* + * 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. + */ + +package thrift_test + +import ( + . "thrift" + "testing" + "net" + "os" + "strconv" +) + +const TRANSPORT_BINARY_DATA_SIZE = 4096 + +var ( + transport_bdata []byte // test data for writing; same as data +) + +func init() { + transport_bdata = make([]byte, TRANSPORT_BINARY_DATA_SIZE) + for i := 0; i < TRANSPORT_BINARY_DATA_SIZE; i++ { + transport_bdata[i] = byte((i + 'a') % 255) + } +} + +func TransportTest(t *testing.T, writeTrans TTransport, readTrans TTransport) { + buf := make([]byte, TRANSPORT_BINARY_DATA_SIZE) + if !writeTrans.IsOpen() { + err := writeTrans.Open() + if err != nil { + t.Fatalf("Transport %T cannot open write transport: %s", writeTrans, err) + } + } + if !readTrans.IsOpen() { + err := readTrans.Open() + if err != nil { + t.Fatalf("Transport %T cannot open read transport: %s", readTrans, err) + } + } + _, err := writeTrans.Write(transport_bdata) + if err != nil { + t.Fatalf("Transport %T cannot write binary data of length %d: %s", writeTrans, len(transport_bdata), err) + } + err = writeTrans.Flush() + if err != nil { + t.Fatalf("Transport %T cannot flush write of binary data: %s", writeTrans, err) + } + n, err := readTrans.ReadAll(buf) + if err != nil { + t.Errorf("Transport %T cannot read binary data of length %d: %s", readTrans, TRANSPORT_BINARY_DATA_SIZE, err) + } + if n != TRANSPORT_BINARY_DATA_SIZE { + t.Errorf("Transport %T read only %d instead of %d bytes of binary data", readTrans, n, TRANSPORT_BINARY_DATA_SIZE) + } + for k, v := range buf { + if v != transport_bdata[k] { + t.Fatalf("Transport %T read %d instead of %d for index %d of binary data 2", readTrans, v, transport_bdata[k], k) + } + } + _, err = writeTrans.Write(transport_bdata) + if err != nil { + t.Fatalf("Transport %T cannot write binary data 2 of length %d: %s", writeTrans, len(transport_bdata), err) + } + err = writeTrans.Flush() + if err != nil { + t.Fatalf("Transport %T cannot flush write binary data 2: %s", writeTrans, err) + } + b := readTrans.Peek() + if b != true { + t.Errorf("Transport %T returned %s for Peek()", readTrans, b) + } + buf = make([]byte, TRANSPORT_BINARY_DATA_SIZE) + read := 1 + for n = 0; n < TRANSPORT_BINARY_DATA_SIZE && read != 0; { + read, err = readTrans.Read(buf[n:]) + if err != nil { + t.Errorf("Transport %T cannot read binary data 2 of total length %d from offset %d: %s", readTrans, TRANSPORT_BINARY_DATA_SIZE, n, err) + } + n += read + } + if n != TRANSPORT_BINARY_DATA_SIZE { + t.Errorf("Transport %T read only %d instead of %d bytes of binary data 2", readTrans, n, TRANSPORT_BINARY_DATA_SIZE) + } + for k, v := range buf { + if v != transport_bdata[k] { + t.Fatalf("Transport %T read %d instead of %d for index %d of binary data 2", readTrans, v, transport_bdata[k], k) + } + } +} + +func CloseTransports(t *testing.T, readTrans TTransport, writeTrans TTransport) { + err := readTrans.Close() + if err != nil { + t.Errorf("Transport %T cannot close read transport: %s", readTrans, err) + } + if writeTrans != readTrans { + err = writeTrans.Close() + if err != nil { + t.Errorf("Transport %T cannot close write transport: %s", writeTrans, err) + } + } +} + +func FindAvailableTCPServerPort(startPort int) (net.Addr, os.Error) { + for i := startPort; i < 65535; i++ { + s := "127.0.0.1:" + strconv.Itoa(i) + l, err := net.Listen("tcp", s) + if err == nil { + l.Close() + return net.ResolveTCPAddr(s) + } + } + return nil, NewTTransportException(UNKNOWN_TRANSPORT_EXCEPTION, "Could not find available server port") +} diff --git a/lib/go/thrift/ttype.go b/lib/go/thrift/ttype.go new file mode 100644 index 00000000..d024b394 --- /dev/null +++ b/lib/go/thrift/ttype.go @@ -0,0 +1,975 @@ +/* + * 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. + */ + + +package thrift + +import ( + "container/list" + "container/vector" + "strconv" +) + +/** + * Type constants in the Thrift protocol. + */ +type TType byte + +const ( + STOP = 0 + VOID = 1 + BOOL = 2 + BYTE = 3 + I08 = 3 + DOUBLE = 4 + I16 = 6 + I32 = 8 + I64 = 10 + STRING = 11 + UTF7 = 11 + STRUCT = 12 + MAP = 13 + SET = 14 + LIST = 15 + ENUM = 16 + UTF8 = 16 + UTF16 = 17 + GENERIC = 127 +) + +func (p TType) String() string { + switch p { + case STOP: + return "STOP" + case VOID: + return "VOID" + case BOOL: + return "BOOL" + case BYTE: + return "BYTE" + case DOUBLE: + return "DOUBLE" + case I16: + return "I16" + case I32: + return "I32" + case I64: + return "I64" + case STRING: + return "STRING" + case STRUCT: + return "STRUCT" + case MAP: + return "MAP" + case SET: + return "SET" + case LIST: + return "LIST" + case ENUM: + return "ENUM" + case UTF16: + return "UTF16" + case GENERIC: + return "GENERIC" + } + return "Unknown" +} + +func (p TType) IsBaseType() bool { + switch p { + case BOOL, BYTE, DOUBLE, I16, I32, I64, STRING, UTF8, UTF16: + return true + default: + return false + } + return false +} + +func (p TType) IsEmptyType() bool { + switch p { + case STOP, VOID: + return true + default: + return false + } + return false +} + +func (p TType) IsEnum() bool { + switch p { + case ENUM: + return true + default: + return false + } + return false +} + +func (p TType) IsNumericType() bool { + switch p { + case ENUM, BOOL, BYTE, DOUBLE, I16, I32, I64: + return true + default: + return false + } + return false +} + +func (p TType) IsStringType() bool { + switch p { + case STRING, UTF8, UTF16: + return true + default: + return false + } + return false +} + +func (p TType) IsContainer() bool { + switch p { + case MAP, SET, LIST: + return true + default: + return false + } + return false +} + +func (p TType) IsStruct() bool { + switch p { + case STRUCT: + return true + default: + return false + } + return false +} + +func (p TType) IsMap() bool { + switch p { + case MAP: + return true + default: + return false + } + return false +} + +func (p TType) IsList() bool { + switch p { + case LIST: + return true + default: + return false + } + return false +} + +func (p TType) IsSet() bool { + switch p { + case SET: + return true + default: + return false + } + return false +} + +func (p TType) IsInt() bool { + switch p { + case BYTE, I16, I32, I64: + return true + default: + return false + } + return false +} + +func (p TType) Coerce(other interface{}) TType { + if other == nil { + return TType(STOP) + } + switch b := other.(type) { + default: + return TType(STOP) + case nil: + return TType(STOP) + case TType: + return b + case byte: + return TType(b) + case int: + return TType(byte(b)) + case int8: + return TType(byte(b)) + case int32: + return TType(byte(b)) + case int64: + return TType(byte(b)) + case uint: + return TType(byte(b)) + case uint32: + return TType(byte(b)) + case uint64: + return TType(byte(b)) + case float32: + return TType(byte(int(b))) + case float64: + return TType(byte(int(b))) + } + return TType(STOP) +} + +func (p TType) LessType(other interface{}) bool { + return p < p.Coerce(other) +} + +func (p TType) Less(i, j interface{}) bool { + cmp, ok := p.Compare(i, j) + return ok && cmp > 0 +} + + +func (p TType) Compare(i, j interface{}) (int, bool) { + if i == j { + return 0, true + } + if i == nil { + if j == nil { + return 0, true + } + return -1, true + } + if j == nil { + return 1, true + } + ci, iok := p.CoerceData(i) + cj, jok := p.CoerceData(j) + if iok && !jok { + return 1, true + } + if !iok && jok { + return -1, true + } + // hopefully this doesn't happen as Compare() would continuously return 0, false + if !iok && !jok { + return 0, false + } + if ci == cj { + return 0, true + } + if ci == nil { + if cj == nil { + return 0, true + } + return -1, true + } + if cj == nil { + return 1, true + } + switch p { + case STOP, VOID: + // hopefully this doesn't happen as Compare() would continuously return 0, false + return 0, false + case BOOL: + vi := ci.(bool) + vj := cj.(bool) + if vi == vj { + return 0, true + } + if vi == false { + return -1, true + } + return 1, true + case BYTE: + vi := ci.(byte) + vj := cj.(byte) + if vi == vj { + return 0, true + } + if vi < vj { + return -1, true + } + return 1, true + case DOUBLE: + vi := ci.(float64) + vj := cj.(float64) + if vi == vj { + return 0, true + } + if vi < vj { + return -1, true + } + return 1, true + case I16: + vi := ci.(int16) + vj := cj.(int16) + if vi == vj { + return 0, true + } + if vi < vj { + return -1, true + } + return 1, true + case I32: + vi := ci.(int32) + vj := cj.(int32) + if vi == vj { + return 0, true + } + if vi < vj { + return -1, true + } + return 1, true + case I64: + vi := ci.(int64) + vj := cj.(int64) + if vi == vj { + return 0, true + } + if vi < vj { + return -1, true + } + return 1, true + case STRING, UTF8, UTF16: + vi := ci.(string) + vj := cj.(string) + if vi == vj { + return 0, true + } + if vi < vj { + return -1, true + } + return 1, true + case STRUCT: + si := ci.(TStruct) + sj := cj.(TStruct) + if cmp := CompareString(si.ThriftName(), sj.ThriftName()); cmp != 0 { + return cmp, true + } + if cmp, ok := si.TStructFields().CompareTo(sj.TStructFields()); !ok || cmp != 0 { + return cmp, ok + } + for field := range si.TStructFields().Iter() { + a := si.AttributeFromFieldId(field.Id()) + b := sj.AttributeFromFieldId(field.Id()) + if cmp, ok := field.TypeId().Compare(a, b); !ok || cmp != 0 { + return cmp, ok + } + } + return 0, true + case MAP: + mi := ci.(TMap) + mj := cj.(TMap) + ei := mi.KeyType() + if ej := mj.KeyType(); ei != ej { + return CompareInt(int(ei), int(ej)), true + } + if size := mi.Len(); size != mj.Len() { + return CompareInt(size, mj.Len()), true + } + if c, cok := ei.Compare(mi.Keys(), mj.Keys()); c != 0 || !cok { + return c, cok + } + return ei.Compare(mi.Values(), mj.Values()) + case LIST: + li := ci.(TList) + lj := cj.(TList) + ei := li.ElemType() + ej := lj.ElemType() + if ei != ej { + return CompareInt(int(ei), int(ej)), true + } + size := li.Len() + if size != lj.Len() { + return CompareInt(size, lj.Len()), true + } + for k := 0; k < size; k++ { + vi := li.At(k) + vj := lj.At(k) + c, cok := ei.Compare(vi, vj) + if c != 0 || !cok { + return c, cok + } + } + return 0, true + case SET: + li := ci.(TSet) + lj := cj.(TSet) + ei := li.ElemType() + ej := lj.ElemType() + if ei != ej { + return CompareInt(int(ei), int(ej)), true + } + size := li.Len() + if size != lj.Len() { + return CompareInt(size, lj.Len()), true + } + return ei.Compare(li.Values(), lj.Values()) + default: + panic("Invalid thrift type to coerce") + } + return 0, false +} + +func (p TType) CompareValueArrays(li, lj []interface{}) (int, bool) { + size := len(li) + if cmp := CompareInt(size, len(lj)); cmp != 0 { + return cmp, true + } + for i := 0; i < size; i++ { + vi := li[i] + vj := lj[i] + c, cok := p.Compare(vi, vj) + if c != 0 || !cok { + return c, cok + } + } + return 0, true +} + +func (p TType) Equals(other interface{}) bool { + return p == p.Coerce(other) +} + +type Stringer interface { + String() string +} + +type Enumer interface { + String() string + Value() int + IsEnum() bool +} + +func TypeFromValue(data interface{}) TType { + switch i := data.(type) { + default: + return STOP + case nil: + return VOID + case bool: + return BOOL + case float32, float64: + return DOUBLE + case int, int32: + return I32 + case byte: + return BYTE + case int8: + return I08 + case int16: + return I16 + case int64: + return I64 + case string: + return STRING + case TStruct: + return STRUCT + case TMap: + return MAP + case TSet: + return SET + case []interface{}, *list.List, *vector.Vector, TList: + return LIST + } + return STOP +} + +func (p TType) CoerceData(data interface{}) (interface{}, bool) { + if data == nil { + switch p { + case STOP: + return nil, true + case VOID: + return nil, true + case BOOL: + return false, true + case BYTE: + return byte(0), true + case DOUBLE: + return float64(0), true + case I16: + return int16(0), true + case I32: + return int32(0), true + case I64: + return int64(0), true + case STRING, UTF8, UTF16: + return "", true + case STRUCT: + return NewTStructEmpty(""), true + case MAP: + return NewTMapDefault(), true + case LIST: + return NewTListDefault(), true + case SET: + return NewTSetDefault(), true + default: + panic("Invalid thrift type to coerce") + } + } + switch p { + case STOP: + return nil, true + case VOID: + return nil, true + case BOOL: + switch b := data.(type) { + default: + return false, false + case bool: + return b, true + case Numeric: + return bool(b.Int() != 0), true + case int: + return b != 0, true + case byte: + return b != 0, true + case int8: + return b != 0, true + case int16: + return b != 0, true + case int32: + return b != 0, true + case int64: + return b != 0, true + case uint: + return b != 0, true + case uint16: + return b != 0, true + case uint32: + return b != 0, true + case uint64: + return b != 0, true + case float32: + return b != 0, true + case float64: + return b != 0, true + case Stringer: + v := b.String() + if v == "false" || v == "0" || len(v) == 0 { + return false, true + } + return true, true + case string: + if b == "false" || b == "0" || len(b) == 0 { + return false, true + } + return true, true + } + case BYTE: + if b, ok := data.(byte); ok { + return b, true + } + if b, ok := data.(Numeric); ok { + return b.Byte(), true + } + if b, ok := data.(bool); ok { + if b { + return byte(1), true + } + return byte(0), true + } + if b, ok := data.(int); ok { + return byte(b), true + } + if b, ok := data.(int8); ok { + return byte(b), true + } + if b, ok := data.(int16); ok { + return byte(b), true + } + if b, ok := data.(int32); ok { + return byte(b), true + } + if b, ok := data.(int64); ok { + return byte(b), true + } + if b, ok := data.(uint); ok { + return byte(b), true + } + if b, ok := data.(uint8); ok { + return byte(b), true + } + if b, ok := data.(uint16); ok { + return byte(b), true + } + if b, ok := data.(uint32); ok { + return byte(b), true + } + if b, ok := data.(uint64); ok { + return byte(b), true + } + if b, ok := data.(float32); ok { + return byte(int(b)), true + } + if b, ok := data.(float64); ok { + return byte(int(b)), true + } + if b, ok := data.(Stringer); ok { + data = b.String() + } + if b, ok := data.(string); ok { + i1, err := strconv.Atoi(b) + if err != nil { + return byte(int(i1)), true + } + } + return byte(0), false + case DOUBLE: + if b, ok := data.(float32); ok { + return float64(b), true + } + if b, ok := data.(float64); ok { + return b, true + } + if b, ok := data.(Numeric); ok { + return bool(b.Float64() != 0.0), true + } + if b, ok := data.(byte); ok { + return float64(b), true + } + if b, ok := data.(bool); ok { + if b { + return float64(1.0), true + } + return float64(0.0), true + } + if b, ok := data.(int); ok { + return float64(b), true + } + if b, ok := data.(int8); ok { + return float64(b), true + } + if b, ok := data.(int16); ok { + return float64(b), true + } + if b, ok := data.(int32); ok { + return float64(b), true + } + if b, ok := data.(int64); ok { + return float64(b), true + } + if b, ok := data.(uint); ok { + return float64(b), true + } + if b, ok := data.(uint8); ok { + return float64(b), true + } + if b, ok := data.(uint16); ok { + return float64(b), true + } + if b, ok := data.(uint32); ok { + return float64(b), true + } + if b, ok := data.(uint64); ok { + return float64(b), true + } + if b, ok := data.(Stringer); ok { + data = b.String() + } + if b, ok := data.(string); ok { + d1, err := strconv.Atof64(b) + if err != nil { + return d1, true + } + } + return float64(0), false + case I16: + if b, ok := data.(int16); ok { + return b, true + } + if b, ok := data.(int); ok { + return int16(b), true + } + if b, ok := data.(Numeric); ok { + return bool(b.Int16() != 0), true + } + if b, ok := data.(byte); ok { + return int16(b), true + } + if b, ok := data.(bool); ok { + if b { + return int16(1.0), true + } + return int16(0.0), true + } + if b, ok := data.(int8); ok { + return int16(b), true + } + if b, ok := data.(int32); ok { + return int16(b), true + } + if b, ok := data.(int64); ok { + return int16(b), true + } + if b, ok := data.(uint); ok { + return int16(b), true + } + if b, ok := data.(uint8); ok { + return int16(b), true + } + if b, ok := data.(uint16); ok { + return int16(b), true + } + if b, ok := data.(uint32); ok { + return int16(b), true + } + if b, ok := data.(uint64); ok { + return int16(b), true + } + if b, ok := data.(float32); ok { + return int16(int(b)), true + } + if b, ok := data.(float64); ok { + return int16(int(b)), true + } + if b, ok := data.(Stringer); ok { + data = b.String() + } + if b, ok := data.(string); ok { + i1, err := strconv.Atoi(b) + if err != nil { + return int16(i1), true + } + } + return int16(0), false + case I32: + if b, ok := data.(int32); ok { + return b, true + } + if b, ok := data.(int); ok { + return int32(b), true + } + if b, ok := data.(Numeric); ok { + return bool(b.Int32() != 0), true + } + if b, ok := data.(byte); ok { + return int32(b), true + } + if b, ok := data.(bool); ok { + if b { + return int32(1.0), true + } + return int32(0.0), true + } + if b, ok := data.(int8); ok { + return int32(b), true + } + if b, ok := data.(int16); ok { + return int32(b), true + } + if b, ok := data.(int64); ok { + return int32(b), true + } + if b, ok := data.(uint); ok { + return int32(b), true + } + if b, ok := data.(uint8); ok { + return int32(b), true + } + if b, ok := data.(uint16); ok { + return int32(b), true + } + if b, ok := data.(uint32); ok { + return int32(b), true + } + if b, ok := data.(uint64); ok { + return int32(b), true + } + if b, ok := data.(float32); ok { + return int32(int(b)), true + } + if b, ok := data.(float64); ok { + return int32(int(b)), true + } + if b, ok := data.(Stringer); ok { + data = b.String() + } + if b, ok := data.(string); ok { + i1, err := strconv.Atoi(b) + if err != nil { + return int32(i1), true + } + } + return int32(0), false + case I64: + if b, ok := data.(int64); ok { + return b, true + } + if b, ok := data.(int32); ok { + return int64(b), true + } + if b, ok := data.(int); ok { + return int64(b), true + } + if b, ok := data.(Numeric); ok { + return bool(b.Int64() != 0), true + } + if b, ok := data.(byte); ok { + return int64(b), true + } + if b, ok := data.(bool); ok { + if b { + return int64(1.0), true + } + return int64(0.0), true + } + if b, ok := data.(int8); ok { + return int64(b), true + } + if b, ok := data.(int16); ok { + return int64(b), true + } + if b, ok := data.(uint); ok { + return int64(b), true + } + if b, ok := data.(uint8); ok { + return int64(b), true + } + if b, ok := data.(uint16); ok { + return int64(b), true + } + if b, ok := data.(uint32); ok { + return int64(b), true + } + if b, ok := data.(uint64); ok { + return int64(b), true + } + if b, ok := data.(float32); ok { + return int64(b), true + } + if b, ok := data.(float64); ok { + return int64(b), true + } + if b, ok := data.(Stringer); ok { + data = b.String() + } + if b, ok := data.(string); ok { + i1, err := strconv.Atoi64(b) + if err != nil { + return i1, true + } + } + return int64(0), false + case STRING, UTF8, UTF16: + if b, ok := data.(Enumer); ok { + if i1, ok := data.(int); ok { + return i1, true + } + return b.String(), true + } + if b, ok := data.(Stringer); ok { + return b.String(), true + } + if b, ok := data.(string); ok { + return b, true + } + if b, ok := data.(int); ok { + return string(b), true + } + if b, ok := data.(byte); ok { + return string(b), true + } + if b, ok := data.(bool); ok { + if b { + return "true", true + } + return "false", true + } + if b, ok := data.(int8); ok { + return string(b), true + } + if b, ok := data.(int16); ok { + return string(b), true + } + if b, ok := data.(int32); ok { + return string(b), true + } + if b, ok := data.(int64); ok { + return string(b), true + } + if b, ok := data.(uint); ok { + return string(b), true + } + if b, ok := data.(uint8); ok { + return string(b), true + } + if b, ok := data.(uint16); ok { + return string(b), true + } + if b, ok := data.(uint32); ok { + return string(b), true + } + if b, ok := data.(uint64); ok { + return string(b), true + } + if b, ok := data.(float32); ok { + return strconv.Ftoa32(b, 'g', -1), true + } + if b, ok := data.(float64); ok { + return strconv.Ftoa64(b, 'g', -1), true + } + return "", false + case STRUCT: + if b, ok := data.(TStruct); ok { + return b, true + } + return NewTStructEmpty(""), true + case MAP: + if b, ok := data.(TMap); ok { + return b, true + } + return NewTMapDefault(), false + case LIST: + if b, ok := data.(TList); ok { + return b, true + } + return NewTListDefault(), false + case SET: + if b, ok := data.(TSet); ok { + return b, true + } + return NewTSetDefault(), false + default: + panic("Invalid thrift type to coerce") + } + return nil, false +} + +type EqualsOtherInterface interface { + Equals(other interface{}) bool +} + +type EqualsMap interface { + Equals(other TMap) bool +} + +type EqualsSet interface { + Equals(other TSet) bool +} + +type EqualsList interface { + Equals(other TList) bool +} + +type EqualsStruct interface { + Equals(other TStruct) bool +} diff --git a/test/ThriftTest.thrift b/test/ThriftTest.thrift index 237023bc..83e4e39c 100644 --- a/test/ThriftTest.thrift +++ b/test/ThriftTest.thrift @@ -31,6 +31,7 @@ namespace js ThriftTest namespace st ThriftTest namespace py ThriftTest namespace py.twisted ThriftTest +namespace go ThriftTest namespace * thrift.test /** diff --git a/tutorial/go/Make.deps b/tutorial/go/Make.deps new file mode 100644 index 00000000..e4c2b20d --- /dev/null +++ b/tutorial/go/Make.deps @@ -0,0 +1,135 @@ +src.install: fmt.install net.install os.install strconv.install thrift.install +archive/tar.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/archive/tar.a +archive/zip.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/archive/zip.a +asn1.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/asn1.a +big.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/big.a +bufio.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/bufio.a +bytes.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/bytes.a +cmath.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/cmath.a +compress/flate.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/compress/flate.a +compress/gzip.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/compress/gzip.a +compress/zlib.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/compress/zlib.a +container/heap.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/container/heap.a +container/list.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/container/list.a +container/ring.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/container/ring.a +container/vector.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/container/vector.a +crypto.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/crypto.a +crypto/aes.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/crypto/aes.a +crypto/block.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/crypto/block.a +crypto/blowfish.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/crypto/blowfish.a +crypto/cast5.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/crypto/cast5.a +crypto/cipher.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/crypto/cipher.a +crypto/dsa.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/crypto/dsa.a +crypto/elliptic.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/crypto/elliptic.a +crypto/hmac.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/crypto/hmac.a +crypto/md4.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/crypto/md4.a +crypto/md5.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/crypto/md5.a +crypto/ocsp.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/crypto/ocsp.a +crypto/rand.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/crypto/rand.a +crypto/rc4.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/crypto/rc4.a +crypto/ripemd160.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/crypto/ripemd160.a +crypto/rsa.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/crypto/rsa.a +crypto/sha1.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/crypto/sha1.a +crypto/sha256.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/crypto/sha256.a +crypto/sha512.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/crypto/sha512.a +crypto/subtle.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/crypto/subtle.a +crypto/tls.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/crypto/tls.a +crypto/twofish.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/crypto/twofish.a +crypto/x509.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/crypto/x509.a +crypto/xtea.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/crypto/xtea.a +debug/dwarf.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/debug/dwarf.a +debug/macho.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/debug/macho.a +debug/elf.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/debug/elf.a +debug/gosym.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/debug/gosym.a +debug/pe.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/debug/pe.a +debug/proc.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/debug/proc.a +ebnf.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/ebnf.a +encoding/ascii85.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/encoding/ascii85.a +encoding/base32.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/encoding/base32.a +encoding/base64.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/encoding/base64.a +encoding/binary.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/encoding/binary.a +encoding/git85.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/encoding/git85.a +encoding/hex.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/encoding/hex.a +encoding/line.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/encoding/line.a +encoding/pem.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/encoding/pem.a +exec.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/exec.a +exp/datafmt.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/exp/datafmt.a +exp/draw.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/exp/draw.a +exp/draw/x11.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/exp/draw/x11.a +exp/eval.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/exp/eval.a +expvar.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/expvar.a +flag.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/flag.a +fmt.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/fmt.a +go/ast.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/go/ast.a +go/doc.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/go/doc.a +go/parser.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/go/parser.a +go/printer.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/go/printer.a +go/scanner.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/go/scanner.a +go/token.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/go/token.a +go/typechecker.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/go/typechecker.a +gob.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/gob.a +hash.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/hash.a +hash/adler32.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/hash/adler32.a +hash/crc32.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/hash/crc32.a +hash/crc64.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/hash/crc64.a +html.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/html.a +http.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/http.a +http/pprof.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/http/pprof.a +image.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/image.a +image/jpeg.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/image/jpeg.a +image/png.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/image/png.a +index/suffixarray.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/index/suffixarray.a +io.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/io.a +io/ioutil.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/io/ioutil.a +json.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/json.a +log.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/log.a +math.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/math.a +mime.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/mime.a +mime/multipart.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/mime/multipart.a +net.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/net.a +net/dict.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/net/dict.a +net/textproto.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/net/textproto.a +netchan.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/netchan.a +os.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/os.a +os/signal.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/os/signal.a +patch.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/patch.a +path.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/path.a +rand.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/rand.a +reflect.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/reflect.a +regexp.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/regexp.a +rpc.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/rpc.a +rpc/jsonrpc.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/rpc/jsonrpc.a +runtime.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/runtime.a +runtime/cgo.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/runtime/cgo.a +runtime/debug.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/runtime/debug.a +runtime/pprof.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/runtime/pprof.a +scanner.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/scanner.a +smtp.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/smtp.a +sort.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/sort.a +strconv.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/strconv.a +strings.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/strings.a +sync.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/sync.a +syscall.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/syscall.a +syslog.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/syslog.a +tabwriter.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/tabwriter.a +template.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/template.a +testing.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/testing.a +testing/iotest.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/testing/iotest.a +testing/quick.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/testing/quick.a +testing/script.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/testing/script.a +time.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/time.a +try.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/try.a +unicode.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/unicode.a +utf16.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/utf16.a +utf8.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/utf8.a +websocket.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/websocket.a +xml.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/xml.a +../cmd/cgo.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/../cmd/cgo.a +../cmd/ebnflint.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/../cmd/ebnflint.a +../cmd/godoc.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/../cmd/godoc.a +../cmd/gofmt.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/../cmd/gofmt.a +../cmd/goinstall.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/../cmd/goinstall.a +../cmd/govet.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/../cmd/govet.a +../cmd/goyacc.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/../cmd/goyacc.a +../cmd/hgpatch.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/../cmd/hgpatch.a +thrift.install: ${GOROOT}/pkg/${GOOS}_${GOARCH}/thrift.a diff --git a/tutorial/go/Makefile b/tutorial/go/Makefile new file mode 100644 index 00000000..50c5dfe6 --- /dev/null +++ b/tutorial/go/Makefile @@ -0,0 +1,44 @@ +# Copyright 2009 The Go Authors. All rights reserved. +# Use of this source code is governed by a BSD-style +# license that can be found in the LICENSE file. + +include $(GOROOT)/src/Make.inc + +all: Make.deps install + +TARG=thriftlib/tutorialcalculator + +DIRS=\ + src/\ + +GOFILES=\ + + +clean.dirs: $(addsuffix .clean, $(DIRS)) +install.dirs: $(addsuffix .install, $(DIRS)) +test.dirs: $(addsuffix .test, $(DIRS)) + + +%.clean: + +cd $* && $(MAKE) clean + +%.install: + +cd $* && $(MAKE) install + +%.test: + +cd $* && $(MAKE) test + + + +Make.deps: + ./deps.bash + +deps: + ./deps.bash + +clean: clean.dirs + +install: install.dirs + +test: test.dirs + diff --git a/tutorial/go/deps.bash b/tutorial/go/deps.bash new file mode 100644 index 00000000..aec6ec2e --- /dev/null +++ b/tutorial/go/deps.bash @@ -0,0 +1,45 @@ +#!/usr/bin/env bash +# Copyright 2009 The Go Authors. All rights reserved. +# Use of this source code is governed by a BSD-style +# license that can be found in the LICENSE file. + +OUT="Make.deps" +TMP="Make.deps.tmp" + +if [ -f $OUT ] && ! [ -w $OUT ]; then + echo "$0: $OUT is read-only; aborting." 1>&2 + exit 1 +fi + +# Get list of directories from Makefile +dirs=$(sed '1,/^DIRS=/d; /^$/,$d; s/\\//g' Makefile) +dirs2=$(sed '1,/^DIRS=/d; /^$/,$d; s/\\//g' $GOROOT/src/pkg/Makefile) +dirs3="thrift" +dirpat=$(echo $dirs $dirs2 $dirs3 | sed 's/ /|/g; s/.*/^(&)$/') + +for dir in $dirs; do ( + cd $dir || exit 1 + + sources=$(sed -n 's/\.go\\/.go/p' Makefile) + sources=$(ls $sources 2> /dev/null) # remove .s, .c, etc. + + deps=$( + sed -n '/^import.*"/p; /^import[ \t]*(/,/^)/p' $sources /dev/null | + cut -d '"' -f2 | + egrep "$dirpat" | + grep -v "^$dir\$" | + sed 's/$/.install/' | + sort -u + ) + + echo $dir.install: $deps +) done > $TMP + +for dir in $dirs2; do ( + echo $dir.install: \${GOROOT}/pkg/\${GOOS}_\${GOARCH}/${dir}.a +) done >> $TMP +for dir in $dirs3; do ( + echo $dir.install: \${GOROOT}/pkg/\${GOOS}_\${GOARCH}/${dir}.a +) done >> $TMP + +mv $TMP $OUT diff --git a/tutorial/go/src/CalculatorHandler.go b/tutorial/go/src/CalculatorHandler.go new file mode 100644 index 00000000..9eb28388 --- /dev/null +++ b/tutorial/go/src/CalculatorHandler.go @@ -0,0 +1,101 @@ +package main; + +/* + * 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. + */ + +import ( + "fmt" + "os" + "strconv" + "thriftlib/tutorial" + "thriftlib/shared" +) + +type CalculatorHandler struct { + log map[int]*shared.SharedStruct +} + +func NewCalculatorHandler() *CalculatorHandler { + return &CalculatorHandler{log:make(map[int]*shared.SharedStruct)} +} + +func (p *CalculatorHandler) Ping() (err os.Error) { + fmt.Print("ping()\n") + return nil +} + +func (p *CalculatorHandler) Add(num1 int32, num2 int32) (retval17 int32, err os.Error) { + fmt.Print("add(", num1, ",", num2, ")\n") + return num1 + num2, nil +} + +func (p *CalculatorHandler) Calculate(logid int32, w *tutorial.Work) (val int32, ouch *tutorial.InvalidOperation, err os.Error) { + fmt.Print("calculate(", logid, ", {", w.Op, ",", w.Num1, ",", w.Num2, "})\n") + switch(w.Op) { + case tutorial.ADD: + val = w.Num1 + w.Num2 + break + case tutorial.SUBTRACT: + val = w.Num1 - w.Num2 + break + case tutorial.MULTIPLY: + val = w.Num1 * w.Num2 + break + case tutorial.DIVIDE: + if w.Num2 == 0 { + ouch = tutorial.NewInvalidOperation() + ouch.What = int32(w.Op) + ouch.Why = "Cannot divide by 0" + return + } + val = w.Num1 / w.Num2 + break + default: + ouch = tutorial.NewInvalidOperation() + ouch.What = int32(w.Op) + ouch.Why = "Unknown operation" + return + } + entry := shared.NewSharedStruct() + entry.Key = logid + entry.Value = strconv.Itoa(int(val)) + k := int(logid) + /* + oldvalue, exists := p.log[k] + if exists { + fmt.Print("Replacing ", oldvalue, " with ", entry, " for key ", k, "\n") + } else { + fmt.Print("Adding ", entry, " for key ", k, "\n") + } + */ + p.log[k] = entry, true + return val, ouch, err +} + +func (p *CalculatorHandler) GetStruct(key int32) (*shared.SharedStruct, os.Error) { + fmt.Print("getStruct(", key, ")\n") + v, _ := p.log[int(key)] + return v, nil +} + +func (p *CalculatorHandler) Zip() (err os.Error) { + fmt.Print("zip()\n") + return nil +} + diff --git a/tutorial/go/src/GoClient.go b/tutorial/go/src/GoClient.go new file mode 100644 index 00000000..94405301 --- /dev/null +++ b/tutorial/go/src/GoClient.go @@ -0,0 +1,92 @@ +package main; + + +/* + * 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. + */ + + +import ( + "fmt" + "net" + "os" + "thrift" + "thriftlib/tutorial" +) + +func Perform(client *tutorial.CalculatorClient) (err os.Error) { + client.Ping() + fmt.Print("ping()\n") + + sum, _ := client.Add(1, 1) + fmt.Print("1+1=", sum, "\n") + + work := tutorial.NewWork() + work.Op = tutorial.DIVIDE + work.Num1 = 1 + work.Num2 = 0 + quotient, ouch, err := client.Calculate(1, work) + if err != nil { + fmt.Print("Error during operation: ", err.String(), "\n") + return err + } else if ouch != nil { + fmt.Print("Invalid operation: ", ouch.String(), "\n") + } else { + fmt.Print("Whoa we can divide by 0 with new value: ", quotient, "\n") + } + + work.Op = tutorial.SUBTRACT + work.Num1 = 15 + work.Num2 = 10 + diff, ouch, err := client.Calculate(1, work) + if err != nil { + fmt.Print("Error during operation: ", err.String(), "\n") + return err + } else if ouch != nil { + fmt.Print("Invalid operation: ", ouch.String(), "\n") + } else { + fmt.Print("15-10=", diff, "\n") + } + + log, err := client.GetStruct(1) + if err != nil { + fmt.Print("Unable to get struct: ", err.String(), "\n") + return err + } else { + fmt.Print("Check log: ", log.Value, "\n") + } + return err +} + + +func RunClient(transportFactory thrift.TTransportFactory, protocolFactory thrift.TProtocolFactory) os.Error { + addr, err := net.ResolveTCPAddr("localhost:9090") + if err != nil { + fmt.Print("Error resolving address: ", err.String(), "\n") + return err + } + transport := thrift.NewTSocketAddr(addr) + if err = transport.Open(); err != nil { + fmt.Print("Error opening connection for protocol ", addr.Network(), " to ", addr.String(), ": ", err.String(), "\n") + return err + } + useTransport := transportFactory.GetTransport(transport) + client := tutorial.NewCalculatorClientFactory(useTransport, protocolFactory) + Perform(client) + return transport.Close() +} diff --git a/tutorial/go/src/GoServer.go b/tutorial/go/src/GoServer.go new file mode 100644 index 00000000..f70a2a95 --- /dev/null +++ b/tutorial/go/src/GoServer.go @@ -0,0 +1,85 @@ +package main; + + +/* + * 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. + */ + + +import ( + "fmt" + "net" + "thrift" + "thriftlib/tutorial" +) + + +type GoServer struct { + handler tutorial.ICalculator + processor *tutorial.CalculatorProcessor +} + +func NewGoServer() *GoServer { + handler := NewCalculatorHandler() + processor := tutorial.NewCalculatorProcessor(handler) + return &GoServer{handler:handler, processor:processor} +} + +func Simple(processor *tutorial.CalculatorProcessor, transportFactory thrift.TTransportFactory, protocolFactory thrift.TProtocolFactory, ch chan int) { + addr, err := net.ResolveTCPAddr("localhost:9090") + if err != nil { + fmt.Print("Error resolving address: ", err.String(), "\n") + return + } + serverTransport, err := thrift.NewTServerSocketAddr(addr) + if err != nil { + fmt.Print("Error creating server socket: ", err.String(), "\n") + return + } + server := thrift.NewTSimpleServer4(processor, serverTransport, transportFactory, protocolFactory) + // Use this for a multithreaded server + // TServer server = new TThreadPoolServer(new TThreadPoolServer.Args(serverTransport).processor(processor)); + + fmt.Print("Starting the simple server... on ", addr, "\n") + for { + err = server.Serve() + if err != nil { + fmt.Print("Error during simple server: ", err.String(), "\n") + return + } + } + fmt.Print("Done with the simple server\n") + ch <- 1 +} + +func Secure(processor *tutorial.CalculatorProcessor) { + addr, _ := net.ResolveTCPAddr("localhost:9091") + serverTransport, _ := thrift.NewTNonblockingServerSocketAddr(addr) + server := thrift.NewTSimpleServer2(processor, serverTransport) + fmt.Print("Starting the secure server... on ", addr, "\n") + server.Serve() + fmt.Print("Done with the secure server\n") +} + +func RunServer(transportFactory thrift.TTransportFactory, protocolFactory thrift.TProtocolFactory) { + server := NewGoServer() + ch := make(chan int) + go Simple(server.processor, transportFactory, protocolFactory, ch) + //go Secure(server.processor) + _ = <- ch +} diff --git a/tutorial/go/src/Makefile b/tutorial/go/src/Makefile new file mode 100644 index 00000000..87acbcad --- /dev/null +++ b/tutorial/go/src/Makefile @@ -0,0 +1,22 @@ +# Copyright 2009 The Go Authors. All rights reserved. +# Use of this source code is governed by a BSD-style +# license that can be found in the LICENSE file. + +include $(GOROOT)/src/Make.inc + +all: install + +TARG=TutorialServerClient + +DIRS=\ + +GOFILES=\ + CalculatorHandler.go\ + GoClient.go\ + GoServer.go\ + main.go + +-include ../Make.deps + +include $(GOROOT)/src/Make.cmd + diff --git a/tutorial/go/src/main.go b/tutorial/go/src/main.go new file mode 100644 index 00000000..30b5d528 --- /dev/null +++ b/tutorial/go/src/main.go @@ -0,0 +1,86 @@ +package main; + + +/* + * 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. + */ + + +import ( + "flag" + "fmt" + "os" + "thrift" +) + +func Usage() { + fmt.Fprint(os.Stderr, "Usage of ", os.Args[0], " <--server | --client>:\n") + flag.PrintDefaults() + fmt.Fprint(os.Stderr, "\n") + os.Exit(0) +} + +func main() { + flag.Usage = Usage + var client bool + var server bool + var protocol string + var framed bool + var useHttp bool + var help bool + + flag.BoolVar(&client, "client", false, "Run client") + flag.BoolVar(&server, "server", false, "Run server") + flag.StringVar(&protocol, "P", "binary", "Specify the protocol (binary, compact, simplejson)") + flag.BoolVar(&framed, "framed", false, "Use framed transport") + flag.BoolVar(&useHttp, "http", false, "Use http") + flag.BoolVar(&help, "help", false, "See usage string") + flag.Parse() + if help || (client && server) || !(client || server) { + fmt.Print("flag.NArg() == ", flag.NArg(), "\n") + flag.Usage() + } + var protocolFactory thrift.TProtocolFactory + switch protocol { + case "compact": + protocolFactory = thrift.NewTCompactProtocolFactory() + case "simplejson": + protocolFactory = thrift.NewTSimpleJSONProtocolFactory() + case "json": + protocolFactory = thrift.NewTJSONProtocolFactory() + case "binary", "": + protocolFactory = thrift.NewTBinaryProtocolFactoryDefault() + default: + fmt.Fprint(os.Stderr, "Invalid protocol specified", protocol, "\n") + Usage() + os.Exit(1) + } + transportFactory := thrift.NewTTransportFactory() + if framed { + transportFactory = thrift.NewTFramedTransportFactory(transportFactory) + } + + if(client) { + RunClient(transportFactory, protocolFactory) + } else if(server) { + RunServer(transportFactory, protocolFactory) + } else { + flag.Usage() + } + os.Exit(0) +} -- 2.17.1