diff --git a/compiler/cpp/src/thrift/generate/t_cpp_generator.cc b/compiler/cpp/src/thrift/generate/t_cpp_generator.cc index a085ada0e7b..e21252e8f6e 100644 --- a/compiler/cpp/src/thrift/generate/t_cpp_generator.cc +++ b/compiler/cpp/src/thrift/generate/t_cpp_generator.cc @@ -296,7 +296,7 @@ class t_cpp_generator : public t_oop_generator { (ttype->annotations_.find("cpp.customostream") != ttype->annotations_.end()); } - /** + /** * Determine if all fields of t_struct's storage do not throw * Move/Copy Constructors and Assignments applicable for 'noexcept' * Move defaults to 'noexcept' @@ -318,7 +318,7 @@ class t_cpp_generator : public t_oop_generator { /** * Returns the legal program name to use for a file generated by program, if the - * program name contains dots then replace it with underscores, otherwise return the + * program name contains dots then replace it with underscores, otherwise return the * original program name. */ std::string get_legal_program_name(std::string program_name); @@ -981,7 +981,7 @@ bool t_cpp_generator::has_field_with_default_value(t_struct* tstruct) for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { t_type* t = get_true_type((*m_iter)->get_type()); - if (is_reference(*m_iter) || t->is_string()) { + if (is_reference(*m_iter) || t->is_string() || t->is_uuid()) { t_const_value* cv = (*m_iter)->get_value(); if (cv != nullptr) { return true; @@ -1032,7 +1032,7 @@ void t_cpp_generator::generate_default_constructor(ostream& out, } else if (t->is_enum()) { dval += "static_cast<" + type_name(t) + ">(0)"; } else { - dval += (t->is_string() || is_reference(*m_iter)) ? "" : "0"; + dval += (t->is_string() || is_reference(*m_iter) || t->is_uuid()) ? "" : "0"; } if (!init_ctor) { init_ctor = true; @@ -1127,7 +1127,7 @@ void t_cpp_generator::generate_constructor_helper(ostream& out, has_nonrequired_fields = true; indent(out) << (*f_iter)->get_name() << " = " << maybeMove( - tmp_name + "." + (*f_iter)->get_name(), + tmp_name + "." + (*f_iter)->get_name(), is_move && is_complex_type((*f_iter)->get_type())) << ";" << endl; } @@ -1177,7 +1177,7 @@ void t_cpp_generator::generate_assignment_helper(ostream& out, t_struct* tstruct has_nonrequired_fields = true; indent(out) << (*f_iter)->get_name() << " = " << maybeMove( - tmp_name + "." + (*f_iter)->get_name(), + tmp_name + "." + (*f_iter)->get_name(), is_move && is_complex_type((*f_iter)->get_type())) << ";" << endl; } @@ -1276,7 +1276,7 @@ void t_cpp_generator::generate_struct_declaration(ostream& out, // Move constructor if (gen_moveable_) { - indent(out) << tstruct->get_name() << "(" << tstruct->get_name() << "&&) noexcept;" + indent(out) << tstruct->get_name() << "(" << tstruct->get_name() << "&&) noexcept;" << endl; } @@ -1286,12 +1286,12 @@ void t_cpp_generator::generate_struct_declaration(ostream& out, // Move assignment operator if (gen_moveable_) { - indent(out) << tstruct->get_name() << "& operator=(" << tstruct->get_name() << "&&) noexcept;" + indent(out) << tstruct->get_name() << "& operator=(" << tstruct->get_name() << "&&) noexcept;" << endl; } bool has_default_value = has_field_with_default_value(tstruct); - + // Default constructor std::string clsname_ctor = tstruct->get_name() + "()"; indent(out) << clsname_ctor << (has_default_value ? "" : " noexcept") << ";" << endl; @@ -1732,7 +1732,7 @@ void t_cpp_generator::generate_struct_result_writer(ostream& out, void t_cpp_generator::generate_struct_swap(ostream& out, t_struct* tstruct) { if (tstruct->get_name() == "a" || tstruct->get_name() == "b") { out << indent() << "void swap(" << tstruct->get_name() << " &a1, " << tstruct->get_name() - << " &a2) {" << endl; + << " &a2) {" << endl; } else { out << indent() << "void swap(" << tstruct->get_name() << " &a, " << tstruct->get_name() << " &b) {" << endl; @@ -1763,7 +1763,7 @@ void t_cpp_generator::generate_struct_swap(ostream& out, t_struct* tstruct) { if (has_nonrequired_fields) { if (tstruct->get_name() == "a" || tstruct->get_name() == "b") { - out << indent() << "swap(a1.__isset, a2.__isset);" << endl; + out << indent() << "swap(a1.__isset, a2.__isset);" << endl; } else { out << indent() << "swap(a.__isset, b.__isset);" << endl; } @@ -1942,7 +1942,7 @@ void t_cpp_generator::generate_service(t_service* tservice) { << endl; if (gen_cob_style_) { f_header_ << "#include " << endl // TMemoryBuffer - << "#include " << endl + << "#include " << endl << "namespace apache { namespace thrift { namespace async {" << endl << "class TAsyncChannel;" << endl << "}}}" << endl; } @@ -2578,7 +2578,7 @@ void t_cpp_generator::generate_service_client(t_service* tservice, string style) f_header_ << ", std::shared_ptr< ::apache::thrift::async::TConcurrentClientSyncInfo> sync"; } f_header_ << ") "; - + if (extends.empty()) { if (style == "Concurrent") { f_header_ << ": sync_(sync)" << endl; @@ -2677,7 +2677,7 @@ void t_cpp_generator::generate_service_client(t_service* tservice, string style) vector::const_iterator f_iter; for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) { generate_java_doc(f_header_, *f_iter); - indent(f_header_) << function_signature(*f_iter, ifstyle) + indent(f_header_) << function_signature(*f_iter, ifstyle) << " override;" << endl; // TODO(dreiss): Use private inheritance to avoid generating thise in cob-style. if (style == "Concurrent" && !(*f_iter)->is_oneway()) { @@ -3227,7 +3227,7 @@ void ProcessorGenerator::generate_class_definition() { f_header_ << indent() << "virtual " << ret_type_ << "dispatchCall(" << finish_cob_ << "::apache::thrift::protocol::TProtocol* iprot, " << "::apache::thrift::protocol::TProtocol* oprot, " - << "const std::string& fname, int32_t seqid" << call_context_ + << "const std::string& fname, int32_t seqid" << call_context_ << ") override;" << endl; if (generator_->gen_templates_) { f_header_ << indent() << "virtual " << ret_type_ << "dispatchCallTemplated(" << finish_cob_ @@ -4046,6 +4046,9 @@ void t_cpp_generator::generate_deserialize_field(ostream& out, case t_base_type::TYPE_VOID: throw "compiler error: cannot serialize void field in a struct: " + name; break; + case t_base_type::TYPE_UUID: + out << "readUUID(" << name << ");"; + break; case t_base_type::TYPE_STRING: if (type->is_binary()) { out << "readBinary(" << name << ");"; @@ -4072,13 +4075,13 @@ void t_cpp_generator::generate_deserialize_field(ostream& out, out << "readDouble(" << name << ");"; break; default: - throw "compiler error: no C++ reader for base type " + t_base_type::t_base_name(tbase) + name; + throw "compiler error: no C++ reader for base type " + t_base_type::t_base_name(tbase) + " " + name; } out << endl; } else if (type->is_enum()) { string t = tmp("ecast"); out << indent() << "int32_t " << t << ";" << endl << indent() << "xfer += iprot->readI32(" << t - << ");" << endl << indent() << name << " = static_cast<" + << ");" << endl << indent() << name << " = static_cast<" << type_name(type) << ">(" << t << ");" << endl; } else { printf("DO NOT KNOW HOW TO DESERIALIZE FIELD '%s' TYPE '%s'\n", @@ -4254,6 +4257,9 @@ void t_cpp_generator::generate_serialize_field(ostream& out, case t_base_type::TYPE_VOID: throw "compiler error: cannot serialize void field in a struct: " + name; break; + case t_base_type::TYPE_UUID: + out << "writeUUID(" << name << ");"; + break; case t_base_type::TYPE_STRING: if (type->is_binary()) { out << "writeBinary(" << name << ");"; @@ -4281,7 +4287,7 @@ void t_cpp_generator::generate_serialize_field(ostream& out, break; default: throw "compiler error: no C++ writer for base type " + t_base_type::t_base_name(tbase) - + name; + + " " + name; } } else if (type->is_enum()) { out << "writeI32(static_cast(" << name << "));"; @@ -4568,6 +4574,9 @@ string t_cpp_generator::base_type_name(t_base_type::t_base tbase) { return "int64_t"; case t_base_type::TYPE_DOUBLE: return "double"; + case t_base_type::TYPE_UUID: + // TODO: discuss possibility of a class TUuid; + return "std::string"; default: throw "compiler error: no C++ base type name for base type " + t_base_type::t_base_name(tbase); } @@ -4609,6 +4618,9 @@ string t_cpp_generator::declare_field(t_field* tfield, case t_base_type::TYPE_VOID: case t_base_type::TYPE_STRING: break; + case t_base_type::TYPE_UUID: + result += " = std::string(\"00000000-0000-0000-0000-000000000000\")"; + break; case t_base_type::TYPE_BOOL: result += " = false"; break; @@ -4735,6 +4747,8 @@ string t_cpp_generator::type_to_enum(t_type* type) { return "::apache::thrift::protocol::T_I64"; case t_base_type::TYPE_DOUBLE: return "::apache::thrift::protocol::T_DOUBLE"; + case t_base_type::TYPE_UUID: + return "::apache::thrift::protocol::T_UUID"; default: break; } @@ -4776,6 +4790,7 @@ bool t_cpp_generator::is_struct_storage_not_throwing(t_struct* tstruct) const { continue; case t_base_type::TYPE_VOID: case t_base_type::TYPE_STRING: + case t_base_type::TYPE_UUID: default: return false; } diff --git a/lib/cpp/CMakeLists.txt b/lib/cpp/CMakeLists.txt index 6a66e5ad1a7..c2f15dd5728 100644 --- a/lib/cpp/CMakeLists.txt +++ b/lib/cpp/CMakeLists.txt @@ -43,6 +43,7 @@ set(thriftcpp_SOURCES src/thrift/protocol/TJSONProtocol.cpp src/thrift/protocol/TMultiplexedProtocol.cpp src/thrift/protocol/TProtocol.cpp + src/thrift/protocol/TUuidUtils.cpp src/thrift/transport/TTransportException.cpp src/thrift/transport/TFDTransport.cpp src/thrift/transport/TSimpleFileTransport.cpp diff --git a/lib/cpp/Makefile.am b/lib/cpp/Makefile.am index c015b0db60f..7e35c63b1a5 100644 --- a/lib/cpp/Makefile.am +++ b/lib/cpp/Makefile.am @@ -69,6 +69,7 @@ libthrift_la_SOURCES = src/thrift/TApplicationException.cpp \ src/thrift/protocol/TBase64Utils.cpp \ src/thrift/protocol/TMultiplexedProtocol.cpp \ src/thrift/protocol/TProtocol.cpp \ + src/thrift/protocol/TUuidUtils.cpp \ src/thrift/transport/TTransportException.cpp \ src/thrift/transport/TFDTransport.cpp \ src/thrift/transport/TFileTransport.cpp \ diff --git a/lib/cpp/libthrift.vcxproj b/lib/cpp/libthrift.vcxproj index 0b5e16de7c4..1b413f8ae8e 100644 --- a/lib/cpp/libthrift.vcxproj +++ b/lib/cpp/libthrift.vcxproj @@ -49,6 +49,7 @@ + diff --git a/lib/cpp/libthrift.vcxproj.filters b/lib/cpp/libthrift.vcxproj.filters index 98426fac85b..fb94f608730 100644 --- a/lib/cpp/libthrift.vcxproj.filters +++ b/lib/cpp/libthrift.vcxproj.filters @@ -30,6 +30,9 @@ protocol + + protocol + transport diff --git a/lib/cpp/src/thrift/protocol/TBinaryProtocol.h b/lib/cpp/src/thrift/protocol/TBinaryProtocol.h index 7b829c76833..24e51f75f4d 100644 --- a/lib/cpp/src/thrift/protocol/TBinaryProtocol.h +++ b/lib/cpp/src/thrift/protocol/TBinaryProtocol.h @@ -119,6 +119,8 @@ class TBinaryProtocolT : public TVirtualProtocol +#include #include #include @@ -193,6 +194,20 @@ uint32_t TBinaryProtocolT::writeBinary(const std::string return TBinaryProtocolT::writeString(str); } +template +uint32_t TBinaryProtocolT::writeUUID(const std::string& str) { + std::string out; + const bool encoded = uuid_encode(str, out); + if(!encoded) + throw TProtocolException(TProtocolException::INVALID_DATA); + // This should not happen, but check for now + if(out.size() != 16) + throw TProtocolException(TProtocolException::UNKNOWN); + // TODO: Consider endian swapping, see lib/delphi/src/Thrift.Utils.pas:377 + this->trans_->write((uint8_t*)out.data(), 16); + return 16; +} + /** * Reading functions */ @@ -286,7 +301,7 @@ uint32_t TBinaryProtocolT::readMapBegin(TType& keyType, throw TProtocolException(TProtocolException::SIZE_LIMIT); } size = (uint32_t)sizei; - + TMap map(keyType, valType, size); checkReadBytesAvailable(map); @@ -428,6 +443,14 @@ uint32_t TBinaryProtocolT::readBinary(std::string& str) return TBinaryProtocolT::readString(str); } +template +uint32_t TBinaryProtocolT::readUUID(std::string& str) { + std::string in; + readStringBody(in, 16); + uuid_decode(in, str); + return 16; +} + template template uint32_t TBinaryProtocolT::readStringBody(StrType& str, int32_t size) { diff --git a/lib/cpp/src/thrift/protocol/TDebugProtocol.cpp b/lib/cpp/src/thrift/protocol/TDebugProtocol.cpp index 0e6d4a2aac3..0a2eaeda035 100644 --- a/lib/cpp/src/thrift/protocol/TDebugProtocol.cpp +++ b/lib/cpp/src/thrift/protocol/TDebugProtocol.cpp @@ -18,6 +18,7 @@ */ #include +#include #include #include @@ -70,10 +71,8 @@ string TDebugProtocol::fieldTypeName(TType type) { return "set"; case T_LIST: return "list"; - case T_UTF8: - return "utf8"; - case T_UTF16: - return "utf16"; + case T_UUID: + return "uuid"; default: return "unknown"; } @@ -388,6 +387,24 @@ uint32_t TDebugProtocol::writeBinary(const string& str) { // XXX Hex? return TDebugProtocol::writeString(str); } + +uint32_t TDebugProtocol::writeUUID(const string& str) { + std::string out_raw; + uuid_encode(str, out_raw); + + std::string out_encoded; + uuid_decode(out_raw, out_encoded); + + size_t size = writePlain("{\n"); + indentUp(); + size += writeIndented("[in ] = \"" + str + "\",\n"); + size += writeIndented("[raw] = "); + size += writeString(out_raw); + size += writeIndented("[enc] = \"" + out_encoded + "\"\n"); + indentDown(); + size += writeIndented("}\n"); + return size; +} } } } // apache::thrift::protocol diff --git a/lib/cpp/src/thrift/protocol/TDebugProtocol.h b/lib/cpp/src/thrift/protocol/TDebugProtocol.h index 41bb0d4ec94..af89cc5679d 100644 --- a/lib/cpp/src/thrift/protocol/TDebugProtocol.h +++ b/lib/cpp/src/thrift/protocol/TDebugProtocol.h @@ -110,6 +110,8 @@ class TDebugProtocol : public TVirtualProtocol { uint32_t writeBinary(const std::string& str); + uint32_t writeUUID(const std::string& str); + private: void indentUp(); void indentDown(); diff --git a/lib/cpp/src/thrift/protocol/TEnum.h b/lib/cpp/src/thrift/protocol/TEnum.h index 9636785e327..bbd1247dc19 100644 --- a/lib/cpp/src/thrift/protocol/TEnum.h +++ b/lib/cpp/src/thrift/protocol/TEnum.h @@ -18,7 +18,7 @@ */ #ifndef _THRIFT_ENUM_H_ -#define _THRIFT_ENUM_H_ +#define _THRIFT_ENUM_H_ namespace apache { namespace thrift { @@ -46,8 +46,7 @@ enum TType { T_MAP = 13, T_SET = 14, T_LIST = 15, - T_UTF8 = 16, - T_UTF16 = 17 + T_UUID = 16, }; /** @@ -63,4 +62,4 @@ enum TMessageType { }}} // apache::thrift::protocol -#endif // #define _THRIFT_ENUM_H_ +#endif // #define _THRIFT_ENUM_H_ diff --git a/lib/cpp/src/thrift/protocol/TJSONProtocol.cpp b/lib/cpp/src/thrift/protocol/TJSONProtocol.cpp index 6e4e8ef0db6..0899f00eefa 100644 --- a/lib/cpp/src/thrift/protocol/TJSONProtocol.cpp +++ b/lib/cpp/src/thrift/protocol/TJSONProtocol.cpp @@ -18,6 +18,7 @@ */ #include +#include #include @@ -68,6 +69,7 @@ static const std::string kTypeNameString("str"); static const std::string kTypeNameMap("map"); static const std::string kTypeNameList("lst"); static const std::string kTypeNameSet("set"); +static const std::string kTypeNameUuid("uid"); static const std::string& getTypeNameForTypeID(TType typeID) { switch (typeID) { @@ -93,6 +95,8 @@ static const std::string& getTypeNameForTypeID(TType typeID) { return kTypeNameSet; case T_LIST: return kTypeNameList; + case T_UUID: + return kTypeNameUuid; default: throw TProtocolException(TProtocolException::NOT_IMPLEMENTED, "Unrecognized type"); } @@ -140,6 +144,9 @@ static TType getTypeIDForTypeName(const std::string& name) { case 't': result = T_BOOL; break; + case 'u': + result = T_UUID; + break; } } if (result == T_STOP) { @@ -710,6 +717,16 @@ uint32_t TJSONProtocol::writeBinary(const std::string& str) { return writeJSONBase64(str); } +uint32_t TJSONProtocol::writeUUID(const std::string& str) { + std::string out_raw; + uuid_encode(str, out_raw); + + std::string out_encoded; + uuid_decode(out_raw, out_encoded); + + return writeJSONString(out_encoded); +} + /** * Reading functions */ @@ -1106,6 +1123,10 @@ uint32_t TJSONProtocol::readBinary(std::string& str) { return readJSONBase64(str); } +uint32_t TJSONProtocol::readUUID(std::string& str) { + return readJSONString(str); +} + // Return the minimum number of bytes a type will consume on the wire int TJSONProtocol::getMinSerializedSize(TType type) { @@ -1113,7 +1134,7 @@ int TJSONProtocol::getMinSerializedSize(TType type) { case T_STOP: return 0; case T_VOID: return 0; - case T_BOOL: return 1; // written as int + case T_BOOL: return 1; // written as int case T_BYTE: return 1; case T_DOUBLE: return 1; case T_I16: return 1; @@ -1124,6 +1145,7 @@ int TJSONProtocol::getMinSerializedSize(TType type) case T_MAP: return 2; // empty map case T_SET: return 2; // empty set case T_LIST: return 2; // empty list + case T_UUID: return 16; // empty UUID default: throw TProtocolException(TProtocolException::UNKNOWN, "unrecognized type code"); } } diff --git a/lib/cpp/src/thrift/protocol/TJSONProtocol.h b/lib/cpp/src/thrift/protocol/TJSONProtocol.h index d01bdf80141..069a9905f53 100644 --- a/lib/cpp/src/thrift/protocol/TJSONProtocol.h +++ b/lib/cpp/src/thrift/protocol/TJSONProtocol.h @@ -198,6 +198,8 @@ class TJSONProtocol : public TVirtualProtocol { uint32_t writeBinary(const std::string& str); + uint32_t writeUUID(const std::string& str); + /** * Reading functions */ @@ -245,6 +247,8 @@ class TJSONProtocol : public TVirtualProtocol { uint32_t readBinary(std::string& str); + uint32_t readUUID(std::string& str); + int getMinSerializedSize(TType type) override; void checkReadBytesAvailable(TSet& set) override diff --git a/lib/cpp/src/thrift/protocol/TProtocol.h b/lib/cpp/src/thrift/protocol/TProtocol.h index 237c1e56883..d29df1c8d32 100644 --- a/lib/cpp/src/thrift/protocol/TProtocol.h +++ b/lib/cpp/src/thrift/protocol/TProtocol.h @@ -275,6 +275,8 @@ class TProtocol { virtual uint32_t writeBinary_virt(const std::string& str) = 0; + virtual uint32_t writeUUID_virt(const std::string& str) = 0; + uint32_t writeMessageBegin(const std::string& name, const TMessageType messageType, const int32_t seqid) { @@ -382,6 +384,11 @@ class TProtocol { return writeBinary_virt(str); } + uint32_t writeUUID(const std::string& str) { + T_VIRTUAL_CALL(); + return writeUUID_virt(str); + } + /** * Reading functions */ @@ -430,6 +437,8 @@ class TProtocol { virtual uint32_t readBinary_virt(std::string& str) = 0; + virtual uint32_t readUUID_virt(std::string& str) = 0; + uint32_t readMessageBegin(std::string& name, TMessageType& messageType, int32_t& seqid) { T_VIRTUAL_CALL(); return readMessageBegin_virt(name, messageType, seqid); @@ -530,6 +539,11 @@ class TProtocol { return readBinary_virt(str); } + uint32_t readUUID(std::string& str) { + T_VIRTUAL_CALL(); + return readUUID_virt(str); + } + /* * std::vector is specialized for bool, and its elements are individual bits * rather than bools. We need to define a different version of readBool() diff --git a/lib/cpp/src/thrift/protocol/TProtocolDecorator.h b/lib/cpp/src/thrift/protocol/TProtocolDecorator.h index 5258159f149..9eb1566ab75 100644 --- a/lib/cpp/src/thrift/protocol/TProtocolDecorator.h +++ b/lib/cpp/src/thrift/protocol/TProtocolDecorator.h @@ -92,6 +92,7 @@ class TProtocolDecorator : public TProtocol { uint32_t writeDouble_virt(const double dub) override { return protocol->writeDouble(dub); } uint32_t writeString_virt(const std::string& str) override { return protocol->writeString(str); } uint32_t writeBinary_virt(const std::string& str) override { return protocol->writeBinary(str); } + uint32_t writeUUID_virt(const std::string& str) override { return protocol->writeUUID(str); } uint32_t readMessageBegin_virt(std::string& name, TMessageType& messageType, @@ -140,6 +141,7 @@ class TProtocolDecorator : public TProtocol { uint32_t readString_virt(std::string& str) override { return protocol->readString(str); } uint32_t readBinary_virt(std::string& str) override { return protocol->readBinary(str); } + uint32_t readUUID_virt(std::string& str) override { return protocol->readUUID(str); } private: shared_ptr protocol; diff --git a/lib/cpp/src/thrift/protocol/TUuidUtils.cpp b/lib/cpp/src/thrift/protocol/TUuidUtils.cpp new file mode 100644 index 00000000000..e1eab22cb11 --- /dev/null +++ b/lib/cpp/src/thrift/protocol/TUuidUtils.cpp @@ -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. + */ + +#include + +#include +#include +#include + +namespace apache { +namespace thrift { +namespace protocol { + +bool uuid_encode(const std::string& in, std::string& out) { + static const boost::uuids::string_generator gen; + static const std::string empty_uuid(boost::uuids::uuid::static_size(), '\0'); + out = empty_uuid; + if (in.empty()) { + return true; + } + try { + const boost::uuids::uuid uuid{gen(in)}; + std::copy(uuid.begin(), uuid.end(), out.begin()); + return true; + } catch (const std::runtime_error&) { + // Invalid string most probably + return false; + } +} + +void uuid_decode(const std::string& in, std::string& out) { + boost::uuids::uuid uuid{}; + const size_t to_copy = std::min(in.size(), uuid.size()); + std::copy(in.begin(), in.begin() + to_copy, uuid.begin()); + out = boost::uuids::to_string(uuid); +} + +} +} +} // apache::thrift::protocol diff --git a/lib/cpp/src/thrift/protocol/TUuidUtils.hpp b/lib/cpp/src/thrift/protocol/TUuidUtils.hpp new file mode 100644 index 00000000000..583147ffd38 --- /dev/null +++ b/lib/cpp/src/thrift/protocol/TUuidUtils.hpp @@ -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. + */ + +#ifndef _THRIFT_PROTOCOL_TUUIDUTILS_H_ +#define _THRIFT_PROTOCOL_TUUIDUTILS_H_ + +#include + +namespace apache { +namespace thrift { +namespace protocol { + +// Encode canonical UUID string to a 16 char representation +// Supported formats for in: +// - "hhhhhhhh-hhhh-hhhh-hhhh-hhhhhhhhhhhh" +// - "{hhhhhhhh-hhhh-hhhh-hhhh-hhhhhhhhhhhh}" +// - "hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh" +// - "{hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh}" +// Returns false if the string was invalid and the value was not encoded. +bool uuid_encode(const std::string& in, std::string& out); + +// Decode 16 char UUID buffer to 36 characted string +void uuid_decode(const std::string& in, std::string& out); + +} +} +} // apache::thrift::protocol + +#endif // #define _THRIFT_PROTOCOL_TUUIDUTILS_H_ diff --git a/lib/cpp/src/thrift/protocol/TVirtualProtocol.h b/lib/cpp/src/thrift/protocol/TVirtualProtocol.h index b7fe929af20..4698081d241 100644 --- a/lib/cpp/src/thrift/protocol/TVirtualProtocol.h +++ b/lib/cpp/src/thrift/protocol/TVirtualProtocol.h @@ -393,6 +393,10 @@ class TVirtualProtocol : public Super_ { return static_cast(this)->writeBinary(str); } + uint32_t writeUUID_virt(const std::string& str) override { + return static_cast(this)->writeUUID(str); + } + /** * Reading functions */ @@ -471,6 +475,10 @@ class TVirtualProtocol : public Super_ { return static_cast(this)->readBinary(str); } + uint32_t readUUID_virt(std::string& str) override { + return static_cast(this)->readUUID(str); + } + uint32_t skip_virt(TType type) override { return static_cast(this)->skip(type); } /* diff --git a/lib/cpp/test/Benchmark.cpp b/lib/cpp/test/Benchmark.cpp index 56adac0b203..97a531767d0 100644 --- a/lib/cpp/test/Benchmark.cpp +++ b/lib/cpp/test/Benchmark.cpp @@ -66,6 +66,7 @@ int main() { ooe.some_characters = "JSON THIS! \"\1"; ooe.zomg_unicode = "\xd7\n\a\t"; ooe.base64 = "\1\2\3\255"; + ooe.rfc4122_uuid = "{5e2ab188-1726-4e75-a04f-1ed9a6a89c4c}"; int num = 100000; std::shared_ptr buf(new TMemoryBuffer(num*1000)); diff --git a/lib/cpp/test/CMakeLists.txt b/lib/cpp/test/CMakeLists.txt index 1117cd9f36a..87ed1094cb4 100644 --- a/lib/cpp/test/CMakeLists.txt +++ b/lib/cpp/test/CMakeLists.txt @@ -360,7 +360,7 @@ add_custom_command(OUTPUT gen-cpp/AnnotationTest_constants.cpp ) add_custom_command(OUTPUT gen-cpp/DebugProtoTest_types.cpp gen-cpp/DebugProtoTest_types.h gen-cpp/EmptyService.cpp gen-cpp/EmptyService.h - COMMAND ${THRIFT_COMPILER} --gen cpp ${PROJECT_SOURCE_DIR}/test/v0.16/DebugProtoTest.thrift + COMMAND ${THRIFT_COMPILER} --gen cpp ${PROJECT_SOURCE_DIR}/test/DebugProtoTest.thrift ) add_custom_command(OUTPUT gen-cpp/EnumTest_types.cpp gen-cpp/EnumTest_types.h @@ -384,7 +384,7 @@ add_custom_command(OUTPUT gen-cpp/Service.cpp gen-cpp/StressTest_types.cpp ) add_custom_command(OUTPUT gen-cpp/SecondService.cpp gen-cpp/ThriftTest_constants.cpp gen-cpp/ThriftTest.cpp gen-cpp/ThriftTest_types.cpp gen-cpp/ThriftTest_types.h - COMMAND ${THRIFT_COMPILER} --gen cpp ${PROJECT_SOURCE_DIR}/test/v0.16/ThriftTest.thrift + COMMAND ${THRIFT_COMPILER} --gen cpp ${PROJECT_SOURCE_DIR}/test/ThriftTest.thrift ) add_custom_command(OUTPUT gen-cpp/OneWayService.cpp gen-cpp/OneWayTest_types.h gen-cpp/OneWayService.h diff --git a/lib/cpp/test/DebugProtoTest.cpp b/lib/cpp/test/DebugProtoTest.cpp index 060f3547dd7..cc4e5ff9558 100644 --- a/lib/cpp/test/DebugProtoTest.cpp +++ b/lib/cpp/test/DebugProtoTest.cpp @@ -74,6 +74,11 @@ BOOST_AUTO_TEST_CASE(test_debug_proto_1) { " [1] = 2,\n" " [2] = 3,\n" " },\n" + " 15: rfc4122_uuid (uuid) = {\n" + " [in ] = \"\",\n" + " [raw] = \"\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\",\n" + " [enc] = \"00000000-0000-0000-0000-000000000000\"\n" + " }\n" "}"); const std::string result(apache::thrift::ThriftDebugString(*ooe)); @@ -98,6 +103,7 @@ void testCaseSetup_2() { "\xb0\xcf\x81\xe2\x84\x8e\x20\xce\x91\x74\x74" "\xce\xb1\xe2\x85\xbd\xce\xba\xc7\x83\xe2\x80" "\xbc"; + n->my_ooe.rfc4122_uuid = "{5e2ab188-1726-4e75-a04f-1ed9a6a89c4c}"; n->my_bonk.type = 31337; n->my_bonk.message = "I am a bonk... xor!"; } @@ -141,6 +147,11 @@ BOOST_AUTO_TEST_CASE(test_debug_proto_2) { " [1] = 2,\n" " [2] = 3,\n" " },\n" + " 15: rfc4122_uuid (uuid) = {\n" + " [in ] = \"{5e2ab188-1726-4e75-a04f-1ed9a6a89c4c}\",\n" + " [raw] = \"^*\\xb1\\x88\\x17&Nu\\xa0O\\x1e\\xd9\\xa6\\xa8\\x9cL\",\n" + " [enc] = \"5e2ab188-1726-4e75-a04f-1ed9a6a89c4c\"\n" + " }\n" " },\n" "}"); const std::string result(apache::thrift::ThriftDebugString(*n)); @@ -228,6 +239,11 @@ BOOST_AUTO_TEST_CASE(test_debug_proto_3) { " [1] = 2,\n" " [2] = 3,\n" " },\n" + " 15: rfc4122_uuid (uuid) = {\n" + " [in ] = \"\",\n" + " [raw] = \"\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\",\n" + " [enc] = \"00000000-0000-0000-0000-000000000000\"\n" + " }\n" " },\n" " [1] = OneOfEach {\n" " 01: im_true (bool) = true,\n" @@ -259,6 +275,11 @@ BOOST_AUTO_TEST_CASE(test_debug_proto_3) { " [1] = 2,\n" " [2] = 3,\n" " },\n" + " 15: rfc4122_uuid (uuid) = {\n" + " [in ] = \"{5e2ab188-1726-4e75-a04f-1ed9a6a89c4c}\",\n" + " [raw] = \"^*\\xb1\\x88\\x17&Nu\\xa0O\\x1e\\xd9\\xa6\\xa8\\x9cL\",\n" + " [enc] = \"5e2ab188-1726-4e75-a04f-1ed9a6a89c4c\"\n" + " }\n" " },\n" " },\n" " 02: contain (set) = set[3] {\n" diff --git a/lib/cpp/test/JSONProtoTest.cpp b/lib/cpp/test/JSONProtoTest.cpp index 082c8a28b75..fedf99e4821 100644 --- a/lib/cpp/test/JSONProtoTest.cpp +++ b/lib/cpp/test/JSONProtoTest.cpp @@ -48,6 +48,7 @@ void testCaseSetup_1() { ooe->some_characters = "JSON THIS! \"\1"; ooe->zomg_unicode = "\xd7\n\a\t"; ooe->base64 = "\1\2\3\255"; + ooe->rfc4122_uuid = "00000000-0000-0000-0000-000000000000"; } BOOST_AUTO_TEST_CASE(test_json_proto_1) { @@ -59,7 +60,7 @@ BOOST_AUTO_TEST_CASE(test_json_proto_1) { "535897931},\"8\":{\"str\":\"JSON THIS! \\\"\\u0001\"},\"9\":{\"str\":\"\xd7\\" "n\\u0007\\t\"},\"10\":{\"tf\":0},\"11\":{\"str\":\"AQIDrQ\"},\"12\":{\"lst\"" ":[\"i8\",3,1,2,3]},\"13\":{\"lst\":[\"i16\",3,1,2,3]},\"14\":{\"lst\":[\"i64" - "\",3,1,2,3]}}"); + "\",3,1,2,3]},\"15\":{\"uid\":\"00000000-0000-0000-0000-000000000000\"}}"); const std::string result(apache::thrift::ThriftJSONString(*ooe)); @@ -84,6 +85,7 @@ void testCaseSetup_2() { "\xb0\xcf\x81\xe2\x84\x8e\x20\xce\x91\x74\x74" "\xce\xb1\xe2\x85\xbd\xce\xba\xc7\x83\xe2\x80" "\xbc"; + n->my_ooe.rfc4122_uuid = "5e2ab188-1726-4e75-a04f-1ed9a6a89c4c"; n->my_bonk.type = 31337; n->my_bonk.message = "I am a bonk... xor!"; } @@ -98,7 +100,8 @@ BOOST_AUTO_TEST_CASE(test_json_proto_2) { "1.6180339887498949},\"8\":{\"str\":\":R (me going \\\"rrrr\\\")\"},\"9\":{" "\"str\":\"ӀⅮΝ Нοⅿоɡгаρℎ Αttαⅽκǃ‼\"},\"10\":{\"tf\":0},\"11\":{\"str\":\"" "AQIDrQ\"},\"12\":{\"lst\":[\"i8\",3,1,2,3]},\"13\":{\"lst\":[\"i16\",3,1,2" - ",3]},\"14\":{\"lst\":[\"i64\",3,1,2,3]}}}}" + ",3]},\"14\":{\"lst\":[\"i64\",3,1,2,3]},\"15\":{\"uid\":\"5e2ab188-1726-" + "4e75-a04f-1ed9a6a89c4c\"}}}}" ); const std::string result(apache::thrift::ThriftJSONString(*n)); @@ -162,12 +165,14 @@ BOOST_AUTO_TEST_CASE(test_json_proto_3) { "},\"7\":{\"dbl\":3.1415926535897931},\"8\":{\"str\":\"JSON THIS! \\\"\\u0001" "\"},\"9\":{\"str\":\"\xd7\\n\\u0007\\t\"},\"10\":{\"tf\":0},\"11\":{\"str\":" "\"AQIDrQ\"},\"12\":{\"lst\":[\"i8\",3,1,2,3]},\"13\":{\"lst\":[\"i16\",3,1,2" - ",3]},\"14\":{\"lst\":[\"i64\",3,1,2,3]}},{\"1\":{\"tf\":1},\"2\":{\"tf\":0}," + ",3]},\"14\":{\"lst\":[\"i64\",3,1,2,3]},\"15\":{\"uid\":\"00000000-0000-0000" + "-0000-000000000000\"}},{\"1\":{\"tf\":1},\"2\":{\"tf\":0}," "\"3\":{\"i8\":51},\"4\":{\"i16\":16},\"5\":{\"i32\":32},\"6\":{\"i64\":64}," "\"7\":{\"dbl\":1.6180339887498949},\"8\":{\"str\":\":R (me going \\\"rrrr\\\"" ")\"},\"9\":{\"str\":\"ӀⅮΝ Нοⅿоɡгаρℎ Αttαⅽκǃ‼\"},\"10\":{\"tf\":0},\"11\":{" "\"str\":\"AQIDrQ\"},\"12\":{\"lst\":[\"i8\",3,1,2,3]},\"13\":{\"lst\":[\"i16" - "\",3,1,2,3]},\"14\":{\"lst\":[\"i64\",3,1,2,3]}}]},\"2\":{\"set\":[\"lst\",3" + "\",3,1,2,3]},\"14\":{\"lst\":[\"i64\",3,1,2,3]},\"15\":{\"uid\":\"5e2ab188-" + "1726-4e75-a04f-1ed9a6a89c4c\"}}]},\"2\":{\"set\":[\"lst\",3" ",[\"str\",0],[\"str\",2,\"and a one\",\"and a two\"],[\"str\",3,\"then a one" ", two\",\"three!\",\"FOUR!!\"]]},\"3\":{\"map\":[\"str\",\"lst\",3,{\"nothin" "g\":[\"rec\",0],\"poe\":[\"rec\",3,{\"1\":{\"i32\":3},\"2\":{\"str\":\"quoth" @@ -192,6 +197,8 @@ BOOST_AUTO_TEST_CASE(test_json_proto_4) { OneOfEach ooe2; ooe2.read(proto.get()); + BOOST_TEST_INFO("written: " << *ooe); + BOOST_TEST_INFO("read : " << ooe2); BOOST_CHECK(*ooe == ooe2); } @@ -205,6 +212,8 @@ BOOST_AUTO_TEST_CASE(test_json_proto_5) { HolyMoley hm2; hm2.read(proto.get()); + BOOST_TEST_INFO("written: " << *hm); + BOOST_TEST_INFO("read : " << hm2); BOOST_CHECK(*hm == hm2); hm2.big[0].a_bite = 0x00; @@ -230,14 +239,14 @@ BOOST_AUTO_TEST_CASE(test_json_proto_6) { ); std::shared_ptr buffer(new TMemoryBuffer()); - std::shared_ptr proto(new TJSONProtocol(buffer)); + std::shared_ptr proto(new TJSONProtocol(buffer)); dub.write(proto.get()); Doubles dub_1; dub_1.read(proto.get()); const std::string result(apache::thrift::ThriftJSONString(dub)); const std::string result_1(apache::thrift::ThriftJSONString(dub_1)); - + BOOST_CHECK_MESSAGE(!expected_result.compare(result), "Expected:\n" << expected_result << "\nGotten:\n" << result); BOOST_CHECK_MESSAGE(!expected_result.compare(result_1), diff --git a/lib/cpp/test/SpecializationTest.cpp b/lib/cpp/test/SpecializationTest.cpp index 008837d319c..097611284a7 100644 --- a/lib/cpp/test/SpecializationTest.cpp +++ b/lib/cpp/test/SpecializationTest.cpp @@ -26,6 +26,7 @@ BOOST_AUTO_TEST_CASE(test_specialization_1) { ooe.some_characters = "JSON THIS! \"\1"; ooe.zomg_unicode = "\xd7\n\a\t"; ooe.base64 = "\1\2\3\255"; + ooe.rfc4122_uuid = "00000000-0000-0000-0000-000000000000"; Nesting n; n.my_ooe = ooe; @@ -89,6 +90,8 @@ BOOST_AUTO_TEST_CASE(test_specialization_1) { OneOfEach ooe2; ooe2.read(proto.get()); + BOOST_TEST_INFO("Write: " << ooe); + BOOST_TEST_INFO("Read : " << ooe2); BOOST_CHECK(ooe == ooe2); hm.write(proto.get()); diff --git a/lib/cpp/test/ToStringTest.cpp b/lib/cpp/test/ToStringTest.cpp index 736b33c0a5b..68c82ad4be1 100644 --- a/lib/cpp/test/ToStringTest.cpp +++ b/lib/cpp/test/ToStringTest.cpp @@ -160,4 +160,13 @@ BOOST_AUTO_TEST_CASE(generated_nested_list_object_to_string) { "ListBonks(bonk=[Bonk(message=a, type=0), Bonk(message=b, type=0)])"); } +BOOST_AUTO_TEST_CASE(generated_uuid_to_string) { + thrift::test::CrazyNesting l; + l.uuid_field = "{4b686716-5f20-4deb-8ce0-9eaf379e8a3d}"; + + BOOST_CHECK_EQUAL(to_string(l), + "CrazyNesting(string_field=, set_field=, list_field=[], binary_field=, " + "uuid_field={4b686716-5f20-4deb-8ce0-9eaf379e8a3d})"); +} + BOOST_AUTO_TEST_SUITE_END() diff --git a/test/cpp/CMakeLists.txt b/test/cpp/CMakeLists.txt index f693a273daf..a6c1fd5cf4e 100644 --- a/test/cpp/CMakeLists.txt +++ b/test/cpp/CMakeLists.txt @@ -100,7 +100,7 @@ add_test(NAME SpecificNameTest COMMAND SpecificNameTest) # add_custom_command(OUTPUT gen-cpp/SecondService.cpp gen-cpp/SecondService.h gen-cpp/ThriftTest.cpp gen-cpp/ThriftTest.h gen-cpp/ThriftTest_types.cpp gen-cpp/ThriftTest_constants.cpp - COMMAND ${THRIFT_COMPILER} --gen cpp:templates,cob_style -r ${PROJECT_SOURCE_DIR}/test/v0.16/ThriftTest.thrift + COMMAND ${THRIFT_COMPILER} --gen cpp:templates,cob_style -r ${PROJECT_SOURCE_DIR}/test/ThriftTest.thrift ) add_custom_command(OUTPUT gen-cpp/Service.cpp diff --git a/test/cpp/src/TestClient.cpp b/test/cpp/src/TestClient.cpp index c4146cc5cf5..fd04ed87063 100644 --- a/test/cpp/src/TestClient.cpp +++ b/test/cpp/src/TestClient.cpp @@ -70,7 +70,7 @@ using namespace thrift::test; // template -class TPedanticProtocol : public Proto +class TPedanticProtocol : public Proto { public: TPedanticProtocol(std::shared_ptr& transport) @@ -178,6 +178,18 @@ bool print_eq(T expected, T actual) { return_code |= ERR_BASETYPES; \ } +#define UUID_TEST(func, value, expected) \ + cout << #func "(" << value << ") = "; \ + try { \ + if (!print_eq(expected, testClient.func(value))) \ + return_code |= ERR_BASETYPES; \ + } catch (TTransportException&) { \ + throw; \ + } catch (exception & ex) { \ + cout << "*** FAILED ***" << endl << ex.what() << endl; \ + return_code |= ERR_BASETYPES; \ + } + int binary_test(ThriftTestClient& testClient, string::size_type siz); BOOST_CONSTEXPR_OR_CONST int ERR_BASETYPES = 1; @@ -638,6 +650,15 @@ int main(int argc, char** argv) { if (i > 0) { i *= 2; } else { ++i; } } + /** + * UUID TEST + */ + const std::string expected_uuid{"5e2ab188-1726-4e75-a04f-1ed9a6a89c4c"}; + UUID_TEST(testUuid, std::string{"{5e2ab188-1726-4e75-a04f-1ed9a6a89c4c}"}, expected_uuid); + UUID_TEST(testUuid, std::string{"5e2ab188-1726-4e75-a04f-1ed9a6a89c4c"}, expected_uuid); + UUID_TEST(testUuid, std::string{"5e2ab18817264e75a04f1ed9a6a89c4c"}, expected_uuid); + UUID_TEST(testUuid, std::string{"{5e2ab18817264e75a04f1ed9a6a89c4c}"}, expected_uuid); + UUID_TEST(testUuid, std::string{}, std::string{"00000000-0000-0000-0000-000000000000"}); /** * STRUCT TEST diff --git a/test/cpp/src/TestServer.cpp b/test/cpp/src/TestServer.cpp index 65317f89c13..6c61f402f8d 100644 --- a/test/cpp/src/TestServer.cpp +++ b/test/cpp/src/TestServer.cpp @@ -132,6 +132,11 @@ class TestHandler : public ThriftTestIf { _return = thing; } + std::string testUuid(const std::string thing) override { + printf("testUuid(\"{%s}\")\n", thing.c_str()); + return thing; + } + void testStruct(Xtruct& out, const Xtruct& thing) override { printf("testStruct({\"%s\", %d, %d, %" PRId64 "})\n", thing.string_thing.c_str(), @@ -442,6 +447,11 @@ class TestHandlerAsync : public ThriftTestCobSvIf { cob(res); } + void testUuid(::std::function cob, const std::string thing) override { + std::string res = _delegate->testUuid(thing); + cob(res); + } + void testStruct(std::function cob, const Xtruct& thing) override { Xtruct res; _delegate->testStruct(res, thing);