From 22aad48719e30aa78d05cb55c42c26ff443a072e Mon Sep 17 00:00:00 2001 From: dongl <2725096176@qq.com> Date: Sun, 7 May 2023 19:40:48 +0800 Subject: [PATCH] =?UTF-8?q?0507=2019:40=20db=20service=20=E5=88=86?= =?UTF-8?q?=E7=A6=BB=20=E6=B7=BB=E5=8A=A0=E5=A5=BD=E5=8F=8B=E8=BF=9B?= =?UTF-8?q?=E5=BA=A6=E4=B8=80=E4=B8=A2=E4=B8=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CMakeLists.txt | 2 + MC/api/main.cpp | 2 +- MDB/imm_mysqldb/CMakeLists.txt | 4 +- MDB/imm_mysqldb/template_table/im_user.h | 1 + .../template_table/im_user_friends.h | 21 + .../template_table/sql_table_struct.h | 10 + MDB/imm_mysqldb/user/DBUser.cpp | 52 - MDB/imm_mysqldb/user/DBUser.h | 24 - MP/proto/mp.body.pb.cc | 112 +- MP/proto/mp.mph.pb.cc | 37 +- MP/proto/mp.sri.pb.cc | 21 +- MS/CMakeLists.txt | 1 + MS/MS.cpp | 2 +- MS/mmm/agreement.cpp | 2 +- MS/mmm/agreement.h | 2 +- MS/mmm/handler.cpp | 11 +- MS/mmm/handler.h | 4 +- MS/mmm/mapping.cpp | 13 +- MS/works/CMakeLists.txt | 2 + MS/works/controller/IMProveController.cpp | 10 + MS/works/controller/IMProveController.h | 22 + MS/works/controller/UserController.cpp | 24 - MS/works/controller/UserController.h | 18 - MS/works/controller/UserProveController.cpp | 33 + MS/works/controller/UserProveController.h | 19 + MS/works/controller/works.h | 3 +- MS/works/db/DB.h | 20 + MS/works/db/UserDB.cpp | 136 +- MS/works/db/UserDB.h | 23 +- MS/works/db/UserFriendsDB.cpp | 123 + MS/works/db/UserFriendsDB.h | 23 + MS/works/db/linkDB.cpp | 8 +- MS/works/db/linkDB.h | 8 +- MS/works/db/po/Po.cpp | 16 + MS/works/db/po/PoJson.cpp | 7 + MS/works/db/po/PoJson.h | 71 + MS/works/db/po/po.h | 24 + MS/works/service/Service.h | 29 + MS/works/service/UserFriendsService.cpp | 60 + MS/works/service/UserFriendsService.h | 29 + MS/works/service/UserService.cpp | 95 +- MS/works/service/UserService.h | 12 +- TEST/CMakeLists.txt | 20 + TEST/main.cpp | 43 + include/gtest/gtest-assertion-result.h | 237 ++ include/gtest/gtest-death-test.h | 345 ++ include/gtest/gtest-matchers.h | 956 +++++ include/gtest/gtest-message.h | 218 ++ include/gtest/gtest-param-test.h | 545 +++ include/gtest/gtest-printers.h | 1055 ++++++ include/gtest/gtest-spi.h | 248 ++ include/gtest/gtest-test-part.h | 190 + include/gtest/gtest-typed-test.h | 331 ++ include/gtest/gtest.h | 2308 ++++++++++++ include/gtest/gtest_pred_impl.h | 279 ++ include/gtest/gtest_prod.h | 60 + include/gtest/internal/custom/README.md | 44 + include/gtest/internal/custom/gtest-port.h | 37 + .../gtest/internal/custom/gtest-printers.h | 42 + include/gtest/internal/custom/gtest.h | 37 + .../internal/gtest-death-test-internal.h | 306 ++ include/gtest/internal/gtest-filepath.h | 220 ++ include/gtest/internal/gtest-internal.h | 1570 ++++++++ include/gtest/internal/gtest-param-util.h | 1028 ++++++ include/gtest/internal/gtest-port-arch.h | 118 + include/gtest/internal/gtest-port.h | 2423 ++++++++++++ include/gtest/internal/gtest-string.h | 177 + include/gtest/internal/gtest-type-util.h | 186 + include/libevent/event2/event.h | 4 +- include/mp/Body.h | 9 + include/mp/proto/mp.body.pb.h | 62 +- include/mp/proto/mp.mph.pb.h | 38 +- include/mp/proto/mp.sri.pb.h | 10 +- include/mp/protohuf/mp.body.proto | 13 +- include/mp/protohuf/mp.mph.proto | 30 +- include/mp/protohuf/mp.sri.proto | 9 + include/rapidjson/allocators.h | 693 ++++ include/rapidjson/cursorstreamwrapper.h | 78 + include/rapidjson/document.h | 3043 +++++++++++++++ include/rapidjson/encodedstream.h | 299 ++ include/rapidjson/encodings.h | 716 ++++ include/rapidjson/error/en.h | 176 + include/rapidjson/error/error.h | 285 ++ include/rapidjson/filereadstream.h | 99 + include/rapidjson/filewritestream.h | 104 + include/rapidjson/fwd.h | 151 + include/rapidjson/internal/biginteger.h | 297 ++ include/rapidjson/internal/clzll.h | 71 + include/rapidjson/internal/diyfp.h | 261 ++ include/rapidjson/internal/dtoa.h | 249 ++ include/rapidjson/internal/ieee754.h | 78 + include/rapidjson/internal/itoa.h | 308 ++ include/rapidjson/internal/meta.h | 186 + include/rapidjson/internal/pow10.h | 55 + include/rapidjson/internal/regex.h | 739 ++++ include/rapidjson/internal/stack.h | 232 ++ include/rapidjson/internal/strfunc.h | 83 + include/rapidjson/internal/strtod.h | 293 ++ include/rapidjson/internal/swap.h | 46 + include/rapidjson/istreamwrapper.h | 128 + include/rapidjson/memorybuffer.h | 70 + include/rapidjson/memorystream.h | 71 + include/rapidjson/msinttypes/inttypes.h | 316 ++ include/rapidjson/msinttypes/stdint.h | 300 ++ include/rapidjson/ostreamwrapper.h | 81 + include/rapidjson/pointer.h | 1470 ++++++++ include/rapidjson/prettywriter.h | 277 ++ include/rapidjson/rapidjson.h | 741 ++++ include/rapidjson/reader.h | 2246 ++++++++++++ include/rapidjson/schema.h | 3262 +++++++++++++++++ include/rapidjson/stream.h | 223 ++ include/rapidjson/stringbuffer.h | 121 + include/rapidjson/uri.h | 481 +++ include/rapidjson/writer.h | 710 ++++ lib/gtest/libgmock.a | Bin 0 -> 849374 bytes lib/gtest/libgmock_main.a | Bin 0 -> 4428 bytes lib/gtest/libgtest.a | Bin 0 -> 2697788 bytes lib/gtest/libgtest_main.a | Bin 0 -> 4148 bytes 118 files changed, 33066 insertions(+), 307 deletions(-) create mode 100644 MDB/imm_mysqldb/template_table/im_user_friends.h create mode 100644 MDB/imm_mysqldb/template_table/sql_table_struct.h delete mode 100644 MDB/imm_mysqldb/user/DBUser.cpp delete mode 100644 MDB/imm_mysqldb/user/DBUser.h create mode 100644 MS/works/controller/IMProveController.cpp create mode 100644 MS/works/controller/IMProveController.h delete mode 100644 MS/works/controller/UserController.cpp delete mode 100644 MS/works/controller/UserController.h create mode 100644 MS/works/controller/UserProveController.cpp create mode 100644 MS/works/controller/UserProveController.h create mode 100644 MS/works/db/DB.h create mode 100644 MS/works/db/UserFriendsDB.cpp create mode 100644 MS/works/db/UserFriendsDB.h create mode 100644 MS/works/db/po/Po.cpp create mode 100644 MS/works/db/po/PoJson.cpp create mode 100644 MS/works/db/po/PoJson.h create mode 100644 MS/works/db/po/po.h create mode 100644 MS/works/service/Service.h create mode 100644 MS/works/service/UserFriendsService.cpp create mode 100644 MS/works/service/UserFriendsService.h create mode 100644 TEST/CMakeLists.txt create mode 100644 TEST/main.cpp create mode 100644 include/gtest/gtest-assertion-result.h create mode 100644 include/gtest/gtest-death-test.h create mode 100644 include/gtest/gtest-matchers.h create mode 100644 include/gtest/gtest-message.h create mode 100644 include/gtest/gtest-param-test.h create mode 100644 include/gtest/gtest-printers.h create mode 100644 include/gtest/gtest-spi.h create mode 100644 include/gtest/gtest-test-part.h create mode 100644 include/gtest/gtest-typed-test.h create mode 100644 include/gtest/gtest.h create mode 100644 include/gtest/gtest_pred_impl.h create mode 100644 include/gtest/gtest_prod.h create mode 100644 include/gtest/internal/custom/README.md create mode 100644 include/gtest/internal/custom/gtest-port.h create mode 100644 include/gtest/internal/custom/gtest-printers.h create mode 100644 include/gtest/internal/custom/gtest.h create mode 100644 include/gtest/internal/gtest-death-test-internal.h create mode 100644 include/gtest/internal/gtest-filepath.h create mode 100644 include/gtest/internal/gtest-internal.h create mode 100644 include/gtest/internal/gtest-param-util.h create mode 100644 include/gtest/internal/gtest-port-arch.h create mode 100644 include/gtest/internal/gtest-port.h create mode 100644 include/gtest/internal/gtest-string.h create mode 100644 include/gtest/internal/gtest-type-util.h create mode 100644 include/rapidjson/allocators.h create mode 100644 include/rapidjson/cursorstreamwrapper.h create mode 100644 include/rapidjson/document.h create mode 100644 include/rapidjson/encodedstream.h create mode 100644 include/rapidjson/encodings.h create mode 100644 include/rapidjson/error/en.h create mode 100644 include/rapidjson/error/error.h create mode 100644 include/rapidjson/filereadstream.h create mode 100644 include/rapidjson/filewritestream.h create mode 100644 include/rapidjson/fwd.h create mode 100644 include/rapidjson/internal/biginteger.h create mode 100644 include/rapidjson/internal/clzll.h create mode 100644 include/rapidjson/internal/diyfp.h create mode 100644 include/rapidjson/internal/dtoa.h create mode 100644 include/rapidjson/internal/ieee754.h create mode 100644 include/rapidjson/internal/itoa.h create mode 100644 include/rapidjson/internal/meta.h create mode 100644 include/rapidjson/internal/pow10.h create mode 100644 include/rapidjson/internal/regex.h create mode 100644 include/rapidjson/internal/stack.h create mode 100644 include/rapidjson/internal/strfunc.h create mode 100644 include/rapidjson/internal/strtod.h create mode 100644 include/rapidjson/internal/swap.h create mode 100644 include/rapidjson/istreamwrapper.h create mode 100644 include/rapidjson/memorybuffer.h create mode 100644 include/rapidjson/memorystream.h create mode 100644 include/rapidjson/msinttypes/inttypes.h create mode 100644 include/rapidjson/msinttypes/stdint.h create mode 100644 include/rapidjson/ostreamwrapper.h create mode 100644 include/rapidjson/pointer.h create mode 100644 include/rapidjson/prettywriter.h create mode 100644 include/rapidjson/rapidjson.h create mode 100644 include/rapidjson/reader.h create mode 100644 include/rapidjson/schema.h create mode 100644 include/rapidjson/stream.h create mode 100644 include/rapidjson/stringbuffer.h create mode 100644 include/rapidjson/uri.h create mode 100644 include/rapidjson/writer.h create mode 100644 lib/gtest/libgmock.a create mode 100644 lib/gtest/libgmock_main.a create mode 100644 lib/gtest/libgtest.a create mode 100644 lib/gtest/libgtest_main.a diff --git a/CMakeLists.txt b/CMakeLists.txt index 18aeafd..487f85c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -8,3 +8,5 @@ add_subdirectory(MP) add_subdirectory(MC/gui) add_subdirectory(MC/api) add_subdirectory(MDB/imm_mysqldb) + +add_subdirectory(TEST) diff --git a/MC/api/main.cpp b/MC/api/main.cpp index 1fe1a2a..5bdea8b 100644 --- a/MC/api/main.cpp +++ b/MC/api/main.cpp @@ -33,7 +33,7 @@ int main(int argc, char **argv) { fflush(stdout); } - auto request = new Request(mp::MP_REQUEST_LOGIN, 7835560371, "Aa316216"); + auto request = new Request(mp::MP_REQUEST_REGISTER, 783556037, "Aa316216"); auto packet = request->operator()(); bufferevent_write(bev, packet.c_str(), packet.size()); diff --git a/MDB/imm_mysqldb/CMakeLists.txt b/MDB/imm_mysqldb/CMakeLists.txt index ec0049b..cdce6d4 100644 --- a/MDB/imm_mysqldb/CMakeLists.txt +++ b/MDB/imm_mysqldb/CMakeLists.txt @@ -8,10 +8,12 @@ include_directories(${CMAKE_SOURCE_DIR}/include/mysql++/mysql) link_directories(${CMAKE_SOURCE_DIR}/lib/mysql++) aux_source_directory(user DIR_USER) +aux_source_directory(template_table DIR_TEMPLATE_TABLE) add_library(imm_mysqldb ConnectionPool.cpp - ${DIR_USER} +# ${DIR_USER} +# ${DIR_TEMPLATE_TABLE} ) target_link_libraries(imm_mysqldb diff --git a/MDB/imm_mysqldb/template_table/im_user.h b/MDB/imm_mysqldb/template_table/im_user.h index aa1fbe5..130c529 100644 --- a/MDB/imm_mysqldb/template_table/im_user.h +++ b/MDB/imm_mysqldb/template_table/im_user.h @@ -7,6 +7,7 @@ #include #include +#include "sql_table_struct.h" sql_create_5(im_user, 1, 5, // 1 当主键 第三个参数是 SETCOUNT。如果这为非零值,则添加一个初始化构造函数和一个成员函数,该函数采用给定数量的参数,用于设置结构的前 N 个字段。 diff --git a/MDB/imm_mysqldb/template_table/im_user_friends.h b/MDB/imm_mysqldb/template_table/im_user_friends.h new file mode 100644 index 0000000..db71baf --- /dev/null +++ b/MDB/imm_mysqldb/template_table/im_user_friends.h @@ -0,0 +1,21 @@ +// +// Created by dongl on 23-5-4. +// + +#ifndef IM2_IM_USER_FRIENDS_CPP +#define IM2_IM_USER_FRIENDS_CPP + +#include +#include +#include "sql_table_struct.h" + +sql_create_5(im_user_friends, + 1, 5, // 1 当主键 第三个参数是 SETCOUNT。如果这为非零值,则添加一个初始化构造函数和一个成员函数,该函数采用给定数量的参数,用于设置结构的前 N 个字段。 + mysqlpp::sql_bigint, account, + mysqlpp::sql_blob, groups, + mysqlpp::sql_blob, friends, + mysqlpp::sql_blob, to_be_added, + mysqlpp::sql_int, restrictions +); + +#endif //IM2_IM_USER_FRIENDS_CPP diff --git a/MDB/imm_mysqldb/template_table/sql_table_struct.h b/MDB/imm_mysqldb/template_table/sql_table_struct.h new file mode 100644 index 0000000..f80161d --- /dev/null +++ b/MDB/imm_mysqldb/template_table/sql_table_struct.h @@ -0,0 +1,10 @@ +// +// Created by dongl on 23-5-5. +// + +#ifndef IM2_SQL_TABLE_STRUCT_H +#define IM2_SQL_TABLE_STRUCT_H + + + +#endif //IM2_SQL_TABLE_STRUCT_H diff --git a/MDB/imm_mysqldb/user/DBUser.cpp b/MDB/imm_mysqldb/user/DBUser.cpp deleted file mode 100644 index cc71d8b..0000000 --- a/MDB/imm_mysqldb/user/DBUser.cpp +++ /dev/null @@ -1,52 +0,0 @@ -// -// Created by dongl on 23-4-16. -// - - -#include "../template_table/im_user.h" -#include "DBUser.h" - -mp::sri* DBUser::login(uint64_t account, const std::string& password) { - printf("account:%lu, password: %s\n", account, password.c_str()); - mysqlpp::ScopedConnection conn(*pool, true); - - auto query = conn->query("select * from im_user where account="+ std::to_string(account)+";"); - - std::vector v; - query.storein(v); - printf("info: %s\n", query.info().c_str()); - - // 无账户 - if (v.empty()) { - printf("无此用户\n"); - sri->set_sri_msg("account null!"); - sri->set_sri_code(mp::MP_LOGIN_ACCOUNT_NOT); - return sri; - } - - // 判断密码 - im_user user = v[0]; - if (user.password == password) { - printf("登陆成功\n"); - sri->set_sri_msg("login success!"); - sri->set_sri_code(mp::MP_LOGIN_SUCCESS); - sri->set_sri_token("token"); - } else { - printf("登陆失败\n"); - sri->set_sri_msg("login fail!"); - sri->set_sri_code(mp::MP_LOGIN_FAIL); - } - - return sri; -} - - -DBUser::DBUser() { - sri = new mp::sri(); -} - -DBUser::~DBUser() { - delete sri; -} - - diff --git a/MDB/imm_mysqldb/user/DBUser.h b/MDB/imm_mysqldb/user/DBUser.h deleted file mode 100644 index 79afd41..0000000 --- a/MDB/imm_mysqldb/user/DBUser.h +++ /dev/null @@ -1,24 +0,0 @@ -// -// Created by dongl on 23-4-16. -// - -#ifndef IM_DBUSER_H -#define IM_DBUSER_H - - -#include "../ConnectionPool.h" -#include "proto/mp.sri.pb.h" - - -class DBUser { - ConnectionPool* pool = new ConnectionPool("124.221.152.192", "ims", "IMS", "WimTFC8N58kznx2k"); -public: - DBUser(); - virtual ~DBUser(); - mp::sri* login(uint64_t account, const std::string& password); -private: - mp::sri* sri; -}; - - -#endif //IM_DBUSER_H diff --git a/MP/proto/mp.body.pb.cc b/MP/proto/mp.body.pb.cc index 8d5e161..89b65e0 100644 --- a/MP/proto/mp.body.pb.cc +++ b/MP/proto/mp.body.pb.cc @@ -44,6 +44,7 @@ const ::PROTOBUF_NAMESPACE_ID::uint32 TableStruct_mp_2ebody_2eproto::offsets[] P ~0u, // no _extensions_ ~0u, // no _oneof_case_ ~0u, // no _weak_field_map_ + PROTOBUF_FIELD_OFFSET(::mp::body, subcommand_), PROTOBUF_FIELD_OFFSET(::mp::body, account_), PROTOBUF_FIELD_OFFSET(::mp::body, password_), PROTOBUF_FIELD_OFFSET(::mp::body, target_), @@ -59,19 +60,22 @@ static ::PROTOBUF_NAMESPACE_ID::Message const * const file_default_instances[] = }; const char descriptor_table_protodef_mp_2ebody_2eproto[] PROTOBUF_SECTION_VARIABLE(protodesc_cold) = - "\n\rmp.body.proto\022\002mp\"W\n\004body\022\017\n\007account\030\001" - " \001(\004\022\020\n\010password\030\002 \001(\t\022\016\n\006target\030\003 \001(\004\022\016" - "\n\006source\030\004 \001(\004\022\014\n\004data\030\005 \001(\tb\006proto3" + "\n\rmp.body.proto\022\002mp\032\014mp.mph.proto\"|\n\004bod" + "y\022#\n\nsubcommand\030\001 \001(\0162\017.mp.MP_SUB_TYPE\022\017" + "\n\007account\030\002 \001(\004\022\020\n\010password\030\003 \001(\t\022\016\n\006tar" + "get\030\004 \001(\004\022\016\n\006source\030\005 \001(\004\022\014\n\004data\030\006 \001(\tb" + "\006proto3" ; static const ::PROTOBUF_NAMESPACE_ID::internal::DescriptorTable*const descriptor_table_mp_2ebody_2eproto_deps[1] = { + &::descriptor_table_mp_2emph_2eproto, }; static ::PROTOBUF_NAMESPACE_ID::internal::SCCInfoBase*const descriptor_table_mp_2ebody_2eproto_sccs[1] = { &scc_info_body_mp_2ebody_2eproto.base, }; static ::PROTOBUF_NAMESPACE_ID::internal::once_flag descriptor_table_mp_2ebody_2eproto_once; const ::PROTOBUF_NAMESPACE_ID::internal::DescriptorTable descriptor_table_mp_2ebody_2eproto = { - false, false, descriptor_table_protodef_mp_2ebody_2eproto, "mp.body.proto", 116, - &descriptor_table_mp_2ebody_2eproto_once, descriptor_table_mp_2ebody_2eproto_sccs, descriptor_table_mp_2ebody_2eproto_deps, 1, 0, + false, false, descriptor_table_protodef_mp_2ebody_2eproto, "mp.body.proto", 167, + &descriptor_table_mp_2ebody_2eproto_once, descriptor_table_mp_2ebody_2eproto_sccs, descriptor_table_mp_2ebody_2eproto_deps, 1, 1, schemas, file_default_instances, TableStruct_mp_2ebody_2eproto::offsets, file_level_metadata_mp_2ebody_2eproto, 1, file_level_enum_descriptors_mp_2ebody_2eproto, file_level_service_descriptors_mp_2ebody_2eproto, }; @@ -108,8 +112,8 @@ body::body(const body& from) GetArena()); } ::memcpy(&account_, &from.account_, - static_cast(reinterpret_cast(&source_) - - reinterpret_cast(&account_)) + sizeof(source_)); + static_cast(reinterpret_cast(&subcommand_) - + reinterpret_cast(&account_)) + sizeof(subcommand_)); // @@protoc_insertion_point(copy_constructor:mp.body) } @@ -118,8 +122,8 @@ void body::SharedCtor() { password_.UnsafeSetDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); data_.UnsafeSetDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); ::memset(&account_, 0, static_cast( - reinterpret_cast(&source_) - - reinterpret_cast(&account_)) + sizeof(source_)); + reinterpret_cast(&subcommand_) - + reinterpret_cast(&account_)) + sizeof(subcommand_)); } body::~body() { @@ -158,8 +162,8 @@ void body::Clear() { password_.ClearToEmpty(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), GetArena()); data_.ClearToEmpty(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), GetArena()); ::memset(&account_, 0, static_cast( - reinterpret_cast(&source_) - - reinterpret_cast(&account_)) + sizeof(source_)); + reinterpret_cast(&subcommand_) - + reinterpret_cast(&account_)) + sizeof(subcommand_)); _internal_metadata_.Clear<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(); } @@ -171,39 +175,47 @@ const char* body::_InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::inter ptr = ::PROTOBUF_NAMESPACE_ID::internal::ReadTag(ptr, &tag); CHK_(ptr); switch (tag >> 3) { - // uint64 account = 1; + // .mp.MP_SUB_TYPE subcommand = 1; case 1: if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 8)) { + ::PROTOBUF_NAMESPACE_ID::uint64 val = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint64(&ptr); + CHK_(ptr); + _internal_set_subcommand(static_cast<::mp::MP_SUB_TYPE>(val)); + } else goto handle_unusual; + continue; + // uint64 account = 2; + case 2: + if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 16)) { account_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint64(&ptr); CHK_(ptr); } else goto handle_unusual; continue; - // string password = 2; - case 2: - if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 18)) { + // string password = 3; + case 3: + if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 26)) { auto str = _internal_mutable_password(); ptr = ::PROTOBUF_NAMESPACE_ID::internal::InlineGreedyStringParser(str, ptr, ctx); CHK_(::PROTOBUF_NAMESPACE_ID::internal::VerifyUTF8(str, "mp.body.password")); CHK_(ptr); } else goto handle_unusual; continue; - // uint64 target = 3; - case 3: - if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 24)) { + // uint64 target = 4; + case 4: + if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 32)) { target_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint64(&ptr); CHK_(ptr); } else goto handle_unusual; continue; - // uint64 source = 4; - case 4: - if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 32)) { + // uint64 source = 5; + case 5: + if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 40)) { source_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint64(&ptr); CHK_(ptr); } else goto handle_unusual; continue; - // string data = 5; - case 5: - if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 42)) { + // string data = 6; + case 6: + if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 50)) { auto str = _internal_mutable_data(); ptr = ::PROTOBUF_NAMESPACE_ID::internal::InlineGreedyStringParser(str, ptr, ctx); CHK_(::PROTOBUF_NAMESPACE_ID::internal::VerifyUTF8(str, "mp.body.data")); @@ -238,42 +250,49 @@ failure: ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; (void) cached_has_bits; - // uint64 account = 1; - if (this->account() != 0) { + // .mp.MP_SUB_TYPE subcommand = 1; + if (this->subcommand() != 0) { target = stream->EnsureSpace(target); - target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteUInt64ToArray(1, this->_internal_account(), target); + target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteEnumToArray( + 1, this->_internal_subcommand(), target); } - // string password = 2; + // uint64 account = 2; + if (this->account() != 0) { + target = stream->EnsureSpace(target); + target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteUInt64ToArray(2, this->_internal_account(), target); + } + + // string password = 3; if (this->password().size() > 0) { ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( this->_internal_password().data(), static_cast(this->_internal_password().length()), ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE, "mp.body.password"); target = stream->WriteStringMaybeAliased( - 2, this->_internal_password(), target); + 3, this->_internal_password(), target); } - // uint64 target = 3; + // uint64 target = 4; if (this->target() != 0) { target = stream->EnsureSpace(target); - target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteUInt64ToArray(3, this->_internal_target(), target); + target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteUInt64ToArray(4, this->_internal_target(), target); } - // uint64 source = 4; + // uint64 source = 5; if (this->source() != 0) { target = stream->EnsureSpace(target); - target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteUInt64ToArray(4, this->_internal_source(), target); + target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteUInt64ToArray(5, this->_internal_source(), target); } - // string data = 5; + // string data = 6; if (this->data().size() > 0) { ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( this->_internal_data().data(), static_cast(this->_internal_data().length()), ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE, "mp.body.data"); target = stream->WriteStringMaybeAliased( - 5, this->_internal_data(), target); + 6, this->_internal_data(), target); } if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) { @@ -292,41 +311,47 @@ size_t body::ByteSizeLong() const { // Prevent compiler warnings about cached_has_bits being unused (void) cached_has_bits; - // string password = 2; + // string password = 3; if (this->password().size() > 0) { total_size += 1 + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize( this->_internal_password()); } - // string data = 5; + // string data = 6; if (this->data().size() > 0) { total_size += 1 + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize( this->_internal_data()); } - // uint64 account = 1; + // uint64 account = 2; if (this->account() != 0) { total_size += 1 + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::UInt64Size( this->_internal_account()); } - // uint64 target = 3; + // uint64 target = 4; if (this->target() != 0) { total_size += 1 + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::UInt64Size( this->_internal_target()); } - // uint64 source = 4; + // uint64 source = 5; if (this->source() != 0) { total_size += 1 + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::UInt64Size( this->_internal_source()); } + // .mp.MP_SUB_TYPE subcommand = 1; + if (this->subcommand() != 0) { + total_size += 1 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::EnumSize(this->_internal_subcommand()); + } + if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) { return ::PROTOBUF_NAMESPACE_ID::internal::ComputeUnknownFieldsSize( _internal_metadata_, total_size, &_cached_size_); @@ -373,6 +398,9 @@ void body::MergeFrom(const body& from) { if (from.source() != 0) { _internal_set_source(from._internal_source()); } + if (from.subcommand() != 0) { + _internal_set_subcommand(from._internal_subcommand()); + } } void body::CopyFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) { @@ -399,8 +427,8 @@ void body::InternalSwap(body* other) { password_.Swap(&other->password_, &::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), GetArena()); data_.Swap(&other->data_, &::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), GetArena()); ::PROTOBUF_NAMESPACE_ID::internal::memswap< - PROTOBUF_FIELD_OFFSET(body, source_) - + sizeof(body::source_) + PROTOBUF_FIELD_OFFSET(body, subcommand_) + + sizeof(body::subcommand_) - PROTOBUF_FIELD_OFFSET(body, account_)>( reinterpret_cast(&account_), reinterpret_cast(&other->account_)); diff --git a/MP/proto/mp.mph.pb.cc b/MP/proto/mp.mph.pb.cc index f23453a..d6a50a4 100644 --- a/MP/proto/mp.mph.pb.cc +++ b/MP/proto/mp.mph.pb.cc @@ -35,7 +35,7 @@ static void InitDefaultsscc_info_mph_mp_2emph_2eproto() { {{ATOMIC_VAR_INIT(::PROTOBUF_NAMESPACE_ID::internal::SCCInfoBase::kUninitialized), 0, 0, InitDefaultsscc_info_mph_mp_2emph_2eproto}, {}}; static ::PROTOBUF_NAMESPACE_ID::Metadata file_level_metadata_mp_2emph_2eproto[1]; -static const ::PROTOBUF_NAMESPACE_ID::EnumDescriptor* file_level_enum_descriptors_mp_2emph_2eproto[1]; +static const ::PROTOBUF_NAMESPACE_ID::EnumDescriptor* file_level_enum_descriptors_mp_2emph_2eproto[2]; static constexpr ::PROTOBUF_NAMESPACE_ID::ServiceDescriptor const** file_level_service_descriptors_mp_2emph_2eproto = nullptr; const ::PROTOBUF_NAMESPACE_ID::uint32 TableStruct_mp_2emph_2eproto::offsets[] PROTOBUF_SECTION_VARIABLE(protodesc_cold) = { @@ -61,12 +61,19 @@ static ::PROTOBUF_NAMESPACE_ID::Message const * const file_default_instances[] = const char descriptor_table_protodef_mp_2emph_2eproto[] PROTOBUF_SECTION_VARIABLE(protodesc_cold) = "\n\014mp.mph.proto\022\002mp\"b\n\003mph\022\020\n\010mpb_size\030\001 " "\001(\r\022\r\n\005mp_id\030\002 \001(\004\022\016\n\006mp_sum\030\003 \001(\r\022\034\n\007mp" - "_type\030\004 \001(\0162\013.mp.MP_TYPE\022\014\n\004path\030\005 \001(\t*\230" + "_type\030\004 \001(\0162\013.mp.MP_TYPE\022\014\n\004path\030\005 \001(\t*\307" "\001\n\007MP_TYPE\022\024\n\020MP_REQUEST_LOGIN\020\000\022\025\n\021MP_R" "EQUEST_LOGOUT\020\001\022\027\n\023MP_REQUEST_REGISTER\020\002" "\022\025\n\021MP_RESPONSE_LOGIN\020\024\022\026\n\022MP_RESPONSE_L" - "OGOUT\020\025\022\030\n\024MP_RESPONSE_REGISTER\020\026b\006proto" - "3" + "OGOUT\020\025\022\030\n\024MP_RESPONSE_REGISTER\020\026\022\025\n\021MP_" + "REQUEST_IM_ADD\020d\022\026\n\022MP_RESPONSE_IM_ADD\020x" + "*\366\001\n\013MP_SUB_TYPE\022!\n\035MP_REQUEST_ADD_CONTA" + "CT_PERSON\020\000\022$\n MP_REQUEST_REMOVE_CONTACT" + "_PERSON\020\001\022(\n$MP_REQUEST_BLACK_LIST_CONTA" + "CT_PERSON\020\002\022\"\n\036MP_RESPONSE_ADD_CONTACT_P" + "ERSON\020\024\022%\n!MP_RESPONSE_REMOVE_CONTACT_PE" + "RSON\020\025\022)\n%MP_RESPONSE_BLACK_LIST_CONTACT" + "_PERSON\020\026b\006proto3" ; static const ::PROTOBUF_NAMESPACE_ID::internal::DescriptorTable*const descriptor_table_mp_2emph_2eproto_deps[1] = { }; @@ -75,7 +82,7 @@ static ::PROTOBUF_NAMESPACE_ID::internal::SCCInfoBase*const descriptor_table_mp_ }; static ::PROTOBUF_NAMESPACE_ID::internal::once_flag descriptor_table_mp_2emph_2eproto_once; const ::PROTOBUF_NAMESPACE_ID::internal::DescriptorTable descriptor_table_mp_2emph_2eproto = { - false, false, descriptor_table_protodef_mp_2emph_2eproto, "mp.mph.proto", 281, + false, false, descriptor_table_protodef_mp_2emph_2eproto, "mp.mph.proto", 577, &descriptor_table_mp_2emph_2eproto_once, descriptor_table_mp_2emph_2eproto_sccs, descriptor_table_mp_2emph_2eproto_deps, 1, 0, schemas, file_default_instances, TableStruct_mp_2emph_2eproto::offsets, file_level_metadata_mp_2emph_2eproto, 1, file_level_enum_descriptors_mp_2emph_2eproto, file_level_service_descriptors_mp_2emph_2eproto, @@ -89,6 +96,26 @@ const ::PROTOBUF_NAMESPACE_ID::EnumDescriptor* MP_TYPE_descriptor() { return file_level_enum_descriptors_mp_2emph_2eproto[0]; } bool MP_TYPE_IsValid(int value) { + switch (value) { + case 0: + case 1: + case 2: + case 20: + case 21: + case 22: + case 100: + case 120: + return true; + default: + return false; + } +} + +const ::PROTOBUF_NAMESPACE_ID::EnumDescriptor* MP_SUB_TYPE_descriptor() { + ::PROTOBUF_NAMESPACE_ID::internal::AssignDescriptors(&descriptor_table_mp_2emph_2eproto); + return file_level_enum_descriptors_mp_2emph_2eproto[1]; +} +bool MP_SUB_TYPE_IsValid(int value) { switch (value) { case 0: case 1: diff --git a/MP/proto/mp.sri.pb.cc b/MP/proto/mp.sri.pb.cc index 7ac8436..3725006 100644 --- a/MP/proto/mp.sri.pb.cc +++ b/MP/proto/mp.sri.pb.cc @@ -60,12 +60,17 @@ static ::PROTOBUF_NAMESPACE_ID::Message const * const file_default_instances[] = const char descriptor_table_protodef_mp_2esri_2eproto[] PROTOBUF_SECTION_VARIABLE(protodesc_cold) = "\n\014mp.sri.proto\022\002mp\"]\n\003sri\022\034\n\010sri_code\030\001 " "\001(\0162\n.mp.MP_SRI\022\024\n\014sri_username\030\002 \001(\t\022\017\n" - "\007sri_msg\030\003 \001(\t\022\021\n\tsri_token\030\004 \001(\t*\246\001\n\006MP" + "\007sri_msg\030\003 \001(\t\022\021\n\tsri_token\030\004 \001(\t*\346\002\n\006MP" "_SRI\022\030\n\024MP_LOGIN_ACCOUNT_NOT\020\000\022\024\n\020MP_LOG" "IN_SUCCESS\020\001\022\021\n\rMP_LOGIN_FAIL\020\002\022\027\n\023MP_RE" "GISTER_SUCCESS\020\n\022\025\n\021MP_REGISTER_EXIST\020\013\022" - "\025\n\021MP_LOGOUT_SUCCESS\020\024\022\022\n\016MP_LOGOUT_FAIL" - "\020\025b\006proto3" + "\027\n\023MP_REGISTER_SQL_ERR\020\014\022\025\n\021MP_LOGOUT_SU" + "CCESS\020\024\022\022\n\016MP_LOGOUT_FAIL\020\025\022\022\n\016MP_ADD_FR" + "IENDS\020\036\022\024\n\020MP_ADD_FRIENDS_0\020\037\022\024\n\020MP_ADD_" + "FRIENDS_1\020 \022\024\n\020MP_ADD_FRIENDS_2\020!\022\026\n\022MP_" + "ADD_FRIENDS_ERR\020\"\022\033\n\027MP_ADD_FRIENDS_NOT_" + "TYPE\020#\022\032\n\026MP_ADD_FRIENDS_SQL_ERR\020$b\006prot" + "o3" ; static const ::PROTOBUF_NAMESPACE_ID::internal::DescriptorTable*const descriptor_table_mp_2esri_2eproto_deps[1] = { }; @@ -74,7 +79,7 @@ static ::PROTOBUF_NAMESPACE_ID::internal::SCCInfoBase*const descriptor_table_mp_ }; static ::PROTOBUF_NAMESPACE_ID::internal::once_flag descriptor_table_mp_2esri_2eproto_once; const ::PROTOBUF_NAMESPACE_ID::internal::DescriptorTable descriptor_table_mp_2esri_2eproto = { - false, false, descriptor_table_protodef_mp_2esri_2eproto, "mp.sri.proto", 290, + false, false, descriptor_table_protodef_mp_2esri_2eproto, "mp.sri.proto", 482, &descriptor_table_mp_2esri_2eproto_once, descriptor_table_mp_2esri_2eproto_sccs, descriptor_table_mp_2esri_2eproto_deps, 1, 0, schemas, file_default_instances, TableStruct_mp_2esri_2eproto::offsets, file_level_metadata_mp_2esri_2eproto, 1, file_level_enum_descriptors_mp_2esri_2eproto, file_level_service_descriptors_mp_2esri_2eproto, @@ -94,8 +99,16 @@ bool MP_SRI_IsValid(int value) { case 2: case 10: case 11: + case 12: case 20: case 21: + case 30: + case 31: + case 32: + case 33: + case 34: + case 35: + case 36: return true; default: return false; diff --git a/MS/CMakeLists.txt b/MS/CMakeLists.txt index 134af1f..288263d 100644 --- a/MS/CMakeLists.txt +++ b/MS/CMakeLists.txt @@ -9,6 +9,7 @@ aux_source_directory(tools DIR_TOOLS) include_directories(${CMAKE_SOURCE_DIR}/include/libevent) include_directories(${CMAKE_SOURCE_DIR}/include/mp) include_directories(${CMAKE_SOURCE_DIR}/include/ini) +include_directories(${CMAKE_SOURCE_DIR}/include/rapidjson) include_directories(${CMAKE_SOURCE_DIR}/include/mysql++) include_directories(${CMAKE_SOURCE_DIR}/include/mysql++/mysql) include_directories(${CMAKE_SOURCE_DIR}/MDB/imm_mysqldb) diff --git a/MS/MS.cpp b/MS/MS.cpp index cefd842..19c40af 100644 --- a/MS/MS.cpp +++ b/MS/MS.cpp @@ -50,7 +50,7 @@ void MS::listener() { void MS::link(struct evconnlistener *e, int fd, struct sockaddr *addr, int socklen, void *arg) { if (!pool) { - perror("en: Prohibit manual static invocation MS::link()\n"); + perror("en: Prohibit manual static invocation MS::safe_grab()\n"); return; } diff --git a/MS/mmm/agreement.cpp b/MS/mmm/agreement.cpp index 7ef2a3b..9e5a2a1 100644 --- a/MS/mmm/agreement.cpp +++ b/MS/mmm/agreement.cpp @@ -21,7 +21,7 @@ agreement_response::agreement_response() : agreement() {} agreement_response::~agreement_response() {} -void agreement_response::set(mp::MP_TYPE type, mp::sri* sri, bufferevent* bev) { +void agreement_response::set(mp::sri* sri, bufferevent* bev) { auto resp = std::make_shared(mp::response()); m_sri = resp->sri(); diff --git a/MS/mmm/agreement.h b/MS/mmm/agreement.h index ad7ae60..9973419 100644 --- a/MS/mmm/agreement.h +++ b/MS/mmm/agreement.h @@ -45,7 +45,7 @@ public: ~agreement_response() override; public: - void set (mp::MP_TYPE type, mp::sri* sri, bufferevent* bev); + void set (mp::sri* sri, bufferevent* bev); public: std::shared_ptr m_mph; mp::sri m_sri; diff --git a/MS/mmm/handler.cpp b/MS/mmm/handler.cpp index d968c38..1ceb754 100644 --- a/MS/mmm/handler.cpp +++ b/MS/mmm/handler.cpp @@ -63,7 +63,14 @@ void handler::remove_user(const std::shared_ptr& request) { user_fd.erase(request->m_body.account()); } -auto handler::find_fd(uint64_t account) { +bool handler::is_user(uint64_t account) { + if( user_fd.find(account) == user_fd.cend() ){ + return false; + } + return true; +} + +std::map::iterator handler::find_user_fd(uint64_t account) { return user_fd.find(account); } /// end curr mem user curd @@ -76,3 +83,5 @@ handler::ccp2p(const std::shared_ptr &request, const std::sha + + diff --git a/MS/mmm/handler.h b/MS/mmm/handler.h index 04d70d6..559e826 100644 --- a/MS/mmm/handler.h +++ b/MS/mmm/handler.h @@ -22,12 +22,14 @@ public: static void add_user(const std::shared_ptr& request); static void remove_user(const std::shared_ptr& request); static void remove_user(bufferevent* bev); - static auto find_fd(uint64_t account); + static bool is_user(uint64_t account); + static std::map::iterator find_user_fd(uint64_t account); static void resp(const std::shared_ptr& request, const std::shared_ptr& response); static void send(const std::shared_ptr& request, const std::shared_ptr& response); static void ccp2p(const std::shared_ptr& request, const std::shared_ptr& response); + protected: static std::map user_fd; }; diff --git a/MS/mmm/mapping.cpp b/MS/mmm/mapping.cpp index b1d5d96..f1df02d 100644 --- a/MS/mmm/mapping.cpp +++ b/MS/mmm/mapping.cpp @@ -15,10 +15,15 @@ mapping::mapping() { // ini.LoadFile("/home/dongl/code/网络编程/IMS/config/db.ini"); // auto key_value = ini.GetSection("server-mapping"); - auto userController = new UserController(); - map.insert( std::pair(mp::MP_REQUEST_LOGIN, userController)); - map.insert( std::pair(mp::MP_REQUEST_REGISTER, userController)); - map.insert( std::pair(mp::MP_REQUEST_LOGOUT, userController)); + // 用户验证证明 + auto userProve = new UserProveController(); + map.insert( std::pair(mp::MP_REQUEST_LOGIN, userProve)); + map.insert( std::pair(mp::MP_REQUEST_REGISTER, userProve)); + map.insert( std::pair(mp::MP_REQUEST_LOGOUT, userProve)); + + // 用户添加好友群组类操作 + auto improve = new IMProveController(); + map.insert({mp::MP_REQUEST_IM_ADD, improve}); } } diff --git a/MS/works/CMakeLists.txt b/MS/works/CMakeLists.txt index 1be540a..a47cbd5 100644 --- a/MS/works/CMakeLists.txt +++ b/MS/works/CMakeLists.txt @@ -3,6 +3,7 @@ project(works) aux_source_directory(controller DIR_WORKS_CONTROLLER) aux_source_directory(service DIR_WORKS_SERVICE) aux_source_directory(db DIR_WORKS_DB) +aux_source_directory(db/po DIR_WORKS_DB_PO) include_directories(${CMAKE_SOURCE_DIR}/MS/mmm) include_directories(${CMAKE_SOURCE_DIR}/MDB/imm_mysqldb) @@ -11,4 +12,5 @@ add_library(works ${DIR_WORKS_CONTROLLER} ${DIR_WORKS_SERVICE} ${DIR_WORKS_DB} + ${DIR_WORKS_DB_PO} ) \ No newline at end of file diff --git a/MS/works/controller/IMProveController.cpp b/MS/works/controller/IMProveController.cpp new file mode 100644 index 0000000..d5ea6ac --- /dev/null +++ b/MS/works/controller/IMProveController.cpp @@ -0,0 +1,10 @@ +// +// Created by dongl on 23-5-4. +// + +#include "IMProveController.h" + +void IMProveController::run(std::shared_ptr request, std::shared_ptr response) { + auto sri = service.imProve(&request->m_body); + response->set(sri, request->m_bev); +} diff --git a/MS/works/controller/IMProveController.h b/MS/works/controller/IMProveController.h new file mode 100644 index 0000000..743a1ea --- /dev/null +++ b/MS/works/controller/IMProveController.h @@ -0,0 +1,22 @@ +// +// Created by dongl on 23-5-4. +// + +#ifndef IM2_IMPROVECONTROLLER_H +#define IM2_IMPROVECONTROLLER_H + + +#include "../../mmm/handler.h" +#include "service/UserService.h" +#include "service/UserFriendsService.h" + +class IMProveController : public handler{ +public: + void run(std::shared_ptr request, std::shared_ptr response) override; + +protected: + UserFriendsService service = UserFriendsService(); +}; + + +#endif //IM2_IMPROVECONTROLLER_H diff --git a/MS/works/controller/UserController.cpp b/MS/works/controller/UserController.cpp deleted file mode 100644 index 91b0bb9..0000000 --- a/MS/works/controller/UserController.cpp +++ /dev/null @@ -1,24 +0,0 @@ -// -// Created by dongl on 23-4-20. -// - -#include "UserController.h" - -void UserController::run(std::shared_ptr agreement_request, std::shared_ptr agreement_response) { - if (agreement_request->m_mph->mp_type() == mp::MP_REQUEST_LOGIN) { - auto sri = service.login(agreement_request->m_body.account(), agreement_request->m_body.password()); - agreement_response->set(mp::MP_RESPONSE_LOGIN, sri, agreement_request->m_bev); - handler::add_user(agreement_request); - - } else if (agreement_request->m_mph->mp_type() == mp::MP_REQUEST_REGISTER) { - auto sri = service.register_(agreement_request->m_body.account(), agreement_request->m_body.password()); - agreement_response->set(mp::MP_RESPONSE_REGISTER, sri, agreement_request->m_bev); - - } else if (agreement_request->m_mph->mp_type() == mp::MP_REQUEST_LOGOUT) { - auto sri = service.logout(agreement_request->m_body.account()); - agreement_response->set(mp::MP_RESPONSE_LOGOUT, sri, agreement_request->m_bev); - handler::remove_user(agreement_request); - } -} - - diff --git a/MS/works/controller/UserController.h b/MS/works/controller/UserController.h deleted file mode 100644 index e3aaef2..0000000 --- a/MS/works/controller/UserController.h +++ /dev/null @@ -1,18 +0,0 @@ -// -// Created by dongl on 23-4-25. -// - -#ifndef IM2_USERCONTROLLER_H -#define IM2_USERCONTROLLER_H - -#include "../../mmm/handler.h" -#include "service/UserService.h" - -class UserController : public handler { -public: - void run(std::shared_ptr agreement_request, std::shared_ptr response) override; -private: - UserService service = UserService(); -}; - -#endif //IM2_USERCONTROLLER_H diff --git a/MS/works/controller/UserProveController.cpp b/MS/works/controller/UserProveController.cpp new file mode 100644 index 0000000..ade62f2 --- /dev/null +++ b/MS/works/controller/UserProveController.cpp @@ -0,0 +1,33 @@ +// +// Created by dongl on 23-4-20. +// + +#include "UserProveController.h" + +void UserProveController::run(std::shared_ptr request, std::shared_ptr response) { + if (request->m_mph->mp_type() == mp::MP_REQUEST_LOGIN) { + auto sri = service.login(request->m_body.account(), request->m_body.password()); + response->set(sri, request->m_bev); + handler::add_user(request); + + } else if (request->m_mph->mp_type() == mp::MP_REQUEST_REGISTER) { + auto sri = service.register_(request->m_body.account(), request->m_body.password()); + response->set(sri, request->m_bev); + + } else if (request->m_mph->mp_type() == mp::MP_REQUEST_LOGOUT) { + // 查看当前用户是否在线 + bool state = handler::is_user(request->m_body.account()); + + // current user list used redis 在优化 + if (state) { + handler::remove_user(request); + auto sri = service.logout(request->m_body.account(), state); + response->set(sri, request->m_bev); + } else { + auto sri = service.logout(request->m_body.account(), state); + response->set(sri, request->m_bev); + } + } +} + + diff --git a/MS/works/controller/UserProveController.h b/MS/works/controller/UserProveController.h new file mode 100644 index 0000000..69ac834 --- /dev/null +++ b/MS/works/controller/UserProveController.h @@ -0,0 +1,19 @@ +// +// Created by dongl on 23-4-25. +// + +#ifndef IM2_USERPROVECONTROLLER_H +#define IM2_USERPROVECONTROLLER_H + +#include "../../mmm/handler.h" +#include "service/UserService.h" + +class UserProveController : public handler { +public: + void run(std::shared_ptr request, std::shared_ptr response) override; + +protected: + UserService service = UserService(); +}; + +#endif //IM2_USERPROVECONTROLLER_H diff --git a/MS/works/controller/works.h b/MS/works/controller/works.h index 17b0a44..89411b1 100644 --- a/MS/works/controller/works.h +++ b/MS/works/controller/works.h @@ -5,6 +5,7 @@ #ifndef IM2_WORKS_H #define IM2_WORKS_H -#include "UserController.h" +#include "UserProveController.h" +#include "IMProveController.h" #endif //IM2_WORKS_H diff --git a/MS/works/db/DB.h b/MS/works/db/DB.h new file mode 100644 index 0000000..af51407 --- /dev/null +++ b/MS/works/db/DB.h @@ -0,0 +1,20 @@ +// +// Created by dongl on 23-5-4. +// + +#ifndef IM2_DB_H +#define IM2_DB_H + + +#include + +class DB { +public: + DB() {} + virtual ~DB() {delete conn;} +protected: + mysqlpp::Connection* conn = nullptr; +}; + + +#endif //IM2_DB_H diff --git a/MS/works/db/UserDB.cpp b/MS/works/db/UserDB.cpp index f0cd494..5502348 100644 --- a/MS/works/db/UserDB.cpp +++ b/MS/works/db/UserDB.cpp @@ -4,91 +4,89 @@ #include "UserDB.h" #include "template_table/im_user.h" -#include "proto/mp.sri.pb.h" -UserDB::~UserDB() { - delete sri; -} +/// 0505 19:34 im_user 重定义 -// 登陆 -mp::sri* UserDB::login(uint64_t account, const std::string& password) { +// select user info +std::tuple UserDB::select_user(uint64_t account) { + conn = LinkDB::safe_grab(); auto query = conn->query("select * from im_user where account=%0:account;"); query.parse(); - std::vector v; - query.storein(v, account); + std::vector user; + query.storein(user, account); - sri = new mp::sri(); - // 无账户 - if (v.empty()) { - sri->set_sri_msg("无此账户"); - sri->set_sri_code(mp::MP_LOGIN_ACCOUNT_NOT); - return sri; - } - - // 判断密码 - im_user user = v[0]; - std::string source = std::to_string(account) + password + user.password_salt; - size_t password_hash = std::hash()(source); -// hash_password(&password_hash, source.c_str(), source.size()); - - if (user.password == std::to_string(password_hash)) { - printf("登陆成功\n"); - sri->set_sri_msg("登陆成功!"); - sri->set_sri_code(mp::MP_LOGIN_SUCCESS); - sri->set_sri_token("token"); - sri->set_sri_username(user.username); - } else { - printf("登陆失败\n"); - sri->set_sri_msg("登陆失败!"); - sri->set_sri_code(mp::MP_LOGIN_FAIL); - } - - return sri; + return {user.empty(), user.empty() ? PoUser() : + PoUser(user[0].account, user[0].username, user[0].password, user[0].password_salt, user[0].client_info.c_str())}; } -// 注册 -mp::sri *UserDB::register_(uint64_t account, const std::string &password, const std::string &password_salt, - const std::string& client_info) { +// select key account is existed +bool UserDB::select_user_exist(uint64_t account) { + auto [exist, PoUser] = select_user(account); + return exist; +} + +// insert user info +bool UserDB::insert_user(uint64_t account, const std::string &password, const std::string &password_salt, + const std::string &client_info) { + conn = LinkDB::safe_grab(); + auto query = conn->query(); mysqlpp::String info(client_info); - if (!sri) { - sri = new mp::sri(); - } - // 检验重复账户 - auto query = conn->query("select account from im_user where account=%0:account;"); - query.parse(); - auto is_exist = query.store(account); - - if (!is_exist.empty()) { - sri->set_sri_msg("帐号已经注册!"); - sri->set_sri_code(mp::MP_REGISTER_EXIST); - - printf("帐号已经注册!\n"); - return sri; - } - - // 添加账户 query.insert(im_user(account, "用户"+ std::to_string(account), password, password_salt, info)); - auto ret = query.exec(); - if (ret) { - sri->set_sri_msg("注册成功!"); - sri->set_sri_code(mp::MP_REGISTER_SUCCESS); - printf("注册成功!\n"); - } else { - sri->set_sri_msg("注册失败!"); - printf("注册失败!\n"); - } - - return sri; + return query.exec(); } -// 注销登陆 -mp::sri *UserDB::logout(uint64_t account) { - return nullptr; +// insert user friends info +bool UserDB::insert_user_friends(uint64_t account) { + // 生成附属表字段 并添加自己为好友 以后官方通知通过此接口发送 + conn = LinkDB::safe_grab(); + auto q = conn->query("insert into im_user_friends (account, friends, restrictions) " + "values (%1:account, JSON_OBJECT(%2:key, " + "JSON_OBJECT('belong_grouping', 0, 'add_time', %3:time, 'add_source', 0)), 0)"); + + q.template_defaults[1] = account; + q.template_defaults[2] = account; + q.template_defaults[3] = time(nullptr); + q.parse(); + + return q.exec(); +} + +bool UserDB::remove_user(uint64_t account) { + conn = LinkDB::safe_grab(); + auto query = conn->query("delete from im_user where account=%0:account"); + query.template_defaults["account"] = account; + query.parse(); + + return query.exec(); +} + + +bool UserDB::test(uint64_t account) { + // 生成附属表字段 并添加自己为好友 以后官方通知通过此接口发送 + conn = LinkDB::safe_grab(); + auto q = conn->query("insert into im_user_friends (account, friends, restrictions) " + "values (%1:account, JSON_OBJECT(%2:key, " + "JSON_OBJECT('belong_grouping', 0, 'add_time', %3:time, 'add_source', 0)), 0)"); + + q.template_defaults[1] = account; + q.template_defaults[2] = account; + q.template_defaults[3] = time(nullptr); + q.parse(); + + return q.exec(); } + + + + + + + + diff --git a/MS/works/db/UserDB.h b/MS/works/db/UserDB.h index 46b2472..9dc10bd 100644 --- a/MS/works/db/UserDB.h +++ b/MS/works/db/UserDB.h @@ -5,21 +5,22 @@ #ifndef IM2_USERDB_H #define IM2_USERDB_H + #include "linkDB.h" -#include "Sri.h" +#include "DB.h" +#include "po/po.h" -class UserDB { +class UserDB : public DB { + /// login ... public: - virtual ~UserDB(); + std::tuple select_user(uint64_t account); + bool select_user_exist(uint64_t account); + bool insert_user(uint64_t account, const std::string &password, const std::string &password_salt, + const std::string& client_info = ""); + bool insert_user_friends(uint64_t account); + bool test(uint64_t account); -public: - virtual mp::sri* login(uint64_t account, const std::string& password); - virtual mp::sri* register_(uint64_t account, const std::string& password, - const std::string& password_salt, const std::string& client_info = ""); - virtual mp::sri* logout(uint64_t account); -private: - mysqlpp::Connection* conn = DB::link(); - mp::sri* sri{}; + bool remove_user(uint64_t account); }; diff --git a/MS/works/db/UserFriendsDB.cpp b/MS/works/db/UserFriendsDB.cpp new file mode 100644 index 0000000..d15892e --- /dev/null +++ b/MS/works/db/UserFriendsDB.cpp @@ -0,0 +1,123 @@ +// +// Created by dongl on 23-5-4. +// + +#include "UserFriendsDB.h" +#include "linkDB.h" + + +// 查询好友添加权限类型 +char UserFriendsDB::select_add_type(uint64_t account) { + conn = LinkDB::safe_grab(); + auto query = conn->query("select restrictions from im_user_friends where account=%0:account"); + query.parse(); + + // 查看添加授权类型 0 直接添加 1 验证问题 2 账户审核 + return query.store(account)[0][0][0]; // 因为 account 唯一 所以结果 至多一个 +} + +// 修改好友列表数据 添加好友 +/// friends :{ +/// uid :info { } +/// } +bool UserFriendsDB::add_friends(uint64_t account, uint64_t friends) { + conn = LinkDB::safe_grab(); + auto query = conn->query("update im_user_friends set friends=" + "JSON_SET(friends, '$.\"%2:friends\"', " + "JSON_OBJECT('belong_grouping', 1, 'add_time', 10000, 'add_source', 1)" + ")" + "where account=%1:account"); + + query.template_defaults[1] = account; + query.template_defaults[2] = friends; + query.parse(); + return query.exec(); +} + +std::vector UserFriendsDB::select_friends_all(uint64_t account) { + conn = LinkDB::safe_grab(); + + auto q = conn->query("select friends from im_user_friends where account=783556037"); + + auto ret = q.store(); + std::vector v; + return v; +} + +rapidjson::Document UserFriendsDB::select_friends_info(uint64_t account, uint64_t friends) { + conn = LinkDB::safe_grab(); + rapidjson::Document document; + + auto q = conn->query("select JSON_EXTRACT(friends, '$.\"%2:friends\"') as friend_info " + "from im_user_friends where account=%1:account"); + q.template_defaults[1] = account; + q.template_defaults[2] = friends; + q.parse(); + + // 查库 + auto ret = q.store(); + + printf("%zu\n", ret.num_rows()); + + if (ret.num_rows() < 1) { + return document; + } + + // 取json字符串 + std::string friend_info; + ret[0][0].to_string(friend_info); + + // 解析json + document.Parse(friend_info.c_str()); + + printf("%d\n", document["add_time"].GetInt()); + printf("user[%ld] friend_info-> %ld : %s", account, friends, friend_info.c_str()); +// printf("%u\n", document.Size()); + return document; +} + + +//std::tuple UserFriendsDB::update_add_info(char type, mp::body *body) { +// // 如果是0 直接添加 +// if (type == '0') { +// auto temp_query = conn->query("update im_user_friends set friends=%1q:groups where account=%0:account"); +// temp_query.parse(); +// +// std::vector friends; +// friends.push_back(body->source()); +// auto ret1 = temp_query.execute(body->target(), vectorSerialization(friends)); +// +// friends.clear(); +// friends.push_back(body->target()); +// auto ret2 = temp_query.execute(body->source(), vectorSerialization(friends)); +// +// if (ret1.rows() < 1 || ret2.rows() < 1) { +// return std::make_tuple(mp::MP_ADD_FRIENDS_ERR, mp::MP_ADD_FRIENDS_SQL_ERR, "定位之于此 sql可能执行结果出错"); +// } +// return std::make_tuple(mp::MP_ADD_FRIENDS, mp::MP_ADD_FRIENDS_0, "添加成功"); +// } +// +// // 1 回答问题 +// else if (type == '1') { +// +// return std::make_tuple(mp::MP_ADD_FRIENDS, mp::MP_ADD_FRIENDS, "添加成功"); +// } +// // 2 被添加账户审核 +// else if (type == '2') { +//// auto temp_query = conn->query("select"); +// +// return std::make_tuple(mp::MP_ADD_FRIENDS, mp::MP_ADD_FRIENDS_2, "待同意"); +// } +// +// return std::make_tuple(mp::MP_ADD_FRIENDS_ERR, mp::MP_ADD_FRIENDS_NOT_TYPE, "找不到此类型的添加决策"); +//} + + + + + + + + + + diff --git a/MS/works/db/UserFriendsDB.h b/MS/works/db/UserFriendsDB.h new file mode 100644 index 0000000..c27e853 --- /dev/null +++ b/MS/works/db/UserFriendsDB.h @@ -0,0 +1,23 @@ +// +// Created by dongl on 23-5-4. +// + +#ifndef IM2_USERFRIENDSDB_H +#define IM2_USERFRIENDSDB_H + + +#include "DB.h" +#include "proto/mp.sri.pb.h" +#include "proto/mp.body.pb.h" +#include "document.h" + +class UserFriendsDB : public DB { +public: + char select_add_type(uint64_t account); + bool add_friends(uint64_t source, uint64_t target); + std::vector select_friends_all(uint64_t account); + rapidjson::Document select_friends_info(uint64_t account, uint64_t friends); +}; + + +#endif //IM2_USERFRIENDSDB_H diff --git a/MS/works/db/linkDB.cpp b/MS/works/db/linkDB.cpp index 72aa039..7cab1c2 100644 --- a/MS/works/db/linkDB.cpp +++ b/MS/works/db/linkDB.cpp @@ -3,14 +3,14 @@ // #include "linkDB.h" -DB::DB() {} -DB::~DB() { +LinkDB::LinkDB() {} +LinkDB::~LinkDB() { delete pool; } -ConnectionPool* DB::pool = nullptr; +ConnectionPool* LinkDB::pool = nullptr; -mysqlpp::Connection* DB::link() { +mysqlpp::Connection* LinkDB::safe_grab() { if (pool == nullptr) { pool = new ConnectionPool("124.221.152.192", "ims", "IMS", "WimTFC8N58kznx2k"); } diff --git a/MS/works/db/linkDB.h b/MS/works/db/linkDB.h index cb12b16..98e14e4 100644 --- a/MS/works/db/linkDB.h +++ b/MS/works/db/linkDB.h @@ -7,13 +7,13 @@ #include "ConnectionPool.h" -class DB { +class LinkDB { public: - static mysqlpp::Connection* link(); + static mysqlpp::Connection* safe_grab(); private: - DB(); + LinkDB(); - virtual ~DB(); + virtual ~LinkDB(); private: static ConnectionPool* pool; }; diff --git a/MS/works/db/po/Po.cpp b/MS/works/db/po/Po.cpp new file mode 100644 index 0000000..f720a24 --- /dev/null +++ b/MS/works/db/po/Po.cpp @@ -0,0 +1,16 @@ +// +// Created by dongl on 23-5-6. +// + +#include "po.h" + + +PoUser::PoUser() { + +} + +PoUser::PoUser(uint64_t account, const std::string& username, const std::string& password, + const std::string& password_salt,const std::string& client_info) : + account(account), username(username), password(password), password_salt(password_salt), client_info(client_info) { + +} diff --git a/MS/works/db/po/PoJson.cpp b/MS/works/db/po/PoJson.cpp new file mode 100644 index 0000000..b8417b6 --- /dev/null +++ b/MS/works/db/po/PoJson.cpp @@ -0,0 +1,7 @@ +// +// Created by dongl on 23-5-7. +// + +#include "PoJson.h" + + diff --git a/MS/works/db/po/PoJson.h b/MS/works/db/po/PoJson.h new file mode 100644 index 0000000..d0c3684 --- /dev/null +++ b/MS/works/db/po/PoJson.h @@ -0,0 +1,71 @@ +// +// Created by dongl on 23-5-7. +// + +#ifndef IM2_POJSON_H +#define IM2_POJSON_H + +#include +#include + +#include "rapidjson.h" + +// uint8_t belong_grouping; // 所属分组 默认好友列表 +// time_t add_time; // 添加时间 +// uint8_t add_source; // 添加来源 + +class PoJsonFriends { +public: + PoJsonFriends() {}; + virtual ~PoJsonFriends() {} +public: + PoJsonFriends* append(uint64_t account, uint8_t belong_grouping, time_t add_time, uint8_t add_source) { + temp.insert({"belong_grouping", belong_grouping}); + temp.insert({"add_time", add_time}); + temp.insert({"add_source", add_source}); + + friends.insert({account, temp}); + + temp.clear(); + return this; + } + +public: + std::map> to_map() { + return friends; + } + + std::string to_string() { + + std::string ret; + ret.append("{ \n"); + + for (const auto &item: friends) { + ret.append(std::to_string(item.first)); + ret.append(" : { "); + + for (const auto &it: item.second) { + ret.append(it.first); + ret.push_back(':'); + ret.append(std::to_string(it.second)); + ret.append(", "); + } + ret.pop_back(); + ret.pop_back(); + + ret.append(" }\n"); + } + + + ret.append("}"); + return ret; + } + +private: + // uid : userinfo + std::map> friends; + std::map temp; +}; + + +#endif //IM2_POJSON_H diff --git a/MS/works/db/po/po.h b/MS/works/db/po/po.h new file mode 100644 index 0000000..dbb6abc --- /dev/null +++ b/MS/works/db/po/po.h @@ -0,0 +1,24 @@ +// +// Created by dongl on 23-5-6. +// + +#ifndef IM2_PO_H +#define IM2_PO_H + +#include + + +class PoUser { +public: + PoUser(uint64_t account, const std::string& username, const std::string& password, + const std::string& password_salt,const std::string& client_info); + PoUser(); +public: + uint64_t account; + std::string username; + std::string password; + std::string password_salt; + std::string client_info; +}; + +#endif //IM2_PO_H diff --git a/MS/works/service/Service.h b/MS/works/service/Service.h new file mode 100644 index 0000000..6587726 --- /dev/null +++ b/MS/works/service/Service.h @@ -0,0 +1,29 @@ +// +// Created by dongl on 23-5-5. +// + +#ifndef IM2_SERVICE_H +#define IM2_SERVICE_H + +#include +#include + +class Service { +public: + Service(){ + sri = new mp::sri(); + } + +protected: + mp::sri* sri; + +protected: + virtual void sri_clear(){ + sri->clear_sri_code(); + sri->clear_sri_msg(); + sri->clear_sri_token(); + sri->clear_sri_username(); + } +}; + +#endif //IM2_SERVICE_H diff --git a/MS/works/service/UserFriendsService.cpp b/MS/works/service/UserFriendsService.cpp new file mode 100644 index 0000000..ef4bf6d --- /dev/null +++ b/MS/works/service/UserFriendsService.cpp @@ -0,0 +1,60 @@ +// +// Created by dongl on 23-5-5. +// + +#include "UserFriendsService.h" + +UserFriendsService::~UserFriendsService() { + delete sri; +} + +mp::sri *UserFriendsService::imProve(mp::body* body) { + if (body->target() == 0 || body->source() == 0) { + printf("请求数据有缺\n"); + sri->set_sri_msg("请求数据有缺"); + return sri; + } + + auto subcommand = body->subcommand(); + + // 添加 + if (subcommand == mp::MP_SUB_TYPE::MP_REQUEST_ADD_CONTACT_PERSON) { + add_contact_person(body); + } + // 拉黑 + else if (subcommand == mp::MP_SUB_TYPE::MP_REQUEST_BLACK_LIST_CONTACT_PERSON) { + + } + // 删除 + else if (subcommand == mp::MP_SUB_TYPE::MP_REQUEST_REMOVE_CONTACT_PERSON) { + + } + + return sri; +} + +mp::sri* UserFriendsService::add_contact_person(mp::body *body) { + // 查看添加目标的权限类型 + char type = imProveDb.select_add_type(body->target()); + + // 直接添加 + if (type == '0') { + + } + // 回答问题 + else if (type == '1') { + + } + // 账户审核 + else if (type == '2') { + + } + +// auto [is_succ, ret_type, msg] = imProveDb.update_add_info(type, body); + +// sri->set_sri_code(ret_type); +// sri->set_sri_msg(msg); + return sri; +} + + diff --git a/MS/works/service/UserFriendsService.h b/MS/works/service/UserFriendsService.h new file mode 100644 index 0000000..f413fb5 --- /dev/null +++ b/MS/works/service/UserFriendsService.h @@ -0,0 +1,29 @@ +// +// Created by dongl on 23-5-5. +// + +#ifndef IM2_USERFRIENDSSERVICE_H +#define IM2_USERFRIENDSSERVICE_H + +#include "Service.h" +#include "proto/mp.body.pb.h" +#include "db/UserFriendsDB.h" + +class UserFriendsService : public Service { +public: + virtual ~UserFriendsService(); + +public: + // 唯一对开接口 + mp::sri* imProve(mp::body* body); + +private: + mp::sri* add_contact_person(mp::body* body); + +private: + mp::sri* sri = nullptr; + UserFriendsDB imProveDb = UserFriendsDB(); +}; + + +#endif //IM2_USERFRIENDSSERVICE_H diff --git a/MS/works/service/UserService.cpp b/MS/works/service/UserService.cpp index 758ea5e..b9fb65f 100644 --- a/MS/works/service/UserService.cpp +++ b/MS/works/service/UserService.cpp @@ -6,10 +6,53 @@ #include mp::sri* UserService::login(uint64_t account, const std::string& password) { - return userDb.login(account, password); + sri_clear(); + + auto exist = userDb.select_user_exist(account); + + // 无账户 + if (!exist) { + sri->set_sri_msg("无此账户"); + sri->set_sri_code(mp::MP_LOGIN_ACCOUNT_NOT); + return sri; + } + + // 判断密码 + auto [sql_exist, user] = userDb.select_user(account); + std::string source = std::to_string(account) + password + user.password_salt; + size_t password_hash = std::hash()(source); + + if (user.password == std::to_string(password_hash)) { + printf("登陆成功\n"); + sri->set_sri_msg("登陆成功!"); + sri->set_sri_code(mp::MP_LOGIN_SUCCESS); + sri->set_sri_token("token"); + sri->set_sri_username(user.username); + + // 这里redis 更新帐号信息 + + + } else { + printf("登陆失败\n"); + sri->set_sri_msg("登陆失败!"); + sri->set_sri_code(mp::MP_LOGIN_FAIL); + } + + return sri; } + mp::sri* UserService::register_(uint64_t account, const std::string &password) { + sri_clear(); + + // 查看账户是否存在 + auto exist = userDb.select_user_exist(account); + if (!exist) { + sri->set_sri_msg("账户已经存在"); + sri->set_sri_code(mp::MP_REGISTER_EXIST); + return sri; + } + // 生成salt std::string password_salt; for (int i = 0; i < std::experimental::randint(5, 8); ++i) { @@ -20,9 +63,55 @@ mp::sri* UserService::register_(uint64_t account, const std::string &password) std::string source = std::to_string(account) + password + password_salt; size_t password_hash = std::hash()(source); - return userDb.register_(account, std::to_string(password_hash), password_salt); + // 注册 插入用户 + auto add_user = userDb.insert_user(account, std::to_string(password_hash), password_salt); + if (!add_user) { + sri->set_sri_msg("注册失败, 用户表, sql"); + sri->set_sri_code(mp::MP_REGISTER_SQL_ERR); + return sri; + } + + auto add_friends_table = userDb.insert_user_friends(account); + if (!add_friends_table) { + if (!userDb.remove_user(account)) { + sri->set_sri_msg("注册失败, 附属好友表生成失败,但用户已添加, sql"); + sri->set_sri_code(mp::MP_REGISTER_SQL_ERR); + } + + sri->set_sri_msg("注册失败, 附属好友表, sql"); + sri->set_sri_code(mp::MP_REGISTER_SQL_ERR); + return sri; + } + + sri->set_sri_msg("注册成功"); + sri->set_sri_code(mp::MP_REGISTER_SUCCESS); + + return sri; } -mp::sri* UserService::logout(uint64_t account) { +mp::sri *UserService::logout(uint64_t account, bool state) { +// return userDb.logout(account, state); return nullptr; } + + + + + + + + + + + + + + + + + + + + + + diff --git a/MS/works/service/UserService.h b/MS/works/service/UserService.h index fa11e11..97b29ea 100644 --- a/MS/works/service/UserService.h +++ b/MS/works/service/UserService.h @@ -4,14 +4,18 @@ #ifndef IM2_USERSERVICE_H #define IM2_USERSERVICE_H + +#include "proto/mp.body.pb.h" +#include "Service.h" #include "db/UserDB.h" -class UserService { +class UserService : public Service { public: - virtual mp::sri* login(uint64_t account, const std::string& password); - virtual mp::sri* register_(uint64_t account, const std::string& password); - virtual mp::sri* logout(uint64_t account); + mp::sri* login(uint64_t account, const std::string& password); + mp::sri* register_(uint64_t account, const std::string& password); + mp::sri* logout(uint64_t account, bool state); + private: UserDB userDb = UserDB(); }; diff --git a/TEST/CMakeLists.txt b/TEST/CMakeLists.txt new file mode 100644 index 0000000..43930e4 --- /dev/null +++ b/TEST/CMakeLists.txt @@ -0,0 +1,20 @@ +project(TEST) + +include_directories(${CMAKE_SOURCE_DIR}/include) +include_directories(${CMAKE_SOURCE_DIR}/include/mysql++) +include_directories(${CMAKE_SOURCE_DIR}/include/mysql++/mysql) +include_directories(${CMAKE_SOURCE_DIR}/MDB/imm_mysqldb) +include_directories(${CMAKE_SOURCE_DIR}/include/mp) +include_directories(${CMAKE_SOURCE_DIR}/include/rapidjson) +include_directories(${CMAKE_SOURCE_DIR}/MS) + +link_directories(${CMAKE_SOURCE_DIR}/lib/gtest) + +add_executable(TEST main.cpp) + +target_link_libraries(TEST + works + imm_mysqldb + libgtest.a libgtest_main.a + libgmock.a libgmock_main.a + ) \ No newline at end of file diff --git a/TEST/main.cpp b/TEST/main.cpp new file mode 100644 index 0000000..437d9b5 --- /dev/null +++ b/TEST/main.cpp @@ -0,0 +1,43 @@ +// +// Created by dongl on 23-5-4. +// + + + +#include "gtest/gtest.h" +#include "works/db/po/PoJson.h" +#include "works/db/linkDB.h" +#include "works/db/UserFriendsDB.h" +#include "works/db/UserDB.h" + +int main(int argc, char** argv) { + testing::InitGoogleTest(&argc,argv); + return RUN_ALL_TESTS(); +} + +TEST(JSON, MYSQL_TYPE_JSON) { + auto i = new PoJsonFriends(); + std::string str = i->append(2725096176, 1, 100344, 1)-> + append(783556037, 1, 1024255, 2)->to_string(); + + printf("%s", str.c_str()); +} + + +TEST(add_friends, add_friends__Test) { + auto i = UserFriendsDB(); + i.add_friends(2725096176, 783556037); + i.add_friends(783556037, 2725096176); +} + + +TEST(UserDB, UserDB_Ues_Test) { + auto i = UserDB(); +// i.insert_user_friends(1111111); + i.test(2222222); +} + +TEST(select_friends, select_friends_Test) { + auto i = UserFriendsDB(); + i.select_friends_info(2725096176, 783556037); +} \ No newline at end of file diff --git a/include/gtest/gtest-assertion-result.h b/include/gtest/gtest-assertion-result.h new file mode 100644 index 0000000..addbb59 --- /dev/null +++ b/include/gtest/gtest-assertion-result.h @@ -0,0 +1,237 @@ +// Copyright 2005, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// The Google C++ Testing and Mocking Framework (Google Test) +// +// This file implements the AssertionResult type. + +// IWYU pragma: private, include "gtest/gtest.h" +// IWYU pragma: friend gtest/.* +// IWYU pragma: friend gmock/.* + +#ifndef GOOGLETEST_INCLUDE_GTEST_GTEST_ASSERTION_RESULT_H_ +#define GOOGLETEST_INCLUDE_GTEST_GTEST_ASSERTION_RESULT_H_ + +#include +#include +#include +#include + +#include "gtest/gtest-message.h" +#include "gtest/internal/gtest-port.h" + +GTEST_DISABLE_MSC_WARNINGS_PUSH_(4251 \ +/* class A needs to have dll-interface to be used by clients of class B */) + +namespace testing { + +// A class for indicating whether an assertion was successful. When +// the assertion wasn't successful, the AssertionResult object +// remembers a non-empty message that describes how it failed. +// +// To create an instance of this class, use one of the factory functions +// (AssertionSuccess() and AssertionFailure()). +// +// This class is useful for two purposes: +// 1. Defining predicate functions to be used with Boolean test assertions +// EXPECT_TRUE/EXPECT_FALSE and their ASSERT_ counterparts +// 2. Defining predicate-format functions to be +// used with predicate assertions (ASSERT_PRED_FORMAT*, etc). +// +// For example, if you define IsEven predicate: +// +// testing::AssertionResult IsEven(int n) { +// if ((n % 2) == 0) +// return testing::AssertionSuccess(); +// else +// return testing::AssertionFailure() << n << " is odd"; +// } +// +// Then the failed expectation EXPECT_TRUE(IsEven(Fib(5))) +// will print the message +// +// Value of: IsEven(Fib(5)) +// Actual: false (5 is odd) +// Expected: true +// +// instead of a more opaque +// +// Value of: IsEven(Fib(5)) +// Actual: false +// Expected: true +// +// in case IsEven is a simple Boolean predicate. +// +// If you expect your predicate to be reused and want to support informative +// messages in EXPECT_FALSE and ASSERT_FALSE (negative assertions show up +// about half as often as positive ones in our tests), supply messages for +// both success and failure cases: +// +// testing::AssertionResult IsEven(int n) { +// if ((n % 2) == 0) +// return testing::AssertionSuccess() << n << " is even"; +// else +// return testing::AssertionFailure() << n << " is odd"; +// } +// +// Then a statement EXPECT_FALSE(IsEven(Fib(6))) will print +// +// Value of: IsEven(Fib(6)) +// Actual: true (8 is even) +// Expected: false +// +// NB: Predicates that support negative Boolean assertions have reduced +// performance in positive ones so be careful not to use them in tests +// that have lots (tens of thousands) of positive Boolean assertions. +// +// To use this class with EXPECT_PRED_FORMAT assertions such as: +// +// // Verifies that Foo() returns an even number. +// EXPECT_PRED_FORMAT1(IsEven, Foo()); +// +// you need to define: +// +// testing::AssertionResult IsEven(const char* expr, int n) { +// if ((n % 2) == 0) +// return testing::AssertionSuccess(); +// else +// return testing::AssertionFailure() +// << "Expected: " << expr << " is even\n Actual: it's " << n; +// } +// +// If Foo() returns 5, you will see the following message: +// +// Expected: Foo() is even +// Actual: it's 5 +// +class GTEST_API_ AssertionResult { + public: + // Copy constructor. + // Used in EXPECT_TRUE/FALSE(assertion_result). + AssertionResult(const AssertionResult& other); + +// C4800 is a level 3 warning in Visual Studio 2015 and earlier. +// This warning is not emitted in Visual Studio 2017. +// This warning is off by default starting in Visual Studio 2019 but can be +// enabled with command-line options. +#if defined(_MSC_VER) && (_MSC_VER < 1910 || _MSC_VER >= 1920) + GTEST_DISABLE_MSC_WARNINGS_PUSH_(4800 /* forcing value to bool */) +#endif + + // Used in the EXPECT_TRUE/FALSE(bool_expression). + // + // T must be contextually convertible to bool. + // + // The second parameter prevents this overload from being considered if + // the argument is implicitly convertible to AssertionResult. In that case + // we want AssertionResult's copy constructor to be used. + template + explicit AssertionResult( + const T& success, + typename std::enable_if< + !std::is_convertible::value>::type* + /*enabler*/ + = nullptr) + : success_(success) {} + +#if defined(_MSC_VER) && (_MSC_VER < 1910 || _MSC_VER >= 1920) + GTEST_DISABLE_MSC_WARNINGS_POP_() +#endif + + // Assignment operator. + AssertionResult& operator=(AssertionResult other) { + swap(other); + return *this; + } + + // Returns true if and only if the assertion succeeded. + operator bool() const { return success_; } // NOLINT + + // Returns the assertion's negation. Used with EXPECT/ASSERT_FALSE. + AssertionResult operator!() const; + + // Returns the text streamed into this AssertionResult. Test assertions + // use it when they fail (i.e., the predicate's outcome doesn't match the + // assertion's expectation). When nothing has been streamed into the + // object, returns an empty string. + const char* message() const { + return message_.get() != nullptr ? message_->c_str() : ""; + } + // Deprecated; please use message() instead. + const char* failure_message() const { return message(); } + + // Streams a custom failure message into this object. + template + AssertionResult& operator<<(const T& value) { + AppendMessage(Message() << value); + return *this; + } + + // Allows streaming basic output manipulators such as endl or flush into + // this object. + AssertionResult& operator<<( + ::std::ostream& (*basic_manipulator)(::std::ostream& stream)) { + AppendMessage(Message() << basic_manipulator); + return *this; + } + + private: + // Appends the contents of message to message_. + void AppendMessage(const Message& a_message) { + if (message_.get() == nullptr) message_.reset(new ::std::string); + message_->append(a_message.GetString().c_str()); + } + + // Swap the contents of this AssertionResult with other. + void swap(AssertionResult& other); + + // Stores result of the assertion predicate. + bool success_; + // Stores the message describing the condition in case the expectation + // construct is not satisfied with the predicate's outcome. + // Referenced via a pointer to avoid taking too much stack frame space + // with test assertions. + std::unique_ptr< ::std::string> message_; +}; + +// Makes a successful assertion result. +GTEST_API_ AssertionResult AssertionSuccess(); + +// Makes a failed assertion result. +GTEST_API_ AssertionResult AssertionFailure(); + +// Makes a failed assertion result with the given failure message. +// Deprecated; use AssertionFailure() << msg. +GTEST_API_ AssertionResult AssertionFailure(const Message& msg); + +} // namespace testing + +GTEST_DISABLE_MSC_WARNINGS_POP_() // 4251 + +#endif // GOOGLETEST_INCLUDE_GTEST_GTEST_ASSERTION_RESULT_H_ diff --git a/include/gtest/gtest-death-test.h b/include/gtest/gtest-death-test.h new file mode 100644 index 0000000..84e5a5b --- /dev/null +++ b/include/gtest/gtest-death-test.h @@ -0,0 +1,345 @@ +// Copyright 2005, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// The Google C++ Testing and Mocking Framework (Google Test) +// +// This header file defines the public API for death tests. It is +// #included by gtest.h so a user doesn't need to include this +// directly. + +// IWYU pragma: private, include "gtest/gtest.h" +// IWYU pragma: friend gtest/.* +// IWYU pragma: friend gmock/.* + +#ifndef GOOGLETEST_INCLUDE_GTEST_GTEST_DEATH_TEST_H_ +#define GOOGLETEST_INCLUDE_GTEST_GTEST_DEATH_TEST_H_ + +#include "gtest/internal/gtest-death-test-internal.h" + +// This flag controls the style of death tests. Valid values are "threadsafe", +// meaning that the death test child process will re-execute the test binary +// from the start, running only a single death test, or "fast", +// meaning that the child process will execute the test logic immediately +// after forking. +GTEST_DECLARE_string_(death_test_style); + +namespace testing { + +#if GTEST_HAS_DEATH_TEST + +namespace internal { + +// Returns a Boolean value indicating whether the caller is currently +// executing in the context of the death test child process. Tools such as +// Valgrind heap checkers may need this to modify their behavior in death +// tests. IMPORTANT: This is an internal utility. Using it may break the +// implementation of death tests. User code MUST NOT use it. +GTEST_API_ bool InDeathTestChild(); + +} // namespace internal + +// The following macros are useful for writing death tests. + +// Here's what happens when an ASSERT_DEATH* or EXPECT_DEATH* is +// executed: +// +// 1. It generates a warning if there is more than one active +// thread. This is because it's safe to fork() or clone() only +// when there is a single thread. +// +// 2. The parent process clone()s a sub-process and runs the death +// test in it; the sub-process exits with code 0 at the end of the +// death test, if it hasn't exited already. +// +// 3. The parent process waits for the sub-process to terminate. +// +// 4. The parent process checks the exit code and error message of +// the sub-process. +// +// Examples: +// +// ASSERT_DEATH(server.SendMessage(56, "Hello"), "Invalid port number"); +// for (int i = 0; i < 5; i++) { +// EXPECT_DEATH(server.ProcessRequest(i), +// "Invalid request .* in ProcessRequest()") +// << "Failed to die on request " << i; +// } +// +// ASSERT_EXIT(server.ExitNow(), ::testing::ExitedWithCode(0), "Exiting"); +// +// bool KilledBySIGHUP(int exit_code) { +// return WIFSIGNALED(exit_code) && WTERMSIG(exit_code) == SIGHUP; +// } +// +// ASSERT_EXIT(client.HangUpServer(), KilledBySIGHUP, "Hanging up!"); +// +// The final parameter to each of these macros is a matcher applied to any data +// the sub-process wrote to stderr. For compatibility with existing tests, a +// bare string is interpreted as a regular expression matcher. +// +// On the regular expressions used in death tests: +// +// On POSIX-compliant systems (*nix), we use the library, +// which uses the POSIX extended regex syntax. +// +// On other platforms (e.g. Windows or Mac), we only support a simple regex +// syntax implemented as part of Google Test. This limited +// implementation should be enough most of the time when writing +// death tests; though it lacks many features you can find in PCRE +// or POSIX extended regex syntax. For example, we don't support +// union ("x|y"), grouping ("(xy)"), brackets ("[xy]"), and +// repetition count ("x{5,7}"), among others. +// +// Below is the syntax that we do support. We chose it to be a +// subset of both PCRE and POSIX extended regex, so it's easy to +// learn wherever you come from. In the following: 'A' denotes a +// literal character, period (.), or a single \\ escape sequence; +// 'x' and 'y' denote regular expressions; 'm' and 'n' are for +// natural numbers. +// +// c matches any literal character c +// \\d matches any decimal digit +// \\D matches any character that's not a decimal digit +// \\f matches \f +// \\n matches \n +// \\r matches \r +// \\s matches any ASCII whitespace, including \n +// \\S matches any character that's not a whitespace +// \\t matches \t +// \\v matches \v +// \\w matches any letter, _, or decimal digit +// \\W matches any character that \\w doesn't match +// \\c matches any literal character c, which must be a punctuation +// . matches any single character except \n +// A? matches 0 or 1 occurrences of A +// A* matches 0 or many occurrences of A +// A+ matches 1 or many occurrences of A +// ^ matches the beginning of a string (not that of each line) +// $ matches the end of a string (not that of each line) +// xy matches x followed by y +// +// If you accidentally use PCRE or POSIX extended regex features +// not implemented by us, you will get a run-time failure. In that +// case, please try to rewrite your regular expression within the +// above syntax. +// +// This implementation is *not* meant to be as highly tuned or robust +// as a compiled regex library, but should perform well enough for a +// death test, which already incurs significant overhead by launching +// a child process. +// +// Known caveats: +// +// A "threadsafe" style death test obtains the path to the test +// program from argv[0] and re-executes it in the sub-process. For +// simplicity, the current implementation doesn't search the PATH +// when launching the sub-process. This means that the user must +// invoke the test program via a path that contains at least one +// path separator (e.g. path/to/foo_test and +// /absolute/path/to/bar_test are fine, but foo_test is not). This +// is rarely a problem as people usually don't put the test binary +// directory in PATH. +// + +// Asserts that a given `statement` causes the program to exit, with an +// integer exit status that satisfies `predicate`, and emitting error output +// that matches `matcher`. +#define ASSERT_EXIT(statement, predicate, matcher) \ + GTEST_DEATH_TEST_(statement, predicate, matcher, GTEST_FATAL_FAILURE_) + +// Like `ASSERT_EXIT`, but continues on to successive tests in the +// test suite, if any: +#define EXPECT_EXIT(statement, predicate, matcher) \ + GTEST_DEATH_TEST_(statement, predicate, matcher, GTEST_NONFATAL_FAILURE_) + +// Asserts that a given `statement` causes the program to exit, either by +// explicitly exiting with a nonzero exit code or being killed by a +// signal, and emitting error output that matches `matcher`. +#define ASSERT_DEATH(statement, matcher) \ + ASSERT_EXIT(statement, ::testing::internal::ExitedUnsuccessfully, matcher) + +// Like `ASSERT_DEATH`, but continues on to successive tests in the +// test suite, if any: +#define EXPECT_DEATH(statement, matcher) \ + EXPECT_EXIT(statement, ::testing::internal::ExitedUnsuccessfully, matcher) + +// Two predicate classes that can be used in {ASSERT,EXPECT}_EXIT*: + +// Tests that an exit code describes a normal exit with a given exit code. +class GTEST_API_ ExitedWithCode { + public: + explicit ExitedWithCode(int exit_code); + ExitedWithCode(const ExitedWithCode&) = default; + void operator=(const ExitedWithCode& other) = delete; + bool operator()(int exit_status) const; + + private: + const int exit_code_; +}; + +#if !GTEST_OS_WINDOWS && !GTEST_OS_FUCHSIA +// Tests that an exit code describes an exit due to termination by a +// given signal. +class GTEST_API_ KilledBySignal { + public: + explicit KilledBySignal(int signum); + bool operator()(int exit_status) const; + + private: + const int signum_; +}; +#endif // !GTEST_OS_WINDOWS + +// EXPECT_DEBUG_DEATH asserts that the given statements die in debug mode. +// The death testing framework causes this to have interesting semantics, +// since the sideeffects of the call are only visible in opt mode, and not +// in debug mode. +// +// In practice, this can be used to test functions that utilize the +// LOG(DFATAL) macro using the following style: +// +// int DieInDebugOr12(int* sideeffect) { +// if (sideeffect) { +// *sideeffect = 12; +// } +// LOG(DFATAL) << "death"; +// return 12; +// } +// +// TEST(TestSuite, TestDieOr12WorksInDgbAndOpt) { +// int sideeffect = 0; +// // Only asserts in dbg. +// EXPECT_DEBUG_DEATH(DieInDebugOr12(&sideeffect), "death"); +// +// #ifdef NDEBUG +// // opt-mode has sideeffect visible. +// EXPECT_EQ(12, sideeffect); +// #else +// // dbg-mode no visible sideeffect. +// EXPECT_EQ(0, sideeffect); +// #endif +// } +// +// This will assert that DieInDebugReturn12InOpt() crashes in debug +// mode, usually due to a DCHECK or LOG(DFATAL), but returns the +// appropriate fallback value (12 in this case) in opt mode. If you +// need to test that a function has appropriate side-effects in opt +// mode, include assertions against the side-effects. A general +// pattern for this is: +// +// EXPECT_DEBUG_DEATH({ +// // Side-effects here will have an effect after this statement in +// // opt mode, but none in debug mode. +// EXPECT_EQ(12, DieInDebugOr12(&sideeffect)); +// }, "death"); +// +#ifdef NDEBUG + +#define EXPECT_DEBUG_DEATH(statement, regex) \ + GTEST_EXECUTE_STATEMENT_(statement, regex) + +#define ASSERT_DEBUG_DEATH(statement, regex) \ + GTEST_EXECUTE_STATEMENT_(statement, regex) + +#else + +#define EXPECT_DEBUG_DEATH(statement, regex) EXPECT_DEATH(statement, regex) + +#define ASSERT_DEBUG_DEATH(statement, regex) ASSERT_DEATH(statement, regex) + +#endif // NDEBUG for EXPECT_DEBUG_DEATH +#endif // GTEST_HAS_DEATH_TEST + +// This macro is used for implementing macros such as +// EXPECT_DEATH_IF_SUPPORTED and ASSERT_DEATH_IF_SUPPORTED on systems where +// death tests are not supported. Those macros must compile on such systems +// if and only if EXPECT_DEATH and ASSERT_DEATH compile with the same parameters +// on systems that support death tests. This allows one to write such a macro on +// a system that does not support death tests and be sure that it will compile +// on a death-test supporting system. It is exposed publicly so that systems +// that have death-tests with stricter requirements than GTEST_HAS_DEATH_TEST +// can write their own equivalent of EXPECT_DEATH_IF_SUPPORTED and +// ASSERT_DEATH_IF_SUPPORTED. +// +// Parameters: +// statement - A statement that a macro such as EXPECT_DEATH would test +// for program termination. This macro has to make sure this +// statement is compiled but not executed, to ensure that +// EXPECT_DEATH_IF_SUPPORTED compiles with a certain +// parameter if and only if EXPECT_DEATH compiles with it. +// regex - A regex that a macro such as EXPECT_DEATH would use to test +// the output of statement. This parameter has to be +// compiled but not evaluated by this macro, to ensure that +// this macro only accepts expressions that a macro such as +// EXPECT_DEATH would accept. +// terminator - Must be an empty statement for EXPECT_DEATH_IF_SUPPORTED +// and a return statement for ASSERT_DEATH_IF_SUPPORTED. +// This ensures that ASSERT_DEATH_IF_SUPPORTED will not +// compile inside functions where ASSERT_DEATH doesn't +// compile. +// +// The branch that has an always false condition is used to ensure that +// statement and regex are compiled (and thus syntactically correct) but +// never executed. The unreachable code macro protects the terminator +// statement from generating an 'unreachable code' warning in case +// statement unconditionally returns or throws. The Message constructor at +// the end allows the syntax of streaming additional messages into the +// macro, for compilational compatibility with EXPECT_DEATH/ASSERT_DEATH. +#define GTEST_UNSUPPORTED_DEATH_TEST(statement, regex, terminator) \ + GTEST_AMBIGUOUS_ELSE_BLOCKER_ \ + if (::testing::internal::AlwaysTrue()) { \ + GTEST_LOG_(WARNING) << "Death tests are not supported on this platform.\n" \ + << "Statement '" #statement "' cannot be verified."; \ + } else if (::testing::internal::AlwaysFalse()) { \ + ::testing::internal::RE::PartialMatch(".*", (regex)); \ + GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement); \ + terminator; \ + } else \ + ::testing::Message() + +// EXPECT_DEATH_IF_SUPPORTED(statement, regex) and +// ASSERT_DEATH_IF_SUPPORTED(statement, regex) expand to real death tests if +// death tests are supported; otherwise they just issue a warning. This is +// useful when you are combining death test assertions with normal test +// assertions in one test. +#if GTEST_HAS_DEATH_TEST +#define EXPECT_DEATH_IF_SUPPORTED(statement, regex) \ + EXPECT_DEATH(statement, regex) +#define ASSERT_DEATH_IF_SUPPORTED(statement, regex) \ + ASSERT_DEATH(statement, regex) +#else +#define EXPECT_DEATH_IF_SUPPORTED(statement, regex) \ + GTEST_UNSUPPORTED_DEATH_TEST(statement, regex, ) +#define ASSERT_DEATH_IF_SUPPORTED(statement, regex) \ + GTEST_UNSUPPORTED_DEATH_TEST(statement, regex, return) +#endif + +} // namespace testing + +#endif // GOOGLETEST_INCLUDE_GTEST_GTEST_DEATH_TEST_H_ diff --git a/include/gtest/gtest-matchers.h b/include/gtest/gtest-matchers.h new file mode 100644 index 0000000..4a60b0d --- /dev/null +++ b/include/gtest/gtest-matchers.h @@ -0,0 +1,956 @@ +// Copyright 2007, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// The Google C++ Testing and Mocking Framework (Google Test) +// +// This file implements just enough of the matcher interface to allow +// EXPECT_DEATH and friends to accept a matcher argument. + +// IWYU pragma: private, include "gtest/gtest.h" +// IWYU pragma: friend gtest/.* +// IWYU pragma: friend gmock/.* + +#ifndef GOOGLETEST_INCLUDE_GTEST_GTEST_MATCHERS_H_ +#define GOOGLETEST_INCLUDE_GTEST_GTEST_MATCHERS_H_ + +#include +#include +#include +#include +#include + +#include "gtest/gtest-printers.h" +#include "gtest/internal/gtest-internal.h" +#include "gtest/internal/gtest-port.h" + +// MSVC warning C5046 is new as of VS2017 version 15.8. +#if defined(_MSC_VER) && _MSC_VER >= 1915 +#define GTEST_MAYBE_5046_ 5046 +#else +#define GTEST_MAYBE_5046_ +#endif + +GTEST_DISABLE_MSC_WARNINGS_PUSH_( + 4251 GTEST_MAYBE_5046_ /* class A needs to have dll-interface to be used by + clients of class B */ + /* Symbol involving type with internal linkage not defined */) + +namespace testing { + +// To implement a matcher Foo for type T, define: +// 1. a class FooMatcherMatcher that implements the matcher interface: +// using is_gtest_matcher = void; +// bool MatchAndExplain(const T&, std::ostream*); +// (MatchResultListener* can also be used instead of std::ostream*) +// void DescribeTo(std::ostream*); +// void DescribeNegationTo(std::ostream*); +// +// 2. a factory function that creates a Matcher object from a +// FooMatcherMatcher. + +class MatchResultListener { + public: + // Creates a listener object with the given underlying ostream. The + // listener does not own the ostream, and does not dereference it + // in the constructor or destructor. + explicit MatchResultListener(::std::ostream* os) : stream_(os) {} + virtual ~MatchResultListener() = 0; // Makes this class abstract. + + // Streams x to the underlying ostream; does nothing if the ostream + // is NULL. + template + MatchResultListener& operator<<(const T& x) { + if (stream_ != nullptr) *stream_ << x; + return *this; + } + + // Returns the underlying ostream. + ::std::ostream* stream() { return stream_; } + + // Returns true if and only if the listener is interested in an explanation + // of the match result. A matcher's MatchAndExplain() method can use + // this information to avoid generating the explanation when no one + // intends to hear it. + bool IsInterested() const { return stream_ != nullptr; } + + private: + ::std::ostream* const stream_; + + MatchResultListener(const MatchResultListener&) = delete; + MatchResultListener& operator=(const MatchResultListener&) = delete; +}; + +inline MatchResultListener::~MatchResultListener() {} + +// An instance of a subclass of this knows how to describe itself as a +// matcher. +class GTEST_API_ MatcherDescriberInterface { + public: + virtual ~MatcherDescriberInterface() {} + + // Describes this matcher to an ostream. The function should print + // a verb phrase that describes the property a value matching this + // matcher should have. The subject of the verb phrase is the value + // being matched. For example, the DescribeTo() method of the Gt(7) + // matcher prints "is greater than 7". + virtual void DescribeTo(::std::ostream* os) const = 0; + + // Describes the negation of this matcher to an ostream. For + // example, if the description of this matcher is "is greater than + // 7", the negated description could be "is not greater than 7". + // You are not required to override this when implementing + // MatcherInterface, but it is highly advised so that your matcher + // can produce good error messages. + virtual void DescribeNegationTo(::std::ostream* os) const { + *os << "not ("; + DescribeTo(os); + *os << ")"; + } +}; + +// The implementation of a matcher. +template +class MatcherInterface : public MatcherDescriberInterface { + public: + // Returns true if and only if the matcher matches x; also explains the + // match result to 'listener' if necessary (see the next paragraph), in + // the form of a non-restrictive relative clause ("which ...", + // "whose ...", etc) that describes x. For example, the + // MatchAndExplain() method of the Pointee(...) matcher should + // generate an explanation like "which points to ...". + // + // Implementations of MatchAndExplain() should add an explanation of + // the match result *if and only if* they can provide additional + // information that's not already present (or not obvious) in the + // print-out of x and the matcher's description. Whether the match + // succeeds is not a factor in deciding whether an explanation is + // needed, as sometimes the caller needs to print a failure message + // when the match succeeds (e.g. when the matcher is used inside + // Not()). + // + // For example, a "has at least 10 elements" matcher should explain + // what the actual element count is, regardless of the match result, + // as it is useful information to the reader; on the other hand, an + // "is empty" matcher probably only needs to explain what the actual + // size is when the match fails, as it's redundant to say that the + // size is 0 when the value is already known to be empty. + // + // You should override this method when defining a new matcher. + // + // It's the responsibility of the caller (Google Test) to guarantee + // that 'listener' is not NULL. This helps to simplify a matcher's + // implementation when it doesn't care about the performance, as it + // can talk to 'listener' without checking its validity first. + // However, in order to implement dummy listeners efficiently, + // listener->stream() may be NULL. + virtual bool MatchAndExplain(T x, MatchResultListener* listener) const = 0; + + // Inherits these methods from MatcherDescriberInterface: + // virtual void DescribeTo(::std::ostream* os) const = 0; + // virtual void DescribeNegationTo(::std::ostream* os) const; +}; + +namespace internal { + +struct AnyEq { + template + bool operator()(const A& a, const B& b) const { + return a == b; + } +}; +struct AnyNe { + template + bool operator()(const A& a, const B& b) const { + return a != b; + } +}; +struct AnyLt { + template + bool operator()(const A& a, const B& b) const { + return a < b; + } +}; +struct AnyGt { + template + bool operator()(const A& a, const B& b) const { + return a > b; + } +}; +struct AnyLe { + template + bool operator()(const A& a, const B& b) const { + return a <= b; + } +}; +struct AnyGe { + template + bool operator()(const A& a, const B& b) const { + return a >= b; + } +}; + +// A match result listener that ignores the explanation. +class DummyMatchResultListener : public MatchResultListener { + public: + DummyMatchResultListener() : MatchResultListener(nullptr) {} + + private: + DummyMatchResultListener(const DummyMatchResultListener&) = delete; + DummyMatchResultListener& operator=(const DummyMatchResultListener&) = delete; +}; + +// A match result listener that forwards the explanation to a given +// ostream. The difference between this and MatchResultListener is +// that the former is concrete. +class StreamMatchResultListener : public MatchResultListener { + public: + explicit StreamMatchResultListener(::std::ostream* os) + : MatchResultListener(os) {} + + private: + StreamMatchResultListener(const StreamMatchResultListener&) = delete; + StreamMatchResultListener& operator=(const StreamMatchResultListener&) = + delete; +}; + +struct SharedPayloadBase { + std::atomic ref{1}; + void Ref() { ref.fetch_add(1, std::memory_order_relaxed); } + bool Unref() { return ref.fetch_sub(1, std::memory_order_acq_rel) == 1; } +}; + +template +struct SharedPayload : SharedPayloadBase { + explicit SharedPayload(const T& v) : value(v) {} + explicit SharedPayload(T&& v) : value(std::move(v)) {} + + static void Destroy(SharedPayloadBase* shared) { + delete static_cast(shared); + } + + T value; +}; + +// An internal class for implementing Matcher, which will derive +// from it. We put functionalities common to all Matcher +// specializations here to avoid code duplication. +template +class MatcherBase : private MatcherDescriberInterface { + public: + // Returns true if and only if the matcher matches x; also explains the + // match result to 'listener'. + bool MatchAndExplain(const T& x, MatchResultListener* listener) const { + GTEST_CHECK_(vtable_ != nullptr); + return vtable_->match_and_explain(*this, x, listener); + } + + // Returns true if and only if this matcher matches x. + bool Matches(const T& x) const { + DummyMatchResultListener dummy; + return MatchAndExplain(x, &dummy); + } + + // Describes this matcher to an ostream. + void DescribeTo(::std::ostream* os) const final { + GTEST_CHECK_(vtable_ != nullptr); + vtable_->describe(*this, os, false); + } + + // Describes the negation of this matcher to an ostream. + void DescribeNegationTo(::std::ostream* os) const final { + GTEST_CHECK_(vtable_ != nullptr); + vtable_->describe(*this, os, true); + } + + // Explains why x matches, or doesn't match, the matcher. + void ExplainMatchResultTo(const T& x, ::std::ostream* os) const { + StreamMatchResultListener listener(os); + MatchAndExplain(x, &listener); + } + + // Returns the describer for this matcher object; retains ownership + // of the describer, which is only guaranteed to be alive when + // this matcher object is alive. + const MatcherDescriberInterface* GetDescriber() const { + if (vtable_ == nullptr) return nullptr; + return vtable_->get_describer(*this); + } + + protected: + MatcherBase() : vtable_(nullptr), buffer_() {} + + // Constructs a matcher from its implementation. + template + explicit MatcherBase(const MatcherInterface* impl) + : vtable_(nullptr), buffer_() { + Init(impl); + } + + template ::type::is_gtest_matcher> + MatcherBase(M&& m) : vtable_(nullptr), buffer_() { // NOLINT + Init(std::forward(m)); + } + + MatcherBase(const MatcherBase& other) + : vtable_(other.vtable_), buffer_(other.buffer_) { + if (IsShared()) buffer_.shared->Ref(); + } + + MatcherBase& operator=(const MatcherBase& other) { + if (this == &other) return *this; + Destroy(); + vtable_ = other.vtable_; + buffer_ = other.buffer_; + if (IsShared()) buffer_.shared->Ref(); + return *this; + } + + MatcherBase(MatcherBase&& other) + : vtable_(other.vtable_), buffer_(other.buffer_) { + other.vtable_ = nullptr; + } + + MatcherBase& operator=(MatcherBase&& other) { + if (this == &other) return *this; + Destroy(); + vtable_ = other.vtable_; + buffer_ = other.buffer_; + other.vtable_ = nullptr; + return *this; + } + + ~MatcherBase() override { Destroy(); } + + private: + struct VTable { + bool (*match_and_explain)(const MatcherBase&, const T&, + MatchResultListener*); + void (*describe)(const MatcherBase&, std::ostream*, bool negation); + // Returns the captured object if it implements the interface, otherwise + // returns the MatcherBase itself. + const MatcherDescriberInterface* (*get_describer)(const MatcherBase&); + // Called on shared instances when the reference count reaches 0. + void (*shared_destroy)(SharedPayloadBase*); + }; + + bool IsShared() const { + return vtable_ != nullptr && vtable_->shared_destroy != nullptr; + } + + // If the implementation uses a listener, call that. + template + static auto MatchAndExplainImpl(const MatcherBase& m, const T& value, + MatchResultListener* listener) + -> decltype(P::Get(m).MatchAndExplain(value, listener->stream())) { + return P::Get(m).MatchAndExplain(value, listener->stream()); + } + + template + static auto MatchAndExplainImpl(const MatcherBase& m, const T& value, + MatchResultListener* listener) + -> decltype(P::Get(m).MatchAndExplain(value, listener)) { + return P::Get(m).MatchAndExplain(value, listener); + } + + template + static void DescribeImpl(const MatcherBase& m, std::ostream* os, + bool negation) { + if (negation) { + P::Get(m).DescribeNegationTo(os); + } else { + P::Get(m).DescribeTo(os); + } + } + + template + static const MatcherDescriberInterface* GetDescriberImpl( + const MatcherBase& m) { + // If the impl is a MatcherDescriberInterface, then return it. + // Otherwise use MatcherBase itself. + // This allows us to implement the GetDescriber() function without support + // from the impl, but some users really want to get their impl back when + // they call GetDescriber(). + // We use std::get on a tuple as a workaround of not having `if constexpr`. + return std::get<( + std::is_convertible::value + ? 1 + : 0)>(std::make_tuple(&m, &P::Get(m))); + } + + template + const VTable* GetVTable() { + static constexpr VTable kVTable = {&MatchAndExplainImpl

, + &DescribeImpl

, &GetDescriberImpl

, + P::shared_destroy}; + return &kVTable; + } + + union Buffer { + // Add some types to give Buffer some common alignment/size use cases. + void* ptr; + double d; + int64_t i; + // And add one for the out-of-line cases. + SharedPayloadBase* shared; + }; + + void Destroy() { + if (IsShared() && buffer_.shared->Unref()) { + vtable_->shared_destroy(buffer_.shared); + } + } + + template + static constexpr bool IsInlined() { + return sizeof(M) <= sizeof(Buffer) && alignof(M) <= alignof(Buffer) && + std::is_trivially_copy_constructible::value && + std::is_trivially_destructible::value; + } + + template ()> + struct ValuePolicy { + static const M& Get(const MatcherBase& m) { + // When inlined along with Init, need to be explicit to avoid violating + // strict aliasing rules. + const M* ptr = + static_cast(static_cast(&m.buffer_)); + return *ptr; + } + static void Init(MatcherBase& m, M impl) { + ::new (static_cast(&m.buffer_)) M(impl); + } + static constexpr auto shared_destroy = nullptr; + }; + + template + struct ValuePolicy { + using Shared = SharedPayload; + static const M& Get(const MatcherBase& m) { + return static_cast(m.buffer_.shared)->value; + } + template + static void Init(MatcherBase& m, Arg&& arg) { + m.buffer_.shared = new Shared(std::forward(arg)); + } + static constexpr auto shared_destroy = &Shared::Destroy; + }; + + template + struct ValuePolicy*, B> { + using M = const MatcherInterface; + using Shared = SharedPayload>; + static const M& Get(const MatcherBase& m) { + return *static_cast(m.buffer_.shared)->value; + } + static void Init(MatcherBase& m, M* impl) { + m.buffer_.shared = new Shared(std::unique_ptr(impl)); + } + + static constexpr auto shared_destroy = &Shared::Destroy; + }; + + template + void Init(M&& m) { + using MM = typename std::decay::type; + using Policy = ValuePolicy; + vtable_ = GetVTable(); + Policy::Init(*this, std::forward(m)); + } + + const VTable* vtable_; + Buffer buffer_; +}; + +} // namespace internal + +// A Matcher is a copyable and IMMUTABLE (except by assignment) +// object that can check whether a value of type T matches. The +// implementation of Matcher is just a std::shared_ptr to const +// MatcherInterface. Don't inherit from Matcher! +template +class Matcher : public internal::MatcherBase { + public: + // Constructs a null matcher. Needed for storing Matcher objects in STL + // containers. A default-constructed matcher is not yet initialized. You + // cannot use it until a valid value has been assigned to it. + explicit Matcher() {} // NOLINT + + // Constructs a matcher from its implementation. + explicit Matcher(const MatcherInterface* impl) + : internal::MatcherBase(impl) {} + + template + explicit Matcher( + const MatcherInterface* impl, + typename std::enable_if::value>::type* = + nullptr) + : internal::MatcherBase(impl) {} + + template ::type::is_gtest_matcher> + Matcher(M&& m) : internal::MatcherBase(std::forward(m)) {} // NOLINT + + // Implicit constructor here allows people to write + // EXPECT_CALL(foo, Bar(5)) instead of EXPECT_CALL(foo, Bar(Eq(5))) sometimes + Matcher(T value); // NOLINT +}; + +// The following two specializations allow the user to write str +// instead of Eq(str) and "foo" instead of Eq("foo") when a std::string +// matcher is expected. +template <> +class GTEST_API_ Matcher + : public internal::MatcherBase { + public: + Matcher() {} + + explicit Matcher(const MatcherInterface* impl) + : internal::MatcherBase(impl) {} + + template ::type::is_gtest_matcher> + Matcher(M&& m) // NOLINT + : internal::MatcherBase(std::forward(m)) {} + + // Allows the user to write str instead of Eq(str) sometimes, where + // str is a std::string object. + Matcher(const std::string& s); // NOLINT + + // Allows the user to write "foo" instead of Eq("foo") sometimes. + Matcher(const char* s); // NOLINT +}; + +template <> +class GTEST_API_ Matcher + : public internal::MatcherBase { + public: + Matcher() {} + + explicit Matcher(const MatcherInterface* impl) + : internal::MatcherBase(impl) {} + explicit Matcher(const MatcherInterface* impl) + : internal::MatcherBase(impl) {} + + template ::type::is_gtest_matcher> + Matcher(M&& m) // NOLINT + : internal::MatcherBase(std::forward(m)) {} + + // Allows the user to write str instead of Eq(str) sometimes, where + // str is a string object. + Matcher(const std::string& s); // NOLINT + + // Allows the user to write "foo" instead of Eq("foo") sometimes. + Matcher(const char* s); // NOLINT +}; + +#if GTEST_INTERNAL_HAS_STRING_VIEW +// The following two specializations allow the user to write str +// instead of Eq(str) and "foo" instead of Eq("foo") when a absl::string_view +// matcher is expected. +template <> +class GTEST_API_ Matcher + : public internal::MatcherBase { + public: + Matcher() {} + + explicit Matcher(const MatcherInterface* impl) + : internal::MatcherBase(impl) {} + + template ::type::is_gtest_matcher> + Matcher(M&& m) // NOLINT + : internal::MatcherBase(std::forward(m)) { + } + + // Allows the user to write str instead of Eq(str) sometimes, where + // str is a std::string object. + Matcher(const std::string& s); // NOLINT + + // Allows the user to write "foo" instead of Eq("foo") sometimes. + Matcher(const char* s); // NOLINT + + // Allows the user to pass absl::string_views or std::string_views directly. + Matcher(internal::StringView s); // NOLINT +}; + +template <> +class GTEST_API_ Matcher + : public internal::MatcherBase { + public: + Matcher() {} + + explicit Matcher(const MatcherInterface* impl) + : internal::MatcherBase(impl) {} + explicit Matcher(const MatcherInterface* impl) + : internal::MatcherBase(impl) {} + + template ::type::is_gtest_matcher> + Matcher(M&& m) // NOLINT + : internal::MatcherBase(std::forward(m)) {} + + // Allows the user to write str instead of Eq(str) sometimes, where + // str is a std::string object. + Matcher(const std::string& s); // NOLINT + + // Allows the user to write "foo" instead of Eq("foo") sometimes. + Matcher(const char* s); // NOLINT + + // Allows the user to pass absl::string_views or std::string_views directly. + Matcher(internal::StringView s); // NOLINT +}; +#endif // GTEST_INTERNAL_HAS_STRING_VIEW + +// Prints a matcher in a human-readable format. +template +std::ostream& operator<<(std::ostream& os, const Matcher& matcher) { + matcher.DescribeTo(&os); + return os; +} + +// The PolymorphicMatcher class template makes it easy to implement a +// polymorphic matcher (i.e. a matcher that can match values of more +// than one type, e.g. Eq(n) and NotNull()). +// +// To define a polymorphic matcher, a user should provide an Impl +// class that has a DescribeTo() method and a DescribeNegationTo() +// method, and define a member function (or member function template) +// +// bool MatchAndExplain(const Value& value, +// MatchResultListener* listener) const; +// +// See the definition of NotNull() for a complete example. +template +class PolymorphicMatcher { + public: + explicit PolymorphicMatcher(const Impl& an_impl) : impl_(an_impl) {} + + // Returns a mutable reference to the underlying matcher + // implementation object. + Impl& mutable_impl() { return impl_; } + + // Returns an immutable reference to the underlying matcher + // implementation object. + const Impl& impl() const { return impl_; } + + template + operator Matcher() const { + return Matcher(new MonomorphicImpl(impl_)); + } + + private: + template + class MonomorphicImpl : public MatcherInterface { + public: + explicit MonomorphicImpl(const Impl& impl) : impl_(impl) {} + + void DescribeTo(::std::ostream* os) const override { impl_.DescribeTo(os); } + + void DescribeNegationTo(::std::ostream* os) const override { + impl_.DescribeNegationTo(os); + } + + bool MatchAndExplain(T x, MatchResultListener* listener) const override { + return impl_.MatchAndExplain(x, listener); + } + + private: + const Impl impl_; + }; + + Impl impl_; +}; + +// Creates a matcher from its implementation. +// DEPRECATED: Especially in the generic code, prefer: +// Matcher(new MyMatcherImpl(...)); +// +// MakeMatcher may create a Matcher that accepts its argument by value, which +// leads to unnecessary copies & lack of support for non-copyable types. +template +inline Matcher MakeMatcher(const MatcherInterface* impl) { + return Matcher(impl); +} + +// Creates a polymorphic matcher from its implementation. This is +// easier to use than the PolymorphicMatcher constructor as it +// doesn't require you to explicitly write the template argument, e.g. +// +// MakePolymorphicMatcher(foo); +// vs +// PolymorphicMatcher(foo); +template +inline PolymorphicMatcher MakePolymorphicMatcher(const Impl& impl) { + return PolymorphicMatcher(impl); +} + +namespace internal { +// Implements a matcher that compares a given value with a +// pre-supplied value using one of the ==, <=, <, etc, operators. The +// two values being compared don't have to have the same type. +// +// The matcher defined here is polymorphic (for example, Eq(5) can be +// used to match an int, a short, a double, etc). Therefore we use +// a template type conversion operator in the implementation. +// +// The following template definition assumes that the Rhs parameter is +// a "bare" type (i.e. neither 'const T' nor 'T&'). +template +class ComparisonBase { + public: + explicit ComparisonBase(const Rhs& rhs) : rhs_(rhs) {} + + using is_gtest_matcher = void; + + template + bool MatchAndExplain(const Lhs& lhs, std::ostream*) const { + return Op()(lhs, Unwrap(rhs_)); + } + void DescribeTo(std::ostream* os) const { + *os << D::Desc() << " "; + UniversalPrint(Unwrap(rhs_), os); + } + void DescribeNegationTo(std::ostream* os) const { + *os << D::NegatedDesc() << " "; + UniversalPrint(Unwrap(rhs_), os); + } + + private: + template + static const T& Unwrap(const T& v) { + return v; + } + template + static const T& Unwrap(std::reference_wrapper v) { + return v; + } + + Rhs rhs_; +}; + +template +class EqMatcher : public ComparisonBase, Rhs, AnyEq> { + public: + explicit EqMatcher(const Rhs& rhs) + : ComparisonBase, Rhs, AnyEq>(rhs) {} + static const char* Desc() { return "is equal to"; } + static const char* NegatedDesc() { return "isn't equal to"; } +}; +template +class NeMatcher : public ComparisonBase, Rhs, AnyNe> { + public: + explicit NeMatcher(const Rhs& rhs) + : ComparisonBase, Rhs, AnyNe>(rhs) {} + static const char* Desc() { return "isn't equal to"; } + static const char* NegatedDesc() { return "is equal to"; } +}; +template +class LtMatcher : public ComparisonBase, Rhs, AnyLt> { + public: + explicit LtMatcher(const Rhs& rhs) + : ComparisonBase, Rhs, AnyLt>(rhs) {} + static const char* Desc() { return "is <"; } + static const char* NegatedDesc() { return "isn't <"; } +}; +template +class GtMatcher : public ComparisonBase, Rhs, AnyGt> { + public: + explicit GtMatcher(const Rhs& rhs) + : ComparisonBase, Rhs, AnyGt>(rhs) {} + static const char* Desc() { return "is >"; } + static const char* NegatedDesc() { return "isn't >"; } +}; +template +class LeMatcher : public ComparisonBase, Rhs, AnyLe> { + public: + explicit LeMatcher(const Rhs& rhs) + : ComparisonBase, Rhs, AnyLe>(rhs) {} + static const char* Desc() { return "is <="; } + static const char* NegatedDesc() { return "isn't <="; } +}; +template +class GeMatcher : public ComparisonBase, Rhs, AnyGe> { + public: + explicit GeMatcher(const Rhs& rhs) + : ComparisonBase, Rhs, AnyGe>(rhs) {} + static const char* Desc() { return "is >="; } + static const char* NegatedDesc() { return "isn't >="; } +}; + +template ::value>::type> +using StringLike = T; + +// Implements polymorphic matchers MatchesRegex(regex) and +// ContainsRegex(regex), which can be used as a Matcher as long as +// T can be converted to a string. +class MatchesRegexMatcher { + public: + MatchesRegexMatcher(const RE* regex, bool full_match) + : regex_(regex), full_match_(full_match) {} + +#if GTEST_INTERNAL_HAS_STRING_VIEW + bool MatchAndExplain(const internal::StringView& s, + MatchResultListener* listener) const { + return MatchAndExplain(std::string(s), listener); + } +#endif // GTEST_INTERNAL_HAS_STRING_VIEW + + // Accepts pointer types, particularly: + // const char* + // char* + // const wchar_t* + // wchar_t* + template + bool MatchAndExplain(CharType* s, MatchResultListener* listener) const { + return s != nullptr && MatchAndExplain(std::string(s), listener); + } + + // Matches anything that can convert to std::string. + // + // This is a template, not just a plain function with const std::string&, + // because absl::string_view has some interfering non-explicit constructors. + template + bool MatchAndExplain(const MatcheeStringType& s, + MatchResultListener* /* listener */) const { + const std::string s2(s); + return full_match_ ? RE::FullMatch(s2, *regex_) + : RE::PartialMatch(s2, *regex_); + } + + void DescribeTo(::std::ostream* os) const { + *os << (full_match_ ? "matches" : "contains") << " regular expression "; + UniversalPrinter::Print(regex_->pattern(), os); + } + + void DescribeNegationTo(::std::ostream* os) const { + *os << "doesn't " << (full_match_ ? "match" : "contain") + << " regular expression "; + UniversalPrinter::Print(regex_->pattern(), os); + } + + private: + const std::shared_ptr regex_; + const bool full_match_; +}; +} // namespace internal + +// Matches a string that fully matches regular expression 'regex'. +// The matcher takes ownership of 'regex'. +inline PolymorphicMatcher MatchesRegex( + const internal::RE* regex) { + return MakePolymorphicMatcher(internal::MatchesRegexMatcher(regex, true)); +} +template +PolymorphicMatcher MatchesRegex( + const internal::StringLike& regex) { + return MatchesRegex(new internal::RE(std::string(regex))); +} + +// Matches a string that contains regular expression 'regex'. +// The matcher takes ownership of 'regex'. +inline PolymorphicMatcher ContainsRegex( + const internal::RE* regex) { + return MakePolymorphicMatcher(internal::MatchesRegexMatcher(regex, false)); +} +template +PolymorphicMatcher ContainsRegex( + const internal::StringLike& regex) { + return ContainsRegex(new internal::RE(std::string(regex))); +} + +// Creates a polymorphic matcher that matches anything equal to x. +// Note: if the parameter of Eq() were declared as const T&, Eq("foo") +// wouldn't compile. +template +inline internal::EqMatcher Eq(T x) { + return internal::EqMatcher(x); +} + +// Constructs a Matcher from a 'value' of type T. The constructed +// matcher matches any value that's equal to 'value'. +template +Matcher::Matcher(T value) { + *this = Eq(value); +} + +// Creates a monomorphic matcher that matches anything with type Lhs +// and equal to rhs. A user may need to use this instead of Eq(...) +// in order to resolve an overloading ambiguity. +// +// TypedEq(x) is just a convenient short-hand for Matcher(Eq(x)) +// or Matcher(x), but more readable than the latter. +// +// We could define similar monomorphic matchers for other comparison +// operations (e.g. TypedLt, TypedGe, and etc), but decided not to do +// it yet as those are used much less than Eq() in practice. A user +// can always write Matcher(Lt(5)) to be explicit about the type, +// for example. +template +inline Matcher TypedEq(const Rhs& rhs) { + return Eq(rhs); +} + +// Creates a polymorphic matcher that matches anything >= x. +template +inline internal::GeMatcher Ge(Rhs x) { + return internal::GeMatcher(x); +} + +// Creates a polymorphic matcher that matches anything > x. +template +inline internal::GtMatcher Gt(Rhs x) { + return internal::GtMatcher(x); +} + +// Creates a polymorphic matcher that matches anything <= x. +template +inline internal::LeMatcher Le(Rhs x) { + return internal::LeMatcher(x); +} + +// Creates a polymorphic matcher that matches anything < x. +template +inline internal::LtMatcher Lt(Rhs x) { + return internal::LtMatcher(x); +} + +// Creates a polymorphic matcher that matches anything != x. +template +inline internal::NeMatcher Ne(Rhs x) { + return internal::NeMatcher(x); +} +} // namespace testing + +GTEST_DISABLE_MSC_WARNINGS_POP_() // 4251 5046 + +#endif // GOOGLETEST_INCLUDE_GTEST_GTEST_MATCHERS_H_ diff --git a/include/gtest/gtest-message.h b/include/gtest/gtest-message.h new file mode 100644 index 0000000..6c8bf90 --- /dev/null +++ b/include/gtest/gtest-message.h @@ -0,0 +1,218 @@ +// Copyright 2005, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// The Google C++ Testing and Mocking Framework (Google Test) +// +// This header file defines the Message class. +// +// IMPORTANT NOTE: Due to limitation of the C++ language, we have to +// leave some internal implementation details in this header file. +// They are clearly marked by comments like this: +// +// // INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. +// +// Such code is NOT meant to be used by a user directly, and is subject +// to CHANGE WITHOUT NOTICE. Therefore DO NOT DEPEND ON IT in a user +// program! + +// IWYU pragma: private, include "gtest/gtest.h" +// IWYU pragma: friend gtest/.* +// IWYU pragma: friend gmock/.* + +#ifndef GOOGLETEST_INCLUDE_GTEST_GTEST_MESSAGE_H_ +#define GOOGLETEST_INCLUDE_GTEST_GTEST_MESSAGE_H_ + +#include +#include +#include + +#include "gtest/internal/gtest-port.h" + +GTEST_DISABLE_MSC_WARNINGS_PUSH_(4251 \ +/* class A needs to have dll-interface to be used by clients of class B */) + +// Ensures that there is at least one operator<< in the global namespace. +// See Message& operator<<(...) below for why. +void operator<<(const testing::internal::Secret&, int); + +namespace testing { + +// The Message class works like an ostream repeater. +// +// Typical usage: +// +// 1. You stream a bunch of values to a Message object. +// It will remember the text in a stringstream. +// 2. Then you stream the Message object to an ostream. +// This causes the text in the Message to be streamed +// to the ostream. +// +// For example; +// +// testing::Message foo; +// foo << 1 << " != " << 2; +// std::cout << foo; +// +// will print "1 != 2". +// +// Message is not intended to be inherited from. In particular, its +// destructor is not virtual. +// +// Note that stringstream behaves differently in gcc and in MSVC. You +// can stream a NULL char pointer to it in the former, but not in the +// latter (it causes an access violation if you do). The Message +// class hides this difference by treating a NULL char pointer as +// "(null)". +class GTEST_API_ Message { + private: + // The type of basic IO manipulators (endl, ends, and flush) for + // narrow streams. + typedef std::ostream& (*BasicNarrowIoManip)(std::ostream&); + + public: + // Constructs an empty Message. + Message(); + + // Copy constructor. + Message(const Message& msg) : ss_(new ::std::stringstream) { // NOLINT + *ss_ << msg.GetString(); + } + + // Constructs a Message from a C-string. + explicit Message(const char* str) : ss_(new ::std::stringstream) { + *ss_ << str; + } + + // Streams a non-pointer value to this object. + template + inline Message& operator<<(const T& val) { + // Some libraries overload << for STL containers. These + // overloads are defined in the global namespace instead of ::std. + // + // C++'s symbol lookup rule (i.e. Koenig lookup) says that these + // overloads are visible in either the std namespace or the global + // namespace, but not other namespaces, including the testing + // namespace which Google Test's Message class is in. + // + // To allow STL containers (and other types that has a << operator + // defined in the global namespace) to be used in Google Test + // assertions, testing::Message must access the custom << operator + // from the global namespace. With this using declaration, + // overloads of << defined in the global namespace and those + // visible via Koenig lookup are both exposed in this function. + using ::operator<<; + *ss_ << val; + return *this; + } + + // Streams a pointer value to this object. + // + // This function is an overload of the previous one. When you + // stream a pointer to a Message, this definition will be used as it + // is more specialized. (The C++ Standard, section + // [temp.func.order].) If you stream a non-pointer, then the + // previous definition will be used. + // + // The reason for this overload is that streaming a NULL pointer to + // ostream is undefined behavior. Depending on the compiler, you + // may get "0", "(nil)", "(null)", or an access violation. To + // ensure consistent result across compilers, we always treat NULL + // as "(null)". + template + inline Message& operator<<(T* const& pointer) { // NOLINT + if (pointer == nullptr) { + *ss_ << "(null)"; + } else { + *ss_ << pointer; + } + return *this; + } + + // Since the basic IO manipulators are overloaded for both narrow + // and wide streams, we have to provide this specialized definition + // of operator <<, even though its body is the same as the + // templatized version above. Without this definition, streaming + // endl or other basic IO manipulators to Message will confuse the + // compiler. + Message& operator<<(BasicNarrowIoManip val) { + *ss_ << val; + return *this; + } + + // Instead of 1/0, we want to see true/false for bool values. + Message& operator<<(bool b) { return *this << (b ? "true" : "false"); } + + // These two overloads allow streaming a wide C string to a Message + // using the UTF-8 encoding. + Message& operator<<(const wchar_t* wide_c_str); + Message& operator<<(wchar_t* wide_c_str); + +#if GTEST_HAS_STD_WSTRING + // Converts the given wide string to a narrow string using the UTF-8 + // encoding, and streams the result to this Message object. + Message& operator<<(const ::std::wstring& wstr); +#endif // GTEST_HAS_STD_WSTRING + + // Gets the text streamed to this object so far as an std::string. + // Each '\0' character in the buffer is replaced with "\\0". + // + // INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. + std::string GetString() const; + + private: + // We'll hold the text streamed to this object here. + const std::unique_ptr< ::std::stringstream> ss_; + + // We declare (but don't implement) this to prevent the compiler + // from implementing the assignment operator. + void operator=(const Message&); +}; + +// Streams a Message to an ostream. +inline std::ostream& operator<<(std::ostream& os, const Message& sb) { + return os << sb.GetString(); +} + +namespace internal { + +// Converts a streamable value to an std::string. A NULL pointer is +// converted to "(null)". When the input value is a ::string, +// ::std::string, ::wstring, or ::std::wstring object, each NUL +// character in it is replaced with "\\0". +template +std::string StreamableToString(const T& streamable) { + return (Message() << streamable).GetString(); +} + +} // namespace internal +} // namespace testing + +GTEST_DISABLE_MSC_WARNINGS_POP_() // 4251 + +#endif // GOOGLETEST_INCLUDE_GTEST_GTEST_MESSAGE_H_ diff --git a/include/gtest/gtest-param-test.h b/include/gtest/gtest-param-test.h new file mode 100644 index 0000000..10d6a07 --- /dev/null +++ b/include/gtest/gtest-param-test.h @@ -0,0 +1,545 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Macros and functions for implementing parameterized tests +// in Google C++ Testing and Mocking Framework (Google Test) + +// IWYU pragma: private, include "gtest/gtest.h" +// IWYU pragma: friend gtest/.* +// IWYU pragma: friend gmock/.* + +#ifndef GOOGLETEST_INCLUDE_GTEST_GTEST_PARAM_TEST_H_ +#define GOOGLETEST_INCLUDE_GTEST_GTEST_PARAM_TEST_H_ + +// Value-parameterized tests allow you to test your code with different +// parameters without writing multiple copies of the same test. +// +// Here is how you use value-parameterized tests: + +#if 0 + +// To write value-parameterized tests, first you should define a fixture +// class. It is usually derived from testing::TestWithParam (see below for +// another inheritance scheme that's sometimes useful in more complicated +// class hierarchies), where the type of your parameter values. +// TestWithParam is itself derived from testing::Test. T can be any +// copyable type. If it's a raw pointer, you are responsible for managing the +// lifespan of the pointed values. + +class FooTest : public ::testing::TestWithParam { + // You can implement all the usual class fixture members here. +}; + +// Then, use the TEST_P macro to define as many parameterized tests +// for this fixture as you want. The _P suffix is for "parameterized" +// or "pattern", whichever you prefer to think. + +TEST_P(FooTest, DoesBlah) { + // Inside a test, access the test parameter with the GetParam() method + // of the TestWithParam class: + EXPECT_TRUE(foo.Blah(GetParam())); + ... +} + +TEST_P(FooTest, HasBlahBlah) { + ... +} + +// Finally, you can use INSTANTIATE_TEST_SUITE_P to instantiate the test +// case with any set of parameters you want. Google Test defines a number +// of functions for generating test parameters. They return what we call +// (surprise!) parameter generators. Here is a summary of them, which +// are all in the testing namespace: +// +// +// Range(begin, end [, step]) - Yields values {begin, begin+step, +// begin+step+step, ...}. The values do not +// include end. step defaults to 1. +// Values(v1, v2, ..., vN) - Yields values {v1, v2, ..., vN}. +// ValuesIn(container) - Yields values from a C-style array, an STL +// ValuesIn(begin,end) container, or an iterator range [begin, end). +// Bool() - Yields sequence {false, true}. +// Combine(g1, g2, ..., gN) - Yields all combinations (the Cartesian product +// for the math savvy) of the values generated +// by the N generators. +// +// For more details, see comments at the definitions of these functions below +// in this file. +// +// The following statement will instantiate tests from the FooTest test suite +// each with parameter values "meeny", "miny", and "moe". + +INSTANTIATE_TEST_SUITE_P(InstantiationName, + FooTest, + Values("meeny", "miny", "moe")); + +// To distinguish different instances of the pattern, (yes, you +// can instantiate it more than once) the first argument to the +// INSTANTIATE_TEST_SUITE_P macro is a prefix that will be added to the +// actual test suite name. Remember to pick unique prefixes for different +// instantiations. The tests from the instantiation above will have +// these names: +// +// * InstantiationName/FooTest.DoesBlah/0 for "meeny" +// * InstantiationName/FooTest.DoesBlah/1 for "miny" +// * InstantiationName/FooTest.DoesBlah/2 for "moe" +// * InstantiationName/FooTest.HasBlahBlah/0 for "meeny" +// * InstantiationName/FooTest.HasBlahBlah/1 for "miny" +// * InstantiationName/FooTest.HasBlahBlah/2 for "moe" +// +// You can use these names in --gtest_filter. +// +// This statement will instantiate all tests from FooTest again, each +// with parameter values "cat" and "dog": + +const char* pets[] = {"cat", "dog"}; +INSTANTIATE_TEST_SUITE_P(AnotherInstantiationName, FooTest, ValuesIn(pets)); + +// The tests from the instantiation above will have these names: +// +// * AnotherInstantiationName/FooTest.DoesBlah/0 for "cat" +// * AnotherInstantiationName/FooTest.DoesBlah/1 for "dog" +// * AnotherInstantiationName/FooTest.HasBlahBlah/0 for "cat" +// * AnotherInstantiationName/FooTest.HasBlahBlah/1 for "dog" +// +// Please note that INSTANTIATE_TEST_SUITE_P will instantiate all tests +// in the given test suite, whether their definitions come before or +// AFTER the INSTANTIATE_TEST_SUITE_P statement. +// +// Please also note that generator expressions (including parameters to the +// generators) are evaluated in InitGoogleTest(), after main() has started. +// This allows the user on one hand, to adjust generator parameters in order +// to dynamically determine a set of tests to run and on the other hand, +// give the user a chance to inspect the generated tests with Google Test +// reflection API before RUN_ALL_TESTS() is executed. +// +// You can see samples/sample7_unittest.cc and samples/sample8_unittest.cc +// for more examples. +// +// In the future, we plan to publish the API for defining new parameter +// generators. But for now this interface remains part of the internal +// implementation and is subject to change. +// +// +// A parameterized test fixture must be derived from testing::Test and from +// testing::WithParamInterface, where T is the type of the parameter +// values. Inheriting from TestWithParam satisfies that requirement because +// TestWithParam inherits from both Test and WithParamInterface. In more +// complicated hierarchies, however, it is occasionally useful to inherit +// separately from Test and WithParamInterface. For example: + +class BaseTest : public ::testing::Test { + // You can inherit all the usual members for a non-parameterized test + // fixture here. +}; + +class DerivedTest : public BaseTest, public ::testing::WithParamInterface { + // The usual test fixture members go here too. +}; + +TEST_F(BaseTest, HasFoo) { + // This is an ordinary non-parameterized test. +} + +TEST_P(DerivedTest, DoesBlah) { + // GetParam works just the same here as if you inherit from TestWithParam. + EXPECT_TRUE(foo.Blah(GetParam())); +} + +#endif // 0 + +#include +#include + +#include "gtest/internal/gtest-internal.h" +#include "gtest/internal/gtest-param-util.h" +#include "gtest/internal/gtest-port.h" + +namespace testing { + +// Functions producing parameter generators. +// +// Google Test uses these generators to produce parameters for value- +// parameterized tests. When a parameterized test suite is instantiated +// with a particular generator, Google Test creates and runs tests +// for each element in the sequence produced by the generator. +// +// In the following sample, tests from test suite FooTest are instantiated +// each three times with parameter values 3, 5, and 8: +// +// class FooTest : public TestWithParam { ... }; +// +// TEST_P(FooTest, TestThis) { +// } +// TEST_P(FooTest, TestThat) { +// } +// INSTANTIATE_TEST_SUITE_P(TestSequence, FooTest, Values(3, 5, 8)); +// + +// Range() returns generators providing sequences of values in a range. +// +// Synopsis: +// Range(start, end) +// - returns a generator producing a sequence of values {start, start+1, +// start+2, ..., }. +// Range(start, end, step) +// - returns a generator producing a sequence of values {start, start+step, +// start+step+step, ..., }. +// Notes: +// * The generated sequences never include end. For example, Range(1, 5) +// returns a generator producing a sequence {1, 2, 3, 4}. Range(1, 9, 2) +// returns a generator producing {1, 3, 5, 7}. +// * start and end must have the same type. That type may be any integral or +// floating-point type or a user defined type satisfying these conditions: +// * It must be assignable (have operator=() defined). +// * It must have operator+() (operator+(int-compatible type) for +// two-operand version). +// * It must have operator<() defined. +// Elements in the resulting sequences will also have that type. +// * Condition start < end must be satisfied in order for resulting sequences +// to contain any elements. +// +template +internal::ParamGenerator Range(T start, T end, IncrementT step) { + return internal::ParamGenerator( + new internal::RangeGenerator(start, end, step)); +} + +template +internal::ParamGenerator Range(T start, T end) { + return Range(start, end, 1); +} + +// ValuesIn() function allows generation of tests with parameters coming from +// a container. +// +// Synopsis: +// ValuesIn(const T (&array)[N]) +// - returns a generator producing sequences with elements from +// a C-style array. +// ValuesIn(const Container& container) +// - returns a generator producing sequences with elements from +// an STL-style container. +// ValuesIn(Iterator begin, Iterator end) +// - returns a generator producing sequences with elements from +// a range [begin, end) defined by a pair of STL-style iterators. These +// iterators can also be plain C pointers. +// +// Please note that ValuesIn copies the values from the containers +// passed in and keeps them to generate tests in RUN_ALL_TESTS(). +// +// Examples: +// +// This instantiates tests from test suite StringTest +// each with C-string values of "foo", "bar", and "baz": +// +// const char* strings[] = {"foo", "bar", "baz"}; +// INSTANTIATE_TEST_SUITE_P(StringSequence, StringTest, ValuesIn(strings)); +// +// This instantiates tests from test suite StlStringTest +// each with STL strings with values "a" and "b": +// +// ::std::vector< ::std::string> GetParameterStrings() { +// ::std::vector< ::std::string> v; +// v.push_back("a"); +// v.push_back("b"); +// return v; +// } +// +// INSTANTIATE_TEST_SUITE_P(CharSequence, +// StlStringTest, +// ValuesIn(GetParameterStrings())); +// +// +// This will also instantiate tests from CharTest +// each with parameter values 'a' and 'b': +// +// ::std::list GetParameterChars() { +// ::std::list list; +// list.push_back('a'); +// list.push_back('b'); +// return list; +// } +// ::std::list l = GetParameterChars(); +// INSTANTIATE_TEST_SUITE_P(CharSequence2, +// CharTest, +// ValuesIn(l.begin(), l.end())); +// +template +internal::ParamGenerator< + typename std::iterator_traits::value_type> +ValuesIn(ForwardIterator begin, ForwardIterator end) { + typedef typename std::iterator_traits::value_type ParamType; + return internal::ParamGenerator( + new internal::ValuesInIteratorRangeGenerator(begin, end)); +} + +template +internal::ParamGenerator ValuesIn(const T (&array)[N]) { + return ValuesIn(array, array + N); +} + +template +internal::ParamGenerator ValuesIn( + const Container& container) { + return ValuesIn(container.begin(), container.end()); +} + +// Values() allows generating tests from explicitly specified list of +// parameters. +// +// Synopsis: +// Values(T v1, T v2, ..., T vN) +// - returns a generator producing sequences with elements v1, v2, ..., vN. +// +// For example, this instantiates tests from test suite BarTest each +// with values "one", "two", and "three": +// +// INSTANTIATE_TEST_SUITE_P(NumSequence, +// BarTest, +// Values("one", "two", "three")); +// +// This instantiates tests from test suite BazTest each with values 1, 2, 3.5. +// The exact type of values will depend on the type of parameter in BazTest. +// +// INSTANTIATE_TEST_SUITE_P(FloatingNumbers, BazTest, Values(1, 2, 3.5)); +// +// +template +internal::ValueArray Values(T... v) { + return internal::ValueArray(std::move(v)...); +} + +// Bool() allows generating tests with parameters in a set of (false, true). +// +// Synopsis: +// Bool() +// - returns a generator producing sequences with elements {false, true}. +// +// It is useful when testing code that depends on Boolean flags. Combinations +// of multiple flags can be tested when several Bool()'s are combined using +// Combine() function. +// +// In the following example all tests in the test suite FlagDependentTest +// will be instantiated twice with parameters false and true. +// +// class FlagDependentTest : public testing::TestWithParam { +// virtual void SetUp() { +// external_flag = GetParam(); +// } +// } +// INSTANTIATE_TEST_SUITE_P(BoolSequence, FlagDependentTest, Bool()); +// +inline internal::ParamGenerator Bool() { return Values(false, true); } + +// Combine() allows the user to combine two or more sequences to produce +// values of a Cartesian product of those sequences' elements. +// +// Synopsis: +// Combine(gen1, gen2, ..., genN) +// - returns a generator producing sequences with elements coming from +// the Cartesian product of elements from the sequences generated by +// gen1, gen2, ..., genN. The sequence elements will have a type of +// std::tuple where T1, T2, ..., TN are the types +// of elements from sequences produces by gen1, gen2, ..., genN. +// +// Example: +// +// This will instantiate tests in test suite AnimalTest each one with +// the parameter values tuple("cat", BLACK), tuple("cat", WHITE), +// tuple("dog", BLACK), and tuple("dog", WHITE): +// +// enum Color { BLACK, GRAY, WHITE }; +// class AnimalTest +// : public testing::TestWithParam > {...}; +// +// TEST_P(AnimalTest, AnimalLooksNice) {...} +// +// INSTANTIATE_TEST_SUITE_P(AnimalVariations, AnimalTest, +// Combine(Values("cat", "dog"), +// Values(BLACK, WHITE))); +// +// This will instantiate tests in FlagDependentTest with all variations of two +// Boolean flags: +// +// class FlagDependentTest +// : public testing::TestWithParam > { +// virtual void SetUp() { +// // Assigns external_flag_1 and external_flag_2 values from the tuple. +// std::tie(external_flag_1, external_flag_2) = GetParam(); +// } +// }; +// +// TEST_P(FlagDependentTest, TestFeature1) { +// // Test your code using external_flag_1 and external_flag_2 here. +// } +// INSTANTIATE_TEST_SUITE_P(TwoBoolSequence, FlagDependentTest, +// Combine(Bool(), Bool())); +// +template +internal::CartesianProductHolder Combine(const Generator&... g) { + return internal::CartesianProductHolder(g...); +} + +// ConvertGenerator() wraps a parameter generator in order to cast each prduced +// value through a known type before supplying it to the test suite +// +// Synopsis: +// ConvertGenerator(gen) +// - returns a generator producing the same elements as generated by gen, but +// each element is static_cast to type T before being returned +// +// It is useful when using the Combine() function to get the generated +// parameters in a custom type instead of std::tuple +// +// Example: +// +// This will instantiate tests in test suite AnimalTest each one with +// the parameter values tuple("cat", BLACK), tuple("cat", WHITE), +// tuple("dog", BLACK), and tuple("dog", WHITE): +// +// enum Color { BLACK, GRAY, WHITE }; +// struct ParamType { +// using TupleT = std::tuple; +// std::string animal; +// Color color; +// ParamType(TupleT t) : animal(std::get<0>(t)), color(std::get<1>(t)) {} +// }; +// class AnimalTest +// : public testing::TestWithParam {...}; +// +// TEST_P(AnimalTest, AnimalLooksNice) {...} +// +// INSTANTIATE_TEST_SUITE_P(AnimalVariations, AnimalTest, +// ConvertGenerator( +// Combine(Values("cat", "dog"), +// Values(BLACK, WHITE)))); +// +template +internal::ParamConverterGenerator ConvertGenerator( + internal::ParamGenerator gen) { + return internal::ParamConverterGenerator(gen); +} + +#define TEST_P(test_suite_name, test_name) \ + class GTEST_TEST_CLASS_NAME_(test_suite_name, test_name) \ + : public test_suite_name, private ::testing::internal::GTestNonCopyable {\ + public: \ + GTEST_TEST_CLASS_NAME_(test_suite_name, test_name)() {} \ + void TestBody() override; \ + \ + private: \ + static int AddToRegistry() { \ + ::testing::UnitTest::GetInstance() \ + ->parameterized_test_registry() \ + .GetTestSuitePatternHolder( \ + GTEST_STRINGIFY_(test_suite_name), \ + ::testing::internal::CodeLocation(__FILE__, __LINE__)) \ + ->AddTestPattern( \ + GTEST_STRINGIFY_(test_suite_name), GTEST_STRINGIFY_(test_name), \ + new ::testing::internal::TestMetaFactory(), \ + ::testing::internal::CodeLocation(__FILE__, __LINE__)); \ + return 0; \ + } \ + static int gtest_registering_dummy_ GTEST_ATTRIBUTE_UNUSED_; \ + }; \ + int GTEST_TEST_CLASS_NAME_(test_suite_name, \ + test_name)::gtest_registering_dummy_ = \ + GTEST_TEST_CLASS_NAME_(test_suite_name, test_name)::AddToRegistry(); \ + void GTEST_TEST_CLASS_NAME_(test_suite_name, test_name)::TestBody() + +// The last argument to INSTANTIATE_TEST_SUITE_P allows the user to specify +// generator and an optional function or functor that generates custom test name +// suffixes based on the test parameters. Such a function or functor should +// accept one argument of type testing::TestParamInfo, and +// return std::string. +// +// testing::PrintToStringParamName is a builtin test suffix generator that +// returns the value of testing::PrintToString(GetParam()). +// +// Note: test names must be non-empty, unique, and may only contain ASCII +// alphanumeric characters or underscore. Because PrintToString adds quotes +// to std::string and C strings, it won't work for these types. + +#define GTEST_EXPAND_(arg) arg +#define GTEST_GET_FIRST_(first, ...) first +#define GTEST_GET_SECOND_(first, second, ...) second + +#define INSTANTIATE_TEST_SUITE_P(prefix, test_suite_name, ...) \ + static ::testing::internal::ParamGenerator \ + gtest_##prefix##test_suite_name##_EvalGenerator_() { \ + return GTEST_EXPAND_(GTEST_GET_FIRST_(__VA_ARGS__, DUMMY_PARAM_)); \ + } \ + static ::std::string gtest_##prefix##test_suite_name##_EvalGenerateName_( \ + const ::testing::TestParamInfo& info) { \ + if (::testing::internal::AlwaysFalse()) { \ + ::testing::internal::TestNotEmpty(GTEST_EXPAND_(GTEST_GET_SECOND_( \ + __VA_ARGS__, \ + ::testing::internal::DefaultParamName, \ + DUMMY_PARAM_))); \ + auto t = std::make_tuple(__VA_ARGS__); \ + static_assert(std::tuple_size::value <= 2, \ + "Too Many Args!"); \ + } \ + return ((GTEST_EXPAND_(GTEST_GET_SECOND_( \ + __VA_ARGS__, \ + ::testing::internal::DefaultParamName, \ + DUMMY_PARAM_))))(info); \ + } \ + static int gtest_##prefix##test_suite_name##_dummy_ \ + GTEST_ATTRIBUTE_UNUSED_ = \ + ::testing::UnitTest::GetInstance() \ + ->parameterized_test_registry() \ + .GetTestSuitePatternHolder( \ + GTEST_STRINGIFY_(test_suite_name), \ + ::testing::internal::CodeLocation(__FILE__, __LINE__)) \ + ->AddTestSuiteInstantiation( \ + GTEST_STRINGIFY_(prefix), \ + >est_##prefix##test_suite_name##_EvalGenerator_, \ + >est_##prefix##test_suite_name##_EvalGenerateName_, \ + __FILE__, __LINE__) + +// Allow Marking a Parameterized test class as not needing to be instantiated. +#define GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(T) \ + namespace gtest_do_not_use_outside_namespace_scope {} \ + static const ::testing::internal::MarkAsIgnored gtest_allow_ignore_##T( \ + GTEST_STRINGIFY_(T)) + +// Legacy API is deprecated but still available +#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_ +#define INSTANTIATE_TEST_CASE_P \ + static_assert(::testing::internal::InstantiateTestCase_P_IsDeprecated(), \ + ""); \ + INSTANTIATE_TEST_SUITE_P +#endif // GTEST_REMOVE_LEGACY_TEST_CASEAPI_ + +} // namespace testing + +#endif // GOOGLETEST_INCLUDE_GTEST_GTEST_PARAM_TEST_H_ diff --git a/include/gtest/gtest-printers.h b/include/gtest/gtest-printers.h new file mode 100644 index 0000000..89215e6 --- /dev/null +++ b/include/gtest/gtest-printers.h @@ -0,0 +1,1055 @@ +// Copyright 2007, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Google Test - The Google C++ Testing and Mocking Framework +// +// This file implements a universal value printer that can print a +// value of any type T: +// +// void ::testing::internal::UniversalPrinter::Print(value, ostream_ptr); +// +// A user can teach this function how to print a class type T by +// defining either operator<<() or PrintTo() in the namespace that +// defines T. More specifically, the FIRST defined function in the +// following list will be used (assuming T is defined in namespace +// foo): +// +// 1. foo::PrintTo(const T&, ostream*) +// 2. operator<<(ostream&, const T&) defined in either foo or the +// global namespace. +// +// However if T is an STL-style container then it is printed element-wise +// unless foo::PrintTo(const T&, ostream*) is defined. Note that +// operator<<() is ignored for container types. +// +// If none of the above is defined, it will print the debug string of +// the value if it is a protocol buffer, or print the raw bytes in the +// value otherwise. +// +// To aid debugging: when T is a reference type, the address of the +// value is also printed; when T is a (const) char pointer, both the +// pointer value and the NUL-terminated string it points to are +// printed. +// +// We also provide some convenient wrappers: +// +// // Prints a value to a string. For a (const or not) char +// // pointer, the NUL-terminated string (but not the pointer) is +// // printed. +// std::string ::testing::PrintToString(const T& value); +// +// // Prints a value tersely: for a reference type, the referenced +// // value (but not the address) is printed; for a (const or not) char +// // pointer, the NUL-terminated string (but not the pointer) is +// // printed. +// void ::testing::internal::UniversalTersePrint(const T& value, ostream*); +// +// // Prints value using the type inferred by the compiler. The difference +// // from UniversalTersePrint() is that this function prints both the +// // pointer and the NUL-terminated string for a (const or not) char pointer. +// void ::testing::internal::UniversalPrint(const T& value, ostream*); +// +// // Prints the fields of a tuple tersely to a string vector, one +// // element for each field. Tuple support must be enabled in +// // gtest-port.h. +// std::vector UniversalTersePrintTupleFieldsToStrings( +// const Tuple& value); +// +// Known limitation: +// +// The print primitives print the elements of an STL-style container +// using the compiler-inferred type of *iter where iter is a +// const_iterator of the container. When const_iterator is an input +// iterator but not a forward iterator, this inferred type may not +// match value_type, and the print output may be incorrect. In +// practice, this is rarely a problem as for most containers +// const_iterator is a forward iterator. We'll fix this if there's an +// actual need for it. Note that this fix cannot rely on value_type +// being defined as many user-defined container types don't have +// value_type. + +// IWYU pragma: private, include "gtest/gtest.h" +// IWYU pragma: friend gtest/.* +// IWYU pragma: friend gmock/.* + +#ifndef GOOGLETEST_INCLUDE_GTEST_GTEST_PRINTERS_H_ +#define GOOGLETEST_INCLUDE_GTEST_GTEST_PRINTERS_H_ + +#include +#include +#include // NOLINT +#include +#include +#include +#include +#include +#include + +#include "gtest/internal/gtest-internal.h" +#include "gtest/internal/gtest-port.h" + +namespace testing { + +// Definitions in the internal* namespaces are subject to change without notice. +// DO NOT USE THEM IN USER CODE! +namespace internal { + +template +void UniversalPrint(const T& value, ::std::ostream* os); + +// Used to print an STL-style container when the user doesn't define +// a PrintTo() for it. +struct ContainerPrinter { + template (0)) == sizeof(IsContainer)) && + !IsRecursiveContainer::value>::type> + static void PrintValue(const T& container, std::ostream* os) { + const size_t kMaxCount = 32; // The maximum number of elements to print. + *os << '{'; + size_t count = 0; + for (auto&& elem : container) { + if (count > 0) { + *os << ','; + if (count == kMaxCount) { // Enough has been printed. + *os << " ..."; + break; + } + } + *os << ' '; + // We cannot call PrintTo(elem, os) here as PrintTo() doesn't + // handle `elem` being a native array. + internal::UniversalPrint(elem, os); + ++count; + } + + if (count > 0) { + *os << ' '; + } + *os << '}'; + } +}; + +// Used to print a pointer that is neither a char pointer nor a member +// pointer, when the user doesn't define PrintTo() for it. (A member +// variable pointer or member function pointer doesn't really point to +// a location in the address space. Their representation is +// implementation-defined. Therefore they will be printed as raw +// bytes.) +struct FunctionPointerPrinter { + template ::value>::type> + static void PrintValue(T* p, ::std::ostream* os) { + if (p == nullptr) { + *os << "NULL"; + } else { + // T is a function type, so '*os << p' doesn't do what we want + // (it just prints p as bool). We want to print p as a const + // void*. + *os << reinterpret_cast(p); + } + } +}; + +struct PointerPrinter { + template + static void PrintValue(T* p, ::std::ostream* os) { + if (p == nullptr) { + *os << "NULL"; + } else { + // T is not a function type. We just call << to print p, + // relying on ADL to pick up user-defined << for their pointer + // types, if any. + *os << p; + } + } +}; + +namespace internal_stream_operator_without_lexical_name_lookup { + +// The presence of an operator<< here will terminate lexical scope lookup +// straight away (even though it cannot be a match because of its argument +// types). Thus, the two operator<< calls in StreamPrinter will find only ADL +// candidates. +struct LookupBlocker {}; +void operator<<(LookupBlocker, LookupBlocker); + +struct StreamPrinter { + template ::value>::type, + // Only accept types for which we can find a streaming operator via + // ADL (possibly involving implicit conversions). + typename = decltype(std::declval() + << std::declval())> + static void PrintValue(const T& value, ::std::ostream* os) { + // Call streaming operator found by ADL, possibly with implicit conversions + // of the arguments. + *os << value; + } +}; + +} // namespace internal_stream_operator_without_lexical_name_lookup + +struct ProtobufPrinter { + // We print a protobuf using its ShortDebugString() when the string + // doesn't exceed this many characters; otherwise we print it using + // DebugString() for better readability. + static const size_t kProtobufOneLinerMaxLength = 50; + + template ::value>::type> + static void PrintValue(const T& value, ::std::ostream* os) { + std::string pretty_str = value.ShortDebugString(); + if (pretty_str.length() > kProtobufOneLinerMaxLength) { + pretty_str = "\n" + value.DebugString(); + } + *os << ("<" + pretty_str + ">"); + } +}; + +struct ConvertibleToIntegerPrinter { + // Since T has no << operator or PrintTo() but can be implicitly + // converted to BiggestInt, we print it as a BiggestInt. + // + // Most likely T is an enum type (either named or unnamed), in which + // case printing it as an integer is the desired behavior. In case + // T is not an enum, printing it as an integer is the best we can do + // given that it has no user-defined printer. + static void PrintValue(internal::BiggestInt value, ::std::ostream* os) { + *os << value; + } +}; + +struct ConvertibleToStringViewPrinter { +#if GTEST_INTERNAL_HAS_STRING_VIEW + static void PrintValue(internal::StringView value, ::std::ostream* os) { + internal::UniversalPrint(value, os); + } +#endif +}; + +// Prints the given number of bytes in the given object to the given +// ostream. +GTEST_API_ void PrintBytesInObjectTo(const unsigned char* obj_bytes, + size_t count, ::std::ostream* os); +struct RawBytesPrinter { + // SFINAE on `sizeof` to make sure we have a complete type. + template + static void PrintValue(const T& value, ::std::ostream* os) { + PrintBytesInObjectTo( + static_cast( + // Load bearing cast to void* to support iOS + reinterpret_cast(std::addressof(value))), + sizeof(value), os); + } +}; + +struct FallbackPrinter { + template + static void PrintValue(const T&, ::std::ostream* os) { + *os << "(incomplete type)"; + } +}; + +// Try every printer in order and return the first one that works. +template +struct FindFirstPrinter : FindFirstPrinter {}; + +template +struct FindFirstPrinter< + T, decltype(Printer::PrintValue(std::declval(), nullptr)), + Printer, Printers...> { + using type = Printer; +}; + +// Select the best printer in the following order: +// - Print containers (they have begin/end/etc). +// - Print function pointers. +// - Print object pointers. +// - Use the stream operator, if available. +// - Print protocol buffers. +// - Print types convertible to BiggestInt. +// - Print types convertible to StringView, if available. +// - Fallback to printing the raw bytes of the object. +template +void PrintWithFallback(const T& value, ::std::ostream* os) { + using Printer = typename FindFirstPrinter< + T, void, ContainerPrinter, FunctionPointerPrinter, PointerPrinter, + internal_stream_operator_without_lexical_name_lookup::StreamPrinter, + ProtobufPrinter, ConvertibleToIntegerPrinter, + ConvertibleToStringViewPrinter, RawBytesPrinter, FallbackPrinter>::type; + Printer::PrintValue(value, os); +} + +// FormatForComparison::Format(value) formats a +// value of type ToPrint that is an operand of a comparison assertion +// (e.g. ASSERT_EQ). OtherOperand is the type of the other operand in +// the comparison, and is used to help determine the best way to +// format the value. In particular, when the value is a C string +// (char pointer) and the other operand is an STL string object, we +// want to format the C string as a string, since we know it is +// compared by value with the string object. If the value is a char +// pointer but the other operand is not an STL string object, we don't +// know whether the pointer is supposed to point to a NUL-terminated +// string, and thus want to print it as a pointer to be safe. +// +// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. + +// The default case. +template +class FormatForComparison { + public: + static ::std::string Format(const ToPrint& value) { + return ::testing::PrintToString(value); + } +}; + +// Array. +template +class FormatForComparison { + public: + static ::std::string Format(const ToPrint* value) { + return FormatForComparison::Format(value); + } +}; + +// By default, print C string as pointers to be safe, as we don't know +// whether they actually point to a NUL-terminated string. + +#define GTEST_IMPL_FORMAT_C_STRING_AS_POINTER_(CharType) \ + template \ + class FormatForComparison { \ + public: \ + static ::std::string Format(CharType* value) { \ + return ::testing::PrintToString(static_cast(value)); \ + } \ + } + +GTEST_IMPL_FORMAT_C_STRING_AS_POINTER_(char); +GTEST_IMPL_FORMAT_C_STRING_AS_POINTER_(const char); +GTEST_IMPL_FORMAT_C_STRING_AS_POINTER_(wchar_t); +GTEST_IMPL_FORMAT_C_STRING_AS_POINTER_(const wchar_t); +#ifdef __cpp_lib_char8_t +GTEST_IMPL_FORMAT_C_STRING_AS_POINTER_(char8_t); +GTEST_IMPL_FORMAT_C_STRING_AS_POINTER_(const char8_t); +#endif +GTEST_IMPL_FORMAT_C_STRING_AS_POINTER_(char16_t); +GTEST_IMPL_FORMAT_C_STRING_AS_POINTER_(const char16_t); +GTEST_IMPL_FORMAT_C_STRING_AS_POINTER_(char32_t); +GTEST_IMPL_FORMAT_C_STRING_AS_POINTER_(const char32_t); + +#undef GTEST_IMPL_FORMAT_C_STRING_AS_POINTER_ + +// If a C string is compared with an STL string object, we know it's meant +// to point to a NUL-terminated string, and thus can print it as a string. + +#define GTEST_IMPL_FORMAT_C_STRING_AS_STRING_(CharType, OtherStringType) \ + template <> \ + class FormatForComparison { \ + public: \ + static ::std::string Format(CharType* value) { \ + return ::testing::PrintToString(value); \ + } \ + } + +GTEST_IMPL_FORMAT_C_STRING_AS_STRING_(char, ::std::string); +GTEST_IMPL_FORMAT_C_STRING_AS_STRING_(const char, ::std::string); +#ifdef __cpp_lib_char8_t +GTEST_IMPL_FORMAT_C_STRING_AS_STRING_(char8_t, ::std::u8string); +GTEST_IMPL_FORMAT_C_STRING_AS_STRING_(const char8_t, ::std::u8string); +#endif +GTEST_IMPL_FORMAT_C_STRING_AS_STRING_(char16_t, ::std::u16string); +GTEST_IMPL_FORMAT_C_STRING_AS_STRING_(const char16_t, ::std::u16string); +GTEST_IMPL_FORMAT_C_STRING_AS_STRING_(char32_t, ::std::u32string); +GTEST_IMPL_FORMAT_C_STRING_AS_STRING_(const char32_t, ::std::u32string); + +#if GTEST_HAS_STD_WSTRING +GTEST_IMPL_FORMAT_C_STRING_AS_STRING_(wchar_t, ::std::wstring); +GTEST_IMPL_FORMAT_C_STRING_AS_STRING_(const wchar_t, ::std::wstring); +#endif + +#undef GTEST_IMPL_FORMAT_C_STRING_AS_STRING_ + +// Formats a comparison assertion (e.g. ASSERT_EQ, EXPECT_LT, and etc) +// operand to be used in a failure message. The type (but not value) +// of the other operand may affect the format. This allows us to +// print a char* as a raw pointer when it is compared against another +// char* or void*, and print it as a C string when it is compared +// against an std::string object, for example. +// +// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. +template +std::string FormatForComparisonFailureMessage(const T1& value, + const T2& /* other_operand */) { + return FormatForComparison::Format(value); +} + +// UniversalPrinter::Print(value, ostream_ptr) prints the given +// value to the given ostream. The caller must ensure that +// 'ostream_ptr' is not NULL, or the behavior is undefined. +// +// We define UniversalPrinter as a class template (as opposed to a +// function template), as we need to partially specialize it for +// reference types, which cannot be done with function templates. +template +class UniversalPrinter; + +// Prints the given value using the << operator if it has one; +// otherwise prints the bytes in it. This is what +// UniversalPrinter::Print() does when PrintTo() is not specialized +// or overloaded for type T. +// +// A user can override this behavior for a class type Foo by defining +// an overload of PrintTo() in the namespace where Foo is defined. We +// give the user this option as sometimes defining a << operator for +// Foo is not desirable (e.g. the coding style may prevent doing it, +// or there is already a << operator but it doesn't do what the user +// wants). +template +void PrintTo(const T& value, ::std::ostream* os) { + internal::PrintWithFallback(value, os); +} + +// The following list of PrintTo() overloads tells +// UniversalPrinter::Print() how to print standard types (built-in +// types, strings, plain arrays, and pointers). + +// Overloads for various char types. +GTEST_API_ void PrintTo(unsigned char c, ::std::ostream* os); +GTEST_API_ void PrintTo(signed char c, ::std::ostream* os); +inline void PrintTo(char c, ::std::ostream* os) { + // When printing a plain char, we always treat it as unsigned. This + // way, the output won't be affected by whether the compiler thinks + // char is signed or not. + PrintTo(static_cast(c), os); +} + +// Overloads for other simple built-in types. +inline void PrintTo(bool x, ::std::ostream* os) { + *os << (x ? "true" : "false"); +} + +// Overload for wchar_t type. +// Prints a wchar_t as a symbol if it is printable or as its internal +// code otherwise and also as its decimal code (except for L'\0'). +// The L'\0' char is printed as "L'\\0'". The decimal code is printed +// as signed integer when wchar_t is implemented by the compiler +// as a signed type and is printed as an unsigned integer when wchar_t +// is implemented as an unsigned type. +GTEST_API_ void PrintTo(wchar_t wc, ::std::ostream* os); + +GTEST_API_ void PrintTo(char32_t c, ::std::ostream* os); +inline void PrintTo(char16_t c, ::std::ostream* os) { + PrintTo(ImplicitCast_(c), os); +} +#ifdef __cpp_char8_t +inline void PrintTo(char8_t c, ::std::ostream* os) { + PrintTo(ImplicitCast_(c), os); +} +#endif + +// gcc/clang __{u,}int128_t +#if defined(__SIZEOF_INT128__) +GTEST_API_ void PrintTo(__uint128_t v, ::std::ostream* os); +GTEST_API_ void PrintTo(__int128_t v, ::std::ostream* os); +#endif // __SIZEOF_INT128__ + +// Overloads for C strings. +GTEST_API_ void PrintTo(const char* s, ::std::ostream* os); +inline void PrintTo(char* s, ::std::ostream* os) { + PrintTo(ImplicitCast_(s), os); +} + +// signed/unsigned char is often used for representing binary data, so +// we print pointers to it as void* to be safe. +inline void PrintTo(const signed char* s, ::std::ostream* os) { + PrintTo(ImplicitCast_(s), os); +} +inline void PrintTo(signed char* s, ::std::ostream* os) { + PrintTo(ImplicitCast_(s), os); +} +inline void PrintTo(const unsigned char* s, ::std::ostream* os) { + PrintTo(ImplicitCast_(s), os); +} +inline void PrintTo(unsigned char* s, ::std::ostream* os) { + PrintTo(ImplicitCast_(s), os); +} +#ifdef __cpp_char8_t +// Overloads for u8 strings. +GTEST_API_ void PrintTo(const char8_t* s, ::std::ostream* os); +inline void PrintTo(char8_t* s, ::std::ostream* os) { + PrintTo(ImplicitCast_(s), os); +} +#endif +// Overloads for u16 strings. +GTEST_API_ void PrintTo(const char16_t* s, ::std::ostream* os); +inline void PrintTo(char16_t* s, ::std::ostream* os) { + PrintTo(ImplicitCast_(s), os); +} +// Overloads for u32 strings. +GTEST_API_ void PrintTo(const char32_t* s, ::std::ostream* os); +inline void PrintTo(char32_t* s, ::std::ostream* os) { + PrintTo(ImplicitCast_(s), os); +} + +// MSVC can be configured to define wchar_t as a typedef of unsigned +// short. It defines _NATIVE_WCHAR_T_DEFINED when wchar_t is a native +// type. When wchar_t is a typedef, defining an overload for const +// wchar_t* would cause unsigned short* be printed as a wide string, +// possibly causing invalid memory accesses. +#if !defined(_MSC_VER) || defined(_NATIVE_WCHAR_T_DEFINED) +// Overloads for wide C strings +GTEST_API_ void PrintTo(const wchar_t* s, ::std::ostream* os); +inline void PrintTo(wchar_t* s, ::std::ostream* os) { + PrintTo(ImplicitCast_(s), os); +} +#endif + +// Overload for C arrays. Multi-dimensional arrays are printed +// properly. + +// Prints the given number of elements in an array, without printing +// the curly braces. +template +void PrintRawArrayTo(const T a[], size_t count, ::std::ostream* os) { + UniversalPrint(a[0], os); + for (size_t i = 1; i != count; i++) { + *os << ", "; + UniversalPrint(a[i], os); + } +} + +// Overloads for ::std::string. +GTEST_API_ void PrintStringTo(const ::std::string& s, ::std::ostream* os); +inline void PrintTo(const ::std::string& s, ::std::ostream* os) { + PrintStringTo(s, os); +} + +// Overloads for ::std::u8string +#ifdef __cpp_lib_char8_t +GTEST_API_ void PrintU8StringTo(const ::std::u8string& s, ::std::ostream* os); +inline void PrintTo(const ::std::u8string& s, ::std::ostream* os) { + PrintU8StringTo(s, os); +} +#endif + +// Overloads for ::std::u16string +GTEST_API_ void PrintU16StringTo(const ::std::u16string& s, ::std::ostream* os); +inline void PrintTo(const ::std::u16string& s, ::std::ostream* os) { + PrintU16StringTo(s, os); +} + +// Overloads for ::std::u32string +GTEST_API_ void PrintU32StringTo(const ::std::u32string& s, ::std::ostream* os); +inline void PrintTo(const ::std::u32string& s, ::std::ostream* os) { + PrintU32StringTo(s, os); +} + +// Overloads for ::std::wstring. +#if GTEST_HAS_STD_WSTRING +GTEST_API_ void PrintWideStringTo(const ::std::wstring& s, ::std::ostream* os); +inline void PrintTo(const ::std::wstring& s, ::std::ostream* os) { + PrintWideStringTo(s, os); +} +#endif // GTEST_HAS_STD_WSTRING + +#if GTEST_INTERNAL_HAS_STRING_VIEW +// Overload for internal::StringView. +inline void PrintTo(internal::StringView sp, ::std::ostream* os) { + PrintTo(::std::string(sp), os); +} +#endif // GTEST_INTERNAL_HAS_STRING_VIEW + +inline void PrintTo(std::nullptr_t, ::std::ostream* os) { *os << "(nullptr)"; } + +#if GTEST_HAS_RTTI +inline void PrintTo(const std::type_info& info, std::ostream* os) { + *os << internal::GetTypeName(info); +} +#endif // GTEST_HAS_RTTI + +template +void PrintTo(std::reference_wrapper ref, ::std::ostream* os) { + UniversalPrinter::Print(ref.get(), os); +} + +inline const void* VoidifyPointer(const void* p) { return p; } +inline const void* VoidifyPointer(volatile const void* p) { + return const_cast(p); +} + +template +void PrintSmartPointer(const Ptr& ptr, std::ostream* os, char) { + if (ptr == nullptr) { + *os << "(nullptr)"; + } else { + // We can't print the value. Just print the pointer.. + *os << "(" << (VoidifyPointer)(ptr.get()) << ")"; + } +} +template ::value && + !std::is_array::value>::type> +void PrintSmartPointer(const Ptr& ptr, std::ostream* os, int) { + if (ptr == nullptr) { + *os << "(nullptr)"; + } else { + *os << "(ptr = " << (VoidifyPointer)(ptr.get()) << ", value = "; + UniversalPrinter::Print(*ptr, os); + *os << ")"; + } +} + +template +void PrintTo(const std::unique_ptr& ptr, std::ostream* os) { + (PrintSmartPointer)(ptr, os, 0); +} + +template +void PrintTo(const std::shared_ptr& ptr, std::ostream* os) { + (PrintSmartPointer)(ptr, os, 0); +} + +// Helper function for printing a tuple. T must be instantiated with +// a tuple type. +template +void PrintTupleTo(const T&, std::integral_constant, + ::std::ostream*) {} + +template +void PrintTupleTo(const T& t, std::integral_constant, + ::std::ostream* os) { + PrintTupleTo(t, std::integral_constant(), os); + GTEST_INTENTIONAL_CONST_COND_PUSH_() + if (I > 1) { + GTEST_INTENTIONAL_CONST_COND_POP_() + *os << ", "; + } + UniversalPrinter::type>::Print( + std::get(t), os); +} + +template +void PrintTo(const ::std::tuple& t, ::std::ostream* os) { + *os << "("; + PrintTupleTo(t, std::integral_constant(), os); + *os << ")"; +} + +// Overload for std::pair. +template +void PrintTo(const ::std::pair& value, ::std::ostream* os) { + *os << '('; + // We cannot use UniversalPrint(value.first, os) here, as T1 may be + // a reference type. The same for printing value.second. + UniversalPrinter::Print(value.first, os); + *os << ", "; + UniversalPrinter::Print(value.second, os); + *os << ')'; +} + +// Implements printing a non-reference type T by letting the compiler +// pick the right overload of PrintTo() for T. +template +class UniversalPrinter { + public: + // MSVC warns about adding const to a function type, so we want to + // disable the warning. + GTEST_DISABLE_MSC_WARNINGS_PUSH_(4180) + + // Note: we deliberately don't call this PrintTo(), as that name + // conflicts with ::testing::internal::PrintTo in the body of the + // function. + static void Print(const T& value, ::std::ostream* os) { + // By default, ::testing::internal::PrintTo() is used for printing + // the value. + // + // Thanks to Koenig look-up, if T is a class and has its own + // PrintTo() function defined in its namespace, that function will + // be visible here. Since it is more specific than the generic ones + // in ::testing::internal, it will be picked by the compiler in the + // following statement - exactly what we want. + PrintTo(value, os); + } + + GTEST_DISABLE_MSC_WARNINGS_POP_() +}; + +// Remove any const-qualifiers before passing a type to UniversalPrinter. +template +class UniversalPrinter : public UniversalPrinter {}; + +#if GTEST_INTERNAL_HAS_ANY + +// Printer for std::any / absl::any + +template <> +class UniversalPrinter { + public: + static void Print(const Any& value, ::std::ostream* os) { + if (value.has_value()) { + *os << "value of type " << GetTypeName(value); + } else { + *os << "no value"; + } + } + + private: + static std::string GetTypeName(const Any& value) { +#if GTEST_HAS_RTTI + return internal::GetTypeName(value.type()); +#else + static_cast(value); // possibly unused + return ""; +#endif // GTEST_HAS_RTTI + } +}; + +#endif // GTEST_INTERNAL_HAS_ANY + +#if GTEST_INTERNAL_HAS_OPTIONAL + +// Printer for std::optional / absl::optional + +template +class UniversalPrinter> { + public: + static void Print(const Optional& value, ::std::ostream* os) { + *os << '('; + if (!value) { + *os << "nullopt"; + } else { + UniversalPrint(*value, os); + } + *os << ')'; + } +}; + +template <> +class UniversalPrinter { + public: + static void Print(decltype(Nullopt()), ::std::ostream* os) { + *os << "(nullopt)"; + } +}; + +#endif // GTEST_INTERNAL_HAS_OPTIONAL + +#if GTEST_INTERNAL_HAS_VARIANT + +// Printer for std::variant / absl::variant + +template +class UniversalPrinter> { + public: + static void Print(const Variant& value, ::std::ostream* os) { + *os << '('; +#if GTEST_HAS_ABSL + absl::visit(Visitor{os, value.index()}, value); +#else + std::visit(Visitor{os, value.index()}, value); +#endif // GTEST_HAS_ABSL + *os << ')'; + } + + private: + struct Visitor { + template + void operator()(const U& u) const { + *os << "'" << GetTypeName() << "(index = " << index + << ")' with value "; + UniversalPrint(u, os); + } + ::std::ostream* os; + std::size_t index; + }; +}; + +#endif // GTEST_INTERNAL_HAS_VARIANT + +// UniversalPrintArray(begin, len, os) prints an array of 'len' +// elements, starting at address 'begin'. +template +void UniversalPrintArray(const T* begin, size_t len, ::std::ostream* os) { + if (len == 0) { + *os << "{}"; + } else { + *os << "{ "; + const size_t kThreshold = 18; + const size_t kChunkSize = 8; + // If the array has more than kThreshold elements, we'll have to + // omit some details by printing only the first and the last + // kChunkSize elements. + if (len <= kThreshold) { + PrintRawArrayTo(begin, len, os); + } else { + PrintRawArrayTo(begin, kChunkSize, os); + *os << ", ..., "; + PrintRawArrayTo(begin + len - kChunkSize, kChunkSize, os); + } + *os << " }"; + } +} +// This overload prints a (const) char array compactly. +GTEST_API_ void UniversalPrintArray(const char* begin, size_t len, + ::std::ostream* os); + +#ifdef __cpp_char8_t +// This overload prints a (const) char8_t array compactly. +GTEST_API_ void UniversalPrintArray(const char8_t* begin, size_t len, + ::std::ostream* os); +#endif + +// This overload prints a (const) char16_t array compactly. +GTEST_API_ void UniversalPrintArray(const char16_t* begin, size_t len, + ::std::ostream* os); + +// This overload prints a (const) char32_t array compactly. +GTEST_API_ void UniversalPrintArray(const char32_t* begin, size_t len, + ::std::ostream* os); + +// This overload prints a (const) wchar_t array compactly. +GTEST_API_ void UniversalPrintArray(const wchar_t* begin, size_t len, + ::std::ostream* os); + +// Implements printing an array type T[N]. +template +class UniversalPrinter { + public: + // Prints the given array, omitting some elements when there are too + // many. + static void Print(const T (&a)[N], ::std::ostream* os) { + UniversalPrintArray(a, N, os); + } +}; + +// Implements printing a reference type T&. +template +class UniversalPrinter { + public: + // MSVC warns about adding const to a function type, so we want to + // disable the warning. + GTEST_DISABLE_MSC_WARNINGS_PUSH_(4180) + + static void Print(const T& value, ::std::ostream* os) { + // Prints the address of the value. We use reinterpret_cast here + // as static_cast doesn't compile when T is a function type. + *os << "@" << reinterpret_cast(&value) << " "; + + // Then prints the value itself. + UniversalPrint(value, os); + } + + GTEST_DISABLE_MSC_WARNINGS_POP_() +}; + +// Prints a value tersely: for a reference type, the referenced value +// (but not the address) is printed; for a (const) char pointer, the +// NUL-terminated string (but not the pointer) is printed. + +template +class UniversalTersePrinter { + public: + static void Print(const T& value, ::std::ostream* os) { + UniversalPrint(value, os); + } +}; +template +class UniversalTersePrinter { + public: + static void Print(const T& value, ::std::ostream* os) { + UniversalPrint(value, os); + } +}; +template +class UniversalTersePrinter> { + public: + static void Print(std::reference_wrapper value, ::std::ostream* os) { + UniversalTersePrinter::Print(value.get(), os); + } +}; +template +class UniversalTersePrinter { + public: + static void Print(const T (&value)[N], ::std::ostream* os) { + UniversalPrinter::Print(value, os); + } +}; +template <> +class UniversalTersePrinter { + public: + static void Print(const char* str, ::std::ostream* os) { + if (str == nullptr) { + *os << "NULL"; + } else { + UniversalPrint(std::string(str), os); + } + } +}; +template <> +class UniversalTersePrinter : public UniversalTersePrinter { +}; + +#ifdef __cpp_char8_t +template <> +class UniversalTersePrinter { + public: + static void Print(const char8_t* str, ::std::ostream* os) { + if (str == nullptr) { + *os << "NULL"; + } else { + UniversalPrint(::std::u8string(str), os); + } + } +}; +template <> +class UniversalTersePrinter + : public UniversalTersePrinter {}; +#endif + +template <> +class UniversalTersePrinter { + public: + static void Print(const char16_t* str, ::std::ostream* os) { + if (str == nullptr) { + *os << "NULL"; + } else { + UniversalPrint(::std::u16string(str), os); + } + } +}; +template <> +class UniversalTersePrinter + : public UniversalTersePrinter {}; + +template <> +class UniversalTersePrinter { + public: + static void Print(const char32_t* str, ::std::ostream* os) { + if (str == nullptr) { + *os << "NULL"; + } else { + UniversalPrint(::std::u32string(str), os); + } + } +}; +template <> +class UniversalTersePrinter + : public UniversalTersePrinter {}; + +#if GTEST_HAS_STD_WSTRING +template <> +class UniversalTersePrinter { + public: + static void Print(const wchar_t* str, ::std::ostream* os) { + if (str == nullptr) { + *os << "NULL"; + } else { + UniversalPrint(::std::wstring(str), os); + } + } +}; +#endif + +template <> +class UniversalTersePrinter { + public: + static void Print(wchar_t* str, ::std::ostream* os) { + UniversalTersePrinter::Print(str, os); + } +}; + +template +void UniversalTersePrint(const T& value, ::std::ostream* os) { + UniversalTersePrinter::Print(value, os); +} + +// Prints a value using the type inferred by the compiler. The +// difference between this and UniversalTersePrint() is that for a +// (const) char pointer, this prints both the pointer and the +// NUL-terminated string. +template +void UniversalPrint(const T& value, ::std::ostream* os) { + // A workarond for the bug in VC++ 7.1 that prevents us from instantiating + // UniversalPrinter with T directly. + typedef T T1; + UniversalPrinter::Print(value, os); +} + +typedef ::std::vector<::std::string> Strings; + +// Tersely prints the first N fields of a tuple to a string vector, +// one element for each field. +template +void TersePrintPrefixToStrings(const Tuple&, std::integral_constant, + Strings*) {} +template +void TersePrintPrefixToStrings(const Tuple& t, + std::integral_constant, + Strings* strings) { + TersePrintPrefixToStrings(t, std::integral_constant(), + strings); + ::std::stringstream ss; + UniversalTersePrint(std::get(t), &ss); + strings->push_back(ss.str()); +} + +// Prints the fields of a tuple tersely to a string vector, one +// element for each field. See the comment before +// UniversalTersePrint() for how we define "tersely". +template +Strings UniversalTersePrintTupleFieldsToStrings(const Tuple& value) { + Strings result; + TersePrintPrefixToStrings( + value, std::integral_constant::value>(), + &result); + return result; +} + +} // namespace internal + +template +::std::string PrintToString(const T& value) { + ::std::stringstream ss; + internal::UniversalTersePrinter::Print(value, &ss); + return ss.str(); +} + +} // namespace testing + +// Include any custom printer added by the local installation. +// We must include this header at the end to make sure it can use the +// declarations from this file. +#include "gtest/internal/custom/gtest-printers.h" + +#endif // GOOGLETEST_INCLUDE_GTEST_GTEST_PRINTERS_H_ diff --git a/include/gtest/gtest-spi.h b/include/gtest/gtest-spi.h new file mode 100644 index 0000000..bec8c48 --- /dev/null +++ b/include/gtest/gtest-spi.h @@ -0,0 +1,248 @@ +// Copyright 2007, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Utilities for testing Google Test itself and code that uses Google Test +// (e.g. frameworks built on top of Google Test). + +#ifndef GOOGLETEST_INCLUDE_GTEST_GTEST_SPI_H_ +#define GOOGLETEST_INCLUDE_GTEST_GTEST_SPI_H_ + +#include "gtest/gtest.h" + +GTEST_DISABLE_MSC_WARNINGS_PUSH_(4251 \ +/* class A needs to have dll-interface to be used by clients of class B */) + +namespace testing { + +// This helper class can be used to mock out Google Test failure reporting +// so that we can test Google Test or code that builds on Google Test. +// +// An object of this class appends a TestPartResult object to the +// TestPartResultArray object given in the constructor whenever a Google Test +// failure is reported. It can either intercept only failures that are +// generated in the same thread that created this object or it can intercept +// all generated failures. The scope of this mock object can be controlled with +// the second argument to the two arguments constructor. +class GTEST_API_ ScopedFakeTestPartResultReporter + : public TestPartResultReporterInterface { + public: + // The two possible mocking modes of this object. + enum InterceptMode { + INTERCEPT_ONLY_CURRENT_THREAD, // Intercepts only thread local failures. + INTERCEPT_ALL_THREADS // Intercepts all failures. + }; + + // The c'tor sets this object as the test part result reporter used + // by Google Test. The 'result' parameter specifies where to report the + // results. This reporter will only catch failures generated in the current + // thread. DEPRECATED + explicit ScopedFakeTestPartResultReporter(TestPartResultArray* result); + + // Same as above, but you can choose the interception scope of this object. + ScopedFakeTestPartResultReporter(InterceptMode intercept_mode, + TestPartResultArray* result); + + // The d'tor restores the previous test part result reporter. + ~ScopedFakeTestPartResultReporter() override; + + // Appends the TestPartResult object to the TestPartResultArray + // received in the constructor. + // + // This method is from the TestPartResultReporterInterface + // interface. + void ReportTestPartResult(const TestPartResult& result) override; + + private: + void Init(); + + const InterceptMode intercept_mode_; + TestPartResultReporterInterface* old_reporter_; + TestPartResultArray* const result_; + + ScopedFakeTestPartResultReporter(const ScopedFakeTestPartResultReporter&) = + delete; + ScopedFakeTestPartResultReporter& operator=( + const ScopedFakeTestPartResultReporter&) = delete; +}; + +namespace internal { + +// A helper class for implementing EXPECT_FATAL_FAILURE() and +// EXPECT_NONFATAL_FAILURE(). Its destructor verifies that the given +// TestPartResultArray contains exactly one failure that has the given +// type and contains the given substring. If that's not the case, a +// non-fatal failure will be generated. +class GTEST_API_ SingleFailureChecker { + public: + // The constructor remembers the arguments. + SingleFailureChecker(const TestPartResultArray* results, + TestPartResult::Type type, const std::string& substr); + ~SingleFailureChecker(); + + private: + const TestPartResultArray* const results_; + const TestPartResult::Type type_; + const std::string substr_; + + SingleFailureChecker(const SingleFailureChecker&) = delete; + SingleFailureChecker& operator=(const SingleFailureChecker&) = delete; +}; + +} // namespace internal + +} // namespace testing + +GTEST_DISABLE_MSC_WARNINGS_POP_() // 4251 + +// A set of macros for testing Google Test assertions or code that's expected +// to generate Google Test fatal failures (e.g. a failure from an ASSERT_EQ, but +// not a non-fatal failure, as from EXPECT_EQ). It verifies that the given +// statement will cause exactly one fatal Google Test failure with 'substr' +// being part of the failure message. +// +// There are two different versions of this macro. EXPECT_FATAL_FAILURE only +// affects and considers failures generated in the current thread and +// EXPECT_FATAL_FAILURE_ON_ALL_THREADS does the same but for all threads. +// +// The verification of the assertion is done correctly even when the statement +// throws an exception or aborts the current function. +// +// Known restrictions: +// - 'statement' cannot reference local non-static variables or +// non-static members of the current object. +// - 'statement' cannot return a value. +// - You cannot stream a failure message to this macro. +// +// Note that even though the implementations of the following two +// macros are much alike, we cannot refactor them to use a common +// helper macro, due to some peculiarity in how the preprocessor +// works. The AcceptsMacroThatExpandsToUnprotectedComma test in +// gtest_unittest.cc will fail to compile if we do that. +#define EXPECT_FATAL_FAILURE(statement, substr) \ + do { \ + class GTestExpectFatalFailureHelper { \ + public: \ + static void Execute() { statement; } \ + }; \ + ::testing::TestPartResultArray gtest_failures; \ + ::testing::internal::SingleFailureChecker gtest_checker( \ + >est_failures, ::testing::TestPartResult::kFatalFailure, (substr)); \ + { \ + ::testing::ScopedFakeTestPartResultReporter gtest_reporter( \ + ::testing::ScopedFakeTestPartResultReporter:: \ + INTERCEPT_ONLY_CURRENT_THREAD, \ + >est_failures); \ + GTestExpectFatalFailureHelper::Execute(); \ + } \ + } while (::testing::internal::AlwaysFalse()) + +#define EXPECT_FATAL_FAILURE_ON_ALL_THREADS(statement, substr) \ + do { \ + class GTestExpectFatalFailureHelper { \ + public: \ + static void Execute() { statement; } \ + }; \ + ::testing::TestPartResultArray gtest_failures; \ + ::testing::internal::SingleFailureChecker gtest_checker( \ + >est_failures, ::testing::TestPartResult::kFatalFailure, (substr)); \ + { \ + ::testing::ScopedFakeTestPartResultReporter gtest_reporter( \ + ::testing::ScopedFakeTestPartResultReporter::INTERCEPT_ALL_THREADS, \ + >est_failures); \ + GTestExpectFatalFailureHelper::Execute(); \ + } \ + } while (::testing::internal::AlwaysFalse()) + +// A macro for testing Google Test assertions or code that's expected to +// generate Google Test non-fatal failures (e.g. a failure from an EXPECT_EQ, +// but not from an ASSERT_EQ). It asserts that the given statement will cause +// exactly one non-fatal Google Test failure with 'substr' being part of the +// failure message. +// +// There are two different versions of this macro. EXPECT_NONFATAL_FAILURE only +// affects and considers failures generated in the current thread and +// EXPECT_NONFATAL_FAILURE_ON_ALL_THREADS does the same but for all threads. +// +// 'statement' is allowed to reference local variables and members of +// the current object. +// +// The verification of the assertion is done correctly even when the statement +// throws an exception or aborts the current function. +// +// Known restrictions: +// - You cannot stream a failure message to this macro. +// +// Note that even though the implementations of the following two +// macros are much alike, we cannot refactor them to use a common +// helper macro, due to some peculiarity in how the preprocessor +// works. If we do that, the code won't compile when the user gives +// EXPECT_NONFATAL_FAILURE() a statement that contains a macro that +// expands to code containing an unprotected comma. The +// AcceptsMacroThatExpandsToUnprotectedComma test in gtest_unittest.cc +// catches that. +// +// For the same reason, we have to write +// if (::testing::internal::AlwaysTrue()) { statement; } +// instead of +// GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement) +// to avoid an MSVC warning on unreachable code. +#define EXPECT_NONFATAL_FAILURE(statement, substr) \ + do { \ + ::testing::TestPartResultArray gtest_failures; \ + ::testing::internal::SingleFailureChecker gtest_checker( \ + >est_failures, ::testing::TestPartResult::kNonFatalFailure, \ + (substr)); \ + { \ + ::testing::ScopedFakeTestPartResultReporter gtest_reporter( \ + ::testing::ScopedFakeTestPartResultReporter:: \ + INTERCEPT_ONLY_CURRENT_THREAD, \ + >est_failures); \ + if (::testing::internal::AlwaysTrue()) { \ + statement; \ + } \ + } \ + } while (::testing::internal::AlwaysFalse()) + +#define EXPECT_NONFATAL_FAILURE_ON_ALL_THREADS(statement, substr) \ + do { \ + ::testing::TestPartResultArray gtest_failures; \ + ::testing::internal::SingleFailureChecker gtest_checker( \ + >est_failures, ::testing::TestPartResult::kNonFatalFailure, \ + (substr)); \ + { \ + ::testing::ScopedFakeTestPartResultReporter gtest_reporter( \ + ::testing::ScopedFakeTestPartResultReporter::INTERCEPT_ALL_THREADS, \ + >est_failures); \ + if (::testing::internal::AlwaysTrue()) { \ + statement; \ + } \ + } \ + } while (::testing::internal::AlwaysFalse()) + +#endif // GOOGLETEST_INCLUDE_GTEST_GTEST_SPI_H_ diff --git a/include/gtest/gtest-test-part.h b/include/gtest/gtest-test-part.h new file mode 100644 index 0000000..09cc8c3 --- /dev/null +++ b/include/gtest/gtest-test-part.h @@ -0,0 +1,190 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// IWYU pragma: private, include "gtest/gtest.h" +// IWYU pragma: friend gtest/.* +// IWYU pragma: friend gmock/.* + +#ifndef GOOGLETEST_INCLUDE_GTEST_GTEST_TEST_PART_H_ +#define GOOGLETEST_INCLUDE_GTEST_GTEST_TEST_PART_H_ + +#include +#include + +#include "gtest/internal/gtest-internal.h" +#include "gtest/internal/gtest-string.h" + +GTEST_DISABLE_MSC_WARNINGS_PUSH_(4251 \ +/* class A needs to have dll-interface to be used by clients of class B */) + +namespace testing { + +// A copyable object representing the result of a test part (i.e. an +// assertion or an explicit FAIL(), ADD_FAILURE(), or SUCCESS()). +// +// Don't inherit from TestPartResult as its destructor is not virtual. +class GTEST_API_ TestPartResult { + public: + // The possible outcomes of a test part (i.e. an assertion or an + // explicit SUCCEED(), FAIL(), or ADD_FAILURE()). + enum Type { + kSuccess, // Succeeded. + kNonFatalFailure, // Failed but the test can continue. + kFatalFailure, // Failed and the test should be terminated. + kSkip // Skipped. + }; + + // C'tor. TestPartResult does NOT have a default constructor. + // Always use this constructor (with parameters) to create a + // TestPartResult object. + TestPartResult(Type a_type, const char* a_file_name, int a_line_number, + const char* a_message) + : type_(a_type), + file_name_(a_file_name == nullptr ? "" : a_file_name), + line_number_(a_line_number), + summary_(ExtractSummary(a_message)), + message_(a_message) {} + + // Gets the outcome of the test part. + Type type() const { return type_; } + + // Gets the name of the source file where the test part took place, or + // NULL if it's unknown. + const char* file_name() const { + return file_name_.empty() ? nullptr : file_name_.c_str(); + } + + // Gets the line in the source file where the test part took place, + // or -1 if it's unknown. + int line_number() const { return line_number_; } + + // Gets the summary of the failure message. + const char* summary() const { return summary_.c_str(); } + + // Gets the message associated with the test part. + const char* message() const { return message_.c_str(); } + + // Returns true if and only if the test part was skipped. + bool skipped() const { return type_ == kSkip; } + + // Returns true if and only if the test part passed. + bool passed() const { return type_ == kSuccess; } + + // Returns true if and only if the test part non-fatally failed. + bool nonfatally_failed() const { return type_ == kNonFatalFailure; } + + // Returns true if and only if the test part fatally failed. + bool fatally_failed() const { return type_ == kFatalFailure; } + + // Returns true if and only if the test part failed. + bool failed() const { return fatally_failed() || nonfatally_failed(); } + + private: + Type type_; + + // Gets the summary of the failure message by omitting the stack + // trace in it. + static std::string ExtractSummary(const char* message); + + // The name of the source file where the test part took place, or + // "" if the source file is unknown. + std::string file_name_; + // The line in the source file where the test part took place, or -1 + // if the line number is unknown. + int line_number_; + std::string summary_; // The test failure summary. + std::string message_; // The test failure message. +}; + +// Prints a TestPartResult object. +std::ostream& operator<<(std::ostream& os, const TestPartResult& result); + +// An array of TestPartResult objects. +// +// Don't inherit from TestPartResultArray as its destructor is not +// virtual. +class GTEST_API_ TestPartResultArray { + public: + TestPartResultArray() {} + + // Appends the given TestPartResult to the array. + void Append(const TestPartResult& result); + + // Returns the TestPartResult at the given index (0-based). + const TestPartResult& GetTestPartResult(int index) const; + + // Returns the number of TestPartResult objects in the array. + int size() const; + + private: + std::vector array_; + + TestPartResultArray(const TestPartResultArray&) = delete; + TestPartResultArray& operator=(const TestPartResultArray&) = delete; +}; + +// This interface knows how to report a test part result. +class GTEST_API_ TestPartResultReporterInterface { + public: + virtual ~TestPartResultReporterInterface() {} + + virtual void ReportTestPartResult(const TestPartResult& result) = 0; +}; + +namespace internal { + +// This helper class is used by {ASSERT|EXPECT}_NO_FATAL_FAILURE to check if a +// statement generates new fatal failures. To do so it registers itself as the +// current test part result reporter. Besides checking if fatal failures were +// reported, it only delegates the reporting to the former result reporter. +// The original result reporter is restored in the destructor. +// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. +class GTEST_API_ HasNewFatalFailureHelper + : public TestPartResultReporterInterface { + public: + HasNewFatalFailureHelper(); + ~HasNewFatalFailureHelper() override; + void ReportTestPartResult(const TestPartResult& result) override; + bool has_new_fatal_failure() const { return has_new_fatal_failure_; } + + private: + bool has_new_fatal_failure_; + TestPartResultReporterInterface* original_reporter_; + + HasNewFatalFailureHelper(const HasNewFatalFailureHelper&) = delete; + HasNewFatalFailureHelper& operator=(const HasNewFatalFailureHelper&) = delete; +}; + +} // namespace internal + +} // namespace testing + +GTEST_DISABLE_MSC_WARNINGS_POP_() // 4251 + +#endif // GOOGLETEST_INCLUDE_GTEST_GTEST_TEST_PART_H_ diff --git a/include/gtest/gtest-typed-test.h b/include/gtest/gtest-typed-test.h new file mode 100644 index 0000000..bd35a32 --- /dev/null +++ b/include/gtest/gtest-typed-test.h @@ -0,0 +1,331 @@ +// Copyright 2008 Google Inc. +// All Rights Reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// IWYU pragma: private, include "gtest/gtest.h" +// IWYU pragma: friend gtest/.* +// IWYU pragma: friend gmock/.* + +#ifndef GOOGLETEST_INCLUDE_GTEST_GTEST_TYPED_TEST_H_ +#define GOOGLETEST_INCLUDE_GTEST_GTEST_TYPED_TEST_H_ + +// This header implements typed tests and type-parameterized tests. + +// Typed (aka type-driven) tests repeat the same test for types in a +// list. You must know which types you want to test with when writing +// typed tests. Here's how you do it: + +#if 0 + +// First, define a fixture class template. It should be parameterized +// by a type. Remember to derive it from testing::Test. +template +class FooTest : public testing::Test { + public: + ... + typedef std::list List; + static T shared_; + T value_; +}; + +// Next, associate a list of types with the test suite, which will be +// repeated for each type in the list. The typedef is necessary for +// the macro to parse correctly. +typedef testing::Types MyTypes; +TYPED_TEST_SUITE(FooTest, MyTypes); + +// If the type list contains only one type, you can write that type +// directly without Types<...>: +// TYPED_TEST_SUITE(FooTest, int); + +// Then, use TYPED_TEST() instead of TEST_F() to define as many typed +// tests for this test suite as you want. +TYPED_TEST(FooTest, DoesBlah) { + // Inside a test, refer to the special name TypeParam to get the type + // parameter. Since we are inside a derived class template, C++ requires + // us to visit the members of FooTest via 'this'. + TypeParam n = this->value_; + + // To visit static members of the fixture, add the TestFixture:: + // prefix. + n += TestFixture::shared_; + + // To refer to typedefs in the fixture, add the "typename + // TestFixture::" prefix. + typename TestFixture::List values; + values.push_back(n); + ... +} + +TYPED_TEST(FooTest, HasPropertyA) { ... } + +// TYPED_TEST_SUITE takes an optional third argument which allows to specify a +// class that generates custom test name suffixes based on the type. This should +// be a class which has a static template function GetName(int index) returning +// a string for each type. The provided integer index equals the index of the +// type in the provided type list. In many cases the index can be ignored. +// +// For example: +// class MyTypeNames { +// public: +// template +// static std::string GetName(int) { +// if (std::is_same()) return "char"; +// if (std::is_same()) return "int"; +// if (std::is_same()) return "unsignedInt"; +// } +// }; +// TYPED_TEST_SUITE(FooTest, MyTypes, MyTypeNames); + +#endif // 0 + +// Type-parameterized tests are abstract test patterns parameterized +// by a type. Compared with typed tests, type-parameterized tests +// allow you to define the test pattern without knowing what the type +// parameters are. The defined pattern can be instantiated with +// different types any number of times, in any number of translation +// units. +// +// If you are designing an interface or concept, you can define a +// suite of type-parameterized tests to verify properties that any +// valid implementation of the interface/concept should have. Then, +// each implementation can easily instantiate the test suite to verify +// that it conforms to the requirements, without having to write +// similar tests repeatedly. Here's an example: + +#if 0 + +// First, define a fixture class template. It should be parameterized +// by a type. Remember to derive it from testing::Test. +template +class FooTest : public testing::Test { + ... +}; + +// Next, declare that you will define a type-parameterized test suite +// (the _P suffix is for "parameterized" or "pattern", whichever you +// prefer): +TYPED_TEST_SUITE_P(FooTest); + +// Then, use TYPED_TEST_P() to define as many type-parameterized tests +// for this type-parameterized test suite as you want. +TYPED_TEST_P(FooTest, DoesBlah) { + // Inside a test, refer to TypeParam to get the type parameter. + TypeParam n = 0; + ... +} + +TYPED_TEST_P(FooTest, HasPropertyA) { ... } + +// Now the tricky part: you need to register all test patterns before +// you can instantiate them. The first argument of the macro is the +// test suite name; the rest are the names of the tests in this test +// case. +REGISTER_TYPED_TEST_SUITE_P(FooTest, + DoesBlah, HasPropertyA); + +// Finally, you are free to instantiate the pattern with the types you +// want. If you put the above code in a header file, you can #include +// it in multiple C++ source files and instantiate it multiple times. +// +// To distinguish different instances of the pattern, the first +// argument to the INSTANTIATE_* macro is a prefix that will be added +// to the actual test suite name. Remember to pick unique prefixes for +// different instances. +typedef testing::Types MyTypes; +INSTANTIATE_TYPED_TEST_SUITE_P(My, FooTest, MyTypes); + +// If the type list contains only one type, you can write that type +// directly without Types<...>: +// INSTANTIATE_TYPED_TEST_SUITE_P(My, FooTest, int); +// +// Similar to the optional argument of TYPED_TEST_SUITE above, +// INSTANTIATE_TEST_SUITE_P takes an optional fourth argument which allows to +// generate custom names. +// INSTANTIATE_TYPED_TEST_SUITE_P(My, FooTest, MyTypes, MyTypeNames); + +#endif // 0 + +#include "gtest/internal/gtest-internal.h" +#include "gtest/internal/gtest-port.h" +#include "gtest/internal/gtest-type-util.h" + +// Implements typed tests. + +// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE. +// +// Expands to the name of the typedef for the type parameters of the +// given test suite. +#define GTEST_TYPE_PARAMS_(TestSuiteName) gtest_type_params_##TestSuiteName##_ + +// Expands to the name of the typedef for the NameGenerator, responsible for +// creating the suffixes of the name. +#define GTEST_NAME_GENERATOR_(TestSuiteName) \ + gtest_type_params_##TestSuiteName##_NameGenerator + +#define TYPED_TEST_SUITE(CaseName, Types, ...) \ + typedef ::testing::internal::GenerateTypeList::type \ + GTEST_TYPE_PARAMS_(CaseName); \ + typedef ::testing::internal::NameGeneratorSelector<__VA_ARGS__>::type \ + GTEST_NAME_GENERATOR_(CaseName) + +#define TYPED_TEST(CaseName, TestName) \ + static_assert(sizeof(GTEST_STRINGIFY_(TestName)) > 1, \ + "test-name must not be empty"); \ + template \ + class GTEST_TEST_CLASS_NAME_(CaseName, TestName) \ + : public CaseName { \ + private: \ + typedef CaseName TestFixture; \ + typedef gtest_TypeParam_ TypeParam; \ + void TestBody() override; \ + }; \ + static bool gtest_##CaseName##_##TestName##_registered_ \ + GTEST_ATTRIBUTE_UNUSED_ = ::testing::internal::TypeParameterizedTest< \ + CaseName, \ + ::testing::internal::TemplateSel, \ + GTEST_TYPE_PARAMS_( \ + CaseName)>::Register("", \ + ::testing::internal::CodeLocation( \ + __FILE__, __LINE__), \ + GTEST_STRINGIFY_(CaseName), \ + GTEST_STRINGIFY_(TestName), 0, \ + ::testing::internal::GenerateNames< \ + GTEST_NAME_GENERATOR_(CaseName), \ + GTEST_TYPE_PARAMS_(CaseName)>()); \ + template \ + void GTEST_TEST_CLASS_NAME_(CaseName, \ + TestName)::TestBody() + +// Legacy API is deprecated but still available +#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_ +#define TYPED_TEST_CASE \ + static_assert(::testing::internal::TypedTestCaseIsDeprecated(), ""); \ + TYPED_TEST_SUITE +#endif // GTEST_REMOVE_LEGACY_TEST_CASEAPI_ + +// Implements type-parameterized tests. + +// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE. +// +// Expands to the namespace name that the type-parameterized tests for +// the given type-parameterized test suite are defined in. The exact +// name of the namespace is subject to change without notice. +#define GTEST_SUITE_NAMESPACE_(TestSuiteName) gtest_suite_##TestSuiteName##_ + +// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE. +// +// Expands to the name of the variable used to remember the names of +// the defined tests in the given test suite. +#define GTEST_TYPED_TEST_SUITE_P_STATE_(TestSuiteName) \ + gtest_typed_test_suite_p_state_##TestSuiteName##_ + +// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE DIRECTLY. +// +// Expands to the name of the variable used to remember the names of +// the registered tests in the given test suite. +#define GTEST_REGISTERED_TEST_NAMES_(TestSuiteName) \ + gtest_registered_test_names_##TestSuiteName##_ + +// The variables defined in the type-parameterized test macros are +// static as typically these macros are used in a .h file that can be +// #included in multiple translation units linked together. +#define TYPED_TEST_SUITE_P(SuiteName) \ + static ::testing::internal::TypedTestSuitePState \ + GTEST_TYPED_TEST_SUITE_P_STATE_(SuiteName) + +// Legacy API is deprecated but still available +#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_ +#define TYPED_TEST_CASE_P \ + static_assert(::testing::internal::TypedTestCase_P_IsDeprecated(), ""); \ + TYPED_TEST_SUITE_P +#endif // GTEST_REMOVE_LEGACY_TEST_CASEAPI_ + +#define TYPED_TEST_P(SuiteName, TestName) \ + namespace GTEST_SUITE_NAMESPACE_(SuiteName) { \ + template \ + class TestName : public SuiteName { \ + private: \ + typedef SuiteName TestFixture; \ + typedef gtest_TypeParam_ TypeParam; \ + void TestBody() override; \ + }; \ + static bool gtest_##TestName##_defined_ GTEST_ATTRIBUTE_UNUSED_ = \ + GTEST_TYPED_TEST_SUITE_P_STATE_(SuiteName).AddTestName( \ + __FILE__, __LINE__, GTEST_STRINGIFY_(SuiteName), \ + GTEST_STRINGIFY_(TestName)); \ + } \ + template \ + void GTEST_SUITE_NAMESPACE_( \ + SuiteName)::TestName::TestBody() + +// Note: this won't work correctly if the trailing arguments are macros. +#define REGISTER_TYPED_TEST_SUITE_P(SuiteName, ...) \ + namespace GTEST_SUITE_NAMESPACE_(SuiteName) { \ + typedef ::testing::internal::Templates<__VA_ARGS__> gtest_AllTests_; \ + } \ + static const char* const GTEST_REGISTERED_TEST_NAMES_( \ + SuiteName) GTEST_ATTRIBUTE_UNUSED_ = \ + GTEST_TYPED_TEST_SUITE_P_STATE_(SuiteName).VerifyRegisteredTestNames( \ + GTEST_STRINGIFY_(SuiteName), __FILE__, __LINE__, #__VA_ARGS__) + +// Legacy API is deprecated but still available +#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_ +#define REGISTER_TYPED_TEST_CASE_P \ + static_assert(::testing::internal::RegisterTypedTestCase_P_IsDeprecated(), \ + ""); \ + REGISTER_TYPED_TEST_SUITE_P +#endif // GTEST_REMOVE_LEGACY_TEST_CASEAPI_ + +#define INSTANTIATE_TYPED_TEST_SUITE_P(Prefix, SuiteName, Types, ...) \ + static_assert(sizeof(GTEST_STRINGIFY_(Prefix)) > 1, \ + "test-suit-prefix must not be empty"); \ + static bool gtest_##Prefix##_##SuiteName GTEST_ATTRIBUTE_UNUSED_ = \ + ::testing::internal::TypeParameterizedTestSuite< \ + SuiteName, GTEST_SUITE_NAMESPACE_(SuiteName)::gtest_AllTests_, \ + ::testing::internal::GenerateTypeList::type>:: \ + Register(GTEST_STRINGIFY_(Prefix), \ + ::testing::internal::CodeLocation(__FILE__, __LINE__), \ + >EST_TYPED_TEST_SUITE_P_STATE_(SuiteName), \ + GTEST_STRINGIFY_(SuiteName), \ + GTEST_REGISTERED_TEST_NAMES_(SuiteName), \ + ::testing::internal::GenerateNames< \ + ::testing::internal::NameGeneratorSelector< \ + __VA_ARGS__>::type, \ + ::testing::internal::GenerateTypeList::type>()) + +// Legacy API is deprecated but still available +#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_ +#define INSTANTIATE_TYPED_TEST_CASE_P \ + static_assert( \ + ::testing::internal::InstantiateTypedTestCase_P_IsDeprecated(), ""); \ + INSTANTIATE_TYPED_TEST_SUITE_P +#endif // GTEST_REMOVE_LEGACY_TEST_CASEAPI_ + +#endif // GOOGLETEST_INCLUDE_GTEST_GTEST_TYPED_TEST_H_ diff --git a/include/gtest/gtest.h b/include/gtest/gtest.h new file mode 100644 index 0000000..7963e8c --- /dev/null +++ b/include/gtest/gtest.h @@ -0,0 +1,2308 @@ +// Copyright 2005, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// The Google C++ Testing and Mocking Framework (Google Test) +// +// This header file defines the public API for Google Test. It should be +// included by any test program that uses Google Test. +// +// IMPORTANT NOTE: Due to limitation of the C++ language, we have to +// leave some internal implementation details in this header file. +// They are clearly marked by comments like this: +// +// // INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. +// +// Such code is NOT meant to be used by a user directly, and is subject +// to CHANGE WITHOUT NOTICE. Therefore DO NOT DEPEND ON IT in a user +// program! +// +// Acknowledgment: Google Test borrowed the idea of automatic test +// registration from Barthelemy Dagenais' (barthelemy@prologique.com) +// easyUnit framework. + +#ifndef GOOGLETEST_INCLUDE_GTEST_GTEST_H_ +#define GOOGLETEST_INCLUDE_GTEST_GTEST_H_ + +#include +#include +#include +#include +#include +#include + +#include "gtest/gtest-assertion-result.h" +#include "gtest/gtest-death-test.h" +#include "gtest/gtest-matchers.h" +#include "gtest/gtest-message.h" +#include "gtest/gtest-param-test.h" +#include "gtest/gtest-printers.h" +#include "gtest/gtest-test-part.h" +#include "gtest/gtest-typed-test.h" +#include "gtest/gtest_pred_impl.h" +#include "gtest/gtest_prod.h" +#include "gtest/internal/gtest-internal.h" +#include "gtest/internal/gtest-string.h" + +GTEST_DISABLE_MSC_WARNINGS_PUSH_(4251 \ +/* class A needs to have dll-interface to be used by clients of class B */) + +// Declares the flags. + +// This flag temporary enables the disabled tests. +GTEST_DECLARE_bool_(also_run_disabled_tests); + +// This flag brings the debugger on an assertion failure. +GTEST_DECLARE_bool_(break_on_failure); + +// This flag controls whether Google Test catches all test-thrown exceptions +// and logs them as failures. +GTEST_DECLARE_bool_(catch_exceptions); + +// This flag enables using colors in terminal output. Available values are +// "yes" to enable colors, "no" (disable colors), or "auto" (the default) +// to let Google Test decide. +GTEST_DECLARE_string_(color); + +// This flag controls whether the test runner should continue execution past +// first failure. +GTEST_DECLARE_bool_(fail_fast); + +// This flag sets up the filter to select by name using a glob pattern +// the tests to run. If the filter is not given all tests are executed. +GTEST_DECLARE_string_(filter); + +// This flag controls whether Google Test installs a signal handler that dumps +// debugging information when fatal signals are raised. +GTEST_DECLARE_bool_(install_failure_signal_handler); + +// This flag causes the Google Test to list tests. None of the tests listed +// are actually run if the flag is provided. +GTEST_DECLARE_bool_(list_tests); + +// This flag controls whether Google Test emits a detailed XML report to a file +// in addition to its normal textual output. +GTEST_DECLARE_string_(output); + +// This flags control whether Google Test prints only test failures. +GTEST_DECLARE_bool_(brief); + +// This flags control whether Google Test prints the elapsed time for each +// test. +GTEST_DECLARE_bool_(print_time); + +// This flags control whether Google Test prints UTF8 characters as text. +GTEST_DECLARE_bool_(print_utf8); + +// This flag specifies the random number seed. +GTEST_DECLARE_int32_(random_seed); + +// This flag sets how many times the tests are repeated. The default value +// is 1. If the value is -1 the tests are repeating forever. +GTEST_DECLARE_int32_(repeat); + +// This flag controls whether Google Test Environments are recreated for each +// repeat of the tests. The default value is true. If set to false the global +// test Environment objects are only set up once, for the first iteration, and +// only torn down once, for the last. +GTEST_DECLARE_bool_(recreate_environments_when_repeating); + +// This flag controls whether Google Test includes Google Test internal +// stack frames in failure stack traces. +GTEST_DECLARE_bool_(show_internal_stack_frames); + +// When this flag is specified, tests' order is randomized on every iteration. +GTEST_DECLARE_bool_(shuffle); + +// This flag specifies the maximum number of stack frames to be +// printed in a failure message. +GTEST_DECLARE_int32_(stack_trace_depth); + +// When this flag is specified, a failed assertion will throw an +// exception if exceptions are enabled, or exit the program with a +// non-zero code otherwise. For use with an external test framework. +GTEST_DECLARE_bool_(throw_on_failure); + +// When this flag is set with a "host:port" string, on supported +// platforms test results are streamed to the specified port on +// the specified host machine. +GTEST_DECLARE_string_(stream_result_to); + +#if GTEST_USE_OWN_FLAGFILE_FLAG_ +GTEST_DECLARE_string_(flagfile); +#endif // GTEST_USE_OWN_FLAGFILE_FLAG_ + +namespace testing { + +// Silence C4100 (unreferenced formal parameter) and 4805 +// unsafe mix of type 'const int' and type 'const bool' +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable : 4805) +#pragma warning(disable : 4100) +#endif + +// The upper limit for valid stack trace depths. +const int kMaxStackTraceDepth = 100; + +namespace internal { + +class AssertHelper; +class DefaultGlobalTestPartResultReporter; +class ExecDeathTest; +class NoExecDeathTest; +class FinalSuccessChecker; +class GTestFlagSaver; +class StreamingListenerTest; +class TestResultAccessor; +class TestEventListenersAccessor; +class TestEventRepeater; +class UnitTestRecordPropertyTestHelper; +class WindowsDeathTest; +class FuchsiaDeathTest; +class UnitTestImpl* GetUnitTestImpl(); +void ReportFailureInUnknownLocation(TestPartResult::Type result_type, + const std::string& message); +std::set* GetIgnoredParameterizedTestSuites(); + +// A base class that prevents subclasses from being copyable. +// We do this instead of using '= delete' so as to avoid triggering warnings +// inside user code regarding any of our declarations. +class GTestNonCopyable { + public: + GTestNonCopyable() = default; + GTestNonCopyable(const GTestNonCopyable &) = delete; + GTestNonCopyable &operator=(const GTestNonCopyable &) = delete; + ~GTestNonCopyable() = default; +}; + +} // namespace internal + +// The friend relationship of some of these classes is cyclic. +// If we don't forward declare them the compiler might confuse the classes +// in friendship clauses with same named classes on the scope. +class Test; +class TestSuite; + +// Old API is still available but deprecated +#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_ +using TestCase = TestSuite; +#endif +class TestInfo; +class UnitTest; + +// The abstract class that all tests inherit from. +// +// In Google Test, a unit test program contains one or many TestSuites, and +// each TestSuite contains one or many Tests. +// +// When you define a test using the TEST macro, you don't need to +// explicitly derive from Test - the TEST macro automatically does +// this for you. +// +// The only time you derive from Test is when defining a test fixture +// to be used in a TEST_F. For example: +// +// class FooTest : public testing::Test { +// protected: +// void SetUp() override { ... } +// void TearDown() override { ... } +// ... +// }; +// +// TEST_F(FooTest, Bar) { ... } +// TEST_F(FooTest, Baz) { ... } +// +// Test is not copyable. +class GTEST_API_ Test { + public: + friend class TestInfo; + + // The d'tor is virtual as we intend to inherit from Test. + virtual ~Test(); + + // Sets up the stuff shared by all tests in this test suite. + // + // Google Test will call Foo::SetUpTestSuite() before running the first + // test in test suite Foo. Hence a sub-class can define its own + // SetUpTestSuite() method to shadow the one defined in the super + // class. + static void SetUpTestSuite() {} + + // Tears down the stuff shared by all tests in this test suite. + // + // Google Test will call Foo::TearDownTestSuite() after running the last + // test in test suite Foo. Hence a sub-class can define its own + // TearDownTestSuite() method to shadow the one defined in the super + // class. + static void TearDownTestSuite() {} + + // Legacy API is deprecated but still available. Use SetUpTestSuite and + // TearDownTestSuite instead. +#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_ + static void TearDownTestCase() {} + static void SetUpTestCase() {} +#endif // GTEST_REMOVE_LEGACY_TEST_CASEAPI_ + + // Returns true if and only if the current test has a fatal failure. + static bool HasFatalFailure(); + + // Returns true if and only if the current test has a non-fatal failure. + static bool HasNonfatalFailure(); + + // Returns true if and only if the current test was skipped. + static bool IsSkipped(); + + // Returns true if and only if the current test has a (either fatal or + // non-fatal) failure. + static bool HasFailure() { return HasFatalFailure() || HasNonfatalFailure(); } + + // Logs a property for the current test, test suite, or for the entire + // invocation of the test program when used outside of the context of a + // test suite. Only the last value for a given key is remembered. These + // are public static so they can be called from utility functions that are + // not members of the test fixture. Calls to RecordProperty made during + // lifespan of the test (from the moment its constructor starts to the + // moment its destructor finishes) will be output in XML as attributes of + // the element. Properties recorded from fixture's + // SetUpTestSuite or TearDownTestSuite are logged as attributes of the + // corresponding element. Calls to RecordProperty made in the + // global context (before or after invocation of RUN_ALL_TESTS and from + // SetUp/TearDown method of Environment objects registered with Google + // Test) will be output as attributes of the element. + static void RecordProperty(const std::string& key, const std::string& value); + static void RecordProperty(const std::string& key, int value); + + protected: + // Creates a Test object. + Test(); + + // Sets up the test fixture. + virtual void SetUp(); + + // Tears down the test fixture. + virtual void TearDown(); + + private: + // Returns true if and only if the current test has the same fixture class + // as the first test in the current test suite. + static bool HasSameFixtureClass(); + + // Runs the test after the test fixture has been set up. + // + // A sub-class must implement this to define the test logic. + // + // DO NOT OVERRIDE THIS FUNCTION DIRECTLY IN A USER PROGRAM. + // Instead, use the TEST or TEST_F macro. + virtual void TestBody() = 0; + + // Sets up, executes, and tears down the test. + void Run(); + + // Deletes self. We deliberately pick an unusual name for this + // internal method to avoid clashing with names used in user TESTs. + void DeleteSelf_() { delete this; } + + const std::unique_ptr gtest_flag_saver_; + + // Often a user misspells SetUp() as Setup() and spends a long time + // wondering why it is never called by Google Test. The declaration of + // the following method is solely for catching such an error at + // compile time: + // + // - The return type is deliberately chosen to be not void, so it + // will be a conflict if void Setup() is declared in the user's + // test fixture. + // + // - This method is private, so it will be another compiler error + // if the method is called from the user's test fixture. + // + // DO NOT OVERRIDE THIS FUNCTION. + // + // If you see an error about overriding the following function or + // about it being private, you have mis-spelled SetUp() as Setup(). + struct Setup_should_be_spelled_SetUp {}; + virtual Setup_should_be_spelled_SetUp* Setup() { return nullptr; } + + // We disallow copying Tests. + Test(const Test&) = delete; + Test& operator=(const Test&) = delete; +}; + +typedef internal::TimeInMillis TimeInMillis; + +// A copyable object representing a user specified test property which can be +// output as a key/value string pair. +// +// Don't inherit from TestProperty as its destructor is not virtual. +class TestProperty { + public: + // C'tor. TestProperty does NOT have a default constructor. + // Always use this constructor (with parameters) to create a + // TestProperty object. + TestProperty(const std::string& a_key, const std::string& a_value) + : key_(a_key), value_(a_value) {} + + // Gets the user supplied key. + const char* key() const { return key_.c_str(); } + + // Gets the user supplied value. + const char* value() const { return value_.c_str(); } + + // Sets a new value, overriding the one supplied in the constructor. + void SetValue(const std::string& new_value) { value_ = new_value; } + + private: + // The key supplied by the user. + std::string key_; + // The value supplied by the user. + std::string value_; +}; + +// The result of a single Test. This includes a list of +// TestPartResults, a list of TestProperties, a count of how many +// death tests there are in the Test, and how much time it took to run +// the Test. +// +// TestResult is not copyable. +class GTEST_API_ TestResult { + public: + // Creates an empty TestResult. + TestResult(); + + // D'tor. Do not inherit from TestResult. + ~TestResult(); + + // Gets the number of all test parts. This is the sum of the number + // of successful test parts and the number of failed test parts. + int total_part_count() const; + + // Returns the number of the test properties. + int test_property_count() const; + + // Returns true if and only if the test passed (i.e. no test part failed). + bool Passed() const { return !Skipped() && !Failed(); } + + // Returns true if and only if the test was skipped. + bool Skipped() const; + + // Returns true if and only if the test failed. + bool Failed() const; + + // Returns true if and only if the test fatally failed. + bool HasFatalFailure() const; + + // Returns true if and only if the test has a non-fatal failure. + bool HasNonfatalFailure() const; + + // Returns the elapsed time, in milliseconds. + TimeInMillis elapsed_time() const { return elapsed_time_; } + + // Gets the time of the test case start, in ms from the start of the + // UNIX epoch. + TimeInMillis start_timestamp() const { return start_timestamp_; } + + // Returns the i-th test part result among all the results. i can range from 0 + // to total_part_count() - 1. If i is not in that range, aborts the program. + const TestPartResult& GetTestPartResult(int i) const; + + // Returns the i-th test property. i can range from 0 to + // test_property_count() - 1. If i is not in that range, aborts the + // program. + const TestProperty& GetTestProperty(int i) const; + + private: + friend class TestInfo; + friend class TestSuite; + friend class UnitTest; + friend class internal::DefaultGlobalTestPartResultReporter; + friend class internal::ExecDeathTest; + friend class internal::TestResultAccessor; + friend class internal::UnitTestImpl; + friend class internal::WindowsDeathTest; + friend class internal::FuchsiaDeathTest; + + // Gets the vector of TestPartResults. + const std::vector& test_part_results() const { + return test_part_results_; + } + + // Gets the vector of TestProperties. + const std::vector& test_properties() const { + return test_properties_; + } + + // Sets the start time. + void set_start_timestamp(TimeInMillis start) { start_timestamp_ = start; } + + // Sets the elapsed time. + void set_elapsed_time(TimeInMillis elapsed) { elapsed_time_ = elapsed; } + + // Adds a test property to the list. The property is validated and may add + // a non-fatal failure if invalid (e.g., if it conflicts with reserved + // key names). If a property is already recorded for the same key, the + // value will be updated, rather than storing multiple values for the same + // key. xml_element specifies the element for which the property is being + // recorded and is used for validation. + void RecordProperty(const std::string& xml_element, + const TestProperty& test_property); + + // Adds a failure if the key is a reserved attribute of Google Test + // testsuite tags. Returns true if the property is valid. + // FIXME: Validate attribute names are legal and human readable. + static bool ValidateTestProperty(const std::string& xml_element, + const TestProperty& test_property); + + // Adds a test part result to the list. + void AddTestPartResult(const TestPartResult& test_part_result); + + // Returns the death test count. + int death_test_count() const { return death_test_count_; } + + // Increments the death test count, returning the new count. + int increment_death_test_count() { return ++death_test_count_; } + + // Clears the test part results. + void ClearTestPartResults(); + + // Clears the object. + void Clear(); + + // Protects mutable state of the property vector and of owned + // properties, whose values may be updated. + internal::Mutex test_properties_mutex_; + + // The vector of TestPartResults + std::vector test_part_results_; + // The vector of TestProperties + std::vector test_properties_; + // Running count of death tests. + int death_test_count_; + // The start time, in milliseconds since UNIX Epoch. + TimeInMillis start_timestamp_; + // The elapsed time, in milliseconds. + TimeInMillis elapsed_time_; + + // We disallow copying TestResult. + TestResult(const TestResult&) = delete; + TestResult& operator=(const TestResult&) = delete; +}; // class TestResult + +// A TestInfo object stores the following information about a test: +// +// Test suite name +// Test name +// Whether the test should be run +// A function pointer that creates the test object when invoked +// Test result +// +// The constructor of TestInfo registers itself with the UnitTest +// singleton such that the RUN_ALL_TESTS() macro knows which tests to +// run. +class GTEST_API_ TestInfo { + public: + // Destructs a TestInfo object. This function is not virtual, so + // don't inherit from TestInfo. + ~TestInfo(); + + // Returns the test suite name. + const char* test_suite_name() const { return test_suite_name_.c_str(); } + +// Legacy API is deprecated but still available +#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_ + const char* test_case_name() const { return test_suite_name(); } +#endif // GTEST_REMOVE_LEGACY_TEST_CASEAPI_ + + // Returns the test name. + const char* name() const { return name_.c_str(); } + + // Returns the name of the parameter type, or NULL if this is not a typed + // or a type-parameterized test. + const char* type_param() const { + if (type_param_.get() != nullptr) return type_param_->c_str(); + return nullptr; + } + + // Returns the text representation of the value parameter, or NULL if this + // is not a value-parameterized test. + const char* value_param() const { + if (value_param_.get() != nullptr) return value_param_->c_str(); + return nullptr; + } + + // Returns the file name where this test is defined. + const char* file() const { return location_.file.c_str(); } + + // Returns the line where this test is defined. + int line() const { return location_.line; } + + // Return true if this test should not be run because it's in another shard. + bool is_in_another_shard() const { return is_in_another_shard_; } + + // Returns true if this test should run, that is if the test is not + // disabled (or it is disabled but the also_run_disabled_tests flag has + // been specified) and its full name matches the user-specified filter. + // + // Google Test allows the user to filter the tests by their full names. + // The full name of a test Bar in test suite Foo is defined as + // "Foo.Bar". Only the tests that match the filter will run. + // + // A filter is a colon-separated list of glob (not regex) patterns, + // optionally followed by a '-' and a colon-separated list of + // negative patterns (tests to exclude). A test is run if it + // matches one of the positive patterns and does not match any of + // the negative patterns. + // + // For example, *A*:Foo.* is a filter that matches any string that + // contains the character 'A' or starts with "Foo.". + bool should_run() const { return should_run_; } + + // Returns true if and only if this test will appear in the XML report. + bool is_reportable() const { + // The XML report includes tests matching the filter, excluding those + // run in other shards. + return matches_filter_ && !is_in_another_shard_; + } + + // Returns the result of the test. + const TestResult* result() const { return &result_; } + + private: +#if GTEST_HAS_DEATH_TEST + friend class internal::DefaultDeathTestFactory; +#endif // GTEST_HAS_DEATH_TEST + friend class Test; + friend class TestSuite; + friend class internal::UnitTestImpl; + friend class internal::StreamingListenerTest; + friend TestInfo* internal::MakeAndRegisterTestInfo( + const char* test_suite_name, const char* name, const char* type_param, + const char* value_param, internal::CodeLocation code_location, + internal::TypeId fixture_class_id, internal::SetUpTestSuiteFunc set_up_tc, + internal::TearDownTestSuiteFunc tear_down_tc, + internal::TestFactoryBase* factory); + + // Constructs a TestInfo object. The newly constructed instance assumes + // ownership of the factory object. + TestInfo(const std::string& test_suite_name, const std::string& name, + const char* a_type_param, // NULL if not a type-parameterized test + const char* a_value_param, // NULL if not a value-parameterized test + internal::CodeLocation a_code_location, + internal::TypeId fixture_class_id, + internal::TestFactoryBase* factory); + + // Increments the number of death tests encountered in this test so + // far. + int increment_death_test_count() { + return result_.increment_death_test_count(); + } + + // Creates the test object, runs it, records its result, and then + // deletes it. + void Run(); + + // Skip and records the test result for this object. + void Skip(); + + static void ClearTestResult(TestInfo* test_info) { + test_info->result_.Clear(); + } + + // These fields are immutable properties of the test. + const std::string test_suite_name_; // test suite name + const std::string name_; // Test name + // Name of the parameter type, or NULL if this is not a typed or a + // type-parameterized test. + const std::unique_ptr type_param_; + // Text representation of the value parameter, or NULL if this is not a + // value-parameterized test. + const std::unique_ptr value_param_; + internal::CodeLocation location_; + const internal::TypeId fixture_class_id_; // ID of the test fixture class + bool should_run_; // True if and only if this test should run + bool is_disabled_; // True if and only if this test is disabled + bool matches_filter_; // True if this test matches the + // user-specified filter. + bool is_in_another_shard_; // Will be run in another shard. + internal::TestFactoryBase* const factory_; // The factory that creates + // the test object + + // This field is mutable and needs to be reset before running the + // test for the second time. + TestResult result_; + + TestInfo(const TestInfo&) = delete; + TestInfo& operator=(const TestInfo&) = delete; +}; + +// A test suite, which consists of a vector of TestInfos. +// +// TestSuite is not copyable. +class GTEST_API_ TestSuite { + public: + // Creates a TestSuite with the given name. + // + // TestSuite does NOT have a default constructor. Always use this + // constructor to create a TestSuite object. + // + // Arguments: + // + // name: name of the test suite + // a_type_param: the name of the test's type parameter, or NULL if + // this is not a type-parameterized test. + // set_up_tc: pointer to the function that sets up the test suite + // tear_down_tc: pointer to the function that tears down the test suite + TestSuite(const char* name, const char* a_type_param, + internal::SetUpTestSuiteFunc set_up_tc, + internal::TearDownTestSuiteFunc tear_down_tc); + + // Destructor of TestSuite. + virtual ~TestSuite(); + + // Gets the name of the TestSuite. + const char* name() const { return name_.c_str(); } + + // Returns the name of the parameter type, or NULL if this is not a + // type-parameterized test suite. + const char* type_param() const { + if (type_param_.get() != nullptr) return type_param_->c_str(); + return nullptr; + } + + // Returns true if any test in this test suite should run. + bool should_run() const { return should_run_; } + + // Gets the number of successful tests in this test suite. + int successful_test_count() const; + + // Gets the number of skipped tests in this test suite. + int skipped_test_count() const; + + // Gets the number of failed tests in this test suite. + int failed_test_count() const; + + // Gets the number of disabled tests that will be reported in the XML report. + int reportable_disabled_test_count() const; + + // Gets the number of disabled tests in this test suite. + int disabled_test_count() const; + + // Gets the number of tests to be printed in the XML report. + int reportable_test_count() const; + + // Get the number of tests in this test suite that should run. + int test_to_run_count() const; + + // Gets the number of all tests in this test suite. + int total_test_count() const; + + // Returns true if and only if the test suite passed. + bool Passed() const { return !Failed(); } + + // Returns true if and only if the test suite failed. + bool Failed() const { + return failed_test_count() > 0 || ad_hoc_test_result().Failed(); + } + + // Returns the elapsed time, in milliseconds. + TimeInMillis elapsed_time() const { return elapsed_time_; } + + // Gets the time of the test suite start, in ms from the start of the + // UNIX epoch. + TimeInMillis start_timestamp() const { return start_timestamp_; } + + // Returns the i-th test among all the tests. i can range from 0 to + // total_test_count() - 1. If i is not in that range, returns NULL. + const TestInfo* GetTestInfo(int i) const; + + // Returns the TestResult that holds test properties recorded during + // execution of SetUpTestSuite and TearDownTestSuite. + const TestResult& ad_hoc_test_result() const { return ad_hoc_test_result_; } + + private: + friend class Test; + friend class internal::UnitTestImpl; + + // Gets the (mutable) vector of TestInfos in this TestSuite. + std::vector& test_info_list() { return test_info_list_; } + + // Gets the (immutable) vector of TestInfos in this TestSuite. + const std::vector& test_info_list() const { + return test_info_list_; + } + + // Returns the i-th test among all the tests. i can range from 0 to + // total_test_count() - 1. If i is not in that range, returns NULL. + TestInfo* GetMutableTestInfo(int i); + + // Sets the should_run member. + void set_should_run(bool should) { should_run_ = should; } + + // Adds a TestInfo to this test suite. Will delete the TestInfo upon + // destruction of the TestSuite object. + void AddTestInfo(TestInfo* test_info); + + // Clears the results of all tests in this test suite. + void ClearResult(); + + // Clears the results of all tests in the given test suite. + static void ClearTestSuiteResult(TestSuite* test_suite) { + test_suite->ClearResult(); + } + + // Runs every test in this TestSuite. + void Run(); + + // Skips the execution of tests under this TestSuite + void Skip(); + + // Runs SetUpTestSuite() for this TestSuite. This wrapper is needed + // for catching exceptions thrown from SetUpTestSuite(). + void RunSetUpTestSuite() { + if (set_up_tc_ != nullptr) { + (*set_up_tc_)(); + } + } + + // Runs TearDownTestSuite() for this TestSuite. This wrapper is + // needed for catching exceptions thrown from TearDownTestSuite(). + void RunTearDownTestSuite() { + if (tear_down_tc_ != nullptr) { + (*tear_down_tc_)(); + } + } + + // Returns true if and only if test passed. + static bool TestPassed(const TestInfo* test_info) { + return test_info->should_run() && test_info->result()->Passed(); + } + + // Returns true if and only if test skipped. + static bool TestSkipped(const TestInfo* test_info) { + return test_info->should_run() && test_info->result()->Skipped(); + } + + // Returns true if and only if test failed. + static bool TestFailed(const TestInfo* test_info) { + return test_info->should_run() && test_info->result()->Failed(); + } + + // Returns true if and only if the test is disabled and will be reported in + // the XML report. + static bool TestReportableDisabled(const TestInfo* test_info) { + return test_info->is_reportable() && test_info->is_disabled_; + } + + // Returns true if and only if test is disabled. + static bool TestDisabled(const TestInfo* test_info) { + return test_info->is_disabled_; + } + + // Returns true if and only if this test will appear in the XML report. + static bool TestReportable(const TestInfo* test_info) { + return test_info->is_reportable(); + } + + // Returns true if the given test should run. + static bool ShouldRunTest(const TestInfo* test_info) { + return test_info->should_run(); + } + + // Shuffles the tests in this test suite. + void ShuffleTests(internal::Random* random); + + // Restores the test order to before the first shuffle. + void UnshuffleTests(); + + // Name of the test suite. + std::string name_; + // Name of the parameter type, or NULL if this is not a typed or a + // type-parameterized test. + const std::unique_ptr type_param_; + // The vector of TestInfos in their original order. It owns the + // elements in the vector. + std::vector test_info_list_; + // Provides a level of indirection for the test list to allow easy + // shuffling and restoring the test order. The i-th element in this + // vector is the index of the i-th test in the shuffled test list. + std::vector test_indices_; + // Pointer to the function that sets up the test suite. + internal::SetUpTestSuiteFunc set_up_tc_; + // Pointer to the function that tears down the test suite. + internal::TearDownTestSuiteFunc tear_down_tc_; + // True if and only if any test in this test suite should run. + bool should_run_; + // The start time, in milliseconds since UNIX Epoch. + TimeInMillis start_timestamp_; + // Elapsed time, in milliseconds. + TimeInMillis elapsed_time_; + // Holds test properties recorded during execution of SetUpTestSuite and + // TearDownTestSuite. + TestResult ad_hoc_test_result_; + + // We disallow copying TestSuites. + TestSuite(const TestSuite&) = delete; + TestSuite& operator=(const TestSuite&) = delete; +}; + +// An Environment object is capable of setting up and tearing down an +// environment. You should subclass this to define your own +// environment(s). +// +// An Environment object does the set-up and tear-down in virtual +// methods SetUp() and TearDown() instead of the constructor and the +// destructor, as: +// +// 1. You cannot safely throw from a destructor. This is a problem +// as in some cases Google Test is used where exceptions are enabled, and +// we may want to implement ASSERT_* using exceptions where they are +// available. +// 2. You cannot use ASSERT_* directly in a constructor or +// destructor. +class Environment { + public: + // The d'tor is virtual as we need to subclass Environment. + virtual ~Environment() {} + + // Override this to define how to set up the environment. + virtual void SetUp() {} + + // Override this to define how to tear down the environment. + virtual void TearDown() {} + + private: + // If you see an error about overriding the following function or + // about it being private, you have mis-spelled SetUp() as Setup(). + struct Setup_should_be_spelled_SetUp {}; + virtual Setup_should_be_spelled_SetUp* Setup() { return nullptr; } +}; + +#if GTEST_HAS_EXCEPTIONS + +// Exception which can be thrown from TestEventListener::OnTestPartResult. +class GTEST_API_ AssertionException + : public internal::GoogleTestFailureException { + public: + explicit AssertionException(const TestPartResult& result) + : GoogleTestFailureException(result) {} +}; + +#endif // GTEST_HAS_EXCEPTIONS + +// The interface for tracing execution of tests. The methods are organized in +// the order the corresponding events are fired. +class TestEventListener { + public: + virtual ~TestEventListener() {} + + // Fired before any test activity starts. + virtual void OnTestProgramStart(const UnitTest& unit_test) = 0; + + // Fired before each iteration of tests starts. There may be more than + // one iteration if GTEST_FLAG(repeat) is set. iteration is the iteration + // index, starting from 0. + virtual void OnTestIterationStart(const UnitTest& unit_test, + int iteration) = 0; + + // Fired before environment set-up for each iteration of tests starts. + virtual void OnEnvironmentsSetUpStart(const UnitTest& unit_test) = 0; + + // Fired after environment set-up for each iteration of tests ends. + virtual void OnEnvironmentsSetUpEnd(const UnitTest& unit_test) = 0; + + // Fired before the test suite starts. + virtual void OnTestSuiteStart(const TestSuite& /*test_suite*/) {} + + // Legacy API is deprecated but still available +#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_ + virtual void OnTestCaseStart(const TestCase& /*test_case*/) {} +#endif // GTEST_REMOVE_LEGACY_TEST_CASEAPI_ + + // Fired before the test starts. + virtual void OnTestStart(const TestInfo& test_info) = 0; + + // Fired when a test is disabled + virtual void OnTestDisabled(const TestInfo& /*test_info*/) {} + + // Fired after a failed assertion or a SUCCEED() invocation. + // If you want to throw an exception from this function to skip to the next + // TEST, it must be AssertionException defined above, or inherited from it. + virtual void OnTestPartResult(const TestPartResult& test_part_result) = 0; + + // Fired after the test ends. + virtual void OnTestEnd(const TestInfo& test_info) = 0; + + // Fired after the test suite ends. + virtual void OnTestSuiteEnd(const TestSuite& /*test_suite*/) {} + +// Legacy API is deprecated but still available +#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_ + virtual void OnTestCaseEnd(const TestCase& /*test_case*/) {} +#endif // GTEST_REMOVE_LEGACY_TEST_CASEAPI_ + + // Fired before environment tear-down for each iteration of tests starts. + virtual void OnEnvironmentsTearDownStart(const UnitTest& unit_test) = 0; + + // Fired after environment tear-down for each iteration of tests ends. + virtual void OnEnvironmentsTearDownEnd(const UnitTest& unit_test) = 0; + + // Fired after each iteration of tests finishes. + virtual void OnTestIterationEnd(const UnitTest& unit_test, int iteration) = 0; + + // Fired after all test activities have ended. + virtual void OnTestProgramEnd(const UnitTest& unit_test) = 0; +}; + +// The convenience class for users who need to override just one or two +// methods and are not concerned that a possible change to a signature of +// the methods they override will not be caught during the build. For +// comments about each method please see the definition of TestEventListener +// above. +class EmptyTestEventListener : public TestEventListener { + public: + void OnTestProgramStart(const UnitTest& /*unit_test*/) override {} + void OnTestIterationStart(const UnitTest& /*unit_test*/, + int /*iteration*/) override {} + void OnEnvironmentsSetUpStart(const UnitTest& /*unit_test*/) override {} + void OnEnvironmentsSetUpEnd(const UnitTest& /*unit_test*/) override {} + void OnTestSuiteStart(const TestSuite& /*test_suite*/) override {} +// Legacy API is deprecated but still available +#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_ + void OnTestCaseStart(const TestCase& /*test_case*/) override {} +#endif // GTEST_REMOVE_LEGACY_TEST_CASEAPI_ + + void OnTestStart(const TestInfo& /*test_info*/) override {} + void OnTestDisabled(const TestInfo& /*test_info*/) override {} + void OnTestPartResult(const TestPartResult& /*test_part_result*/) override {} + void OnTestEnd(const TestInfo& /*test_info*/) override {} + void OnTestSuiteEnd(const TestSuite& /*test_suite*/) override {} +#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_ + void OnTestCaseEnd(const TestCase& /*test_case*/) override {} +#endif // GTEST_REMOVE_LEGACY_TEST_CASEAPI_ + + void OnEnvironmentsTearDownStart(const UnitTest& /*unit_test*/) override {} + void OnEnvironmentsTearDownEnd(const UnitTest& /*unit_test*/) override {} + void OnTestIterationEnd(const UnitTest& /*unit_test*/, + int /*iteration*/) override {} + void OnTestProgramEnd(const UnitTest& /*unit_test*/) override {} +}; + +// TestEventListeners lets users add listeners to track events in Google Test. +class GTEST_API_ TestEventListeners { + public: + TestEventListeners(); + ~TestEventListeners(); + + // Appends an event listener to the end of the list. Google Test assumes + // the ownership of the listener (i.e. it will delete the listener when + // the test program finishes). + void Append(TestEventListener* listener); + + // Removes the given event listener from the list and returns it. It then + // becomes the caller's responsibility to delete the listener. Returns + // NULL if the listener is not found in the list. + TestEventListener* Release(TestEventListener* listener); + + // Returns the standard listener responsible for the default console + // output. Can be removed from the listeners list to shut down default + // console output. Note that removing this object from the listener list + // with Release transfers its ownership to the caller and makes this + // function return NULL the next time. + TestEventListener* default_result_printer() const { + return default_result_printer_; + } + + // Returns the standard listener responsible for the default XML output + // controlled by the --gtest_output=xml flag. Can be removed from the + // listeners list by users who want to shut down the default XML output + // controlled by this flag and substitute it with custom one. Note that + // removing this object from the listener list with Release transfers its + // ownership to the caller and makes this function return NULL the next + // time. + TestEventListener* default_xml_generator() const { + return default_xml_generator_; + } + + private: + friend class TestSuite; + friend class TestInfo; + friend class internal::DefaultGlobalTestPartResultReporter; + friend class internal::NoExecDeathTest; + friend class internal::TestEventListenersAccessor; + friend class internal::UnitTestImpl; + + // Returns repeater that broadcasts the TestEventListener events to all + // subscribers. + TestEventListener* repeater(); + + // Sets the default_result_printer attribute to the provided listener. + // The listener is also added to the listener list and previous + // default_result_printer is removed from it and deleted. The listener can + // also be NULL in which case it will not be added to the list. Does + // nothing if the previous and the current listener objects are the same. + void SetDefaultResultPrinter(TestEventListener* listener); + + // Sets the default_xml_generator attribute to the provided listener. The + // listener is also added to the listener list and previous + // default_xml_generator is removed from it and deleted. The listener can + // also be NULL in which case it will not be added to the list. Does + // nothing if the previous and the current listener objects are the same. + void SetDefaultXmlGenerator(TestEventListener* listener); + + // Controls whether events will be forwarded by the repeater to the + // listeners in the list. + bool EventForwardingEnabled() const; + void SuppressEventForwarding(); + + // The actual list of listeners. + internal::TestEventRepeater* repeater_; + // Listener responsible for the standard result output. + TestEventListener* default_result_printer_; + // Listener responsible for the creation of the XML output file. + TestEventListener* default_xml_generator_; + + // We disallow copying TestEventListeners. + TestEventListeners(const TestEventListeners&) = delete; + TestEventListeners& operator=(const TestEventListeners&) = delete; +}; + +// A UnitTest consists of a vector of TestSuites. +// +// This is a singleton class. The only instance of UnitTest is +// created when UnitTest::GetInstance() is first called. This +// instance is never deleted. +// +// UnitTest is not copyable. +// +// This class is thread-safe as long as the methods are called +// according to their specification. +class GTEST_API_ UnitTest { + public: + // Gets the singleton UnitTest object. The first time this method + // is called, a UnitTest object is constructed and returned. + // Consecutive calls will return the same object. + static UnitTest* GetInstance(); + + // Runs all tests in this UnitTest object and prints the result. + // Returns 0 if successful, or 1 otherwise. + // + // This method can only be called from the main thread. + // + // INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. + int Run() GTEST_MUST_USE_RESULT_; + + // Returns the working directory when the first TEST() or TEST_F() + // was executed. The UnitTest object owns the string. + const char* original_working_dir() const; + + // Returns the TestSuite object for the test that's currently running, + // or NULL if no test is running. + const TestSuite* current_test_suite() const GTEST_LOCK_EXCLUDED_(mutex_); + +// Legacy API is still available but deprecated +#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_ + const TestCase* current_test_case() const GTEST_LOCK_EXCLUDED_(mutex_); +#endif + + // Returns the TestInfo object for the test that's currently running, + // or NULL if no test is running. + const TestInfo* current_test_info() const GTEST_LOCK_EXCLUDED_(mutex_); + + // Returns the random seed used at the start of the current test run. + int random_seed() const; + + // Returns the ParameterizedTestSuiteRegistry object used to keep track of + // value-parameterized tests and instantiate and register them. + // + // INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. + internal::ParameterizedTestSuiteRegistry& parameterized_test_registry() + GTEST_LOCK_EXCLUDED_(mutex_); + + // Gets the number of successful test suites. + int successful_test_suite_count() const; + + // Gets the number of failed test suites. + int failed_test_suite_count() const; + + // Gets the number of all test suites. + int total_test_suite_count() const; + + // Gets the number of all test suites that contain at least one test + // that should run. + int test_suite_to_run_count() const; + + // Legacy API is deprecated but still available +#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_ + int successful_test_case_count() const; + int failed_test_case_count() const; + int total_test_case_count() const; + int test_case_to_run_count() const; +#endif // GTEST_REMOVE_LEGACY_TEST_CASEAPI_ + + // Gets the number of successful tests. + int successful_test_count() const; + + // Gets the number of skipped tests. + int skipped_test_count() const; + + // Gets the number of failed tests. + int failed_test_count() const; + + // Gets the number of disabled tests that will be reported in the XML report. + int reportable_disabled_test_count() const; + + // Gets the number of disabled tests. + int disabled_test_count() const; + + // Gets the number of tests to be printed in the XML report. + int reportable_test_count() const; + + // Gets the number of all tests. + int total_test_count() const; + + // Gets the number of tests that should run. + int test_to_run_count() const; + + // Gets the time of the test program start, in ms from the start of the + // UNIX epoch. + TimeInMillis start_timestamp() const; + + // Gets the elapsed time, in milliseconds. + TimeInMillis elapsed_time() const; + + // Returns true if and only if the unit test passed (i.e. all test suites + // passed). + bool Passed() const; + + // Returns true if and only if the unit test failed (i.e. some test suite + // failed or something outside of all tests failed). + bool Failed() const; + + // Gets the i-th test suite among all the test suites. i can range from 0 to + // total_test_suite_count() - 1. If i is not in that range, returns NULL. + const TestSuite* GetTestSuite(int i) const; + +// Legacy API is deprecated but still available +#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_ + const TestCase* GetTestCase(int i) const; +#endif // GTEST_REMOVE_LEGACY_TEST_CASEAPI_ + + // Returns the TestResult containing information on test failures and + // properties logged outside of individual test suites. + const TestResult& ad_hoc_test_result() const; + + // Returns the list of event listeners that can be used to track events + // inside Google Test. + TestEventListeners& listeners(); + + private: + // Registers and returns a global test environment. When a test + // program is run, all global test environments will be set-up in + // the order they were registered. After all tests in the program + // have finished, all global test environments will be torn-down in + // the *reverse* order they were registered. + // + // The UnitTest object takes ownership of the given environment. + // + // This method can only be called from the main thread. + Environment* AddEnvironment(Environment* env); + + // Adds a TestPartResult to the current TestResult object. All + // Google Test assertion macros (e.g. ASSERT_TRUE, EXPECT_EQ, etc) + // eventually call this to report their results. The user code + // should use the assertion macros instead of calling this directly. + void AddTestPartResult(TestPartResult::Type result_type, + const char* file_name, int line_number, + const std::string& message, + const std::string& os_stack_trace) + GTEST_LOCK_EXCLUDED_(mutex_); + + // Adds a TestProperty to the current TestResult object when invoked from + // inside a test, to current TestSuite's ad_hoc_test_result_ when invoked + // from SetUpTestSuite or TearDownTestSuite, or to the global property set + // when invoked elsewhere. If the result already contains a property with + // the same key, the value will be updated. + void RecordProperty(const std::string& key, const std::string& value); + + // Gets the i-th test suite among all the test suites. i can range from 0 to + // total_test_suite_count() - 1. If i is not in that range, returns NULL. + TestSuite* GetMutableTestSuite(int i); + + // Accessors for the implementation object. + internal::UnitTestImpl* impl() { return impl_; } + const internal::UnitTestImpl* impl() const { return impl_; } + + // These classes and functions are friends as they need to access private + // members of UnitTest. + friend class ScopedTrace; + friend class Test; + friend class internal::AssertHelper; + friend class internal::StreamingListenerTest; + friend class internal::UnitTestRecordPropertyTestHelper; + friend Environment* AddGlobalTestEnvironment(Environment* env); + friend std::set* internal::GetIgnoredParameterizedTestSuites(); + friend internal::UnitTestImpl* internal::GetUnitTestImpl(); + friend void internal::ReportFailureInUnknownLocation( + TestPartResult::Type result_type, const std::string& message); + + // Creates an empty UnitTest. + UnitTest(); + + // D'tor + virtual ~UnitTest(); + + // Pushes a trace defined by SCOPED_TRACE() on to the per-thread + // Google Test trace stack. + void PushGTestTrace(const internal::TraceInfo& trace) + GTEST_LOCK_EXCLUDED_(mutex_); + + // Pops a trace from the per-thread Google Test trace stack. + void PopGTestTrace() GTEST_LOCK_EXCLUDED_(mutex_); + + // Protects mutable state in *impl_. This is mutable as some const + // methods need to lock it too. + mutable internal::Mutex mutex_; + + // Opaque implementation object. This field is never changed once + // the object is constructed. We don't mark it as const here, as + // doing so will cause a warning in the constructor of UnitTest. + // Mutable state in *impl_ is protected by mutex_. + internal::UnitTestImpl* impl_; + + // We disallow copying UnitTest. + UnitTest(const UnitTest&) = delete; + UnitTest& operator=(const UnitTest&) = delete; +}; + +// A convenient wrapper for adding an environment for the test +// program. +// +// You should call this before RUN_ALL_TESTS() is called, probably in +// main(). If you use gtest_main, you need to call this before main() +// starts for it to take effect. For example, you can define a global +// variable like this: +// +// testing::Environment* const foo_env = +// testing::AddGlobalTestEnvironment(new FooEnvironment); +// +// However, we strongly recommend you to write your own main() and +// call AddGlobalTestEnvironment() there, as relying on initialization +// of global variables makes the code harder to read and may cause +// problems when you register multiple environments from different +// translation units and the environments have dependencies among them +// (remember that the compiler doesn't guarantee the order in which +// global variables from different translation units are initialized). +inline Environment* AddGlobalTestEnvironment(Environment* env) { + return UnitTest::GetInstance()->AddEnvironment(env); +} + +// Initializes Google Test. This must be called before calling +// RUN_ALL_TESTS(). In particular, it parses a command line for the +// flags that Google Test recognizes. Whenever a Google Test flag is +// seen, it is removed from argv, and *argc is decremented. +// +// No value is returned. Instead, the Google Test flag variables are +// updated. +// +// Calling the function for the second time has no user-visible effect. +GTEST_API_ void InitGoogleTest(int* argc, char** argv); + +// This overloaded version can be used in Windows programs compiled in +// UNICODE mode. +GTEST_API_ void InitGoogleTest(int* argc, wchar_t** argv); + +// This overloaded version can be used on Arduino/embedded platforms where +// there is no argc/argv. +GTEST_API_ void InitGoogleTest(); + +namespace internal { + +// Separate the error generating code from the code path to reduce the stack +// frame size of CmpHelperEQ. This helps reduce the overhead of some sanitizers +// when calling EXPECT_* in a tight loop. +template +AssertionResult CmpHelperEQFailure(const char* lhs_expression, + const char* rhs_expression, const T1& lhs, + const T2& rhs) { + return EqFailure(lhs_expression, rhs_expression, + FormatForComparisonFailureMessage(lhs, rhs), + FormatForComparisonFailureMessage(rhs, lhs), false); +} + +// This block of code defines operator==/!= +// to block lexical scope lookup. +// It prevents using invalid operator==/!= defined at namespace scope. +struct faketype {}; +inline bool operator==(faketype, faketype) { return true; } +inline bool operator!=(faketype, faketype) { return false; } + +// The helper function for {ASSERT|EXPECT}_EQ. +template +AssertionResult CmpHelperEQ(const char* lhs_expression, + const char* rhs_expression, const T1& lhs, + const T2& rhs) { + if (lhs == rhs) { + return AssertionSuccess(); + } + + return CmpHelperEQFailure(lhs_expression, rhs_expression, lhs, rhs); +} + +class EqHelper { + public: + // This templatized version is for the general case. + template < + typename T1, typename T2, + // Disable this overload for cases where one argument is a pointer + // and the other is the null pointer constant. + typename std::enable_if::value || + !std::is_pointer::value>::type* = nullptr> + static AssertionResult Compare(const char* lhs_expression, + const char* rhs_expression, const T1& lhs, + const T2& rhs) { + return CmpHelperEQ(lhs_expression, rhs_expression, lhs, rhs); + } + + // With this overloaded version, we allow anonymous enums to be used + // in {ASSERT|EXPECT}_EQ when compiled with gcc 4, as anonymous + // enums can be implicitly cast to BiggestInt. + // + // Even though its body looks the same as the above version, we + // cannot merge the two, as it will make anonymous enums unhappy. + static AssertionResult Compare(const char* lhs_expression, + const char* rhs_expression, BiggestInt lhs, + BiggestInt rhs) { + return CmpHelperEQ(lhs_expression, rhs_expression, lhs, rhs); + } + + template + static AssertionResult Compare( + const char* lhs_expression, const char* rhs_expression, + // Handle cases where '0' is used as a null pointer literal. + std::nullptr_t /* lhs */, T* rhs) { + // We already know that 'lhs' is a null pointer. + return CmpHelperEQ(lhs_expression, rhs_expression, static_cast(nullptr), + rhs); + } +}; + +// Separate the error generating code from the code path to reduce the stack +// frame size of CmpHelperOP. This helps reduce the overhead of some sanitizers +// when calling EXPECT_OP in a tight loop. +template +AssertionResult CmpHelperOpFailure(const char* expr1, const char* expr2, + const T1& val1, const T2& val2, + const char* op) { + return AssertionFailure() + << "Expected: (" << expr1 << ") " << op << " (" << expr2 + << "), actual: " << FormatForComparisonFailureMessage(val1, val2) + << " vs " << FormatForComparisonFailureMessage(val2, val1); +} + +// A macro for implementing the helper functions needed to implement +// ASSERT_?? and EXPECT_??. It is here just to avoid copy-and-paste +// of similar code. +// +// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. + +#define GTEST_IMPL_CMP_HELPER_(op_name, op) \ + template \ + AssertionResult CmpHelper##op_name(const char* expr1, const char* expr2, \ + const T1& val1, const T2& val2) { \ + if (val1 op val2) { \ + return AssertionSuccess(); \ + } else { \ + return CmpHelperOpFailure(expr1, expr2, val1, val2, #op); \ + } \ + } + +// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. + +// Implements the helper function for {ASSERT|EXPECT}_NE +GTEST_IMPL_CMP_HELPER_(NE, !=) +// Implements the helper function for {ASSERT|EXPECT}_LE +GTEST_IMPL_CMP_HELPER_(LE, <=) +// Implements the helper function for {ASSERT|EXPECT}_LT +GTEST_IMPL_CMP_HELPER_(LT, <) +// Implements the helper function for {ASSERT|EXPECT}_GE +GTEST_IMPL_CMP_HELPER_(GE, >=) +// Implements the helper function for {ASSERT|EXPECT}_GT +GTEST_IMPL_CMP_HELPER_(GT, >) + +#undef GTEST_IMPL_CMP_HELPER_ + +// The helper function for {ASSERT|EXPECT}_STREQ. +// +// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. +GTEST_API_ AssertionResult CmpHelperSTREQ(const char* s1_expression, + const char* s2_expression, + const char* s1, const char* s2); + +// The helper function for {ASSERT|EXPECT}_STRCASEEQ. +// +// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. +GTEST_API_ AssertionResult CmpHelperSTRCASEEQ(const char* s1_expression, + const char* s2_expression, + const char* s1, const char* s2); + +// The helper function for {ASSERT|EXPECT}_STRNE. +// +// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. +GTEST_API_ AssertionResult CmpHelperSTRNE(const char* s1_expression, + const char* s2_expression, + const char* s1, const char* s2); + +// The helper function for {ASSERT|EXPECT}_STRCASENE. +// +// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. +GTEST_API_ AssertionResult CmpHelperSTRCASENE(const char* s1_expression, + const char* s2_expression, + const char* s1, const char* s2); + +// Helper function for *_STREQ on wide strings. +// +// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. +GTEST_API_ AssertionResult CmpHelperSTREQ(const char* s1_expression, + const char* s2_expression, + const wchar_t* s1, const wchar_t* s2); + +// Helper function for *_STRNE on wide strings. +// +// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. +GTEST_API_ AssertionResult CmpHelperSTRNE(const char* s1_expression, + const char* s2_expression, + const wchar_t* s1, const wchar_t* s2); + +} // namespace internal + +// IsSubstring() and IsNotSubstring() are intended to be used as the +// first argument to {EXPECT,ASSERT}_PRED_FORMAT2(), not by +// themselves. They check whether needle is a substring of haystack +// (NULL is considered a substring of itself only), and return an +// appropriate error message when they fail. +// +// The {needle,haystack}_expr arguments are the stringified +// expressions that generated the two real arguments. +GTEST_API_ AssertionResult IsSubstring(const char* needle_expr, + const char* haystack_expr, + const char* needle, + const char* haystack); +GTEST_API_ AssertionResult IsSubstring(const char* needle_expr, + const char* haystack_expr, + const wchar_t* needle, + const wchar_t* haystack); +GTEST_API_ AssertionResult IsNotSubstring(const char* needle_expr, + const char* haystack_expr, + const char* needle, + const char* haystack); +GTEST_API_ AssertionResult IsNotSubstring(const char* needle_expr, + const char* haystack_expr, + const wchar_t* needle, + const wchar_t* haystack); +GTEST_API_ AssertionResult IsSubstring(const char* needle_expr, + const char* haystack_expr, + const ::std::string& needle, + const ::std::string& haystack); +GTEST_API_ AssertionResult IsNotSubstring(const char* needle_expr, + const char* haystack_expr, + const ::std::string& needle, + const ::std::string& haystack); + +#if GTEST_HAS_STD_WSTRING +GTEST_API_ AssertionResult IsSubstring(const char* needle_expr, + const char* haystack_expr, + const ::std::wstring& needle, + const ::std::wstring& haystack); +GTEST_API_ AssertionResult IsNotSubstring(const char* needle_expr, + const char* haystack_expr, + const ::std::wstring& needle, + const ::std::wstring& haystack); +#endif // GTEST_HAS_STD_WSTRING + +namespace internal { + +// Helper template function for comparing floating-points. +// +// Template parameter: +// +// RawType: the raw floating-point type (either float or double) +// +// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. +template +AssertionResult CmpHelperFloatingPointEQ(const char* lhs_expression, + const char* rhs_expression, + RawType lhs_value, RawType rhs_value) { + const FloatingPoint lhs(lhs_value), rhs(rhs_value); + + if (lhs.AlmostEquals(rhs)) { + return AssertionSuccess(); + } + + ::std::stringstream lhs_ss; + lhs_ss << std::setprecision(std::numeric_limits::digits10 + 2) + << lhs_value; + + ::std::stringstream rhs_ss; + rhs_ss << std::setprecision(std::numeric_limits::digits10 + 2) + << rhs_value; + + return EqFailure(lhs_expression, rhs_expression, + StringStreamToString(&lhs_ss), StringStreamToString(&rhs_ss), + false); +} + +// Helper function for implementing ASSERT_NEAR. +// +// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. +GTEST_API_ AssertionResult DoubleNearPredFormat(const char* expr1, + const char* expr2, + const char* abs_error_expr, + double val1, double val2, + double abs_error); + +// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE. +// A class that enables one to stream messages to assertion macros +class GTEST_API_ AssertHelper { + public: + // Constructor. + AssertHelper(TestPartResult::Type type, const char* file, int line, + const char* message); + ~AssertHelper(); + + // Message assignment is a semantic trick to enable assertion + // streaming; see the GTEST_MESSAGE_ macro below. + void operator=(const Message& message) const; + + private: + // We put our data in a struct so that the size of the AssertHelper class can + // be as small as possible. This is important because gcc is incapable of + // re-using stack space even for temporary variables, so every EXPECT_EQ + // reserves stack space for another AssertHelper. + struct AssertHelperData { + AssertHelperData(TestPartResult::Type t, const char* srcfile, int line_num, + const char* msg) + : type(t), file(srcfile), line(line_num), message(msg) {} + + TestPartResult::Type const type; + const char* const file; + int const line; + std::string const message; + + private: + AssertHelperData(const AssertHelperData&) = delete; + AssertHelperData& operator=(const AssertHelperData&) = delete; + }; + + AssertHelperData* const data_; + + AssertHelper(const AssertHelper&) = delete; + AssertHelper& operator=(const AssertHelper&) = delete; +}; + +} // namespace internal + +// The pure interface class that all value-parameterized tests inherit from. +// A value-parameterized class must inherit from both ::testing::Test and +// ::testing::WithParamInterface. In most cases that just means inheriting +// from ::testing::TestWithParam, but more complicated test hierarchies +// may need to inherit from Test and WithParamInterface at different levels. +// +// This interface has support for accessing the test parameter value via +// the GetParam() method. +// +// Use it with one of the parameter generator defining functions, like Range(), +// Values(), ValuesIn(), Bool(), Combine(), and ConvertGenerator(). +// +// class FooTest : public ::testing::TestWithParam { +// protected: +// FooTest() { +// // Can use GetParam() here. +// } +// ~FooTest() override { +// // Can use GetParam() here. +// } +// void SetUp() override { +// // Can use GetParam() here. +// } +// void TearDown override { +// // Can use GetParam() here. +// } +// }; +// TEST_P(FooTest, DoesBar) { +// // Can use GetParam() method here. +// Foo foo; +// ASSERT_TRUE(foo.DoesBar(GetParam())); +// } +// INSTANTIATE_TEST_SUITE_P(OneToTenRange, FooTest, ::testing::Range(1, 10)); + +template +class WithParamInterface { + public: + typedef T ParamType; + virtual ~WithParamInterface() {} + + // The current parameter value. Is also available in the test fixture's + // constructor. + static const ParamType& GetParam() { + GTEST_CHECK_(parameter_ != nullptr) + << "GetParam() can only be called inside a value-parameterized test " + << "-- did you intend to write TEST_P instead of TEST_F?"; + return *parameter_; + } + + private: + // Sets parameter value. The caller is responsible for making sure the value + // remains alive and unchanged throughout the current test. + static void SetParam(const ParamType* parameter) { parameter_ = parameter; } + + // Static value used for accessing parameter during a test lifetime. + static const ParamType* parameter_; + + // TestClass must be a subclass of WithParamInterface and Test. + template + friend class internal::ParameterizedTestFactory; +}; + +template +const T* WithParamInterface::parameter_ = nullptr; + +// Most value-parameterized classes can ignore the existence of +// WithParamInterface, and can just inherit from ::testing::TestWithParam. + +template +class TestWithParam : public Test, public WithParamInterface {}; + +// Macros for indicating success/failure in test code. + +// Skips test in runtime. +// Skipping test aborts current function. +// Skipped tests are neither successful nor failed. +#define GTEST_SKIP() GTEST_SKIP_("") + +// ADD_FAILURE unconditionally adds a failure to the current test. +// SUCCEED generates a success - it doesn't automatically make the +// current test successful, as a test is only successful when it has +// no failure. +// +// EXPECT_* verifies that a certain condition is satisfied. If not, +// it behaves like ADD_FAILURE. In particular: +// +// EXPECT_TRUE verifies that a Boolean condition is true. +// EXPECT_FALSE verifies that a Boolean condition is false. +// +// FAIL and ASSERT_* are similar to ADD_FAILURE and EXPECT_*, except +// that they will also abort the current function on failure. People +// usually want the fail-fast behavior of FAIL and ASSERT_*, but those +// writing data-driven tests often find themselves using ADD_FAILURE +// and EXPECT_* more. + +// Generates a nonfatal failure with a generic message. +#define ADD_FAILURE() GTEST_NONFATAL_FAILURE_("Failed") + +// Generates a nonfatal failure at the given source file location with +// a generic message. +#define ADD_FAILURE_AT(file, line) \ + GTEST_MESSAGE_AT_(file, line, "Failed", \ + ::testing::TestPartResult::kNonFatalFailure) + +// Generates a fatal failure with a generic message. +#define GTEST_FAIL() GTEST_FATAL_FAILURE_("Failed") + +// Like GTEST_FAIL(), but at the given source file location. +#define GTEST_FAIL_AT(file, line) \ + GTEST_MESSAGE_AT_(file, line, "Failed", \ + ::testing::TestPartResult::kFatalFailure) + +// Define this macro to 1 to omit the definition of FAIL(), which is a +// generic name and clashes with some other libraries. +#if !GTEST_DONT_DEFINE_FAIL +#define FAIL() GTEST_FAIL() +#endif + +// Generates a success with a generic message. +#define GTEST_SUCCEED() GTEST_SUCCESS_("Succeeded") + +// Define this macro to 1 to omit the definition of SUCCEED(), which +// is a generic name and clashes with some other libraries. +#if !GTEST_DONT_DEFINE_SUCCEED +#define SUCCEED() GTEST_SUCCEED() +#endif + +// Macros for testing exceptions. +// +// * {ASSERT|EXPECT}_THROW(statement, expected_exception): +// Tests that the statement throws the expected exception. +// * {ASSERT|EXPECT}_NO_THROW(statement): +// Tests that the statement doesn't throw any exception. +// * {ASSERT|EXPECT}_ANY_THROW(statement): +// Tests that the statement throws an exception. + +#define EXPECT_THROW(statement, expected_exception) \ + GTEST_TEST_THROW_(statement, expected_exception, GTEST_NONFATAL_FAILURE_) +#define EXPECT_NO_THROW(statement) \ + GTEST_TEST_NO_THROW_(statement, GTEST_NONFATAL_FAILURE_) +#define EXPECT_ANY_THROW(statement) \ + GTEST_TEST_ANY_THROW_(statement, GTEST_NONFATAL_FAILURE_) +#define ASSERT_THROW(statement, expected_exception) \ + GTEST_TEST_THROW_(statement, expected_exception, GTEST_FATAL_FAILURE_) +#define ASSERT_NO_THROW(statement) \ + GTEST_TEST_NO_THROW_(statement, GTEST_FATAL_FAILURE_) +#define ASSERT_ANY_THROW(statement) \ + GTEST_TEST_ANY_THROW_(statement, GTEST_FATAL_FAILURE_) + +// Boolean assertions. Condition can be either a Boolean expression or an +// AssertionResult. For more information on how to use AssertionResult with +// these macros see comments on that class. +#define GTEST_EXPECT_TRUE(condition) \ + GTEST_TEST_BOOLEAN_(condition, #condition, false, true, \ + GTEST_NONFATAL_FAILURE_) +#define GTEST_EXPECT_FALSE(condition) \ + GTEST_TEST_BOOLEAN_(!(condition), #condition, true, false, \ + GTEST_NONFATAL_FAILURE_) +#define GTEST_ASSERT_TRUE(condition) \ + GTEST_TEST_BOOLEAN_(condition, #condition, false, true, GTEST_FATAL_FAILURE_) +#define GTEST_ASSERT_FALSE(condition) \ + GTEST_TEST_BOOLEAN_(!(condition), #condition, true, false, \ + GTEST_FATAL_FAILURE_) + +// Define these macros to 1 to omit the definition of the corresponding +// EXPECT or ASSERT, which clashes with some users' own code. + +#if !GTEST_DONT_DEFINE_EXPECT_TRUE +#define EXPECT_TRUE(condition) GTEST_EXPECT_TRUE(condition) +#endif + +#if !GTEST_DONT_DEFINE_EXPECT_FALSE +#define EXPECT_FALSE(condition) GTEST_EXPECT_FALSE(condition) +#endif + +#if !GTEST_DONT_DEFINE_ASSERT_TRUE +#define ASSERT_TRUE(condition) GTEST_ASSERT_TRUE(condition) +#endif + +#if !GTEST_DONT_DEFINE_ASSERT_FALSE +#define ASSERT_FALSE(condition) GTEST_ASSERT_FALSE(condition) +#endif + +// Macros for testing equalities and inequalities. +// +// * {ASSERT|EXPECT}_EQ(v1, v2): Tests that v1 == v2 +// * {ASSERT|EXPECT}_NE(v1, v2): Tests that v1 != v2 +// * {ASSERT|EXPECT}_LT(v1, v2): Tests that v1 < v2 +// * {ASSERT|EXPECT}_LE(v1, v2): Tests that v1 <= v2 +// * {ASSERT|EXPECT}_GT(v1, v2): Tests that v1 > v2 +// * {ASSERT|EXPECT}_GE(v1, v2): Tests that v1 >= v2 +// +// When they are not, Google Test prints both the tested expressions and +// their actual values. The values must be compatible built-in types, +// or you will get a compiler error. By "compatible" we mean that the +// values can be compared by the respective operator. +// +// Note: +// +// 1. It is possible to make a user-defined type work with +// {ASSERT|EXPECT}_??(), but that requires overloading the +// comparison operators and is thus discouraged by the Google C++ +// Usage Guide. Therefore, you are advised to use the +// {ASSERT|EXPECT}_TRUE() macro to assert that two objects are +// equal. +// +// 2. The {ASSERT|EXPECT}_??() macros do pointer comparisons on +// pointers (in particular, C strings). Therefore, if you use it +// with two C strings, you are testing how their locations in memory +// are related, not how their content is related. To compare two C +// strings by content, use {ASSERT|EXPECT}_STR*(). +// +// 3. {ASSERT|EXPECT}_EQ(v1, v2) is preferred to +// {ASSERT|EXPECT}_TRUE(v1 == v2), as the former tells you +// what the actual value is when it fails, and similarly for the +// other comparisons. +// +// 4. Do not depend on the order in which {ASSERT|EXPECT}_??() +// evaluate their arguments, which is undefined. +// +// 5. These macros evaluate their arguments exactly once. +// +// Examples: +// +// EXPECT_NE(Foo(), 5); +// EXPECT_EQ(a_pointer, NULL); +// ASSERT_LT(i, array_size); +// ASSERT_GT(records.size(), 0) << "There is no record left."; + +#define EXPECT_EQ(val1, val2) \ + EXPECT_PRED_FORMAT2(::testing::internal::EqHelper::Compare, val1, val2) +#define EXPECT_NE(val1, val2) \ + EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperNE, val1, val2) +#define EXPECT_LE(val1, val2) \ + EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperLE, val1, val2) +#define EXPECT_LT(val1, val2) \ + EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperLT, val1, val2) +#define EXPECT_GE(val1, val2) \ + EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperGE, val1, val2) +#define EXPECT_GT(val1, val2) \ + EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperGT, val1, val2) + +#define GTEST_ASSERT_EQ(val1, val2) \ + ASSERT_PRED_FORMAT2(::testing::internal::EqHelper::Compare, val1, val2) +#define GTEST_ASSERT_NE(val1, val2) \ + ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperNE, val1, val2) +#define GTEST_ASSERT_LE(val1, val2) \ + ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperLE, val1, val2) +#define GTEST_ASSERT_LT(val1, val2) \ + ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperLT, val1, val2) +#define GTEST_ASSERT_GE(val1, val2) \ + ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperGE, val1, val2) +#define GTEST_ASSERT_GT(val1, val2) \ + ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperGT, val1, val2) + +// Define macro GTEST_DONT_DEFINE_ASSERT_XY to 1 to omit the definition of +// ASSERT_XY(), which clashes with some users' own code. + +#if !GTEST_DONT_DEFINE_ASSERT_EQ +#define ASSERT_EQ(val1, val2) GTEST_ASSERT_EQ(val1, val2) +#endif + +#if !GTEST_DONT_DEFINE_ASSERT_NE +#define ASSERT_NE(val1, val2) GTEST_ASSERT_NE(val1, val2) +#endif + +#if !GTEST_DONT_DEFINE_ASSERT_LE +#define ASSERT_LE(val1, val2) GTEST_ASSERT_LE(val1, val2) +#endif + +#if !GTEST_DONT_DEFINE_ASSERT_LT +#define ASSERT_LT(val1, val2) GTEST_ASSERT_LT(val1, val2) +#endif + +#if !GTEST_DONT_DEFINE_ASSERT_GE +#define ASSERT_GE(val1, val2) GTEST_ASSERT_GE(val1, val2) +#endif + +#if !GTEST_DONT_DEFINE_ASSERT_GT +#define ASSERT_GT(val1, val2) GTEST_ASSERT_GT(val1, val2) +#endif + +// C-string Comparisons. All tests treat NULL and any non-NULL string +// as different. Two NULLs are equal. +// +// * {ASSERT|EXPECT}_STREQ(s1, s2): Tests that s1 == s2 +// * {ASSERT|EXPECT}_STRNE(s1, s2): Tests that s1 != s2 +// * {ASSERT|EXPECT}_STRCASEEQ(s1, s2): Tests that s1 == s2, ignoring case +// * {ASSERT|EXPECT}_STRCASENE(s1, s2): Tests that s1 != s2, ignoring case +// +// For wide or narrow string objects, you can use the +// {ASSERT|EXPECT}_??() macros. +// +// Don't depend on the order in which the arguments are evaluated, +// which is undefined. +// +// These macros evaluate their arguments exactly once. + +#define EXPECT_STREQ(s1, s2) \ + EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperSTREQ, s1, s2) +#define EXPECT_STRNE(s1, s2) \ + EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperSTRNE, s1, s2) +#define EXPECT_STRCASEEQ(s1, s2) \ + EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperSTRCASEEQ, s1, s2) +#define EXPECT_STRCASENE(s1, s2) \ + EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperSTRCASENE, s1, s2) + +#define ASSERT_STREQ(s1, s2) \ + ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperSTREQ, s1, s2) +#define ASSERT_STRNE(s1, s2) \ + ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperSTRNE, s1, s2) +#define ASSERT_STRCASEEQ(s1, s2) \ + ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperSTRCASEEQ, s1, s2) +#define ASSERT_STRCASENE(s1, s2) \ + ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperSTRCASENE, s1, s2) + +// Macros for comparing floating-point numbers. +// +// * {ASSERT|EXPECT}_FLOAT_EQ(val1, val2): +// Tests that two float values are almost equal. +// * {ASSERT|EXPECT}_DOUBLE_EQ(val1, val2): +// Tests that two double values are almost equal. +// * {ASSERT|EXPECT}_NEAR(v1, v2, abs_error): +// Tests that v1 and v2 are within the given distance to each other. +// +// Google Test uses ULP-based comparison to automatically pick a default +// error bound that is appropriate for the operands. See the +// FloatingPoint template class in gtest-internal.h if you are +// interested in the implementation details. + +#define EXPECT_FLOAT_EQ(val1, val2) \ + EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperFloatingPointEQ, \ + val1, val2) + +#define EXPECT_DOUBLE_EQ(val1, val2) \ + EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperFloatingPointEQ, \ + val1, val2) + +#define ASSERT_FLOAT_EQ(val1, val2) \ + ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperFloatingPointEQ, \ + val1, val2) + +#define ASSERT_DOUBLE_EQ(val1, val2) \ + ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperFloatingPointEQ, \ + val1, val2) + +#define EXPECT_NEAR(val1, val2, abs_error) \ + EXPECT_PRED_FORMAT3(::testing::internal::DoubleNearPredFormat, val1, val2, \ + abs_error) + +#define ASSERT_NEAR(val1, val2, abs_error) \ + ASSERT_PRED_FORMAT3(::testing::internal::DoubleNearPredFormat, val1, val2, \ + abs_error) + +// These predicate format functions work on floating-point values, and +// can be used in {ASSERT|EXPECT}_PRED_FORMAT2*(), e.g. +// +// EXPECT_PRED_FORMAT2(testing::DoubleLE, Foo(), 5.0); + +// Asserts that val1 is less than, or almost equal to, val2. Fails +// otherwise. In particular, it fails if either val1 or val2 is NaN. +GTEST_API_ AssertionResult FloatLE(const char* expr1, const char* expr2, + float val1, float val2); +GTEST_API_ AssertionResult DoubleLE(const char* expr1, const char* expr2, + double val1, double val2); + +#if GTEST_OS_WINDOWS + +// Macros that test for HRESULT failure and success, these are only useful +// on Windows, and rely on Windows SDK macros and APIs to compile. +// +// * {ASSERT|EXPECT}_HRESULT_{SUCCEEDED|FAILED}(expr) +// +// When expr unexpectedly fails or succeeds, Google Test prints the +// expected result and the actual result with both a human-readable +// string representation of the error, if available, as well as the +// hex result code. +#define EXPECT_HRESULT_SUCCEEDED(expr) \ + EXPECT_PRED_FORMAT1(::testing::internal::IsHRESULTSuccess, (expr)) + +#define ASSERT_HRESULT_SUCCEEDED(expr) \ + ASSERT_PRED_FORMAT1(::testing::internal::IsHRESULTSuccess, (expr)) + +#define EXPECT_HRESULT_FAILED(expr) \ + EXPECT_PRED_FORMAT1(::testing::internal::IsHRESULTFailure, (expr)) + +#define ASSERT_HRESULT_FAILED(expr) \ + ASSERT_PRED_FORMAT1(::testing::internal::IsHRESULTFailure, (expr)) + +#endif // GTEST_OS_WINDOWS + +// Macros that execute statement and check that it doesn't generate new fatal +// failures in the current thread. +// +// * {ASSERT|EXPECT}_NO_FATAL_FAILURE(statement); +// +// Examples: +// +// EXPECT_NO_FATAL_FAILURE(Process()); +// ASSERT_NO_FATAL_FAILURE(Process()) << "Process() failed"; +// +#define ASSERT_NO_FATAL_FAILURE(statement) \ + GTEST_TEST_NO_FATAL_FAILURE_(statement, GTEST_FATAL_FAILURE_) +#define EXPECT_NO_FATAL_FAILURE(statement) \ + GTEST_TEST_NO_FATAL_FAILURE_(statement, GTEST_NONFATAL_FAILURE_) + +// Causes a trace (including the given source file path and line number, +// and the given message) to be included in every test failure message generated +// by code in the scope of the lifetime of an instance of this class. The effect +// is undone with the destruction of the instance. +// +// The message argument can be anything streamable to std::ostream. +// +// Example: +// testing::ScopedTrace trace("file.cc", 123, "message"); +// +class GTEST_API_ ScopedTrace { + public: + // The c'tor pushes the given source file location and message onto + // a trace stack maintained by Google Test. + + // Template version. Uses Message() to convert the values into strings. + // Slow, but flexible. + template + ScopedTrace(const char* file, int line, const T& message) { + PushTrace(file, line, (Message() << message).GetString()); + } + + // Optimize for some known types. + ScopedTrace(const char* file, int line, const char* message) { + PushTrace(file, line, message ? message : "(null)"); + } + + ScopedTrace(const char* file, int line, const std::string& message) { + PushTrace(file, line, message); + } + + // The d'tor pops the info pushed by the c'tor. + // + // Note that the d'tor is not virtual in order to be efficient. + // Don't inherit from ScopedTrace! + ~ScopedTrace(); + + private: + void PushTrace(const char* file, int line, std::string message); + + ScopedTrace(const ScopedTrace&) = delete; + ScopedTrace& operator=(const ScopedTrace&) = delete; +} GTEST_ATTRIBUTE_UNUSED_; // A ScopedTrace object does its job in its + // c'tor and d'tor. Therefore it doesn't + // need to be used otherwise. + +// Causes a trace (including the source file path, the current line +// number, and the given message) to be included in every test failure +// message generated by code in the current scope. The effect is +// undone when the control leaves the current scope. +// +// The message argument can be anything streamable to std::ostream. +// +// In the implementation, we include the current line number as part +// of the dummy variable name, thus allowing multiple SCOPED_TRACE()s +// to appear in the same block - as long as they are on different +// lines. +// +// Assuming that each thread maintains its own stack of traces. +// Therefore, a SCOPED_TRACE() would (correctly) only affect the +// assertions in its own thread. +#define SCOPED_TRACE(message) \ + ::testing::ScopedTrace GTEST_CONCAT_TOKEN_(gtest_trace_, __LINE__)( \ + __FILE__, __LINE__, (message)) + +// Compile-time assertion for type equality. +// StaticAssertTypeEq() compiles if and only if type1 and type2 +// are the same type. The value it returns is not interesting. +// +// Instead of making StaticAssertTypeEq a class template, we make it a +// function template that invokes a helper class template. This +// prevents a user from misusing StaticAssertTypeEq by +// defining objects of that type. +// +// CAVEAT: +// +// When used inside a method of a class template, +// StaticAssertTypeEq() is effective ONLY IF the method is +// instantiated. For example, given: +// +// template class Foo { +// public: +// void Bar() { testing::StaticAssertTypeEq(); } +// }; +// +// the code: +// +// void Test1() { Foo foo; } +// +// will NOT generate a compiler error, as Foo::Bar() is never +// actually instantiated. Instead, you need: +// +// void Test2() { Foo foo; foo.Bar(); } +// +// to cause a compiler error. +template +constexpr bool StaticAssertTypeEq() noexcept { + static_assert(std::is_same::value, "T1 and T2 are not the same type"); + return true; +} + +// Defines a test. +// +// The first parameter is the name of the test suite, and the second +// parameter is the name of the test within the test suite. +// +// The convention is to end the test suite name with "Test". For +// example, a test suite for the Foo class can be named FooTest. +// +// Test code should appear between braces after an invocation of +// this macro. Example: +// +// TEST(FooTest, InitializesCorrectly) { +// Foo foo; +// EXPECT_TRUE(foo.StatusIsOK()); +// } + +// Note that we call GetTestTypeId() instead of GetTypeId< +// ::testing::Test>() here to get the type ID of testing::Test. This +// is to work around a suspected linker bug when using Google Test as +// a framework on Mac OS X. The bug causes GetTypeId< +// ::testing::Test>() to return different values depending on whether +// the call is from the Google Test framework itself or from user test +// code. GetTestTypeId() is guaranteed to always return the same +// value, as it always calls GetTypeId<>() from the Google Test +// framework. +#define GTEST_TEST(test_suite_name, test_name) \ + GTEST_TEST_(test_suite_name, test_name, ::testing::Test, \ + ::testing::internal::GetTestTypeId()) + +// Define this macro to 1 to omit the definition of TEST(), which +// is a generic name and clashes with some other libraries. +#if !GTEST_DONT_DEFINE_TEST +#define TEST(test_suite_name, test_name) GTEST_TEST(test_suite_name, test_name) +#endif + +// Defines a test that uses a test fixture. +// +// The first parameter is the name of the test fixture class, which +// also doubles as the test suite name. The second parameter is the +// name of the test within the test suite. +// +// A test fixture class must be declared earlier. The user should put +// the test code between braces after using this macro. Example: +// +// class FooTest : public testing::Test { +// protected: +// void SetUp() override { b_.AddElement(3); } +// +// Foo a_; +// Foo b_; +// }; +// +// TEST_F(FooTest, InitializesCorrectly) { +// EXPECT_TRUE(a_.StatusIsOK()); +// } +// +// TEST_F(FooTest, ReturnsElementCountCorrectly) { +// EXPECT_EQ(a_.size(), 0); +// EXPECT_EQ(b_.size(), 1); +// } +#define GTEST_TEST_F(test_fixture, test_name) \ + GTEST_TEST_(test_fixture, test_name, test_fixture, \ + ::testing::internal::GetTypeId()) +#if !GTEST_DONT_DEFINE_TEST_F +#define TEST_F(test_fixture, test_name) GTEST_TEST_F(test_fixture, test_name) +#endif + +// Returns a path to temporary directory. +// Tries to determine an appropriate directory for the platform. +GTEST_API_ std::string TempDir(); + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +// Dynamically registers a test with the framework. +// +// This is an advanced API only to be used when the `TEST` macros are +// insufficient. The macros should be preferred when possible, as they avoid +// most of the complexity of calling this function. +// +// The `factory` argument is a factory callable (move-constructible) object or +// function pointer that creates a new instance of the Test object. It +// handles ownership to the caller. The signature of the callable is +// `Fixture*()`, where `Fixture` is the test fixture class for the test. All +// tests registered with the same `test_suite_name` must return the same +// fixture type. This is checked at runtime. +// +// The framework will infer the fixture class from the factory and will call +// the `SetUpTestSuite` and `TearDownTestSuite` for it. +// +// Must be called before `RUN_ALL_TESTS()` is invoked, otherwise behavior is +// undefined. +// +// Use case example: +// +// class MyFixture : public ::testing::Test { +// public: +// // All of these optional, just like in regular macro usage. +// static void SetUpTestSuite() { ... } +// static void TearDownTestSuite() { ... } +// void SetUp() override { ... } +// void TearDown() override { ... } +// }; +// +// class MyTest : public MyFixture { +// public: +// explicit MyTest(int data) : data_(data) {} +// void TestBody() override { ... } +// +// private: +// int data_; +// }; +// +// void RegisterMyTests(const std::vector& values) { +// for (int v : values) { +// ::testing::RegisterTest( +// "MyFixture", ("Test" + std::to_string(v)).c_str(), nullptr, +// std::to_string(v).c_str(), +// __FILE__, __LINE__, +// // Important to use the fixture type as the return type here. +// [=]() -> MyFixture* { return new MyTest(v); }); +// } +// } +// ... +// int main(int argc, char** argv) { +// ::testing::InitGoogleTest(&argc, argv); +// std::vector values_to_test = LoadValuesFromConfig(); +// RegisterMyTests(values_to_test); +// ... +// return RUN_ALL_TESTS(); +// } +// +template +TestInfo* RegisterTest(const char* test_suite_name, const char* test_name, + const char* type_param, const char* value_param, + const char* file, int line, Factory factory) { + using TestT = typename std::remove_pointer::type; + + class FactoryImpl : public internal::TestFactoryBase { + public: + explicit FactoryImpl(Factory f) : factory_(std::move(f)) {} + Test* CreateTest() override { return factory_(); } + + private: + Factory factory_; + }; + + return internal::MakeAndRegisterTestInfo( + test_suite_name, test_name, type_param, value_param, + internal::CodeLocation(file, line), internal::GetTypeId(), + internal::SuiteApiResolver::GetSetUpCaseOrSuite(file, line), + internal::SuiteApiResolver::GetTearDownCaseOrSuite(file, line), + new FactoryImpl{std::move(factory)}); +} + +} // namespace testing + +// Use this function in main() to run all tests. It returns 0 if all +// tests are successful, or 1 otherwise. +// +// RUN_ALL_TESTS() should be invoked after the command line has been +// parsed by InitGoogleTest(). +// +// This function was formerly a macro; thus, it is in the global +// namespace and has an all-caps name. +int RUN_ALL_TESTS() GTEST_MUST_USE_RESULT_; + +inline int RUN_ALL_TESTS() { return ::testing::UnitTest::GetInstance()->Run(); } + +GTEST_DISABLE_MSC_WARNINGS_POP_() // 4251 + +#endif // GOOGLETEST_INCLUDE_GTEST_GTEST_H_ diff --git a/include/gtest/gtest_pred_impl.h b/include/gtest/gtest_pred_impl.h new file mode 100644 index 0000000..47a24aa --- /dev/null +++ b/include/gtest/gtest_pred_impl.h @@ -0,0 +1,279 @@ +// Copyright 2006, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Implements a family of generic predicate assertion macros. + +// IWYU pragma: private, include "gtest/gtest.h" +// IWYU pragma: friend gtest/.* +// IWYU pragma: friend gmock/.* + +#ifndef GOOGLETEST_INCLUDE_GTEST_GTEST_PRED_IMPL_H_ +#define GOOGLETEST_INCLUDE_GTEST_GTEST_PRED_IMPL_H_ + +#include "gtest/gtest-assertion-result.h" +#include "gtest/internal/gtest-internal.h" +#include "gtest/internal/gtest-port.h" + +namespace testing { + +// This header implements a family of generic predicate assertion +// macros: +// +// ASSERT_PRED_FORMAT1(pred_format, v1) +// ASSERT_PRED_FORMAT2(pred_format, v1, v2) +// ... +// +// where pred_format is a function or functor that takes n (in the +// case of ASSERT_PRED_FORMATn) values and their source expression +// text, and returns a testing::AssertionResult. See the definition +// of ASSERT_EQ in gtest.h for an example. +// +// If you don't care about formatting, you can use the more +// restrictive version: +// +// ASSERT_PRED1(pred, v1) +// ASSERT_PRED2(pred, v1, v2) +// ... +// +// where pred is an n-ary function or functor that returns bool, +// and the values v1, v2, ..., must support the << operator for +// streaming to std::ostream. +// +// We also define the EXPECT_* variations. +// +// For now we only support predicates whose arity is at most 5. +// Please email googletestframework@googlegroups.com if you need +// support for higher arities. + +// GTEST_ASSERT_ is the basic statement to which all of the assertions +// in this file reduce. Don't use this in your code. + +#define GTEST_ASSERT_(expression, on_failure) \ + GTEST_AMBIGUOUS_ELSE_BLOCKER_ \ + if (const ::testing::AssertionResult gtest_ar = (expression)) \ + ; \ + else \ + on_failure(gtest_ar.failure_message()) + +// Helper function for implementing {EXPECT|ASSERT}_PRED1. Don't use +// this in your code. +template +AssertionResult AssertPred1Helper(const char* pred_text, const char* e1, + Pred pred, const T1& v1) { + if (pred(v1)) return AssertionSuccess(); + + return AssertionFailure() + << pred_text << "(" << e1 << ") evaluates to false, where" + << "\n" + << e1 << " evaluates to " << ::testing::PrintToString(v1); +} + +// Internal macro for implementing {EXPECT|ASSERT}_PRED_FORMAT1. +// Don't use this in your code. +#define GTEST_PRED_FORMAT1_(pred_format, v1, on_failure) \ + GTEST_ASSERT_(pred_format(#v1, v1), on_failure) + +// Internal macro for implementing {EXPECT|ASSERT}_PRED1. Don't use +// this in your code. +#define GTEST_PRED1_(pred, v1, on_failure) \ + GTEST_ASSERT_(::testing::AssertPred1Helper(#pred, #v1, pred, v1), on_failure) + +// Unary predicate assertion macros. +#define EXPECT_PRED_FORMAT1(pred_format, v1) \ + GTEST_PRED_FORMAT1_(pred_format, v1, GTEST_NONFATAL_FAILURE_) +#define EXPECT_PRED1(pred, v1) GTEST_PRED1_(pred, v1, GTEST_NONFATAL_FAILURE_) +#define ASSERT_PRED_FORMAT1(pred_format, v1) \ + GTEST_PRED_FORMAT1_(pred_format, v1, GTEST_FATAL_FAILURE_) +#define ASSERT_PRED1(pred, v1) GTEST_PRED1_(pred, v1, GTEST_FATAL_FAILURE_) + +// Helper function for implementing {EXPECT|ASSERT}_PRED2. Don't use +// this in your code. +template +AssertionResult AssertPred2Helper(const char* pred_text, const char* e1, + const char* e2, Pred pred, const T1& v1, + const T2& v2) { + if (pred(v1, v2)) return AssertionSuccess(); + + return AssertionFailure() + << pred_text << "(" << e1 << ", " << e2 + << ") evaluates to false, where" + << "\n" + << e1 << " evaluates to " << ::testing::PrintToString(v1) << "\n" + << e2 << " evaluates to " << ::testing::PrintToString(v2); +} + +// Internal macro for implementing {EXPECT|ASSERT}_PRED_FORMAT2. +// Don't use this in your code. +#define GTEST_PRED_FORMAT2_(pred_format, v1, v2, on_failure) \ + GTEST_ASSERT_(pred_format(#v1, #v2, v1, v2), on_failure) + +// Internal macro for implementing {EXPECT|ASSERT}_PRED2. Don't use +// this in your code. +#define GTEST_PRED2_(pred, v1, v2, on_failure) \ + GTEST_ASSERT_(::testing::AssertPred2Helper(#pred, #v1, #v2, pred, v1, v2), \ + on_failure) + +// Binary predicate assertion macros. +#define EXPECT_PRED_FORMAT2(pred_format, v1, v2) \ + GTEST_PRED_FORMAT2_(pred_format, v1, v2, GTEST_NONFATAL_FAILURE_) +#define EXPECT_PRED2(pred, v1, v2) \ + GTEST_PRED2_(pred, v1, v2, GTEST_NONFATAL_FAILURE_) +#define ASSERT_PRED_FORMAT2(pred_format, v1, v2) \ + GTEST_PRED_FORMAT2_(pred_format, v1, v2, GTEST_FATAL_FAILURE_) +#define ASSERT_PRED2(pred, v1, v2) \ + GTEST_PRED2_(pred, v1, v2, GTEST_FATAL_FAILURE_) + +// Helper function for implementing {EXPECT|ASSERT}_PRED3. Don't use +// this in your code. +template +AssertionResult AssertPred3Helper(const char* pred_text, const char* e1, + const char* e2, const char* e3, Pred pred, + const T1& v1, const T2& v2, const T3& v3) { + if (pred(v1, v2, v3)) return AssertionSuccess(); + + return AssertionFailure() + << pred_text << "(" << e1 << ", " << e2 << ", " << e3 + << ") evaluates to false, where" + << "\n" + << e1 << " evaluates to " << ::testing::PrintToString(v1) << "\n" + << e2 << " evaluates to " << ::testing::PrintToString(v2) << "\n" + << e3 << " evaluates to " << ::testing::PrintToString(v3); +} + +// Internal macro for implementing {EXPECT|ASSERT}_PRED_FORMAT3. +// Don't use this in your code. +#define GTEST_PRED_FORMAT3_(pred_format, v1, v2, v3, on_failure) \ + GTEST_ASSERT_(pred_format(#v1, #v2, #v3, v1, v2, v3), on_failure) + +// Internal macro for implementing {EXPECT|ASSERT}_PRED3. Don't use +// this in your code. +#define GTEST_PRED3_(pred, v1, v2, v3, on_failure) \ + GTEST_ASSERT_( \ + ::testing::AssertPred3Helper(#pred, #v1, #v2, #v3, pred, v1, v2, v3), \ + on_failure) + +// Ternary predicate assertion macros. +#define EXPECT_PRED_FORMAT3(pred_format, v1, v2, v3) \ + GTEST_PRED_FORMAT3_(pred_format, v1, v2, v3, GTEST_NONFATAL_FAILURE_) +#define EXPECT_PRED3(pred, v1, v2, v3) \ + GTEST_PRED3_(pred, v1, v2, v3, GTEST_NONFATAL_FAILURE_) +#define ASSERT_PRED_FORMAT3(pred_format, v1, v2, v3) \ + GTEST_PRED_FORMAT3_(pred_format, v1, v2, v3, GTEST_FATAL_FAILURE_) +#define ASSERT_PRED3(pred, v1, v2, v3) \ + GTEST_PRED3_(pred, v1, v2, v3, GTEST_FATAL_FAILURE_) + +// Helper function for implementing {EXPECT|ASSERT}_PRED4. Don't use +// this in your code. +template +AssertionResult AssertPred4Helper(const char* pred_text, const char* e1, + const char* e2, const char* e3, + const char* e4, Pred pred, const T1& v1, + const T2& v2, const T3& v3, const T4& v4) { + if (pred(v1, v2, v3, v4)) return AssertionSuccess(); + + return AssertionFailure() + << pred_text << "(" << e1 << ", " << e2 << ", " << e3 << ", " << e4 + << ") evaluates to false, where" + << "\n" + << e1 << " evaluates to " << ::testing::PrintToString(v1) << "\n" + << e2 << " evaluates to " << ::testing::PrintToString(v2) << "\n" + << e3 << " evaluates to " << ::testing::PrintToString(v3) << "\n" + << e4 << " evaluates to " << ::testing::PrintToString(v4); +} + +// Internal macro for implementing {EXPECT|ASSERT}_PRED_FORMAT4. +// Don't use this in your code. +#define GTEST_PRED_FORMAT4_(pred_format, v1, v2, v3, v4, on_failure) \ + GTEST_ASSERT_(pred_format(#v1, #v2, #v3, #v4, v1, v2, v3, v4), on_failure) + +// Internal macro for implementing {EXPECT|ASSERT}_PRED4. Don't use +// this in your code. +#define GTEST_PRED4_(pred, v1, v2, v3, v4, on_failure) \ + GTEST_ASSERT_(::testing::AssertPred4Helper(#pred, #v1, #v2, #v3, #v4, pred, \ + v1, v2, v3, v4), \ + on_failure) + +// 4-ary predicate assertion macros. +#define EXPECT_PRED_FORMAT4(pred_format, v1, v2, v3, v4) \ + GTEST_PRED_FORMAT4_(pred_format, v1, v2, v3, v4, GTEST_NONFATAL_FAILURE_) +#define EXPECT_PRED4(pred, v1, v2, v3, v4) \ + GTEST_PRED4_(pred, v1, v2, v3, v4, GTEST_NONFATAL_FAILURE_) +#define ASSERT_PRED_FORMAT4(pred_format, v1, v2, v3, v4) \ + GTEST_PRED_FORMAT4_(pred_format, v1, v2, v3, v4, GTEST_FATAL_FAILURE_) +#define ASSERT_PRED4(pred, v1, v2, v3, v4) \ + GTEST_PRED4_(pred, v1, v2, v3, v4, GTEST_FATAL_FAILURE_) + +// Helper function for implementing {EXPECT|ASSERT}_PRED5. Don't use +// this in your code. +template +AssertionResult AssertPred5Helper(const char* pred_text, const char* e1, + const char* e2, const char* e3, + const char* e4, const char* e5, Pred pred, + const T1& v1, const T2& v2, const T3& v3, + const T4& v4, const T5& v5) { + if (pred(v1, v2, v3, v4, v5)) return AssertionSuccess(); + + return AssertionFailure() + << pred_text << "(" << e1 << ", " << e2 << ", " << e3 << ", " << e4 + << ", " << e5 << ") evaluates to false, where" + << "\n" + << e1 << " evaluates to " << ::testing::PrintToString(v1) << "\n" + << e2 << " evaluates to " << ::testing::PrintToString(v2) << "\n" + << e3 << " evaluates to " << ::testing::PrintToString(v3) << "\n" + << e4 << " evaluates to " << ::testing::PrintToString(v4) << "\n" + << e5 << " evaluates to " << ::testing::PrintToString(v5); +} + +// Internal macro for implementing {EXPECT|ASSERT}_PRED_FORMAT5. +// Don't use this in your code. +#define GTEST_PRED_FORMAT5_(pred_format, v1, v2, v3, v4, v5, on_failure) \ + GTEST_ASSERT_(pred_format(#v1, #v2, #v3, #v4, #v5, v1, v2, v3, v4, v5), \ + on_failure) + +// Internal macro for implementing {EXPECT|ASSERT}_PRED5. Don't use +// this in your code. +#define GTEST_PRED5_(pred, v1, v2, v3, v4, v5, on_failure) \ + GTEST_ASSERT_(::testing::AssertPred5Helper(#pred, #v1, #v2, #v3, #v4, #v5, \ + pred, v1, v2, v3, v4, v5), \ + on_failure) + +// 5-ary predicate assertion macros. +#define EXPECT_PRED_FORMAT5(pred_format, v1, v2, v3, v4, v5) \ + GTEST_PRED_FORMAT5_(pred_format, v1, v2, v3, v4, v5, GTEST_NONFATAL_FAILURE_) +#define EXPECT_PRED5(pred, v1, v2, v3, v4, v5) \ + GTEST_PRED5_(pred, v1, v2, v3, v4, v5, GTEST_NONFATAL_FAILURE_) +#define ASSERT_PRED_FORMAT5(pred_format, v1, v2, v3, v4, v5) \ + GTEST_PRED_FORMAT5_(pred_format, v1, v2, v3, v4, v5, GTEST_FATAL_FAILURE_) +#define ASSERT_PRED5(pred, v1, v2, v3, v4, v5) \ + GTEST_PRED5_(pred, v1, v2, v3, v4, v5, GTEST_FATAL_FAILURE_) + +} // namespace testing + +#endif // GOOGLETEST_INCLUDE_GTEST_GTEST_PRED_IMPL_H_ diff --git a/include/gtest/gtest_prod.h b/include/gtest/gtest_prod.h new file mode 100644 index 0000000..1f37dc3 --- /dev/null +++ b/include/gtest/gtest_prod.h @@ -0,0 +1,60 @@ +// Copyright 2006, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Google C++ Testing and Mocking Framework definitions useful in production +// code. + +#ifndef GOOGLETEST_INCLUDE_GTEST_GTEST_PROD_H_ +#define GOOGLETEST_INCLUDE_GTEST_GTEST_PROD_H_ + +// When you need to test the private or protected members of a class, +// use the FRIEND_TEST macro to declare your tests as friends of the +// class. For example: +// +// class MyClass { +// private: +// void PrivateMethod(); +// FRIEND_TEST(MyClassTest, PrivateMethodWorks); +// }; +// +// class MyClassTest : public testing::Test { +// // ... +// }; +// +// TEST_F(MyClassTest, PrivateMethodWorks) { +// // Can call MyClass::PrivateMethod() here. +// } +// +// Note: The test class must be in the same namespace as the class being tested. +// For example, putting MyClassTest in an anonymous namespace will not work. + +#define FRIEND_TEST(test_case_name, test_name) \ + friend class test_case_name##_##test_name##_Test + +#endif // GOOGLETEST_INCLUDE_GTEST_GTEST_PROD_H_ diff --git a/include/gtest/internal/custom/README.md b/include/gtest/internal/custom/README.md new file mode 100644 index 0000000..cb49e2c --- /dev/null +++ b/include/gtest/internal/custom/README.md @@ -0,0 +1,44 @@ +# Customization Points + +The custom directory is an injection point for custom user configurations. + +## Header `gtest.h` + +### The following macros can be defined: + +* `GTEST_OS_STACK_TRACE_GETTER_` - The name of an implementation of + `OsStackTraceGetterInterface`. +* `GTEST_CUSTOM_TEMPDIR_FUNCTION_` - An override for `testing::TempDir()`. See + `testing::TempDir` for semantics and signature. + +## Header `gtest-port.h` + +The following macros can be defined: + +### Logging: + +* `GTEST_LOG_(severity)` +* `GTEST_CHECK_(condition)` +* Functions `LogToStderr()` and `FlushInfoLog()` have to be provided too. + +### Threading: + +* `GTEST_HAS_NOTIFICATION_` - Enabled if Notification is already provided. +* `GTEST_HAS_MUTEX_AND_THREAD_LOCAL_` - Enabled if `Mutex` and `ThreadLocal` + are already provided. Must also provide `GTEST_DECLARE_STATIC_MUTEX_(mutex)` + and `GTEST_DEFINE_STATIC_MUTEX_(mutex)` +* `GTEST_EXCLUSIVE_LOCK_REQUIRED_(locks)` +* `GTEST_LOCK_EXCLUDED_(locks)` + +### Underlying library support features + +* `GTEST_HAS_CXXABI_H_` + +### Exporting API symbols: + +* `GTEST_API_` - Specifier for exported symbols. + +## Header `gtest-printers.h` + +* See documentation at `gtest/gtest-printers.h` for details on how to define a + custom printer. diff --git a/include/gtest/internal/custom/gtest-port.h b/include/gtest/internal/custom/gtest-port.h new file mode 100644 index 0000000..db02881 --- /dev/null +++ b/include/gtest/internal/custom/gtest-port.h @@ -0,0 +1,37 @@ +// Copyright 2015, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Injection point for custom user configurations. See README for details +// +// ** Custom implementation starts here ** + +#ifndef GOOGLETEST_INCLUDE_GTEST_INTERNAL_CUSTOM_GTEST_PORT_H_ +#define GOOGLETEST_INCLUDE_GTEST_INTERNAL_CUSTOM_GTEST_PORT_H_ + +#endif // GOOGLETEST_INCLUDE_GTEST_INTERNAL_CUSTOM_GTEST_PORT_H_ diff --git a/include/gtest/internal/custom/gtest-printers.h b/include/gtest/internal/custom/gtest-printers.h new file mode 100644 index 0000000..b9495d8 --- /dev/null +++ b/include/gtest/internal/custom/gtest-printers.h @@ -0,0 +1,42 @@ +// Copyright 2015, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// This file provides an injection point for custom printers in a local +// installation of gTest. +// It will be included from gtest-printers.h and the overrides in this file +// will be visible to everyone. +// +// Injection point for custom user configurations. See README for details +// +// ** Custom implementation starts here ** + +#ifndef GOOGLETEST_INCLUDE_GTEST_INTERNAL_CUSTOM_GTEST_PRINTERS_H_ +#define GOOGLETEST_INCLUDE_GTEST_INTERNAL_CUSTOM_GTEST_PRINTERS_H_ + +#endif // GOOGLETEST_INCLUDE_GTEST_INTERNAL_CUSTOM_GTEST_PRINTERS_H_ diff --git a/include/gtest/internal/custom/gtest.h b/include/gtest/internal/custom/gtest.h new file mode 100644 index 0000000..afaaf17 --- /dev/null +++ b/include/gtest/internal/custom/gtest.h @@ -0,0 +1,37 @@ +// Copyright 2015, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Injection point for custom user configurations. See README for details +// +// ** Custom implementation starts here ** + +#ifndef GOOGLETEST_INCLUDE_GTEST_INTERNAL_CUSTOM_GTEST_H_ +#define GOOGLETEST_INCLUDE_GTEST_INTERNAL_CUSTOM_GTEST_H_ + +#endif // GOOGLETEST_INCLUDE_GTEST_INTERNAL_CUSTOM_GTEST_H_ diff --git a/include/gtest/internal/gtest-death-test-internal.h b/include/gtest/internal/gtest-death-test-internal.h new file mode 100644 index 0000000..45580ae --- /dev/null +++ b/include/gtest/internal/gtest-death-test-internal.h @@ -0,0 +1,306 @@ +// Copyright 2005, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// The Google C++ Testing and Mocking Framework (Google Test) +// +// This header file defines internal utilities needed for implementing +// death tests. They are subject to change without notice. + +// IWYU pragma: private, include "gtest/gtest.h" +// IWYU pragma: friend gtest/.* +// IWYU pragma: friend gmock/.* + +#ifndef GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_DEATH_TEST_INTERNAL_H_ +#define GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_DEATH_TEST_INTERNAL_H_ + +#include + +#include + +#include "gtest/gtest-matchers.h" +#include "gtest/internal/gtest-internal.h" + +GTEST_DECLARE_string_(internal_run_death_test); + +namespace testing { +namespace internal { + +// Names of the flags (needed for parsing Google Test flags). +const char kDeathTestStyleFlag[] = "death_test_style"; +const char kDeathTestUseFork[] = "death_test_use_fork"; +const char kInternalRunDeathTestFlag[] = "internal_run_death_test"; + +#if GTEST_HAS_DEATH_TEST + +GTEST_DISABLE_MSC_WARNINGS_PUSH_(4251 \ +/* class A needs to have dll-interface to be used by clients of class B */) + +// DeathTest is a class that hides much of the complexity of the +// GTEST_DEATH_TEST_ macro. It is abstract; its static Create method +// returns a concrete class that depends on the prevailing death test +// style, as defined by the --gtest_death_test_style and/or +// --gtest_internal_run_death_test flags. + +// In describing the results of death tests, these terms are used with +// the corresponding definitions: +// +// exit status: The integer exit information in the format specified +// by wait(2) +// exit code: The integer code passed to exit(3), _exit(2), or +// returned from main() +class GTEST_API_ DeathTest { + public: + // Create returns false if there was an error determining the + // appropriate action to take for the current death test; for example, + // if the gtest_death_test_style flag is set to an invalid value. + // The LastMessage method will return a more detailed message in that + // case. Otherwise, the DeathTest pointer pointed to by the "test" + // argument is set. If the death test should be skipped, the pointer + // is set to NULL; otherwise, it is set to the address of a new concrete + // DeathTest object that controls the execution of the current test. + static bool Create(const char* statement, Matcher matcher, + const char* file, int line, DeathTest** test); + DeathTest(); + virtual ~DeathTest() {} + + // A helper class that aborts a death test when it's deleted. + class ReturnSentinel { + public: + explicit ReturnSentinel(DeathTest* test) : test_(test) {} + ~ReturnSentinel() { test_->Abort(TEST_ENCOUNTERED_RETURN_STATEMENT); } + + private: + DeathTest* const test_; + ReturnSentinel(const ReturnSentinel&) = delete; + ReturnSentinel& operator=(const ReturnSentinel&) = delete; + } GTEST_ATTRIBUTE_UNUSED_; + + // An enumeration of possible roles that may be taken when a death + // test is encountered. EXECUTE means that the death test logic should + // be executed immediately. OVERSEE means that the program should prepare + // the appropriate environment for a child process to execute the death + // test, then wait for it to complete. + enum TestRole { OVERSEE_TEST, EXECUTE_TEST }; + + // An enumeration of the three reasons that a test might be aborted. + enum AbortReason { + TEST_ENCOUNTERED_RETURN_STATEMENT, + TEST_THREW_EXCEPTION, + TEST_DID_NOT_DIE + }; + + // Assumes one of the above roles. + virtual TestRole AssumeRole() = 0; + + // Waits for the death test to finish and returns its status. + virtual int Wait() = 0; + + // Returns true if the death test passed; that is, the test process + // exited during the test, its exit status matches a user-supplied + // predicate, and its stderr output matches a user-supplied regular + // expression. + // The user-supplied predicate may be a macro expression rather + // than a function pointer or functor, or else Wait and Passed could + // be combined. + virtual bool Passed(bool exit_status_ok) = 0; + + // Signals that the death test did not die as expected. + virtual void Abort(AbortReason reason) = 0; + + // Returns a human-readable outcome message regarding the outcome of + // the last death test. + static const char* LastMessage(); + + static void set_last_death_test_message(const std::string& message); + + private: + // A string containing a description of the outcome of the last death test. + static std::string last_death_test_message_; + + DeathTest(const DeathTest&) = delete; + DeathTest& operator=(const DeathTest&) = delete; +}; + +GTEST_DISABLE_MSC_WARNINGS_POP_() // 4251 + +// Factory interface for death tests. May be mocked out for testing. +class DeathTestFactory { + public: + virtual ~DeathTestFactory() {} + virtual bool Create(const char* statement, + Matcher matcher, const char* file, + int line, DeathTest** test) = 0; +}; + +// A concrete DeathTestFactory implementation for normal use. +class DefaultDeathTestFactory : public DeathTestFactory { + public: + bool Create(const char* statement, Matcher matcher, + const char* file, int line, DeathTest** test) override; +}; + +// Returns true if exit_status describes a process that was terminated +// by a signal, or exited normally with a nonzero exit code. +GTEST_API_ bool ExitedUnsuccessfully(int exit_status); + +// A string passed to EXPECT_DEATH (etc.) is caught by one of these overloads +// and interpreted as a regex (rather than an Eq matcher) for legacy +// compatibility. +inline Matcher MakeDeathTestMatcher( + ::testing::internal::RE regex) { + return ContainsRegex(regex.pattern()); +} +inline Matcher MakeDeathTestMatcher(const char* regex) { + return ContainsRegex(regex); +} +inline Matcher MakeDeathTestMatcher( + const ::std::string& regex) { + return ContainsRegex(regex); +} + +// If a Matcher is passed to EXPECT_DEATH (etc.), it's +// used directly. +inline Matcher MakeDeathTestMatcher( + Matcher matcher) { + return matcher; +} + +// Traps C++ exceptions escaping statement and reports them as test +// failures. Note that trapping SEH exceptions is not implemented here. +#if GTEST_HAS_EXCEPTIONS +#define GTEST_EXECUTE_DEATH_TEST_STATEMENT_(statement, death_test) \ + try { \ + GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement); \ + } catch (const ::std::exception& gtest_exception) { \ + fprintf( \ + stderr, \ + "\n%s: Caught std::exception-derived exception escaping the " \ + "death test statement. Exception message: %s\n", \ + ::testing::internal::FormatFileLocation(__FILE__, __LINE__).c_str(), \ + gtest_exception.what()); \ + fflush(stderr); \ + death_test->Abort(::testing::internal::DeathTest::TEST_THREW_EXCEPTION); \ + } catch (...) { \ + death_test->Abort(::testing::internal::DeathTest::TEST_THREW_EXCEPTION); \ + } + +#else +#define GTEST_EXECUTE_DEATH_TEST_STATEMENT_(statement, death_test) \ + GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement) + +#endif + +// This macro is for implementing ASSERT_DEATH*, EXPECT_DEATH*, +// ASSERT_EXIT*, and EXPECT_EXIT*. +#define GTEST_DEATH_TEST_(statement, predicate, regex_or_matcher, fail) \ + GTEST_AMBIGUOUS_ELSE_BLOCKER_ \ + if (::testing::internal::AlwaysTrue()) { \ + ::testing::internal::DeathTest* gtest_dt; \ + if (!::testing::internal::DeathTest::Create( \ + #statement, \ + ::testing::internal::MakeDeathTestMatcher(regex_or_matcher), \ + __FILE__, __LINE__, >est_dt)) { \ + goto GTEST_CONCAT_TOKEN_(gtest_label_, __LINE__); \ + } \ + if (gtest_dt != nullptr) { \ + std::unique_ptr< ::testing::internal::DeathTest> gtest_dt_ptr(gtest_dt); \ + switch (gtest_dt->AssumeRole()) { \ + case ::testing::internal::DeathTest::OVERSEE_TEST: \ + if (!gtest_dt->Passed(predicate(gtest_dt->Wait()))) { \ + goto GTEST_CONCAT_TOKEN_(gtest_label_, __LINE__); \ + } \ + break; \ + case ::testing::internal::DeathTest::EXECUTE_TEST: { \ + ::testing::internal::DeathTest::ReturnSentinel gtest_sentinel( \ + gtest_dt); \ + GTEST_EXECUTE_DEATH_TEST_STATEMENT_(statement, gtest_dt); \ + gtest_dt->Abort(::testing::internal::DeathTest::TEST_DID_NOT_DIE); \ + break; \ + } \ + } \ + } \ + } else \ + GTEST_CONCAT_TOKEN_(gtest_label_, __LINE__) \ + : fail(::testing::internal::DeathTest::LastMessage()) +// The symbol "fail" here expands to something into which a message +// can be streamed. + +// This macro is for implementing ASSERT/EXPECT_DEBUG_DEATH when compiled in +// NDEBUG mode. In this case we need the statements to be executed and the macro +// must accept a streamed message even though the message is never printed. +// The regex object is not evaluated, but it is used to prevent "unused" +// warnings and to avoid an expression that doesn't compile in debug mode. +#define GTEST_EXECUTE_STATEMENT_(statement, regex_or_matcher) \ + GTEST_AMBIGUOUS_ELSE_BLOCKER_ \ + if (::testing::internal::AlwaysTrue()) { \ + GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement); \ + } else if (!::testing::internal::AlwaysTrue()) { \ + ::testing::internal::MakeDeathTestMatcher(regex_or_matcher); \ + } else \ + ::testing::Message() + +// A class representing the parsed contents of the +// --gtest_internal_run_death_test flag, as it existed when +// RUN_ALL_TESTS was called. +class InternalRunDeathTestFlag { + public: + InternalRunDeathTestFlag(const std::string& a_file, int a_line, int an_index, + int a_write_fd) + : file_(a_file), line_(a_line), index_(an_index), write_fd_(a_write_fd) {} + + ~InternalRunDeathTestFlag() { + if (write_fd_ >= 0) posix::Close(write_fd_); + } + + const std::string& file() const { return file_; } + int line() const { return line_; } + int index() const { return index_; } + int write_fd() const { return write_fd_; } + + private: + std::string file_; + int line_; + int index_; + int write_fd_; + + InternalRunDeathTestFlag(const InternalRunDeathTestFlag&) = delete; + InternalRunDeathTestFlag& operator=(const InternalRunDeathTestFlag&) = delete; +}; + +// Returns a newly created InternalRunDeathTestFlag object with fields +// initialized from the GTEST_FLAG(internal_run_death_test) flag if +// the flag is specified; otherwise returns NULL. +InternalRunDeathTestFlag* ParseInternalRunDeathTestFlag(); + +#endif // GTEST_HAS_DEATH_TEST + +} // namespace internal +} // namespace testing + +#endif // GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_DEATH_TEST_INTERNAL_H_ diff --git a/include/gtest/internal/gtest-filepath.h b/include/gtest/internal/gtest-filepath.h new file mode 100644 index 0000000..dd7a3a4 --- /dev/null +++ b/include/gtest/internal/gtest-filepath.h @@ -0,0 +1,220 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Google Test filepath utilities +// +// This header file declares classes and functions used internally by +// Google Test. They are subject to change without notice. +// +// This file is #included in gtest/internal/gtest-internal.h. +// Do not include this header file separately! + +// IWYU pragma: private, include "gtest/gtest.h" +// IWYU pragma: friend gtest/.* +// IWYU pragma: friend gmock/.* + +#ifndef GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_FILEPATH_H_ +#define GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_FILEPATH_H_ + +#include "gtest/internal/gtest-string.h" + +GTEST_DISABLE_MSC_WARNINGS_PUSH_(4251 \ +/* class A needs to have dll-interface to be used by clients of class B */) + +namespace testing { +namespace internal { + +// FilePath - a class for file and directory pathname manipulation which +// handles platform-specific conventions (like the pathname separator). +// Used for helper functions for naming files in a directory for xml output. +// Except for Set methods, all methods are const or static, which provides an +// "immutable value object" -- useful for peace of mind. +// A FilePath with a value ending in a path separator ("like/this/") represents +// a directory, otherwise it is assumed to represent a file. In either case, +// it may or may not represent an actual file or directory in the file system. +// Names are NOT checked for syntax correctness -- no checking for illegal +// characters, malformed paths, etc. + +class GTEST_API_ FilePath { + public: + FilePath() : pathname_("") {} + FilePath(const FilePath& rhs) : pathname_(rhs.pathname_) {} + + explicit FilePath(const std::string& pathname) : pathname_(pathname) { + Normalize(); + } + + FilePath& operator=(const FilePath& rhs) { + Set(rhs); + return *this; + } + + void Set(const FilePath& rhs) { pathname_ = rhs.pathname_; } + + const std::string& string() const { return pathname_; } + const char* c_str() const { return pathname_.c_str(); } + + // Returns the current working directory, or "" if unsuccessful. + static FilePath GetCurrentDir(); + + // Given directory = "dir", base_name = "test", number = 0, + // extension = "xml", returns "dir/test.xml". If number is greater + // than zero (e.g., 12), returns "dir/test_12.xml". + // On Windows platform, uses \ as the separator rather than /. + static FilePath MakeFileName(const FilePath& directory, + const FilePath& base_name, int number, + const char* extension); + + // Given directory = "dir", relative_path = "test.xml", + // returns "dir/test.xml". + // On Windows, uses \ as the separator rather than /. + static FilePath ConcatPaths(const FilePath& directory, + const FilePath& relative_path); + + // Returns a pathname for a file that does not currently exist. The pathname + // will be directory/base_name.extension or + // directory/base_name_.extension if directory/base_name.extension + // already exists. The number will be incremented until a pathname is found + // that does not already exist. + // Examples: 'dir/foo_test.xml' or 'dir/foo_test_1.xml'. + // There could be a race condition if two or more processes are calling this + // function at the same time -- they could both pick the same filename. + static FilePath GenerateUniqueFileName(const FilePath& directory, + const FilePath& base_name, + const char* extension); + + // Returns true if and only if the path is "". + bool IsEmpty() const { return pathname_.empty(); } + + // If input name has a trailing separator character, removes it and returns + // the name, otherwise return the name string unmodified. + // On Windows platform, uses \ as the separator, other platforms use /. + FilePath RemoveTrailingPathSeparator() const; + + // Returns a copy of the FilePath with the directory part removed. + // Example: FilePath("path/to/file").RemoveDirectoryName() returns + // FilePath("file"). If there is no directory part ("just_a_file"), it returns + // the FilePath unmodified. If there is no file part ("just_a_dir/") it + // returns an empty FilePath (""). + // On Windows platform, '\' is the path separator, otherwise it is '/'. + FilePath RemoveDirectoryName() const; + + // RemoveFileName returns the directory path with the filename removed. + // Example: FilePath("path/to/file").RemoveFileName() returns "path/to/". + // If the FilePath is "a_file" or "/a_file", RemoveFileName returns + // FilePath("./") or, on Windows, FilePath(".\\"). If the filepath does + // not have a file, like "just/a/dir/", it returns the FilePath unmodified. + // On Windows platform, '\' is the path separator, otherwise it is '/'. + FilePath RemoveFileName() const; + + // Returns a copy of the FilePath with the case-insensitive extension removed. + // Example: FilePath("dir/file.exe").RemoveExtension("EXE") returns + // FilePath("dir/file"). If a case-insensitive extension is not + // found, returns a copy of the original FilePath. + FilePath RemoveExtension(const char* extension) const; + + // Creates directories so that path exists. Returns true if successful or if + // the directories already exist; returns false if unable to create + // directories for any reason. Will also return false if the FilePath does + // not represent a directory (that is, it doesn't end with a path separator). + bool CreateDirectoriesRecursively() const; + + // Create the directory so that path exists. Returns true if successful or + // if the directory already exists; returns false if unable to create the + // directory for any reason, including if the parent directory does not + // exist. Not named "CreateDirectory" because that's a macro on Windows. + bool CreateFolder() const; + + // Returns true if FilePath describes something in the file-system, + // either a file, directory, or whatever, and that something exists. + bool FileOrDirectoryExists() const; + + // Returns true if pathname describes a directory in the file-system + // that exists. + bool DirectoryExists() const; + + // Returns true if FilePath ends with a path separator, which indicates that + // it is intended to represent a directory. Returns false otherwise. + // This does NOT check that a directory (or file) actually exists. + bool IsDirectory() const; + + // Returns true if pathname describes a root directory. (Windows has one + // root directory per disk drive.) + bool IsRootDirectory() const; + + // Returns true if pathname describes an absolute path. + bool IsAbsolutePath() const; + + private: + // Replaces multiple consecutive separators with a single separator. + // For example, "bar///foo" becomes "bar/foo". Does not eliminate other + // redundancies that might be in a pathname involving "." or "..". + // + // A pathname with multiple consecutive separators may occur either through + // user error or as a result of some scripts or APIs that generate a pathname + // with a trailing separator. On other platforms the same API or script + // may NOT generate a pathname with a trailing "/". Then elsewhere that + // pathname may have another "/" and pathname components added to it, + // without checking for the separator already being there. + // The script language and operating system may allow paths like "foo//bar" + // but some of the functions in FilePath will not handle that correctly. In + // particular, RemoveTrailingPathSeparator() only removes one separator, and + // it is called in CreateDirectoriesRecursively() assuming that it will change + // a pathname from directory syntax (trailing separator) to filename syntax. + // + // On Windows this method also replaces the alternate path separator '/' with + // the primary path separator '\\', so that for example "bar\\/\\foo" becomes + // "bar\\foo". + + void Normalize(); + + // Returns a pointer to the last occurrence of a valid path separator in + // the FilePath. On Windows, for example, both '/' and '\' are valid path + // separators. Returns NULL if no path separator was found. + const char* FindLastPathSeparator() const; + + // Returns the length of the path root, including the directory separator at + // the end of the prefix. Returns zero by definition if the path is relative. + // Examples: + // - [Windows] "..\Sibling" => 0 + // - [Windows] "\Windows" => 1 + // - [Windows] "C:/Windows\Notepad.exe" => 3 + // - [Windows] "\\Host\Share\C$/Windows" => 13 + // - [UNIX] "/bin" => 1 + size_t CalculateRootLength() const; + + std::string pathname_; +}; // class FilePath + +} // namespace internal +} // namespace testing + +GTEST_DISABLE_MSC_WARNINGS_POP_() // 4251 + +#endif // GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_FILEPATH_H_ diff --git a/include/gtest/internal/gtest-internal.h b/include/gtest/internal/gtest-internal.h new file mode 100644 index 0000000..e9c2441 --- /dev/null +++ b/include/gtest/internal/gtest-internal.h @@ -0,0 +1,1570 @@ +// Copyright 2005, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// The Google C++ Testing and Mocking Framework (Google Test) +// +// This header file declares functions and macros used internally by +// Google Test. They are subject to change without notice. + +// IWYU pragma: private, include "gtest/gtest.h" +// IWYU pragma: friend gtest/.* +// IWYU pragma: friend gmock/.* + +#ifndef GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_INTERNAL_H_ +#define GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_INTERNAL_H_ + +#include "gtest/internal/gtest-port.h" + +#if GTEST_OS_LINUX +#include +#include +#include +#include +#endif // GTEST_OS_LINUX + +#if GTEST_HAS_EXCEPTIONS +#include +#endif + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "gtest/gtest-message.h" +#include "gtest/internal/gtest-filepath.h" +#include "gtest/internal/gtest-string.h" +#include "gtest/internal/gtest-type-util.h" + +// Due to C++ preprocessor weirdness, we need double indirection to +// concatenate two tokens when one of them is __LINE__. Writing +// +// foo ## __LINE__ +// +// will result in the token foo__LINE__, instead of foo followed by +// the current line number. For more details, see +// http://www.parashift.com/c++-faq-lite/misc-technical-issues.html#faq-39.6 +#define GTEST_CONCAT_TOKEN_(foo, bar) GTEST_CONCAT_TOKEN_IMPL_(foo, bar) +#define GTEST_CONCAT_TOKEN_IMPL_(foo, bar) foo##bar + +// Stringifies its argument. +// Work around a bug in visual studio which doesn't accept code like this: +// +// #define GTEST_STRINGIFY_(name) #name +// #define MACRO(a, b, c) ... GTEST_STRINGIFY_(a) ... +// MACRO(, x, y) +// +// Complaining about the argument to GTEST_STRINGIFY_ being empty. +// This is allowed by the spec. +#define GTEST_STRINGIFY_HELPER_(name, ...) #name +#define GTEST_STRINGIFY_(...) GTEST_STRINGIFY_HELPER_(__VA_ARGS__, ) + +namespace proto2 { +class MessageLite; +} + +namespace testing { + +// Forward declarations. + +class AssertionResult; // Result of an assertion. +class Message; // Represents a failure message. +class Test; // Represents a test. +class TestInfo; // Information about a test. +class TestPartResult; // Result of a test part. +class UnitTest; // A collection of test suites. + +template +::std::string PrintToString(const T& value); + +namespace internal { + +struct TraceInfo; // Information about a trace point. +class TestInfoImpl; // Opaque implementation of TestInfo +class UnitTestImpl; // Opaque implementation of UnitTest + +// The text used in failure messages to indicate the start of the +// stack trace. +GTEST_API_ extern const char kStackTraceMarker[]; + +// An IgnoredValue object can be implicitly constructed from ANY value. +class IgnoredValue { + struct Sink {}; + + public: + // This constructor template allows any value to be implicitly + // converted to IgnoredValue. The object has no data member and + // doesn't try to remember anything about the argument. We + // deliberately omit the 'explicit' keyword in order to allow the + // conversion to be implicit. + // Disable the conversion if T already has a magical conversion operator. + // Otherwise we get ambiguity. + template ::value, + int>::type = 0> + IgnoredValue(const T& /* ignored */) {} // NOLINT(runtime/explicit) +}; + +// Appends the user-supplied message to the Google-Test-generated message. +GTEST_API_ std::string AppendUserMessage(const std::string& gtest_msg, + const Message& user_msg); + +#if GTEST_HAS_EXCEPTIONS + +GTEST_DISABLE_MSC_WARNINGS_PUSH_( + 4275 /* an exported class was derived from a class that was not exported */) + +// This exception is thrown by (and only by) a failed Google Test +// assertion when GTEST_FLAG(throw_on_failure) is true (if exceptions +// are enabled). We derive it from std::runtime_error, which is for +// errors presumably detectable only at run time. Since +// std::runtime_error inherits from std::exception, many testing +// frameworks know how to extract and print the message inside it. +class GTEST_API_ GoogleTestFailureException : public ::std::runtime_error { + public: + explicit GoogleTestFailureException(const TestPartResult& failure); +}; + +GTEST_DISABLE_MSC_WARNINGS_POP_() // 4275 + +#endif // GTEST_HAS_EXCEPTIONS + +namespace edit_distance { +// Returns the optimal edits to go from 'left' to 'right'. +// All edits cost the same, with replace having lower priority than +// add/remove. +// Simple implementation of the Wagner-Fischer algorithm. +// See http://en.wikipedia.org/wiki/Wagner-Fischer_algorithm +enum EditType { kMatch, kAdd, kRemove, kReplace }; +GTEST_API_ std::vector CalculateOptimalEdits( + const std::vector& left, const std::vector& right); + +// Same as above, but the input is represented as strings. +GTEST_API_ std::vector CalculateOptimalEdits( + const std::vector& left, + const std::vector& right); + +// Create a diff of the input strings in Unified diff format. +GTEST_API_ std::string CreateUnifiedDiff(const std::vector& left, + const std::vector& right, + size_t context = 2); + +} // namespace edit_distance + +// Constructs and returns the message for an equality assertion +// (e.g. ASSERT_EQ, EXPECT_STREQ, etc) failure. +// +// The first four parameters are the expressions used in the assertion +// and their values, as strings. For example, for ASSERT_EQ(foo, bar) +// where foo is 5 and bar is 6, we have: +// +// expected_expression: "foo" +// actual_expression: "bar" +// expected_value: "5" +// actual_value: "6" +// +// The ignoring_case parameter is true if and only if the assertion is a +// *_STRCASEEQ*. When it's true, the string " (ignoring case)" will +// be inserted into the message. +GTEST_API_ AssertionResult EqFailure(const char* expected_expression, + const char* actual_expression, + const std::string& expected_value, + const std::string& actual_value, + bool ignoring_case); + +// Constructs a failure message for Boolean assertions such as EXPECT_TRUE. +GTEST_API_ std::string GetBoolAssertionFailureMessage( + const AssertionResult& assertion_result, const char* expression_text, + const char* actual_predicate_value, const char* expected_predicate_value); + +// This template class represents an IEEE floating-point number +// (either single-precision or double-precision, depending on the +// template parameters). +// +// The purpose of this class is to do more sophisticated number +// comparison. (Due to round-off error, etc, it's very unlikely that +// two floating-points will be equal exactly. Hence a naive +// comparison by the == operation often doesn't work.) +// +// Format of IEEE floating-point: +// +// The most-significant bit being the leftmost, an IEEE +// floating-point looks like +// +// sign_bit exponent_bits fraction_bits +// +// Here, sign_bit is a single bit that designates the sign of the +// number. +// +// For float, there are 8 exponent bits and 23 fraction bits. +// +// For double, there are 11 exponent bits and 52 fraction bits. +// +// More details can be found at +// http://en.wikipedia.org/wiki/IEEE_floating-point_standard. +// +// Template parameter: +// +// RawType: the raw floating-point type (either float or double) +template +class FloatingPoint { + public: + // Defines the unsigned integer type that has the same size as the + // floating point number. + typedef typename TypeWithSize::UInt Bits; + + // Constants. + + // # of bits in a number. + static const size_t kBitCount = 8 * sizeof(RawType); + + // # of fraction bits in a number. + static const size_t kFractionBitCount = + std::numeric_limits::digits - 1; + + // # of exponent bits in a number. + static const size_t kExponentBitCount = kBitCount - 1 - kFractionBitCount; + + // The mask for the sign bit. + static const Bits kSignBitMask = static_cast(1) << (kBitCount - 1); + + // The mask for the fraction bits. + static const Bits kFractionBitMask = ~static_cast(0) >> + (kExponentBitCount + 1); + + // The mask for the exponent bits. + static const Bits kExponentBitMask = ~(kSignBitMask | kFractionBitMask); + + // How many ULP's (Units in the Last Place) we want to tolerate when + // comparing two numbers. The larger the value, the more error we + // allow. A 0 value means that two numbers must be exactly the same + // to be considered equal. + // + // The maximum error of a single floating-point operation is 0.5 + // units in the last place. On Intel CPU's, all floating-point + // calculations are done with 80-bit precision, while double has 64 + // bits. Therefore, 4 should be enough for ordinary use. + // + // See the following article for more details on ULP: + // http://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/ + static const uint32_t kMaxUlps = 4; + + // Constructs a FloatingPoint from a raw floating-point number. + // + // On an Intel CPU, passing a non-normalized NAN (Not a Number) + // around may change its bits, although the new value is guaranteed + // to be also a NAN. Therefore, don't expect this constructor to + // preserve the bits in x when x is a NAN. + explicit FloatingPoint(const RawType& x) { u_.value_ = x; } + + // Static methods + + // Reinterprets a bit pattern as a floating-point number. + // + // This function is needed to test the AlmostEquals() method. + static RawType ReinterpretBits(const Bits bits) { + FloatingPoint fp(0); + fp.u_.bits_ = bits; + return fp.u_.value_; + } + + // Returns the floating-point number that represent positive infinity. + static RawType Infinity() { return ReinterpretBits(kExponentBitMask); } + + // Returns the maximum representable finite floating-point number. + static RawType Max(); + + // Non-static methods + + // Returns the bits that represents this number. + const Bits& bits() const { return u_.bits_; } + + // Returns the exponent bits of this number. + Bits exponent_bits() const { return kExponentBitMask & u_.bits_; } + + // Returns the fraction bits of this number. + Bits fraction_bits() const { return kFractionBitMask & u_.bits_; } + + // Returns the sign bit of this number. + Bits sign_bit() const { return kSignBitMask & u_.bits_; } + + // Returns true if and only if this is NAN (not a number). + bool is_nan() const { + // It's a NAN if the exponent bits are all ones and the fraction + // bits are not entirely zeros. + return (exponent_bits() == kExponentBitMask) && (fraction_bits() != 0); + } + + // Returns true if and only if this number is at most kMaxUlps ULP's away + // from rhs. In particular, this function: + // + // - returns false if either number is (or both are) NAN. + // - treats really large numbers as almost equal to infinity. + // - thinks +0.0 and -0.0 are 0 DLP's apart. + bool AlmostEquals(const FloatingPoint& rhs) const { + // The IEEE standard says that any comparison operation involving + // a NAN must return false. + if (is_nan() || rhs.is_nan()) return false; + + return DistanceBetweenSignAndMagnitudeNumbers(u_.bits_, rhs.u_.bits_) <= + kMaxUlps; + } + + private: + // The data type used to store the actual floating-point number. + union FloatingPointUnion { + RawType value_; // The raw floating-point number. + Bits bits_; // The bits that represent the number. + }; + + // Converts an integer from the sign-and-magnitude representation to + // the biased representation. More precisely, let N be 2 to the + // power of (kBitCount - 1), an integer x is represented by the + // unsigned number x + N. + // + // For instance, + // + // -N + 1 (the most negative number representable using + // sign-and-magnitude) is represented by 1; + // 0 is represented by N; and + // N - 1 (the biggest number representable using + // sign-and-magnitude) is represented by 2N - 1. + // + // Read http://en.wikipedia.org/wiki/Signed_number_representations + // for more details on signed number representations. + static Bits SignAndMagnitudeToBiased(const Bits& sam) { + if (kSignBitMask & sam) { + // sam represents a negative number. + return ~sam + 1; + } else { + // sam represents a positive number. + return kSignBitMask | sam; + } + } + + // Given two numbers in the sign-and-magnitude representation, + // returns the distance between them as an unsigned number. + static Bits DistanceBetweenSignAndMagnitudeNumbers(const Bits& sam1, + const Bits& sam2) { + const Bits biased1 = SignAndMagnitudeToBiased(sam1); + const Bits biased2 = SignAndMagnitudeToBiased(sam2); + return (biased1 >= biased2) ? (biased1 - biased2) : (biased2 - biased1); + } + + FloatingPointUnion u_; +}; + +// We cannot use std::numeric_limits::max() as it clashes with the max() +// macro defined by . +template <> +inline float FloatingPoint::Max() { + return FLT_MAX; +} +template <> +inline double FloatingPoint::Max() { + return DBL_MAX; +} + +// Typedefs the instances of the FloatingPoint template class that we +// care to use. +typedef FloatingPoint Float; +typedef FloatingPoint Double; + +// In order to catch the mistake of putting tests that use different +// test fixture classes in the same test suite, we need to assign +// unique IDs to fixture classes and compare them. The TypeId type is +// used to hold such IDs. The user should treat TypeId as an opaque +// type: the only operation allowed on TypeId values is to compare +// them for equality using the == operator. +typedef const void* TypeId; + +template +class TypeIdHelper { + public: + // dummy_ must not have a const type. Otherwise an overly eager + // compiler (e.g. MSVC 7.1 & 8.0) may try to merge + // TypeIdHelper::dummy_ for different Ts as an "optimization". + static bool dummy_; +}; + +template +bool TypeIdHelper::dummy_ = false; + +// GetTypeId() returns the ID of type T. Different values will be +// returned for different types. Calling the function twice with the +// same type argument is guaranteed to return the same ID. +template +TypeId GetTypeId() { + // The compiler is required to allocate a different + // TypeIdHelper::dummy_ variable for each T used to instantiate + // the template. Therefore, the address of dummy_ is guaranteed to + // be unique. + return &(TypeIdHelper::dummy_); +} + +// Returns the type ID of ::testing::Test. Always call this instead +// of GetTypeId< ::testing::Test>() to get the type ID of +// ::testing::Test, as the latter may give the wrong result due to a +// suspected linker bug when compiling Google Test as a Mac OS X +// framework. +GTEST_API_ TypeId GetTestTypeId(); + +// Defines the abstract factory interface that creates instances +// of a Test object. +class TestFactoryBase { + public: + virtual ~TestFactoryBase() {} + + // Creates a test instance to run. The instance is both created and destroyed + // within TestInfoImpl::Run() + virtual Test* CreateTest() = 0; + + protected: + TestFactoryBase() {} + + private: + TestFactoryBase(const TestFactoryBase&) = delete; + TestFactoryBase& operator=(const TestFactoryBase&) = delete; +}; + +// This class provides implementation of TestFactoryBase interface. +// It is used in TEST and TEST_F macros. +template +class TestFactoryImpl : public TestFactoryBase { + public: + Test* CreateTest() override { return new TestClass; } +}; + +#if GTEST_OS_WINDOWS + +// Predicate-formatters for implementing the HRESULT checking macros +// {ASSERT|EXPECT}_HRESULT_{SUCCEEDED|FAILED} +// We pass a long instead of HRESULT to avoid causing an +// include dependency for the HRESULT type. +GTEST_API_ AssertionResult IsHRESULTSuccess(const char* expr, + long hr); // NOLINT +GTEST_API_ AssertionResult IsHRESULTFailure(const char* expr, + long hr); // NOLINT + +#endif // GTEST_OS_WINDOWS + +// Types of SetUpTestSuite() and TearDownTestSuite() functions. +using SetUpTestSuiteFunc = void (*)(); +using TearDownTestSuiteFunc = void (*)(); + +struct CodeLocation { + CodeLocation(const std::string& a_file, int a_line) + : file(a_file), line(a_line) {} + + std::string file; + int line; +}; + +// Helper to identify which setup function for TestCase / TestSuite to call. +// Only one function is allowed, either TestCase or TestSute but not both. + +// Utility functions to help SuiteApiResolver +using SetUpTearDownSuiteFuncType = void (*)(); + +inline SetUpTearDownSuiteFuncType GetNotDefaultOrNull( + SetUpTearDownSuiteFuncType a, SetUpTearDownSuiteFuncType def) { + return a == def ? nullptr : a; +} + +template +// Note that SuiteApiResolver inherits from T because +// SetUpTestSuite()/TearDownTestSuite() could be protected. This way +// SuiteApiResolver can access them. +struct SuiteApiResolver : T { + // testing::Test is only forward declared at this point. So we make it a + // dependent class for the compiler to be OK with it. + using Test = + typename std::conditional::type; + + static SetUpTearDownSuiteFuncType GetSetUpCaseOrSuite(const char* filename, + int line_num) { +#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_ + SetUpTearDownSuiteFuncType test_case_fp = + GetNotDefaultOrNull(&T::SetUpTestCase, &Test::SetUpTestCase); + SetUpTearDownSuiteFuncType test_suite_fp = + GetNotDefaultOrNull(&T::SetUpTestSuite, &Test::SetUpTestSuite); + + GTEST_CHECK_(!test_case_fp || !test_suite_fp) + << "Test can not provide both SetUpTestSuite and SetUpTestCase, please " + "make sure there is only one present at " + << filename << ":" << line_num; + + return test_case_fp != nullptr ? test_case_fp : test_suite_fp; +#else + (void)(filename); + (void)(line_num); + return &T::SetUpTestSuite; +#endif + } + + static SetUpTearDownSuiteFuncType GetTearDownCaseOrSuite(const char* filename, + int line_num) { +#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_ + SetUpTearDownSuiteFuncType test_case_fp = + GetNotDefaultOrNull(&T::TearDownTestCase, &Test::TearDownTestCase); + SetUpTearDownSuiteFuncType test_suite_fp = + GetNotDefaultOrNull(&T::TearDownTestSuite, &Test::TearDownTestSuite); + + GTEST_CHECK_(!test_case_fp || !test_suite_fp) + << "Test can not provide both TearDownTestSuite and TearDownTestCase," + " please make sure there is only one present at" + << filename << ":" << line_num; + + return test_case_fp != nullptr ? test_case_fp : test_suite_fp; +#else + (void)(filename); + (void)(line_num); + return &T::TearDownTestSuite; +#endif + } +}; + +// Creates a new TestInfo object and registers it with Google Test; +// returns the created object. +// +// Arguments: +// +// test_suite_name: name of the test suite +// name: name of the test +// type_param: the name of the test's type parameter, or NULL if +// this is not a typed or a type-parameterized test. +// value_param: text representation of the test's value parameter, +// or NULL if this is not a type-parameterized test. +// code_location: code location where the test is defined +// fixture_class_id: ID of the test fixture class +// set_up_tc: pointer to the function that sets up the test suite +// tear_down_tc: pointer to the function that tears down the test suite +// factory: pointer to the factory that creates a test object. +// The newly created TestInfo instance will assume +// ownership of the factory object. +GTEST_API_ TestInfo* MakeAndRegisterTestInfo( + const char* test_suite_name, const char* name, const char* type_param, + const char* value_param, CodeLocation code_location, + TypeId fixture_class_id, SetUpTestSuiteFunc set_up_tc, + TearDownTestSuiteFunc tear_down_tc, TestFactoryBase* factory); + +// If *pstr starts with the given prefix, modifies *pstr to be right +// past the prefix and returns true; otherwise leaves *pstr unchanged +// and returns false. None of pstr, *pstr, and prefix can be NULL. +GTEST_API_ bool SkipPrefix(const char* prefix, const char** pstr); + +GTEST_DISABLE_MSC_WARNINGS_PUSH_(4251 \ +/* class A needs to have dll-interface to be used by clients of class B */) + +// State of the definition of a type-parameterized test suite. +class GTEST_API_ TypedTestSuitePState { + public: + TypedTestSuitePState() : registered_(false) {} + + // Adds the given test name to defined_test_names_ and return true + // if the test suite hasn't been registered; otherwise aborts the + // program. + bool AddTestName(const char* file, int line, const char* case_name, + const char* test_name) { + if (registered_) { + fprintf(stderr, + "%s Test %s must be defined before " + "REGISTER_TYPED_TEST_SUITE_P(%s, ...).\n", + FormatFileLocation(file, line).c_str(), test_name, case_name); + fflush(stderr); + posix::Abort(); + } + registered_tests_.insert( + ::std::make_pair(test_name, CodeLocation(file, line))); + return true; + } + + bool TestExists(const std::string& test_name) const { + return registered_tests_.count(test_name) > 0; + } + + const CodeLocation& GetCodeLocation(const std::string& test_name) const { + RegisteredTestsMap::const_iterator it = registered_tests_.find(test_name); + GTEST_CHECK_(it != registered_tests_.end()); + return it->second; + } + + // Verifies that registered_tests match the test names in + // defined_test_names_; returns registered_tests if successful, or + // aborts the program otherwise. + const char* VerifyRegisteredTestNames(const char* test_suite_name, + const char* file, int line, + const char* registered_tests); + + private: + typedef ::std::map> RegisteredTestsMap; + + bool registered_; + RegisteredTestsMap registered_tests_; +}; + +// Legacy API is deprecated but still available +#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_ +using TypedTestCasePState = TypedTestSuitePState; +#endif // GTEST_REMOVE_LEGACY_TEST_CASEAPI_ + +GTEST_DISABLE_MSC_WARNINGS_POP_() // 4251 + +// Skips to the first non-space char after the first comma in 'str'; +// returns NULL if no comma is found in 'str'. +inline const char* SkipComma(const char* str) { + const char* comma = strchr(str, ','); + if (comma == nullptr) { + return nullptr; + } + while (IsSpace(*(++comma))) { + } + return comma; +} + +// Returns the prefix of 'str' before the first comma in it; returns +// the entire string if it contains no comma. +inline std::string GetPrefixUntilComma(const char* str) { + const char* comma = strchr(str, ','); + return comma == nullptr ? str : std::string(str, comma); +} + +// Splits a given string on a given delimiter, populating a given +// vector with the fields. +void SplitString(const ::std::string& str, char delimiter, + ::std::vector<::std::string>* dest); + +// The default argument to the template below for the case when the user does +// not provide a name generator. +struct DefaultNameGenerator { + template + static std::string GetName(int i) { + return StreamableToString(i); + } +}; + +template +struct NameGeneratorSelector { + typedef Provided type; +}; + +template +void GenerateNamesRecursively(internal::None, std::vector*, int) {} + +template +void GenerateNamesRecursively(Types, std::vector* result, int i) { + result->push_back(NameGenerator::template GetName(i)); + GenerateNamesRecursively(typename Types::Tail(), result, + i + 1); +} + +template +std::vector GenerateNames() { + std::vector result; + GenerateNamesRecursively(Types(), &result, 0); + return result; +} + +// TypeParameterizedTest::Register() +// registers a list of type-parameterized tests with Google Test. The +// return value is insignificant - we just need to return something +// such that we can call this function in a namespace scope. +// +// Implementation note: The GTEST_TEMPLATE_ macro declares a template +// template parameter. It's defined in gtest-type-util.h. +template +class TypeParameterizedTest { + public: + // 'index' is the index of the test in the type list 'Types' + // specified in INSTANTIATE_TYPED_TEST_SUITE_P(Prefix, TestSuite, + // Types). Valid values for 'index' are [0, N - 1] where N is the + // length of Types. + static bool Register(const char* prefix, const CodeLocation& code_location, + const char* case_name, const char* test_names, int index, + const std::vector& type_names = + GenerateNames()) { + typedef typename Types::Head Type; + typedef Fixture FixtureClass; + typedef typename GTEST_BIND_(TestSel, Type) TestClass; + + // First, registers the first type-parameterized test in the type + // list. + MakeAndRegisterTestInfo( + (std::string(prefix) + (prefix[0] == '\0' ? "" : "/") + case_name + + "/" + type_names[static_cast(index)]) + .c_str(), + StripTrailingSpaces(GetPrefixUntilComma(test_names)).c_str(), + GetTypeName().c_str(), + nullptr, // No value parameter. + code_location, GetTypeId(), + SuiteApiResolver::GetSetUpCaseOrSuite( + code_location.file.c_str(), code_location.line), + SuiteApiResolver::GetTearDownCaseOrSuite( + code_location.file.c_str(), code_location.line), + new TestFactoryImpl); + + // Next, recurses (at compile time) with the tail of the type list. + return TypeParameterizedTest::Register(prefix, + code_location, + case_name, + test_names, + index + 1, + type_names); + } +}; + +// The base case for the compile time recursion. +template +class TypeParameterizedTest { + public: + static bool Register(const char* /*prefix*/, const CodeLocation&, + const char* /*case_name*/, const char* /*test_names*/, + int /*index*/, + const std::vector& = + std::vector() /*type_names*/) { + return true; + } +}; + +GTEST_API_ void RegisterTypeParameterizedTestSuite(const char* test_suite_name, + CodeLocation code_location); +GTEST_API_ void RegisterTypeParameterizedTestSuiteInstantiation( + const char* case_name); + +// TypeParameterizedTestSuite::Register() +// registers *all combinations* of 'Tests' and 'Types' with Google +// Test. The return value is insignificant - we just need to return +// something such that we can call this function in a namespace scope. +template +class TypeParameterizedTestSuite { + public: + static bool Register(const char* prefix, CodeLocation code_location, + const TypedTestSuitePState* state, const char* case_name, + const char* test_names, + const std::vector& type_names = + GenerateNames()) { + RegisterTypeParameterizedTestSuiteInstantiation(case_name); + std::string test_name = + StripTrailingSpaces(GetPrefixUntilComma(test_names)); + if (!state->TestExists(test_name)) { + fprintf(stderr, "Failed to get code location for test %s.%s at %s.", + case_name, test_name.c_str(), + FormatFileLocation(code_location.file.c_str(), code_location.line) + .c_str()); + fflush(stderr); + posix::Abort(); + } + const CodeLocation& test_location = state->GetCodeLocation(test_name); + + typedef typename Tests::Head Head; + + // First, register the first test in 'Test' for each type in 'Types'. + TypeParameterizedTest::Register( + prefix, test_location, case_name, test_names, 0, type_names); + + // Next, recurses (at compile time) with the tail of the test list. + return TypeParameterizedTestSuite::Register(prefix, code_location, + state, case_name, + SkipComma(test_names), + type_names); + } +}; + +// The base case for the compile time recursion. +template +class TypeParameterizedTestSuite { + public: + static bool Register(const char* /*prefix*/, const CodeLocation&, + const TypedTestSuitePState* /*state*/, + const char* /*case_name*/, const char* /*test_names*/, + const std::vector& = + std::vector() /*type_names*/) { + return true; + } +}; + +// Returns the current OS stack trace as an std::string. +// +// The maximum number of stack frames to be included is specified by +// the gtest_stack_trace_depth flag. The skip_count parameter +// specifies the number of top frames to be skipped, which doesn't +// count against the number of frames to be included. +// +// For example, if Foo() calls Bar(), which in turn calls +// GetCurrentOsStackTraceExceptTop(..., 1), Foo() will be included in +// the trace but Bar() and GetCurrentOsStackTraceExceptTop() won't. +GTEST_API_ std::string GetCurrentOsStackTraceExceptTop(int skip_count); + +// Helpers for suppressing warnings on unreachable code or constant +// condition. + +// Always returns true. +GTEST_API_ bool AlwaysTrue(); + +// Always returns false. +inline bool AlwaysFalse() { return !AlwaysTrue(); } + +// Helper for suppressing false warning from Clang on a const char* +// variable declared in a conditional expression always being NULL in +// the else branch. +struct GTEST_API_ ConstCharPtr { + ConstCharPtr(const char* str) : value(str) {} + operator bool() const { return true; } + const char* value; +}; + +// Helper for declaring std::string within 'if' statement +// in pre C++17 build environment. +struct TrueWithString { + TrueWithString() = default; + explicit TrueWithString(const char* str) : value(str) {} + explicit TrueWithString(const std::string& str) : value(str) {} + explicit operator bool() const { return true; } + std::string value; +}; + +// A simple Linear Congruential Generator for generating random +// numbers with a uniform distribution. Unlike rand() and srand(), it +// doesn't use global state (and therefore can't interfere with user +// code). Unlike rand_r(), it's portable. An LCG isn't very random, +// but it's good enough for our purposes. +class GTEST_API_ Random { + public: + static const uint32_t kMaxRange = 1u << 31; + + explicit Random(uint32_t seed) : state_(seed) {} + + void Reseed(uint32_t seed) { state_ = seed; } + + // Generates a random number from [0, range). Crashes if 'range' is + // 0 or greater than kMaxRange. + uint32_t Generate(uint32_t range); + + private: + uint32_t state_; + Random(const Random&) = delete; + Random& operator=(const Random&) = delete; +}; + +// Turns const U&, U&, const U, and U all into U. +#define GTEST_REMOVE_REFERENCE_AND_CONST_(T) \ + typename std::remove_const::type>::type + +// HasDebugStringAndShortDebugString::value is a compile-time bool constant +// that's true if and only if T has methods DebugString() and ShortDebugString() +// that return std::string. +template +class HasDebugStringAndShortDebugString { + private: + template + static auto CheckDebugString(C*) -> typename std::is_same< + std::string, decltype(std::declval().DebugString())>::type; + template + static std::false_type CheckDebugString(...); + + template + static auto CheckShortDebugString(C*) -> typename std::is_same< + std::string, decltype(std::declval().ShortDebugString())>::type; + template + static std::false_type CheckShortDebugString(...); + + using HasDebugStringType = decltype(CheckDebugString(nullptr)); + using HasShortDebugStringType = decltype(CheckShortDebugString(nullptr)); + + public: + static constexpr bool value = + HasDebugStringType::value && HasShortDebugStringType::value; +}; + +template +constexpr bool HasDebugStringAndShortDebugString::value; + +// When the compiler sees expression IsContainerTest(0), if C is an +// STL-style container class, the first overload of IsContainerTest +// will be viable (since both C::iterator* and C::const_iterator* are +// valid types and NULL can be implicitly converted to them). It will +// be picked over the second overload as 'int' is a perfect match for +// the type of argument 0. If C::iterator or C::const_iterator is not +// a valid type, the first overload is not viable, and the second +// overload will be picked. Therefore, we can determine whether C is +// a container class by checking the type of IsContainerTest(0). +// The value of the expression is insignificant. +// +// In C++11 mode we check the existence of a const_iterator and that an +// iterator is properly implemented for the container. +// +// For pre-C++11 that we look for both C::iterator and C::const_iterator. +// The reason is that C++ injects the name of a class as a member of the +// class itself (e.g. you can refer to class iterator as either +// 'iterator' or 'iterator::iterator'). If we look for C::iterator +// only, for example, we would mistakenly think that a class named +// iterator is an STL container. +// +// Also note that the simpler approach of overloading +// IsContainerTest(typename C::const_iterator*) and +// IsContainerTest(...) doesn't work with Visual Age C++ and Sun C++. +typedef int IsContainer; +template ().begin()), + class = decltype(::std::declval().end()), + class = decltype(++::std::declval()), + class = decltype(*::std::declval()), + class = typename C::const_iterator> +IsContainer IsContainerTest(int /* dummy */) { + return 0; +} + +typedef char IsNotContainer; +template +IsNotContainer IsContainerTest(long /* dummy */) { + return '\0'; +} + +// Trait to detect whether a type T is a hash table. +// The heuristic used is that the type contains an inner type `hasher` and does +// not contain an inner type `reverse_iterator`. +// If the container is iterable in reverse, then order might actually matter. +template +struct IsHashTable { + private: + template + static char test(typename U::hasher*, typename U::reverse_iterator*); + template + static int test(typename U::hasher*, ...); + template + static char test(...); + + public: + static const bool value = sizeof(test(nullptr, nullptr)) == sizeof(int); +}; + +template +const bool IsHashTable::value; + +template (0)) == sizeof(IsContainer)> +struct IsRecursiveContainerImpl; + +template +struct IsRecursiveContainerImpl : public std::false_type {}; + +// Since the IsRecursiveContainerImpl depends on the IsContainerTest we need to +// obey the same inconsistencies as the IsContainerTest, namely check if +// something is a container is relying on only const_iterator in C++11 and +// is relying on both const_iterator and iterator otherwise +template +struct IsRecursiveContainerImpl { + using value_type = decltype(*std::declval()); + using type = + std::is_same::type>::type, + C>; +}; + +// IsRecursiveContainer is a unary compile-time predicate that +// evaluates whether C is a recursive container type. A recursive container +// type is a container type whose value_type is equal to the container type +// itself. An example for a recursive container type is +// boost::filesystem::path, whose iterator has a value_type that is equal to +// boost::filesystem::path. +template +struct IsRecursiveContainer : public IsRecursiveContainerImpl::type {}; + +// Utilities for native arrays. + +// ArrayEq() compares two k-dimensional native arrays using the +// elements' operator==, where k can be any integer >= 0. When k is +// 0, ArrayEq() degenerates into comparing a single pair of values. + +template +bool ArrayEq(const T* lhs, size_t size, const U* rhs); + +// This generic version is used when k is 0. +template +inline bool ArrayEq(const T& lhs, const U& rhs) { + return lhs == rhs; +} + +// This overload is used when k >= 1. +template +inline bool ArrayEq(const T (&lhs)[N], const U (&rhs)[N]) { + return internal::ArrayEq(lhs, N, rhs); +} + +// This helper reduces code bloat. If we instead put its logic inside +// the previous ArrayEq() function, arrays with different sizes would +// lead to different copies of the template code. +template +bool ArrayEq(const T* lhs, size_t size, const U* rhs) { + for (size_t i = 0; i != size; i++) { + if (!internal::ArrayEq(lhs[i], rhs[i])) return false; + } + return true; +} + +// Finds the first element in the iterator range [begin, end) that +// equals elem. Element may be a native array type itself. +template +Iter ArrayAwareFind(Iter begin, Iter end, const Element& elem) { + for (Iter it = begin; it != end; ++it) { + if (internal::ArrayEq(*it, elem)) return it; + } + return end; +} + +// CopyArray() copies a k-dimensional native array using the elements' +// operator=, where k can be any integer >= 0. When k is 0, +// CopyArray() degenerates into copying a single value. + +template +void CopyArray(const T* from, size_t size, U* to); + +// This generic version is used when k is 0. +template +inline void CopyArray(const T& from, U* to) { + *to = from; +} + +// This overload is used when k >= 1. +template +inline void CopyArray(const T (&from)[N], U (*to)[N]) { + internal::CopyArray(from, N, *to); +} + +// This helper reduces code bloat. If we instead put its logic inside +// the previous CopyArray() function, arrays with different sizes +// would lead to different copies of the template code. +template +void CopyArray(const T* from, size_t size, U* to) { + for (size_t i = 0; i != size; i++) { + internal::CopyArray(from[i], to + i); + } +} + +// The relation between an NativeArray object (see below) and the +// native array it represents. +// We use 2 different structs to allow non-copyable types to be used, as long +// as RelationToSourceReference() is passed. +struct RelationToSourceReference {}; +struct RelationToSourceCopy {}; + +// Adapts a native array to a read-only STL-style container. Instead +// of the complete STL container concept, this adaptor only implements +// members useful for Google Mock's container matchers. New members +// should be added as needed. To simplify the implementation, we only +// support Element being a raw type (i.e. having no top-level const or +// reference modifier). It's the client's responsibility to satisfy +// this requirement. Element can be an array type itself (hence +// multi-dimensional arrays are supported). +template +class NativeArray { + public: + // STL-style container typedefs. + typedef Element value_type; + typedef Element* iterator; + typedef const Element* const_iterator; + + // Constructs from a native array. References the source. + NativeArray(const Element* array, size_t count, RelationToSourceReference) { + InitRef(array, count); + } + + // Constructs from a native array. Copies the source. + NativeArray(const Element* array, size_t count, RelationToSourceCopy) { + InitCopy(array, count); + } + + // Copy constructor. + NativeArray(const NativeArray& rhs) { + (this->*rhs.clone_)(rhs.array_, rhs.size_); + } + + ~NativeArray() { + if (clone_ != &NativeArray::InitRef) delete[] array_; + } + + // STL-style container methods. + size_t size() const { return size_; } + const_iterator begin() const { return array_; } + const_iterator end() const { return array_ + size_; } + bool operator==(const NativeArray& rhs) const { + return size() == rhs.size() && ArrayEq(begin(), size(), rhs.begin()); + } + + private: + static_assert(!std::is_const::value, "Type must not be const"); + static_assert(!std::is_reference::value, + "Type must not be a reference"); + + // Initializes this object with a copy of the input. + void InitCopy(const Element* array, size_t a_size) { + Element* const copy = new Element[a_size]; + CopyArray(array, a_size, copy); + array_ = copy; + size_ = a_size; + clone_ = &NativeArray::InitCopy; + } + + // Initializes this object with a reference of the input. + void InitRef(const Element* array, size_t a_size) { + array_ = array; + size_ = a_size; + clone_ = &NativeArray::InitRef; + } + + const Element* array_; + size_t size_; + void (NativeArray::*clone_)(const Element*, size_t); +}; + +// Backport of std::index_sequence. +template +struct IndexSequence { + using type = IndexSequence; +}; + +// Double the IndexSequence, and one if plus_one is true. +template +struct DoubleSequence; +template +struct DoubleSequence, sizeofT> { + using type = IndexSequence; +}; +template +struct DoubleSequence, sizeofT> { + using type = IndexSequence; +}; + +// Backport of std::make_index_sequence. +// It uses O(ln(N)) instantiation depth. +template +struct MakeIndexSequenceImpl + : DoubleSequence::type, + N / 2>::type {}; + +template <> +struct MakeIndexSequenceImpl<0> : IndexSequence<> {}; + +template +using MakeIndexSequence = typename MakeIndexSequenceImpl::type; + +template +using IndexSequenceFor = typename MakeIndexSequence::type; + +template +struct Ignore { + Ignore(...); // NOLINT +}; + +template +struct ElemFromListImpl; +template +struct ElemFromListImpl> { + // We make Ignore a template to solve a problem with MSVC. + // A non-template Ignore would work fine with `decltype(Ignore(I))...`, but + // MSVC doesn't understand how to deal with that pack expansion. + // Use `0 * I` to have a single instantiation of Ignore. + template + static R Apply(Ignore<0 * I>..., R (*)(), ...); +}; + +template +struct ElemFromList { + using type = + decltype(ElemFromListImpl::type>::Apply( + static_cast(nullptr)...)); +}; + +struct FlatTupleConstructTag {}; + +template +class FlatTuple; + +template +struct FlatTupleElemBase; + +template +struct FlatTupleElemBase, I> { + using value_type = typename ElemFromList::type; + FlatTupleElemBase() = default; + template + explicit FlatTupleElemBase(FlatTupleConstructTag, Arg&& t) + : value(std::forward(t)) {} + value_type value; +}; + +template +struct FlatTupleBase; + +template +struct FlatTupleBase, IndexSequence> + : FlatTupleElemBase, Idx>... { + using Indices = IndexSequence; + FlatTupleBase() = default; + template + explicit FlatTupleBase(FlatTupleConstructTag, Args&&... args) + : FlatTupleElemBase, Idx>(FlatTupleConstructTag{}, + std::forward(args))... {} + + template + const typename ElemFromList::type& Get() const { + return FlatTupleElemBase, I>::value; + } + + template + typename ElemFromList::type& Get() { + return FlatTupleElemBase, I>::value; + } + + template + auto Apply(F&& f) -> decltype(std::forward(f)(this->Get()...)) { + return std::forward(f)(Get()...); + } + + template + auto Apply(F&& f) const -> decltype(std::forward(f)(this->Get()...)) { + return std::forward(f)(Get()...); + } +}; + +// Analog to std::tuple but with different tradeoffs. +// This class minimizes the template instantiation depth, thus allowing more +// elements than std::tuple would. std::tuple has been seen to require an +// instantiation depth of more than 10x the number of elements in some +// implementations. +// FlatTuple and ElemFromList are not recursive and have a fixed depth +// regardless of T... +// MakeIndexSequence, on the other hand, it is recursive but with an +// instantiation depth of O(ln(N)). +template +class FlatTuple + : private FlatTupleBase, + typename MakeIndexSequence::type> { + using Indices = typename FlatTupleBase< + FlatTuple, typename MakeIndexSequence::type>::Indices; + + public: + FlatTuple() = default; + template + explicit FlatTuple(FlatTupleConstructTag tag, Args&&... args) + : FlatTuple::FlatTupleBase(tag, std::forward(args)...) {} + + using FlatTuple::FlatTupleBase::Apply; + using FlatTuple::FlatTupleBase::Get; +}; + +// Utility functions to be called with static_assert to induce deprecation +// warnings. +GTEST_INTERNAL_DEPRECATED( + "INSTANTIATE_TEST_CASE_P is deprecated, please use " + "INSTANTIATE_TEST_SUITE_P") +constexpr bool InstantiateTestCase_P_IsDeprecated() { return true; } + +GTEST_INTERNAL_DEPRECATED( + "TYPED_TEST_CASE_P is deprecated, please use " + "TYPED_TEST_SUITE_P") +constexpr bool TypedTestCase_P_IsDeprecated() { return true; } + +GTEST_INTERNAL_DEPRECATED( + "TYPED_TEST_CASE is deprecated, please use " + "TYPED_TEST_SUITE") +constexpr bool TypedTestCaseIsDeprecated() { return true; } + +GTEST_INTERNAL_DEPRECATED( + "REGISTER_TYPED_TEST_CASE_P is deprecated, please use " + "REGISTER_TYPED_TEST_SUITE_P") +constexpr bool RegisterTypedTestCase_P_IsDeprecated() { return true; } + +GTEST_INTERNAL_DEPRECATED( + "INSTANTIATE_TYPED_TEST_CASE_P is deprecated, please use " + "INSTANTIATE_TYPED_TEST_SUITE_P") +constexpr bool InstantiateTypedTestCase_P_IsDeprecated() { return true; } + +} // namespace internal +} // namespace testing + +namespace std { +// Some standard library implementations use `struct tuple_size` and some use +// `class tuple_size`. Clang warns about the mismatch. +// https://reviews.llvm.org/D55466 +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wmismatched-tags" +#endif +template +struct tuple_size> + : std::integral_constant {}; +#ifdef __clang__ +#pragma clang diagnostic pop +#endif +} // namespace std + +#define GTEST_MESSAGE_AT_(file, line, message, result_type) \ + ::testing::internal::AssertHelper(result_type, file, line, message) = \ + ::testing::Message() + +#define GTEST_MESSAGE_(message, result_type) \ + GTEST_MESSAGE_AT_(__FILE__, __LINE__, message, result_type) + +#define GTEST_FATAL_FAILURE_(message) \ + return GTEST_MESSAGE_(message, ::testing::TestPartResult::kFatalFailure) + +#define GTEST_NONFATAL_FAILURE_(message) \ + GTEST_MESSAGE_(message, ::testing::TestPartResult::kNonFatalFailure) + +#define GTEST_SUCCESS_(message) \ + GTEST_MESSAGE_(message, ::testing::TestPartResult::kSuccess) + +#define GTEST_SKIP_(message) \ + return GTEST_MESSAGE_(message, ::testing::TestPartResult::kSkip) + +// Suppress MSVC warning 4072 (unreachable code) for the code following +// statement if it returns or throws (or doesn't return or throw in some +// situations). +// NOTE: The "else" is important to keep this expansion to prevent a top-level +// "else" from attaching to our "if". +#define GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement) \ + if (::testing::internal::AlwaysTrue()) { \ + statement; \ + } else /* NOLINT */ \ + static_assert(true, "") // User must have a semicolon after expansion. + +#if GTEST_HAS_EXCEPTIONS + +namespace testing { +namespace internal { + +class NeverThrown { + public: + const char* what() const noexcept { + return "this exception should never be thrown"; + } +}; + +} // namespace internal +} // namespace testing + +#if GTEST_HAS_RTTI + +#define GTEST_EXCEPTION_TYPE_(e) ::testing::internal::GetTypeName(typeid(e)) + +#else // GTEST_HAS_RTTI + +#define GTEST_EXCEPTION_TYPE_(e) \ + std::string { "an std::exception-derived error" } + +#endif // GTEST_HAS_RTTI + +#define GTEST_TEST_THROW_CATCH_STD_EXCEPTION_(statement, expected_exception) \ + catch (typename std::conditional< \ + std::is_same::type>::type, \ + std::exception>::value, \ + const ::testing::internal::NeverThrown&, const std::exception&>::type \ + e) { \ + gtest_msg.value = "Expected: " #statement \ + " throws an exception of type " #expected_exception \ + ".\n Actual: it throws "; \ + gtest_msg.value += GTEST_EXCEPTION_TYPE_(e); \ + gtest_msg.value += " with description \""; \ + gtest_msg.value += e.what(); \ + gtest_msg.value += "\"."; \ + goto GTEST_CONCAT_TOKEN_(gtest_label_testthrow_, __LINE__); \ + } + +#else // GTEST_HAS_EXCEPTIONS + +#define GTEST_TEST_THROW_CATCH_STD_EXCEPTION_(statement, expected_exception) + +#endif // GTEST_HAS_EXCEPTIONS + +#define GTEST_TEST_THROW_(statement, expected_exception, fail) \ + GTEST_AMBIGUOUS_ELSE_BLOCKER_ \ + if (::testing::internal::TrueWithString gtest_msg{}) { \ + bool gtest_caught_expected = false; \ + try { \ + GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement); \ + } catch (expected_exception const&) { \ + gtest_caught_expected = true; \ + } \ + GTEST_TEST_THROW_CATCH_STD_EXCEPTION_(statement, expected_exception) \ + catch (...) { \ + gtest_msg.value = "Expected: " #statement \ + " throws an exception of type " #expected_exception \ + ".\n Actual: it throws a different type."; \ + goto GTEST_CONCAT_TOKEN_(gtest_label_testthrow_, __LINE__); \ + } \ + if (!gtest_caught_expected) { \ + gtest_msg.value = "Expected: " #statement \ + " throws an exception of type " #expected_exception \ + ".\n Actual: it throws nothing."; \ + goto GTEST_CONCAT_TOKEN_(gtest_label_testthrow_, __LINE__); \ + } \ + } else /*NOLINT*/ \ + GTEST_CONCAT_TOKEN_(gtest_label_testthrow_, __LINE__) \ + : fail(gtest_msg.value.c_str()) + +#if GTEST_HAS_EXCEPTIONS + +#define GTEST_TEST_NO_THROW_CATCH_STD_EXCEPTION_() \ + catch (std::exception const& e) { \ + gtest_msg.value = "it throws "; \ + gtest_msg.value += GTEST_EXCEPTION_TYPE_(e); \ + gtest_msg.value += " with description \""; \ + gtest_msg.value += e.what(); \ + gtest_msg.value += "\"."; \ + goto GTEST_CONCAT_TOKEN_(gtest_label_testnothrow_, __LINE__); \ + } + +#else // GTEST_HAS_EXCEPTIONS + +#define GTEST_TEST_NO_THROW_CATCH_STD_EXCEPTION_() + +#endif // GTEST_HAS_EXCEPTIONS + +#define GTEST_TEST_NO_THROW_(statement, fail) \ + GTEST_AMBIGUOUS_ELSE_BLOCKER_ \ + if (::testing::internal::TrueWithString gtest_msg{}) { \ + try { \ + GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement); \ + } \ + GTEST_TEST_NO_THROW_CATCH_STD_EXCEPTION_() \ + catch (...) { \ + gtest_msg.value = "it throws."; \ + goto GTEST_CONCAT_TOKEN_(gtest_label_testnothrow_, __LINE__); \ + } \ + } else \ + GTEST_CONCAT_TOKEN_(gtest_label_testnothrow_, __LINE__) \ + : fail(("Expected: " #statement " doesn't throw an exception.\n" \ + " Actual: " + \ + gtest_msg.value) \ + .c_str()) + +#define GTEST_TEST_ANY_THROW_(statement, fail) \ + GTEST_AMBIGUOUS_ELSE_BLOCKER_ \ + if (::testing::internal::AlwaysTrue()) { \ + bool gtest_caught_any = false; \ + try { \ + GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement); \ + } catch (...) { \ + gtest_caught_any = true; \ + } \ + if (!gtest_caught_any) { \ + goto GTEST_CONCAT_TOKEN_(gtest_label_testanythrow_, __LINE__); \ + } \ + } else \ + GTEST_CONCAT_TOKEN_(gtest_label_testanythrow_, __LINE__) \ + : fail("Expected: " #statement \ + " throws an exception.\n" \ + " Actual: it doesn't.") + +// Implements Boolean test assertions such as EXPECT_TRUE. expression can be +// either a boolean expression or an AssertionResult. text is a textual +// representation of expression as it was passed into the EXPECT_TRUE. +#define GTEST_TEST_BOOLEAN_(expression, text, actual, expected, fail) \ + GTEST_AMBIGUOUS_ELSE_BLOCKER_ \ + if (const ::testing::AssertionResult gtest_ar_ = \ + ::testing::AssertionResult(expression)) \ + ; \ + else \ + fail(::testing::internal::GetBoolAssertionFailureMessage( \ + gtest_ar_, text, #actual, #expected) \ + .c_str()) + +#define GTEST_TEST_NO_FATAL_FAILURE_(statement, fail) \ + GTEST_AMBIGUOUS_ELSE_BLOCKER_ \ + if (::testing::internal::AlwaysTrue()) { \ + ::testing::internal::HasNewFatalFailureHelper gtest_fatal_failure_checker; \ + GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement); \ + if (gtest_fatal_failure_checker.has_new_fatal_failure()) { \ + goto GTEST_CONCAT_TOKEN_(gtest_label_testnofatal_, __LINE__); \ + } \ + } else \ + GTEST_CONCAT_TOKEN_(gtest_label_testnofatal_, __LINE__) \ + : fail("Expected: " #statement \ + " doesn't generate new fatal " \ + "failures in the current thread.\n" \ + " Actual: it does.") + +// Expands to the name of the class that implements the given test. +#define GTEST_TEST_CLASS_NAME_(test_suite_name, test_name) \ + test_suite_name##_##test_name##_Test + +// Helper macro for defining tests. +#define GTEST_TEST_(test_suite_name, test_name, parent_class, parent_id) \ + static_assert(sizeof(GTEST_STRINGIFY_(test_suite_name)) > 1, \ + "test_suite_name must not be empty"); \ + static_assert(sizeof(GTEST_STRINGIFY_(test_name)) > 1, \ + "test_name must not be empty"); \ + class GTEST_TEST_CLASS_NAME_(test_suite_name, test_name) \ + : public parent_class { \ + public: \ + GTEST_TEST_CLASS_NAME_(test_suite_name, test_name)() = default; \ + ~GTEST_TEST_CLASS_NAME_(test_suite_name, test_name)() override = default; \ + GTEST_TEST_CLASS_NAME_(test_suite_name, test_name) \ + (const GTEST_TEST_CLASS_NAME_(test_suite_name, test_name) &) = delete; \ + GTEST_TEST_CLASS_NAME_(test_suite_name, test_name) & operator=( \ + const GTEST_TEST_CLASS_NAME_(test_suite_name, \ + test_name) &) = delete; /* NOLINT */ \ + GTEST_TEST_CLASS_NAME_(test_suite_name, test_name) \ + (GTEST_TEST_CLASS_NAME_(test_suite_name, test_name) &&) noexcept = delete; \ + GTEST_TEST_CLASS_NAME_(test_suite_name, test_name) & operator=( \ + GTEST_TEST_CLASS_NAME_(test_suite_name, \ + test_name) &&) noexcept = delete; /* NOLINT */ \ + \ + private: \ + void TestBody() override; \ + static ::testing::TestInfo* const test_info_ GTEST_ATTRIBUTE_UNUSED_; \ + }; \ + \ + ::testing::TestInfo* const GTEST_TEST_CLASS_NAME_(test_suite_name, \ + test_name)::test_info_ = \ + ::testing::internal::MakeAndRegisterTestInfo( \ + #test_suite_name, #test_name, nullptr, nullptr, \ + ::testing::internal::CodeLocation(__FILE__, __LINE__), (parent_id), \ + ::testing::internal::SuiteApiResolver< \ + parent_class>::GetSetUpCaseOrSuite(__FILE__, __LINE__), \ + ::testing::internal::SuiteApiResolver< \ + parent_class>::GetTearDownCaseOrSuite(__FILE__, __LINE__), \ + new ::testing::internal::TestFactoryImpl); \ + void GTEST_TEST_CLASS_NAME_(test_suite_name, test_name)::TestBody() + +#endif // GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_INTERNAL_H_ diff --git a/include/gtest/internal/gtest-param-util.h b/include/gtest/internal/gtest-param-util.h new file mode 100644 index 0000000..fb989e0 --- /dev/null +++ b/include/gtest/internal/gtest-param-util.h @@ -0,0 +1,1028 @@ +// Copyright 2008 Google Inc. +// All Rights Reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Type and function utilities for implementing parameterized tests. + +// IWYU pragma: private, include "gtest/gtest.h" +// IWYU pragma: friend gtest/.* +// IWYU pragma: friend gmock/.* + +#ifndef GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_PARAM_UTIL_H_ +#define GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_PARAM_UTIL_H_ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "gtest/gtest-printers.h" +#include "gtest/gtest-test-part.h" +#include "gtest/internal/gtest-internal.h" +#include "gtest/internal/gtest-port.h" + +namespace testing { +// Input to a parameterized test name generator, describing a test parameter. +// Consists of the parameter value and the integer parameter index. +template +struct TestParamInfo { + TestParamInfo(const ParamType& a_param, size_t an_index) + : param(a_param), index(an_index) {} + ParamType param; + size_t index; +}; + +// A builtin parameterized test name generator which returns the result of +// testing::PrintToString. +struct PrintToStringParamName { + template + std::string operator()(const TestParamInfo& info) const { + return PrintToString(info.param); + } +}; + +namespace internal { + +// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE. +// Utility Functions + +// Outputs a message explaining invalid registration of different +// fixture class for the same test suite. This may happen when +// TEST_P macro is used to define two tests with the same name +// but in different namespaces. +GTEST_API_ void ReportInvalidTestSuiteType(const char* test_suite_name, + CodeLocation code_location); + +template +class ParamGeneratorInterface; +template +class ParamGenerator; + +// Interface for iterating over elements provided by an implementation +// of ParamGeneratorInterface. +template +class ParamIteratorInterface { + public: + virtual ~ParamIteratorInterface() {} + // A pointer to the base generator instance. + // Used only for the purposes of iterator comparison + // to make sure that two iterators belong to the same generator. + virtual const ParamGeneratorInterface* BaseGenerator() const = 0; + // Advances iterator to point to the next element + // provided by the generator. The caller is responsible + // for not calling Advance() on an iterator equal to + // BaseGenerator()->End(). + virtual void Advance() = 0; + // Clones the iterator object. Used for implementing copy semantics + // of ParamIterator. + virtual ParamIteratorInterface* Clone() const = 0; + // Dereferences the current iterator and provides (read-only) access + // to the pointed value. It is the caller's responsibility not to call + // Current() on an iterator equal to BaseGenerator()->End(). + // Used for implementing ParamGenerator::operator*(). + virtual const T* Current() const = 0; + // Determines whether the given iterator and other point to the same + // element in the sequence generated by the generator. + // Used for implementing ParamGenerator::operator==(). + virtual bool Equals(const ParamIteratorInterface& other) const = 0; +}; + +// Class iterating over elements provided by an implementation of +// ParamGeneratorInterface. It wraps ParamIteratorInterface +// and implements the const forward iterator concept. +template +class ParamIterator { + public: + typedef T value_type; + typedef const T& reference; + typedef ptrdiff_t difference_type; + + // ParamIterator assumes ownership of the impl_ pointer. + ParamIterator(const ParamIterator& other) : impl_(other.impl_->Clone()) {} + ParamIterator& operator=(const ParamIterator& other) { + if (this != &other) impl_.reset(other.impl_->Clone()); + return *this; + } + + const T& operator*() const { return *impl_->Current(); } + const T* operator->() const { return impl_->Current(); } + // Prefix version of operator++. + ParamIterator& operator++() { + impl_->Advance(); + return *this; + } + // Postfix version of operator++. + ParamIterator operator++(int /*unused*/) { + ParamIteratorInterface* clone = impl_->Clone(); + impl_->Advance(); + return ParamIterator(clone); + } + bool operator==(const ParamIterator& other) const { + return impl_.get() == other.impl_.get() || impl_->Equals(*other.impl_); + } + bool operator!=(const ParamIterator& other) const { + return !(*this == other); + } + + private: + friend class ParamGenerator; + explicit ParamIterator(ParamIteratorInterface* impl) : impl_(impl) {} + std::unique_ptr> impl_; +}; + +// ParamGeneratorInterface is the binary interface to access generators +// defined in other translation units. +template +class ParamGeneratorInterface { + public: + typedef T ParamType; + + virtual ~ParamGeneratorInterface() {} + + // Generator interface definition + virtual ParamIteratorInterface* Begin() const = 0; + virtual ParamIteratorInterface* End() const = 0; +}; + +// Wraps ParamGeneratorInterface and provides general generator syntax +// compatible with the STL Container concept. +// This class implements copy initialization semantics and the contained +// ParamGeneratorInterface instance is shared among all copies +// of the original object. This is possible because that instance is immutable. +template +class ParamGenerator { + public: + typedef ParamIterator iterator; + + explicit ParamGenerator(ParamGeneratorInterface* impl) : impl_(impl) {} + ParamGenerator(const ParamGenerator& other) : impl_(other.impl_) {} + + ParamGenerator& operator=(const ParamGenerator& other) { + impl_ = other.impl_; + return *this; + } + + iterator begin() const { return iterator(impl_->Begin()); } + iterator end() const { return iterator(impl_->End()); } + + private: + std::shared_ptr> impl_; +}; + +// Generates values from a range of two comparable values. Can be used to +// generate sequences of user-defined types that implement operator+() and +// operator<(). +// This class is used in the Range() function. +template +class RangeGenerator : public ParamGeneratorInterface { + public: + RangeGenerator(T begin, T end, IncrementT step) + : begin_(begin), + end_(end), + step_(step), + end_index_(CalculateEndIndex(begin, end, step)) {} + ~RangeGenerator() override {} + + ParamIteratorInterface* Begin() const override { + return new Iterator(this, begin_, 0, step_); + } + ParamIteratorInterface* End() const override { + return new Iterator(this, end_, end_index_, step_); + } + + private: + class Iterator : public ParamIteratorInterface { + public: + Iterator(const ParamGeneratorInterface* base, T value, int index, + IncrementT step) + : base_(base), value_(value), index_(index), step_(step) {} + ~Iterator() override {} + + const ParamGeneratorInterface* BaseGenerator() const override { + return base_; + } + void Advance() override { + value_ = static_cast(value_ + step_); + index_++; + } + ParamIteratorInterface* Clone() const override { + return new Iterator(*this); + } + const T* Current() const override { return &value_; } + bool Equals(const ParamIteratorInterface& other) const override { + // Having the same base generator guarantees that the other + // iterator is of the same type and we can downcast. + GTEST_CHECK_(BaseGenerator() == other.BaseGenerator()) + << "The program attempted to compare iterators " + << "from different generators." << std::endl; + const int other_index = + CheckedDowncastToActualType(&other)->index_; + return index_ == other_index; + } + + private: + Iterator(const Iterator& other) + : ParamIteratorInterface(), + base_(other.base_), + value_(other.value_), + index_(other.index_), + step_(other.step_) {} + + // No implementation - assignment is unsupported. + void operator=(const Iterator& other); + + const ParamGeneratorInterface* const base_; + T value_; + int index_; + const IncrementT step_; + }; // class RangeGenerator::Iterator + + static int CalculateEndIndex(const T& begin, const T& end, + const IncrementT& step) { + int end_index = 0; + for (T i = begin; i < end; i = static_cast(i + step)) end_index++; + return end_index; + } + + // No implementation - assignment is unsupported. + void operator=(const RangeGenerator& other); + + const T begin_; + const T end_; + const IncrementT step_; + // The index for the end() iterator. All the elements in the generated + // sequence are indexed (0-based) to aid iterator comparison. + const int end_index_; +}; // class RangeGenerator + +// Generates values from a pair of STL-style iterators. Used in the +// ValuesIn() function. The elements are copied from the source range +// since the source can be located on the stack, and the generator +// is likely to persist beyond that stack frame. +template +class ValuesInIteratorRangeGenerator : public ParamGeneratorInterface { + public: + template + ValuesInIteratorRangeGenerator(ForwardIterator begin, ForwardIterator end) + : container_(begin, end) {} + ~ValuesInIteratorRangeGenerator() override {} + + ParamIteratorInterface* Begin() const override { + return new Iterator(this, container_.begin()); + } + ParamIteratorInterface* End() const override { + return new Iterator(this, container_.end()); + } + + private: + typedef typename ::std::vector ContainerType; + + class Iterator : public ParamIteratorInterface { + public: + Iterator(const ParamGeneratorInterface* base, + typename ContainerType::const_iterator iterator) + : base_(base), iterator_(iterator) {} + ~Iterator() override {} + + const ParamGeneratorInterface* BaseGenerator() const override { + return base_; + } + void Advance() override { + ++iterator_; + value_.reset(); + } + ParamIteratorInterface* Clone() const override { + return new Iterator(*this); + } + // We need to use cached value referenced by iterator_ because *iterator_ + // can return a temporary object (and of type other then T), so just + // having "return &*iterator_;" doesn't work. + // value_ is updated here and not in Advance() because Advance() + // can advance iterator_ beyond the end of the range, and we cannot + // detect that fact. The client code, on the other hand, is + // responsible for not calling Current() on an out-of-range iterator. + const T* Current() const override { + if (value_.get() == nullptr) value_.reset(new T(*iterator_)); + return value_.get(); + } + bool Equals(const ParamIteratorInterface& other) const override { + // Having the same base generator guarantees that the other + // iterator is of the same type and we can downcast. + GTEST_CHECK_(BaseGenerator() == other.BaseGenerator()) + << "The program attempted to compare iterators " + << "from different generators." << std::endl; + return iterator_ == + CheckedDowncastToActualType(&other)->iterator_; + } + + private: + Iterator(const Iterator& other) + // The explicit constructor call suppresses a false warning + // emitted by gcc when supplied with the -Wextra option. + : ParamIteratorInterface(), + base_(other.base_), + iterator_(other.iterator_) {} + + const ParamGeneratorInterface* const base_; + typename ContainerType::const_iterator iterator_; + // A cached value of *iterator_. We keep it here to allow access by + // pointer in the wrapping iterator's operator->(). + // value_ needs to be mutable to be accessed in Current(). + // Use of std::unique_ptr helps manage cached value's lifetime, + // which is bound by the lifespan of the iterator itself. + mutable std::unique_ptr value_; + }; // class ValuesInIteratorRangeGenerator::Iterator + + // No implementation - assignment is unsupported. + void operator=(const ValuesInIteratorRangeGenerator& other); + + const ContainerType container_; +}; // class ValuesInIteratorRangeGenerator + +// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE. +// +// Default parameterized test name generator, returns a string containing the +// integer test parameter index. +template +std::string DefaultParamName(const TestParamInfo& info) { + Message name_stream; + name_stream << info.index; + return name_stream.GetString(); +} + +template +void TestNotEmpty() { + static_assert(sizeof(T) == 0, "Empty arguments are not allowed."); +} +template +void TestNotEmpty(const T&) {} + +// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE. +// +// Stores a parameter value and later creates tests parameterized with that +// value. +template +class ParameterizedTestFactory : public TestFactoryBase { + public: + typedef typename TestClass::ParamType ParamType; + explicit ParameterizedTestFactory(ParamType parameter) + : parameter_(parameter) {} + Test* CreateTest() override { + TestClass::SetParam(¶meter_); + return new TestClass(); + } + + private: + const ParamType parameter_; + + ParameterizedTestFactory(const ParameterizedTestFactory&) = delete; + ParameterizedTestFactory& operator=(const ParameterizedTestFactory&) = delete; +}; + +// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE. +// +// TestMetaFactoryBase is a base class for meta-factories that create +// test factories for passing into MakeAndRegisterTestInfo function. +template +class TestMetaFactoryBase { + public: + virtual ~TestMetaFactoryBase() {} + + virtual TestFactoryBase* CreateTestFactory(ParamType parameter) = 0; +}; + +// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE. +// +// TestMetaFactory creates test factories for passing into +// MakeAndRegisterTestInfo function. Since MakeAndRegisterTestInfo receives +// ownership of test factory pointer, same factory object cannot be passed +// into that method twice. But ParameterizedTestSuiteInfo is going to call +// it for each Test/Parameter value combination. Thus it needs meta factory +// creator class. +template +class TestMetaFactory + : public TestMetaFactoryBase { + public: + using ParamType = typename TestSuite::ParamType; + + TestMetaFactory() {} + + TestFactoryBase* CreateTestFactory(ParamType parameter) override { + return new ParameterizedTestFactory(parameter); + } + + private: + TestMetaFactory(const TestMetaFactory&) = delete; + TestMetaFactory& operator=(const TestMetaFactory&) = delete; +}; + +// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE. +// +// ParameterizedTestSuiteInfoBase is a generic interface +// to ParameterizedTestSuiteInfo classes. ParameterizedTestSuiteInfoBase +// accumulates test information provided by TEST_P macro invocations +// and generators provided by INSTANTIATE_TEST_SUITE_P macro invocations +// and uses that information to register all resulting test instances +// in RegisterTests method. The ParameterizeTestSuiteRegistry class holds +// a collection of pointers to the ParameterizedTestSuiteInfo objects +// and calls RegisterTests() on each of them when asked. +class ParameterizedTestSuiteInfoBase { + public: + virtual ~ParameterizedTestSuiteInfoBase() {} + + // Base part of test suite name for display purposes. + virtual const std::string& GetTestSuiteName() const = 0; + // Test suite id to verify identity. + virtual TypeId GetTestSuiteTypeId() const = 0; + // UnitTest class invokes this method to register tests in this + // test suite right before running them in RUN_ALL_TESTS macro. + // This method should not be called more than once on any single + // instance of a ParameterizedTestSuiteInfoBase derived class. + virtual void RegisterTests() = 0; + + protected: + ParameterizedTestSuiteInfoBase() {} + + private: + ParameterizedTestSuiteInfoBase(const ParameterizedTestSuiteInfoBase&) = + delete; + ParameterizedTestSuiteInfoBase& operator=( + const ParameterizedTestSuiteInfoBase&) = delete; +}; + +// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE. +// +// Report a the name of a test_suit as safe to ignore +// as the side effect of construction of this type. +struct GTEST_API_ MarkAsIgnored { + explicit MarkAsIgnored(const char* test_suite); +}; + +GTEST_API_ void InsertSyntheticTestCase(const std::string& name, + CodeLocation location, bool has_test_p); + +// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE. +// +// ParameterizedTestSuiteInfo accumulates tests obtained from TEST_P +// macro invocations for a particular test suite and generators +// obtained from INSTANTIATE_TEST_SUITE_P macro invocations for that +// test suite. It registers tests with all values generated by all +// generators when asked. +template +class ParameterizedTestSuiteInfo : public ParameterizedTestSuiteInfoBase { + public: + // ParamType and GeneratorCreationFunc are private types but are required + // for declarations of public methods AddTestPattern() and + // AddTestSuiteInstantiation(). + using ParamType = typename TestSuite::ParamType; + // A function that returns an instance of appropriate generator type. + typedef ParamGenerator(GeneratorCreationFunc)(); + using ParamNameGeneratorFunc = std::string(const TestParamInfo&); + + explicit ParameterizedTestSuiteInfo(const char* name, + CodeLocation code_location) + : test_suite_name_(name), code_location_(code_location) {} + + // Test suite base name for display purposes. + const std::string& GetTestSuiteName() const override { + return test_suite_name_; + } + // Test suite id to verify identity. + TypeId GetTestSuiteTypeId() const override { return GetTypeId(); } + // TEST_P macro uses AddTestPattern() to record information + // about a single test in a LocalTestInfo structure. + // test_suite_name is the base name of the test suite (without invocation + // prefix). test_base_name is the name of an individual test without + // parameter index. For the test SequenceA/FooTest.DoBar/1 FooTest is + // test suite base name and DoBar is test base name. + void AddTestPattern(const char* test_suite_name, const char* test_base_name, + TestMetaFactoryBase* meta_factory, + CodeLocation code_location) { + tests_.push_back(std::shared_ptr(new TestInfo( + test_suite_name, test_base_name, meta_factory, code_location))); + } + // INSTANTIATE_TEST_SUITE_P macro uses AddGenerator() to record information + // about a generator. + int AddTestSuiteInstantiation(const std::string& instantiation_name, + GeneratorCreationFunc* func, + ParamNameGeneratorFunc* name_func, + const char* file, int line) { + instantiations_.push_back( + InstantiationInfo(instantiation_name, func, name_func, file, line)); + return 0; // Return value used only to run this method in namespace scope. + } + // UnitTest class invokes this method to register tests in this test suite + // right before running tests in RUN_ALL_TESTS macro. + // This method should not be called more than once on any single + // instance of a ParameterizedTestSuiteInfoBase derived class. + // UnitTest has a guard to prevent from calling this method more than once. + void RegisterTests() override { + bool generated_instantiations = false; + + for (typename TestInfoContainer::iterator test_it = tests_.begin(); + test_it != tests_.end(); ++test_it) { + std::shared_ptr test_info = *test_it; + for (typename InstantiationContainer::iterator gen_it = + instantiations_.begin(); + gen_it != instantiations_.end(); ++gen_it) { + const std::string& instantiation_name = gen_it->name; + ParamGenerator generator((*gen_it->generator)()); + ParamNameGeneratorFunc* name_func = gen_it->name_func; + const char* file = gen_it->file; + int line = gen_it->line; + + std::string test_suite_name; + if (!instantiation_name.empty()) + test_suite_name = instantiation_name + "/"; + test_suite_name += test_info->test_suite_base_name; + + size_t i = 0; + std::set test_param_names; + for (typename ParamGenerator::iterator param_it = + generator.begin(); + param_it != generator.end(); ++param_it, ++i) { + generated_instantiations = true; + + Message test_name_stream; + + std::string param_name = + name_func(TestParamInfo(*param_it, i)); + + GTEST_CHECK_(IsValidParamName(param_name)) + << "Parameterized test name '" << param_name + << "' is invalid, in " << file << " line " << line << std::endl; + + GTEST_CHECK_(test_param_names.count(param_name) == 0) + << "Duplicate parameterized test name '" << param_name << "', in " + << file << " line " << line << std::endl; + + test_param_names.insert(param_name); + + if (!test_info->test_base_name.empty()) { + test_name_stream << test_info->test_base_name << "/"; + } + test_name_stream << param_name; + MakeAndRegisterTestInfo( + test_suite_name.c_str(), test_name_stream.GetString().c_str(), + nullptr, // No type parameter. + PrintToString(*param_it).c_str(), test_info->code_location, + GetTestSuiteTypeId(), + SuiteApiResolver::GetSetUpCaseOrSuite(file, line), + SuiteApiResolver::GetTearDownCaseOrSuite(file, line), + test_info->test_meta_factory->CreateTestFactory(*param_it)); + } // for param_it + } // for gen_it + } // for test_it + + if (!generated_instantiations) { + // There are no generaotrs, or they all generate nothing ... + InsertSyntheticTestCase(GetTestSuiteName(), code_location_, + !tests_.empty()); + } + } // RegisterTests + + private: + // LocalTestInfo structure keeps information about a single test registered + // with TEST_P macro. + struct TestInfo { + TestInfo(const char* a_test_suite_base_name, const char* a_test_base_name, + TestMetaFactoryBase* a_test_meta_factory, + CodeLocation a_code_location) + : test_suite_base_name(a_test_suite_base_name), + test_base_name(a_test_base_name), + test_meta_factory(a_test_meta_factory), + code_location(a_code_location) {} + + const std::string test_suite_base_name; + const std::string test_base_name; + const std::unique_ptr> test_meta_factory; + const CodeLocation code_location; + }; + using TestInfoContainer = ::std::vector>; + // Records data received from INSTANTIATE_TEST_SUITE_P macros: + // + struct InstantiationInfo { + InstantiationInfo(const std::string& name_in, + GeneratorCreationFunc* generator_in, + ParamNameGeneratorFunc* name_func_in, const char* file_in, + int line_in) + : name(name_in), + generator(generator_in), + name_func(name_func_in), + file(file_in), + line(line_in) {} + + std::string name; + GeneratorCreationFunc* generator; + ParamNameGeneratorFunc* name_func; + const char* file; + int line; + }; + typedef ::std::vector InstantiationContainer; + + static bool IsValidParamName(const std::string& name) { + // Check for empty string + if (name.empty()) return false; + + // Check for invalid characters + for (std::string::size_type index = 0; index < name.size(); ++index) { + if (!IsAlNum(name[index]) && name[index] != '_') return false; + } + + return true; + } + + const std::string test_suite_name_; + CodeLocation code_location_; + TestInfoContainer tests_; + InstantiationContainer instantiations_; + + ParameterizedTestSuiteInfo(const ParameterizedTestSuiteInfo&) = delete; + ParameterizedTestSuiteInfo& operator=(const ParameterizedTestSuiteInfo&) = + delete; +}; // class ParameterizedTestSuiteInfo + +// Legacy API is deprecated but still available +#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_ +template +using ParameterizedTestCaseInfo = ParameterizedTestSuiteInfo; +#endif // GTEST_REMOVE_LEGACY_TEST_CASEAPI_ + +// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE. +// +// ParameterizedTestSuiteRegistry contains a map of +// ParameterizedTestSuiteInfoBase classes accessed by test suite names. TEST_P +// and INSTANTIATE_TEST_SUITE_P macros use it to locate their corresponding +// ParameterizedTestSuiteInfo descriptors. +class ParameterizedTestSuiteRegistry { + public: + ParameterizedTestSuiteRegistry() {} + ~ParameterizedTestSuiteRegistry() { + for (auto& test_suite_info : test_suite_infos_) { + delete test_suite_info; + } + } + + // Looks up or creates and returns a structure containing information about + // tests and instantiations of a particular test suite. + template + ParameterizedTestSuiteInfo* GetTestSuitePatternHolder( + const char* test_suite_name, CodeLocation code_location) { + ParameterizedTestSuiteInfo* typed_test_info = nullptr; + for (auto& test_suite_info : test_suite_infos_) { + if (test_suite_info->GetTestSuiteName() == test_suite_name) { + if (test_suite_info->GetTestSuiteTypeId() != GetTypeId()) { + // Complain about incorrect usage of Google Test facilities + // and terminate the program since we cannot guaranty correct + // test suite setup and tear-down in this case. + ReportInvalidTestSuiteType(test_suite_name, code_location); + posix::Abort(); + } else { + // At this point we are sure that the object we found is of the same + // type we are looking for, so we downcast it to that type + // without further checks. + typed_test_info = CheckedDowncastToActualType< + ParameterizedTestSuiteInfo>(test_suite_info); + } + break; + } + } + if (typed_test_info == nullptr) { + typed_test_info = new ParameterizedTestSuiteInfo( + test_suite_name, code_location); + test_suite_infos_.push_back(typed_test_info); + } + return typed_test_info; + } + void RegisterTests() { + for (auto& test_suite_info : test_suite_infos_) { + test_suite_info->RegisterTests(); + } + } +// Legacy API is deprecated but still available +#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_ + template + ParameterizedTestCaseInfo* GetTestCasePatternHolder( + const char* test_case_name, CodeLocation code_location) { + return GetTestSuitePatternHolder(test_case_name, code_location); + } + +#endif // GTEST_REMOVE_LEGACY_TEST_CASEAPI_ + + private: + using TestSuiteInfoContainer = ::std::vector; + + TestSuiteInfoContainer test_suite_infos_; + + ParameterizedTestSuiteRegistry(const ParameterizedTestSuiteRegistry&) = + delete; + ParameterizedTestSuiteRegistry& operator=( + const ParameterizedTestSuiteRegistry&) = delete; +}; + +// Keep track of what type-parameterized test suite are defined and +// where as well as which are intatiated. This allows susequently +// identifying suits that are defined but never used. +class TypeParameterizedTestSuiteRegistry { + public: + // Add a suite definition + void RegisterTestSuite(const char* test_suite_name, + CodeLocation code_location); + + // Add an instantiation of a suit. + void RegisterInstantiation(const char* test_suite_name); + + // For each suit repored as defined but not reported as instantiation, + // emit a test that reports that fact (configurably, as an error). + void CheckForInstantiations(); + + private: + struct TypeParameterizedTestSuiteInfo { + explicit TypeParameterizedTestSuiteInfo(CodeLocation c) + : code_location(c), instantiated(false) {} + + CodeLocation code_location; + bool instantiated; + }; + + std::map suites_; +}; + +} // namespace internal + +// Forward declarations of ValuesIn(), which is implemented in +// include/gtest/gtest-param-test.h. +template +internal::ParamGenerator ValuesIn( + const Container& container); + +namespace internal { +// Used in the Values() function to provide polymorphic capabilities. + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable : 4100) +#endif + +template +class ValueArray { + public: + explicit ValueArray(Ts... v) : v_(FlatTupleConstructTag{}, std::move(v)...) {} + + template + operator ParamGenerator() const { // NOLINT + return ValuesIn(MakeVector(MakeIndexSequence())); + } + + private: + template + std::vector MakeVector(IndexSequence) const { + return std::vector{static_cast(v_.template Get())...}; + } + + FlatTuple v_; +}; + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +template +class CartesianProductGenerator + : public ParamGeneratorInterface<::std::tuple> { + public: + typedef ::std::tuple ParamType; + + CartesianProductGenerator(const std::tuple...>& g) + : generators_(g) {} + ~CartesianProductGenerator() override {} + + ParamIteratorInterface* Begin() const override { + return new Iterator(this, generators_, false); + } + ParamIteratorInterface* End() const override { + return new Iterator(this, generators_, true); + } + + private: + template + class IteratorImpl; + template + class IteratorImpl> + : public ParamIteratorInterface { + public: + IteratorImpl(const ParamGeneratorInterface* base, + const std::tuple...>& generators, + bool is_end) + : base_(base), + begin_(std::get(generators).begin()...), + end_(std::get(generators).end()...), + current_(is_end ? end_ : begin_) { + ComputeCurrentValue(); + } + ~IteratorImpl() override {} + + const ParamGeneratorInterface* BaseGenerator() const override { + return base_; + } + // Advance should not be called on beyond-of-range iterators + // so no component iterators must be beyond end of range, either. + void Advance() override { + assert(!AtEnd()); + // Advance the last iterator. + ++std::get(current_); + // if that reaches end, propagate that up. + AdvanceIfEnd(); + ComputeCurrentValue(); + } + ParamIteratorInterface* Clone() const override { + return new IteratorImpl(*this); + } + + const ParamType* Current() const override { return current_value_.get(); } + + bool Equals(const ParamIteratorInterface& other) const override { + // Having the same base generator guarantees that the other + // iterator is of the same type and we can downcast. + GTEST_CHECK_(BaseGenerator() == other.BaseGenerator()) + << "The program attempted to compare iterators " + << "from different generators." << std::endl; + const IteratorImpl* typed_other = + CheckedDowncastToActualType(&other); + + // We must report iterators equal if they both point beyond their + // respective ranges. That can happen in a variety of fashions, + // so we have to consult AtEnd(). + if (AtEnd() && typed_other->AtEnd()) return true; + + bool same = true; + bool dummy[] = { + (same = same && std::get(current_) == + std::get(typed_other->current_))...}; + (void)dummy; + return same; + } + + private: + template + void AdvanceIfEnd() { + if (std::get(current_) != std::get(end_)) return; + + bool last = ThisI == 0; + if (last) { + // We are done. Nothing else to propagate. + return; + } + + constexpr size_t NextI = ThisI - (ThisI != 0); + std::get(current_) = std::get(begin_); + ++std::get(current_); + AdvanceIfEnd(); + } + + void ComputeCurrentValue() { + if (!AtEnd()) + current_value_ = std::make_shared(*std::get(current_)...); + } + bool AtEnd() const { + bool at_end = false; + bool dummy[] = { + (at_end = at_end || std::get(current_) == std::get(end_))...}; + (void)dummy; + return at_end; + } + + const ParamGeneratorInterface* const base_; + std::tuple::iterator...> begin_; + std::tuple::iterator...> end_; + std::tuple::iterator...> current_; + std::shared_ptr current_value_; + }; + + using Iterator = IteratorImpl::type>; + + std::tuple...> generators_; +}; + +template +class CartesianProductHolder { + public: + CartesianProductHolder(const Gen&... g) : generators_(g...) {} + template + operator ParamGenerator<::std::tuple>() const { + return ParamGenerator<::std::tuple>( + new CartesianProductGenerator(generators_)); + } + + private: + std::tuple generators_; +}; + +template +class ParamGeneratorConverter : public ParamGeneratorInterface { + public: + ParamGeneratorConverter(ParamGenerator gen) // NOLINT + : generator_(std::move(gen)) {} + + ParamIteratorInterface* Begin() const override { + return new Iterator(this, generator_.begin(), generator_.end()); + } + ParamIteratorInterface* End() const override { + return new Iterator(this, generator_.end(), generator_.end()); + } + + private: + class Iterator : public ParamIteratorInterface { + public: + Iterator(const ParamGeneratorInterface* base, ParamIterator it, + ParamIterator end) + : base_(base), it_(it), end_(end) { + if (it_ != end_) value_ = std::make_shared(static_cast(*it_)); + } + ~Iterator() override {} + + const ParamGeneratorInterface* BaseGenerator() const override { + return base_; + } + void Advance() override { + ++it_; + if (it_ != end_) value_ = std::make_shared(static_cast(*it_)); + } + ParamIteratorInterface* Clone() const override { + return new Iterator(*this); + } + const To* Current() const override { return value_.get(); } + bool Equals(const ParamIteratorInterface& other) const override { + // Having the same base generator guarantees that the other + // iterator is of the same type and we can downcast. + GTEST_CHECK_(BaseGenerator() == other.BaseGenerator()) + << "The program attempted to compare iterators " + << "from different generators." << std::endl; + const ParamIterator other_it = + CheckedDowncastToActualType(&other)->it_; + return it_ == other_it; + } + + private: + Iterator(const Iterator& other) = default; + + const ParamGeneratorInterface* const base_; + ParamIterator it_; + ParamIterator end_; + std::shared_ptr value_; + }; // class ParamGeneratorConverter::Iterator + + ParamGenerator generator_; +}; // class ParamGeneratorConverter + +template +class ParamConverterGenerator { + public: + ParamConverterGenerator(ParamGenerator g) // NOLINT + : generator_(std::move(g)) {} + + template + operator ParamGenerator() const { // NOLINT + return ParamGenerator(new ParamGeneratorConverter(generator_)); + } + + private: + ParamGenerator generator_; +}; + +} // namespace internal +} // namespace testing + +#endif // GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_PARAM_UTIL_H_ diff --git a/include/gtest/internal/gtest-port-arch.h b/include/gtest/internal/gtest-port-arch.h new file mode 100644 index 0000000..0406460 --- /dev/null +++ b/include/gtest/internal/gtest-port-arch.h @@ -0,0 +1,118 @@ +// Copyright 2015, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// The Google C++ Testing and Mocking Framework (Google Test) +// +// This header file defines the GTEST_OS_* macro. +// It is separate from gtest-port.h so that custom/gtest-port.h can include it. + +#ifndef GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_PORT_ARCH_H_ +#define GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_PORT_ARCH_H_ + +// Determines the platform on which Google Test is compiled. +#ifdef __CYGWIN__ +#define GTEST_OS_CYGWIN 1 +#elif defined(__MINGW__) || defined(__MINGW32__) || defined(__MINGW64__) +#define GTEST_OS_WINDOWS_MINGW 1 +#define GTEST_OS_WINDOWS 1 +#elif defined _WIN32 +#define GTEST_OS_WINDOWS 1 +#ifdef _WIN32_WCE +#define GTEST_OS_WINDOWS_MOBILE 1 +#elif defined(WINAPI_FAMILY) +#include +#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) +#define GTEST_OS_WINDOWS_DESKTOP 1 +#elif WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_PHONE_APP) +#define GTEST_OS_WINDOWS_PHONE 1 +#elif WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP) +#define GTEST_OS_WINDOWS_RT 1 +#elif WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_TV_TITLE) +#define GTEST_OS_WINDOWS_PHONE 1 +#define GTEST_OS_WINDOWS_TV_TITLE 1 +#else +// WINAPI_FAMILY defined but no known partition matched. +// Default to desktop. +#define GTEST_OS_WINDOWS_DESKTOP 1 +#endif +#else +#define GTEST_OS_WINDOWS_DESKTOP 1 +#endif // _WIN32_WCE +#elif defined __OS2__ +#define GTEST_OS_OS2 1 +#elif defined __APPLE__ +#define GTEST_OS_MAC 1 +#include +#if TARGET_OS_IPHONE +#define GTEST_OS_IOS 1 +#endif +#elif defined __DragonFly__ +#define GTEST_OS_DRAGONFLY 1 +#elif defined __FreeBSD__ +#define GTEST_OS_FREEBSD 1 +#elif defined __Fuchsia__ +#define GTEST_OS_FUCHSIA 1 +#elif defined(__GNU__) +#define GTEST_OS_GNU_HURD 1 +#elif defined(__GLIBC__) && defined(__FreeBSD_kernel__) +#define GTEST_OS_GNU_KFREEBSD 1 +#elif defined __linux__ +#define GTEST_OS_LINUX 1 +#if defined __ANDROID__ +#define GTEST_OS_LINUX_ANDROID 1 +#endif +#elif defined __MVS__ +#define GTEST_OS_ZOS 1 +#elif defined(__sun) && defined(__SVR4) +#define GTEST_OS_SOLARIS 1 +#elif defined(_AIX) +#define GTEST_OS_AIX 1 +#elif defined(__hpux) +#define GTEST_OS_HPUX 1 +#elif defined __native_client__ +#define GTEST_OS_NACL 1 +#elif defined __NetBSD__ +#define GTEST_OS_NETBSD 1 +#elif defined __OpenBSD__ +#define GTEST_OS_OPENBSD 1 +#elif defined __QNX__ +#define GTEST_OS_QNX 1 +#elif defined(__HAIKU__) +#define GTEST_OS_HAIKU 1 +#elif defined ESP8266 +#define GTEST_OS_ESP8266 1 +#elif defined ESP32 +#define GTEST_OS_ESP32 1 +#elif defined(__XTENSA__) +#define GTEST_OS_XTENSA 1 +#elif defined(__hexagon__) +#define GTEST_OS_QURT 1 +#endif // __CYGWIN__ + +#endif // GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_PORT_ARCH_H_ diff --git a/include/gtest/internal/gtest-port.h b/include/gtest/internal/gtest-port.h new file mode 100644 index 0000000..005a0c0 --- /dev/null +++ b/include/gtest/internal/gtest-port.h @@ -0,0 +1,2423 @@ +// Copyright 2005, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Low-level types and utilities for porting Google Test to various +// platforms. All macros ending with _ and symbols defined in an +// internal namespace are subject to change without notice. Code +// outside Google Test MUST NOT USE THEM DIRECTLY. Macros that don't +// end with _ are part of Google Test's public API and can be used by +// code outside Google Test. +// +// This file is fundamental to Google Test. All other Google Test source +// files are expected to #include this. Therefore, it cannot #include +// any other Google Test header. + +// IWYU pragma: private, include "gtest/gtest.h" +// IWYU pragma: friend gtest/.* +// IWYU pragma: friend gmock/.* + +#ifndef GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_PORT_H_ +#define GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_PORT_H_ + +// Environment-describing macros +// ----------------------------- +// +// Google Test can be used in many different environments. Macros in +// this section tell Google Test what kind of environment it is being +// used in, such that Google Test can provide environment-specific +// features and implementations. +// +// Google Test tries to automatically detect the properties of its +// environment, so users usually don't need to worry about these +// macros. However, the automatic detection is not perfect. +// Sometimes it's necessary for a user to define some of the following +// macros in the build script to override Google Test's decisions. +// +// If the user doesn't define a macro in the list, Google Test will +// provide a default definition. After this header is #included, all +// macros in this list will be defined to either 1 or 0. +// +// Notes to maintainers: +// - Each macro here is a user-tweakable knob; do not grow the list +// lightly. +// - Use #if to key off these macros. Don't use #ifdef or "#if +// defined(...)", which will not work as these macros are ALWAYS +// defined. +// +// GTEST_HAS_CLONE - Define it to 1/0 to indicate that clone(2) +// is/isn't available. +// GTEST_HAS_EXCEPTIONS - Define it to 1/0 to indicate that exceptions +// are enabled. +// GTEST_HAS_POSIX_RE - Define it to 1/0 to indicate that POSIX regular +// expressions are/aren't available. +// GTEST_HAS_PTHREAD - Define it to 1/0 to indicate that +// is/isn't available. +// GTEST_HAS_RTTI - Define it to 1/0 to indicate that RTTI is/isn't +// enabled. +// GTEST_HAS_STD_WSTRING - Define it to 1/0 to indicate that +// std::wstring does/doesn't work (Google Test can +// be used where std::wstring is unavailable). +// GTEST_HAS_SEH - Define it to 1/0 to indicate whether the +// compiler supports Microsoft's "Structured +// Exception Handling". +// GTEST_HAS_STREAM_REDIRECTION +// - Define it to 1/0 to indicate whether the +// platform supports I/O stream redirection using +// dup() and dup2(). +// GTEST_LINKED_AS_SHARED_LIBRARY +// - Define to 1 when compiling tests that use +// Google Test as a shared library (known as +// DLL on Windows). +// GTEST_CREATE_SHARED_LIBRARY +// - Define to 1 when compiling Google Test itself +// as a shared library. +// GTEST_DEFAULT_DEATH_TEST_STYLE +// - The default value of --gtest_death_test_style. +// The legacy default has been "fast" in the open +// source version since 2008. The recommended value +// is "threadsafe", and can be set in +// custom/gtest-port.h. + +// Platform-indicating macros +// -------------------------- +// +// Macros indicating the platform on which Google Test is being used +// (a macro is defined to 1 if compiled on the given platform; +// otherwise UNDEFINED -- it's never defined to 0.). Google Test +// defines these macros automatically. Code outside Google Test MUST +// NOT define them. +// +// GTEST_OS_AIX - IBM AIX +// GTEST_OS_CYGWIN - Cygwin +// GTEST_OS_DRAGONFLY - DragonFlyBSD +// GTEST_OS_FREEBSD - FreeBSD +// GTEST_OS_FUCHSIA - Fuchsia +// GTEST_OS_GNU_HURD - GNU/Hurd +// GTEST_OS_GNU_KFREEBSD - GNU/kFreeBSD +// GTEST_OS_HAIKU - Haiku +// GTEST_OS_HPUX - HP-UX +// GTEST_OS_LINUX - Linux +// GTEST_OS_LINUX_ANDROID - Google Android +// GTEST_OS_MAC - Mac OS X +// GTEST_OS_IOS - iOS +// GTEST_OS_NACL - Google Native Client (NaCl) +// GTEST_OS_NETBSD - NetBSD +// GTEST_OS_OPENBSD - OpenBSD +// GTEST_OS_OS2 - OS/2 +// GTEST_OS_QNX - QNX +// GTEST_OS_SOLARIS - Sun Solaris +// GTEST_OS_WINDOWS - Windows (Desktop, MinGW, or Mobile) +// GTEST_OS_WINDOWS_DESKTOP - Windows Desktop +// GTEST_OS_WINDOWS_MINGW - MinGW +// GTEST_OS_WINDOWS_MOBILE - Windows Mobile +// GTEST_OS_WINDOWS_PHONE - Windows Phone +// GTEST_OS_WINDOWS_RT - Windows Store App/WinRT +// GTEST_OS_ZOS - z/OS +// +// Among the platforms, Cygwin, Linux, Mac OS X, and Windows have the +// most stable support. Since core members of the Google Test project +// don't have access to other platforms, support for them may be less +// stable. If you notice any problems on your platform, please notify +// googletestframework@googlegroups.com (patches for fixing them are +// even more welcome!). +// +// It is possible that none of the GTEST_OS_* macros are defined. + +// Feature-indicating macros +// ------------------------- +// +// Macros indicating which Google Test features are available (a macro +// is defined to 1 if the corresponding feature is supported; +// otherwise UNDEFINED -- it's never defined to 0.). Google Test +// defines these macros automatically. Code outside Google Test MUST +// NOT define them. +// +// These macros are public so that portable tests can be written. +// Such tests typically surround code using a feature with an #if +// which controls that code. For example: +// +// #if GTEST_HAS_DEATH_TEST +// EXPECT_DEATH(DoSomethingDeadly()); +// #endif +// +// GTEST_HAS_DEATH_TEST - death tests +// GTEST_HAS_TYPED_TEST - typed tests +// GTEST_HAS_TYPED_TEST_P - type-parameterized tests +// GTEST_IS_THREADSAFE - Google Test is thread-safe. +// GTEST_USES_RE2 - the RE2 regular expression library is used +// GTEST_USES_POSIX_RE - enhanced POSIX regex is used. Do not confuse with +// GTEST_HAS_POSIX_RE (see above) which users can +// define themselves. +// GTEST_USES_SIMPLE_RE - our own simple regex is used; +// the above RE\b(s) are mutually exclusive. + +// Misc public macros +// ------------------ +// +// GTEST_FLAG(flag_name) - references the variable corresponding to +// the given Google Test flag. + +// Internal utilities +// ------------------ +// +// The following macros and utilities are for Google Test's INTERNAL +// use only. Code outside Google Test MUST NOT USE THEM DIRECTLY. +// +// Macros for basic C++ coding: +// GTEST_AMBIGUOUS_ELSE_BLOCKER_ - for disabling a gcc warning. +// GTEST_ATTRIBUTE_UNUSED_ - declares that a class' instances or a +// variable don't have to be used. +// GTEST_MUST_USE_RESULT_ - declares that a function's result must be used. +// GTEST_INTENTIONAL_CONST_COND_PUSH_ - start code section where MSVC C4127 is +// suppressed (constant conditional). +// GTEST_INTENTIONAL_CONST_COND_POP_ - finish code section where MSVC C4127 +// is suppressed. +// GTEST_INTERNAL_HAS_ANY - for enabling UniversalPrinter or +// UniversalPrinter specializations. +// GTEST_INTERNAL_HAS_OPTIONAL - for enabling UniversalPrinter +// or +// UniversalPrinter +// specializations. +// GTEST_INTERNAL_HAS_STRING_VIEW - for enabling Matcher or +// Matcher +// specializations. +// GTEST_INTERNAL_HAS_VARIANT - for enabling UniversalPrinter or +// UniversalPrinter +// specializations. +// +// Synchronization: +// Mutex, MutexLock, ThreadLocal, GetThreadCount() +// - synchronization primitives. +// +// Regular expressions: +// RE - a simple regular expression class using +// 1) the RE2 syntax on all platforms when built with RE2 +// and Abseil as dependencies +// 2) the POSIX Extended Regular Expression syntax on +// UNIX-like platforms, +// 3) A reduced regular exception syntax on other platforms, +// including Windows. +// Logging: +// GTEST_LOG_() - logs messages at the specified severity level. +// LogToStderr() - directs all log messages to stderr. +// FlushInfoLog() - flushes informational log messages. +// +// Stdout and stderr capturing: +// CaptureStdout() - starts capturing stdout. +// GetCapturedStdout() - stops capturing stdout and returns the captured +// string. +// CaptureStderr() - starts capturing stderr. +// GetCapturedStderr() - stops capturing stderr and returns the captured +// string. +// +// Integer types: +// TypeWithSize - maps an integer to a int type. +// TimeInMillis - integers of known sizes. +// BiggestInt - the biggest signed integer type. +// +// Command-line utilities: +// GetInjectableArgvs() - returns the command line as a vector of strings. +// +// Environment variable utilities: +// GetEnv() - gets the value of an environment variable. +// BoolFromGTestEnv() - parses a bool environment variable. +// Int32FromGTestEnv() - parses an int32_t environment variable. +// StringFromGTestEnv() - parses a string environment variable. +// +// Deprecation warnings: +// GTEST_INTERNAL_DEPRECATED(message) - attribute marking a function as +// deprecated; calling a marked function +// should generate a compiler warning + +#include // for isspace, etc +#include // for ptrdiff_t +#include +#include +#include + +#include +// #include // Guarded by GTEST_IS_THREADSAFE below +#include +#include +#include +#include +#include +#include +// #include // Guarded by GTEST_IS_THREADSAFE below +#include +#include +#include + +#ifndef _WIN32_WCE +#include +#include +#endif // !_WIN32_WCE + +#if defined __APPLE__ +#include +#include +#endif + +#include "gtest/internal/custom/gtest-port.h" +#include "gtest/internal/gtest-port-arch.h" + +#if GTEST_HAS_ABSL +#include "absl/flags/declare.h" +#include "absl/flags/flag.h" +#include "absl/flags/reflection.h" +#endif + +#if !defined(GTEST_DEV_EMAIL_) +#define GTEST_DEV_EMAIL_ "googletestframework@@googlegroups.com" +#define GTEST_FLAG_PREFIX_ "gtest_" +#define GTEST_FLAG_PREFIX_DASH_ "gtest-" +#define GTEST_FLAG_PREFIX_UPPER_ "GTEST_" +#define GTEST_NAME_ "Google Test" +#define GTEST_PROJECT_URL_ "https://github.com/google/googletest/" +#endif // !defined(GTEST_DEV_EMAIL_) + +#if !defined(GTEST_INIT_GOOGLE_TEST_NAME_) +#define GTEST_INIT_GOOGLE_TEST_NAME_ "testing::InitGoogleTest" +#endif // !defined(GTEST_INIT_GOOGLE_TEST_NAME_) + +// Determines the version of gcc that is used to compile this. +#ifdef __GNUC__ +// 40302 means version 4.3.2. +#define GTEST_GCC_VER_ \ + (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) +#endif // __GNUC__ + +// Macros for disabling Microsoft Visual C++ warnings. +// +// GTEST_DISABLE_MSC_WARNINGS_PUSH_(4800 4385) +// /* code that triggers warnings C4800 and C4385 */ +// GTEST_DISABLE_MSC_WARNINGS_POP_() +#if defined(_MSC_VER) +#define GTEST_DISABLE_MSC_WARNINGS_PUSH_(warnings) \ + __pragma(warning(push)) __pragma(warning(disable : warnings)) +#define GTEST_DISABLE_MSC_WARNINGS_POP_() __pragma(warning(pop)) +#else +// Not all compilers are MSVC +#define GTEST_DISABLE_MSC_WARNINGS_PUSH_(warnings) +#define GTEST_DISABLE_MSC_WARNINGS_POP_() +#endif + +// Clang on Windows does not understand MSVC's pragma warning. +// We need clang-specific way to disable function deprecation warning. +#ifdef __clang__ +#define GTEST_DISABLE_MSC_DEPRECATED_PUSH_() \ + _Pragma("clang diagnostic push") \ + _Pragma("clang diagnostic ignored \"-Wdeprecated-declarations\"") \ + _Pragma("clang diagnostic ignored \"-Wdeprecated-implementations\"") +#define GTEST_DISABLE_MSC_DEPRECATED_POP_() _Pragma("clang diagnostic pop") +#else +#define GTEST_DISABLE_MSC_DEPRECATED_PUSH_() \ + GTEST_DISABLE_MSC_WARNINGS_PUSH_(4996) +#define GTEST_DISABLE_MSC_DEPRECATED_POP_() GTEST_DISABLE_MSC_WARNINGS_POP_() +#endif + +// Brings in definitions for functions used in the testing::internal::posix +// namespace (read, write, close, chdir, isatty, stat). We do not currently +// use them on Windows Mobile. +#if GTEST_OS_WINDOWS +#if !GTEST_OS_WINDOWS_MOBILE +#include +#include +#endif +// In order to avoid having to include , use forward declaration +#if GTEST_OS_WINDOWS_MINGW && !defined(__MINGW64_VERSION_MAJOR) +// MinGW defined _CRITICAL_SECTION and _RTL_CRITICAL_SECTION as two +// separate (equivalent) structs, instead of using typedef +typedef struct _CRITICAL_SECTION GTEST_CRITICAL_SECTION; +#else +// Assume CRITICAL_SECTION is a typedef of _RTL_CRITICAL_SECTION. +// This assumption is verified by +// WindowsTypesTest.CRITICAL_SECTIONIs_RTL_CRITICAL_SECTION. +typedef struct _RTL_CRITICAL_SECTION GTEST_CRITICAL_SECTION; +#endif +#elif GTEST_OS_XTENSA +#include +// Xtensa toolchains define strcasecmp in the string.h header instead of +// strings.h. string.h is already included. +#else +// This assumes that non-Windows OSes provide unistd.h. For OSes where this +// is not the case, we need to include headers that provide the functions +// mentioned above. +#include +#include +#endif // GTEST_OS_WINDOWS + +#if GTEST_OS_LINUX_ANDROID +// Used to define __ANDROID_API__ matching the target NDK API level. +#include // NOLINT +#endif + +// Defines this to true if and only if Google Test can use POSIX regular +// expressions. +#ifndef GTEST_HAS_POSIX_RE +#if GTEST_OS_LINUX_ANDROID +// On Android, is only available starting with Gingerbread. +#define GTEST_HAS_POSIX_RE (__ANDROID_API__ >= 9) +#else +#define GTEST_HAS_POSIX_RE \ + !(GTEST_OS_WINDOWS || GTEST_OS_XTENSA || GTEST_OS_QURT) +#endif +#endif + +// Select the regular expression implementation. +#if GTEST_HAS_ABSL +// When using Abseil, RE2 is required. +#include "absl/strings/string_view.h" +#include "re2/re2.h" +#define GTEST_USES_RE2 1 +#elif GTEST_HAS_POSIX_RE +#include // NOLINT +#define GTEST_USES_POSIX_RE 1 +#else +// Use our own simple regex implementation. +#define GTEST_USES_SIMPLE_RE 1 +#endif + +#ifndef GTEST_HAS_EXCEPTIONS +// The user didn't tell us whether exceptions are enabled, so we need +// to figure it out. +#if defined(_MSC_VER) && defined(_CPPUNWIND) +// MSVC defines _CPPUNWIND to 1 if and only if exceptions are enabled. +#define GTEST_HAS_EXCEPTIONS 1 +#elif defined(__BORLANDC__) +// C++Builder's implementation of the STL uses the _HAS_EXCEPTIONS +// macro to enable exceptions, so we'll do the same. +// Assumes that exceptions are enabled by default. +#ifndef _HAS_EXCEPTIONS +#define _HAS_EXCEPTIONS 1 +#endif // _HAS_EXCEPTIONS +#define GTEST_HAS_EXCEPTIONS _HAS_EXCEPTIONS +#elif defined(__clang__) +// clang defines __EXCEPTIONS if and only if exceptions are enabled before clang +// 220714, but if and only if cleanups are enabled after that. In Obj-C++ files, +// there can be cleanups for ObjC exceptions which also need cleanups, even if +// C++ exceptions are disabled. clang has __has_feature(cxx_exceptions) which +// checks for C++ exceptions starting at clang r206352, but which checked for +// cleanups prior to that. To reliably check for C++ exception availability with +// clang, check for +// __EXCEPTIONS && __has_feature(cxx_exceptions). +#define GTEST_HAS_EXCEPTIONS (__EXCEPTIONS && __has_feature(cxx_exceptions)) +#elif defined(__GNUC__) && __EXCEPTIONS +// gcc defines __EXCEPTIONS to 1 if and only if exceptions are enabled. +#define GTEST_HAS_EXCEPTIONS 1 +#elif defined(__SUNPRO_CC) +// Sun Pro CC supports exceptions. However, there is no compile-time way of +// detecting whether they are enabled or not. Therefore, we assume that +// they are enabled unless the user tells us otherwise. +#define GTEST_HAS_EXCEPTIONS 1 +#elif defined(__IBMCPP__) && __EXCEPTIONS +// xlC defines __EXCEPTIONS to 1 if and only if exceptions are enabled. +#define GTEST_HAS_EXCEPTIONS 1 +#elif defined(__HP_aCC) +// Exception handling is in effect by default in HP aCC compiler. It has to +// be turned of by +noeh compiler option if desired. +#define GTEST_HAS_EXCEPTIONS 1 +#else +// For other compilers, we assume exceptions are disabled to be +// conservative. +#define GTEST_HAS_EXCEPTIONS 0 +#endif // defined(_MSC_VER) || defined(__BORLANDC__) +#endif // GTEST_HAS_EXCEPTIONS + +#ifndef GTEST_HAS_STD_WSTRING +// The user didn't tell us whether ::std::wstring is available, so we need +// to figure it out. +// Cygwin 1.7 and below doesn't support ::std::wstring. +// Solaris' libc++ doesn't support it either. Android has +// no support for it at least as recent as Froyo (2.2). +#define GTEST_HAS_STD_WSTRING \ + (!(GTEST_OS_LINUX_ANDROID || GTEST_OS_CYGWIN || GTEST_OS_SOLARIS || \ + GTEST_OS_HAIKU || GTEST_OS_ESP32 || GTEST_OS_ESP8266 || \ + GTEST_OS_XTENSA || GTEST_OS_QURT)) + +#endif // GTEST_HAS_STD_WSTRING + +// Determines whether RTTI is available. +#ifndef GTEST_HAS_RTTI +// The user didn't tell us whether RTTI is enabled, so we need to +// figure it out. + +#ifdef _MSC_VER + +#ifdef _CPPRTTI // MSVC defines this macro if and only if RTTI is enabled. +#define GTEST_HAS_RTTI 1 +#else +#define GTEST_HAS_RTTI 0 +#endif + +// Starting with version 4.3.2, gcc defines __GXX_RTTI if and only if RTTI is +// enabled. +#elif defined(__GNUC__) + +#ifdef __GXX_RTTI +// When building against STLport with the Android NDK and with +// -frtti -fno-exceptions, the build fails at link time with undefined +// references to __cxa_bad_typeid. Note sure if STL or toolchain bug, +// so disable RTTI when detected. +#if GTEST_OS_LINUX_ANDROID && defined(_STLPORT_MAJOR) && !defined(__EXCEPTIONS) +#define GTEST_HAS_RTTI 0 +#else +#define GTEST_HAS_RTTI 1 +#endif // GTEST_OS_LINUX_ANDROID && __STLPORT_MAJOR && !__EXCEPTIONS +#else +#define GTEST_HAS_RTTI 0 +#endif // __GXX_RTTI + +// Clang defines __GXX_RTTI starting with version 3.0, but its manual recommends +// using has_feature instead. has_feature(cxx_rtti) is supported since 2.7, the +// first version with C++ support. +#elif defined(__clang__) + +#define GTEST_HAS_RTTI __has_feature(cxx_rtti) + +// Starting with version 9.0 IBM Visual Age defines __RTTI_ALL__ to 1 if +// both the typeid and dynamic_cast features are present. +#elif defined(__IBMCPP__) && (__IBMCPP__ >= 900) + +#ifdef __RTTI_ALL__ +#define GTEST_HAS_RTTI 1 +#else +#define GTEST_HAS_RTTI 0 +#endif + +#else + +// For all other compilers, we assume RTTI is enabled. +#define GTEST_HAS_RTTI 1 + +#endif // _MSC_VER + +#endif // GTEST_HAS_RTTI + +// It's this header's responsibility to #include when RTTI +// is enabled. +#if GTEST_HAS_RTTI +#include +#endif + +// Determines whether Google Test can use the pthreads library. +#ifndef GTEST_HAS_PTHREAD +// The user didn't tell us explicitly, so we make reasonable assumptions about +// which platforms have pthreads support. +// +// To disable threading support in Google Test, add -DGTEST_HAS_PTHREAD=0 +// to your compiler flags. +#define GTEST_HAS_PTHREAD \ + (GTEST_OS_LINUX || GTEST_OS_MAC || GTEST_OS_HPUX || GTEST_OS_QNX || \ + GTEST_OS_FREEBSD || GTEST_OS_NACL || GTEST_OS_NETBSD || GTEST_OS_FUCHSIA || \ + GTEST_OS_DRAGONFLY || GTEST_OS_GNU_KFREEBSD || GTEST_OS_OPENBSD || \ + GTEST_OS_HAIKU || GTEST_OS_GNU_HURD) +#endif // GTEST_HAS_PTHREAD + +#if GTEST_HAS_PTHREAD +// gtest-port.h guarantees to #include when GTEST_HAS_PTHREAD is +// true. +#include // NOLINT + +// For timespec and nanosleep, used below. +#include // NOLINT +#endif + +// Determines whether clone(2) is supported. +// Usually it will only be available on Linux, excluding +// Linux on the Itanium architecture. +// Also see http://linux.die.net/man/2/clone. +#ifndef GTEST_HAS_CLONE +// The user didn't tell us, so we need to figure it out. + +#if GTEST_OS_LINUX && !defined(__ia64__) +#if GTEST_OS_LINUX_ANDROID +// On Android, clone() became available at different API levels for each 32-bit +// architecture. +#if defined(__LP64__) || (defined(__arm__) && __ANDROID_API__ >= 9) || \ + (defined(__mips__) && __ANDROID_API__ >= 12) || \ + (defined(__i386__) && __ANDROID_API__ >= 17) +#define GTEST_HAS_CLONE 1 +#else +#define GTEST_HAS_CLONE 0 +#endif +#else +#define GTEST_HAS_CLONE 1 +#endif +#else +#define GTEST_HAS_CLONE 0 +#endif // GTEST_OS_LINUX && !defined(__ia64__) + +#endif // GTEST_HAS_CLONE + +// Determines whether to support stream redirection. This is used to test +// output correctness and to implement death tests. +#ifndef GTEST_HAS_STREAM_REDIRECTION +// By default, we assume that stream redirection is supported on all +// platforms except known mobile / embedded ones. +#if GTEST_OS_WINDOWS_MOBILE || GTEST_OS_WINDOWS_PHONE || \ + GTEST_OS_WINDOWS_RT || GTEST_OS_ESP8266 || GTEST_OS_XTENSA || \ + GTEST_OS_QURT +#define GTEST_HAS_STREAM_REDIRECTION 0 +#else +#define GTEST_HAS_STREAM_REDIRECTION 1 +#endif // !GTEST_OS_WINDOWS_MOBILE +#endif // GTEST_HAS_STREAM_REDIRECTION + +// Determines whether to support death tests. +// pops up a dialog window that cannot be suppressed programmatically. +#if (GTEST_OS_LINUX || GTEST_OS_CYGWIN || GTEST_OS_SOLARIS || \ + (GTEST_OS_MAC && !GTEST_OS_IOS) || \ + (GTEST_OS_WINDOWS_DESKTOP && _MSC_VER) || GTEST_OS_WINDOWS_MINGW || \ + GTEST_OS_AIX || GTEST_OS_HPUX || GTEST_OS_OPENBSD || GTEST_OS_QNX || \ + GTEST_OS_FREEBSD || GTEST_OS_NETBSD || GTEST_OS_FUCHSIA || \ + GTEST_OS_DRAGONFLY || GTEST_OS_GNU_KFREEBSD || GTEST_OS_HAIKU || \ + GTEST_OS_GNU_HURD) +#define GTEST_HAS_DEATH_TEST 1 +#endif + +// Determines whether to support type-driven tests. + +// Typed tests need and variadic macros, which GCC, VC++ 8.0, +// Sun Pro CC, IBM Visual Age, and HP aCC support. +#if defined(__GNUC__) || defined(_MSC_VER) || defined(__SUNPRO_CC) || \ + defined(__IBMCPP__) || defined(__HP_aCC) +#define GTEST_HAS_TYPED_TEST 1 +#define GTEST_HAS_TYPED_TEST_P 1 +#endif + +// Determines whether the system compiler uses UTF-16 for encoding wide strings. +#define GTEST_WIDE_STRING_USES_UTF16_ \ + (GTEST_OS_WINDOWS || GTEST_OS_CYGWIN || GTEST_OS_AIX || GTEST_OS_OS2) + +// Determines whether test results can be streamed to a socket. +#if GTEST_OS_LINUX || GTEST_OS_GNU_KFREEBSD || GTEST_OS_DRAGONFLY || \ + GTEST_OS_FREEBSD || GTEST_OS_NETBSD || GTEST_OS_OPENBSD || \ + GTEST_OS_GNU_HURD +#define GTEST_CAN_STREAM_RESULTS_ 1 +#endif + +// Defines some utility macros. + +// The GNU compiler emits a warning if nested "if" statements are followed by +// an "else" statement and braces are not used to explicitly disambiguate the +// "else" binding. This leads to problems with code like: +// +// if (gate) +// ASSERT_*(condition) << "Some message"; +// +// The "switch (0) case 0:" idiom is used to suppress this. +#ifdef __INTEL_COMPILER +#define GTEST_AMBIGUOUS_ELSE_BLOCKER_ +#else +#define GTEST_AMBIGUOUS_ELSE_BLOCKER_ \ + switch (0) \ + case 0: \ + default: // NOLINT +#endif + +// GTEST_HAVE_ATTRIBUTE_ +// +// A function-like feature checking macro that is a wrapper around +// `__has_attribute`, which is defined by GCC 5+ and Clang and evaluates to a +// nonzero constant integer if the attribute is supported or 0 if not. +// +// It evaluates to zero if `__has_attribute` is not defined by the compiler. +// +// GCC: https://gcc.gnu.org/gcc-5/changes.html +// Clang: https://clang.llvm.org/docs/LanguageExtensions.html +#ifdef __has_attribute +#define GTEST_HAVE_ATTRIBUTE_(x) __has_attribute(x) +#else +#define GTEST_HAVE_ATTRIBUTE_(x) 0 +#endif + +// GTEST_HAVE_FEATURE_ +// +// A function-like feature checking macro that is a wrapper around +// `__has_feature`. +#ifdef __has_feature +#define GTEST_HAVE_FEATURE_(x) __has_feature(x) +#else +#define GTEST_HAVE_FEATURE_(x) 0 +#endif + +// Use this annotation at the end of a struct/class definition to +// prevent the compiler from optimizing away instances that are never +// used. This is useful when all interesting logic happens inside the +// c'tor and / or d'tor. Example: +// +// struct Foo { +// Foo() { ... } +// } GTEST_ATTRIBUTE_UNUSED_; +// +// Also use it after a variable or parameter declaration to tell the +// compiler the variable/parameter does not have to be used. +#if GTEST_HAVE_ATTRIBUTE_(unused) +#define GTEST_ATTRIBUTE_UNUSED_ __attribute__((unused)) +#else +#define GTEST_ATTRIBUTE_UNUSED_ +#endif + +// Use this annotation before a function that takes a printf format string. +#if GTEST_HAVE_ATTRIBUTE_(format) && defined(__MINGW_PRINTF_FORMAT) +// MinGW has two different printf implementations. Ensure the format macro +// matches the selected implementation. See +// https://sourceforge.net/p/mingw-w64/wiki2/gnu%20printf/. +#define GTEST_ATTRIBUTE_PRINTF_(string_index, first_to_check) \ + __attribute__((format(__MINGW_PRINTF_FORMAT, string_index, first_to_check))) +#elif GTEST_HAVE_ATTRIBUTE_(format) +#define GTEST_ATTRIBUTE_PRINTF_(string_index, first_to_check) \ + __attribute__((format(printf, string_index, first_to_check))) +#else +#define GTEST_ATTRIBUTE_PRINTF_(string_index, first_to_check) +#endif + +// Tell the compiler to warn about unused return values for functions declared +// with this macro. The macro should be used on function declarations +// following the argument list: +// +// Sprocket* AllocateSprocket() GTEST_MUST_USE_RESULT_; +#if GTEST_HAVE_ATTRIBUTE_(warn_unused_result) +#define GTEST_MUST_USE_RESULT_ __attribute__((warn_unused_result)) +#else +#define GTEST_MUST_USE_RESULT_ +#endif + +// MS C++ compiler emits warning when a conditional expression is compile time +// constant. In some contexts this warning is false positive and needs to be +// suppressed. Use the following two macros in such cases: +// +// GTEST_INTENTIONAL_CONST_COND_PUSH_() +// while (true) { +// GTEST_INTENTIONAL_CONST_COND_POP_() +// } +#define GTEST_INTENTIONAL_CONST_COND_PUSH_() \ + GTEST_DISABLE_MSC_WARNINGS_PUSH_(4127) +#define GTEST_INTENTIONAL_CONST_COND_POP_() GTEST_DISABLE_MSC_WARNINGS_POP_() + +// Determine whether the compiler supports Microsoft's Structured Exception +// Handling. This is supported by several Windows compilers but generally +// does not exist on any other system. +#ifndef GTEST_HAS_SEH +// The user didn't tell us, so we need to figure it out. + +#if defined(_MSC_VER) || defined(__BORLANDC__) +// These two compilers are known to support SEH. +#define GTEST_HAS_SEH 1 +#else +// Assume no SEH. +#define GTEST_HAS_SEH 0 +#endif + +#endif // GTEST_HAS_SEH + +#ifndef GTEST_IS_THREADSAFE + +#define GTEST_IS_THREADSAFE \ + (GTEST_HAS_MUTEX_AND_THREAD_LOCAL_ || \ + (GTEST_OS_WINDOWS && !GTEST_OS_WINDOWS_PHONE && !GTEST_OS_WINDOWS_RT) || \ + GTEST_HAS_PTHREAD) + +#endif // GTEST_IS_THREADSAFE + +#if GTEST_IS_THREADSAFE +// Some platforms don't support including these threading related headers. +#include // NOLINT +#include // NOLINT +#endif // GTEST_IS_THREADSAFE + +// GTEST_API_ qualifies all symbols that must be exported. The definitions below +// are guarded by #ifndef to give embedders a chance to define GTEST_API_ in +// gtest/internal/custom/gtest-port.h +#ifndef GTEST_API_ + +#ifdef _MSC_VER +#if GTEST_LINKED_AS_SHARED_LIBRARY +#define GTEST_API_ __declspec(dllimport) +#elif GTEST_CREATE_SHARED_LIBRARY +#define GTEST_API_ __declspec(dllexport) +#endif +#elif GTEST_HAVE_ATTRIBUTE_(visibility) +#define GTEST_API_ __attribute__((visibility("default"))) +#endif // _MSC_VER + +#endif // GTEST_API_ + +#ifndef GTEST_API_ +#define GTEST_API_ +#endif // GTEST_API_ + +#ifndef GTEST_DEFAULT_DEATH_TEST_STYLE +#define GTEST_DEFAULT_DEATH_TEST_STYLE "fast" +#endif // GTEST_DEFAULT_DEATH_TEST_STYLE + +#if GTEST_HAVE_ATTRIBUTE_(noinline) +// Ask the compiler to never inline a given function. +#define GTEST_NO_INLINE_ __attribute__((noinline)) +#else +#define GTEST_NO_INLINE_ +#endif + +#if GTEST_HAVE_ATTRIBUTE_(disable_tail_calls) +// Ask the compiler not to perform tail call optimization inside +// the marked function. +#define GTEST_NO_TAIL_CALL_ __attribute__((disable_tail_calls)) +#elif __GNUC__ +#define GTEST_NO_TAIL_CALL_ \ + __attribute__((optimize("no-optimize-sibling-calls"))) +#else +#define GTEST_NO_TAIL_CALL_ +#endif + +// _LIBCPP_VERSION is defined by the libc++ library from the LLVM project. +#if !defined(GTEST_HAS_CXXABI_H_) +#if defined(__GLIBCXX__) || (defined(_LIBCPP_VERSION) && !defined(_MSC_VER)) +#define GTEST_HAS_CXXABI_H_ 1 +#else +#define GTEST_HAS_CXXABI_H_ 0 +#endif +#endif + +// A function level attribute to disable checking for use of uninitialized +// memory when built with MemorySanitizer. +#if GTEST_HAVE_ATTRIBUTE_(no_sanitize_memory) +#define GTEST_ATTRIBUTE_NO_SANITIZE_MEMORY_ __attribute__((no_sanitize_memory)) +#else +#define GTEST_ATTRIBUTE_NO_SANITIZE_MEMORY_ +#endif + +// A function level attribute to disable AddressSanitizer instrumentation. +#if GTEST_HAVE_ATTRIBUTE_(no_sanitize_address) +#define GTEST_ATTRIBUTE_NO_SANITIZE_ADDRESS_ \ + __attribute__((no_sanitize_address)) +#else +#define GTEST_ATTRIBUTE_NO_SANITIZE_ADDRESS_ +#endif + +// A function level attribute to disable HWAddressSanitizer instrumentation. +#if GTEST_HAVE_FEATURE_(hwaddress_sanitizer) && \ + GTEST_HAVE_ATTRIBUTE_(no_sanitize) +#define GTEST_ATTRIBUTE_NO_SANITIZE_HWADDRESS_ \ + __attribute__((no_sanitize("hwaddress"))) +#else +#define GTEST_ATTRIBUTE_NO_SANITIZE_HWADDRESS_ +#endif + +// A function level attribute to disable ThreadSanitizer instrumentation. +#if GTEST_HAVE_ATTRIBUTE_(no_sanitize_thread) +#define GTEST_ATTRIBUTE_NO_SANITIZE_THREAD_ __attribute((no_sanitize_thread)) +#else +#define GTEST_ATTRIBUTE_NO_SANITIZE_THREAD_ +#endif + +namespace testing { + +class Message; + +// Legacy imports for backwards compatibility. +// New code should use std:: names directly. +using std::get; +using std::make_tuple; +using std::tuple; +using std::tuple_element; +using std::tuple_size; + +namespace internal { + +// A secret type that Google Test users don't know about. It has no +// definition on purpose. Therefore it's impossible to create a +// Secret object, which is what we want. +class Secret; + +// A helper for suppressing warnings on constant condition. It just +// returns 'condition'. +GTEST_API_ bool IsTrue(bool condition); + +// Defines RE. + +#if GTEST_USES_RE2 + +// This is almost `using RE = ::RE2`, except it is copy-constructible, and it +// needs to disambiguate the `std::string`, `absl::string_view`, and `const +// char*` constructors. +class GTEST_API_ RE { + public: + RE(absl::string_view regex) : regex_(regex) {} // NOLINT + RE(const char* regex) : RE(absl::string_view(regex)) {} // NOLINT + RE(const std::string& regex) : RE(absl::string_view(regex)) {} // NOLINT + RE(const RE& other) : RE(other.pattern()) {} + + const std::string& pattern() const { return regex_.pattern(); } + + static bool FullMatch(absl::string_view str, const RE& re) { + return RE2::FullMatch(str, re.regex_); + } + static bool PartialMatch(absl::string_view str, const RE& re) { + return RE2::PartialMatch(str, re.regex_); + } + + private: + RE2 regex_; +}; + +#elif GTEST_USES_POSIX_RE || GTEST_USES_SIMPLE_RE + +// A simple C++ wrapper for . It uses the POSIX Extended +// Regular Expression syntax. +class GTEST_API_ RE { + public: + // A copy constructor is required by the Standard to initialize object + // references from r-values. + RE(const RE& other) { Init(other.pattern()); } + + // Constructs an RE from a string. + RE(const ::std::string& regex) { Init(regex.c_str()); } // NOLINT + + RE(const char* regex) { Init(regex); } // NOLINT + ~RE(); + + // Returns the string representation of the regex. + const char* pattern() const { return pattern_; } + + // FullMatch(str, re) returns true if and only if regular expression re + // matches the entire str. + // PartialMatch(str, re) returns true if and only if regular expression re + // matches a substring of str (including str itself). + static bool FullMatch(const ::std::string& str, const RE& re) { + return FullMatch(str.c_str(), re); + } + static bool PartialMatch(const ::std::string& str, const RE& re) { + return PartialMatch(str.c_str(), re); + } + + static bool FullMatch(const char* str, const RE& re); + static bool PartialMatch(const char* str, const RE& re); + + private: + void Init(const char* regex); + const char* pattern_; + bool is_valid_; + +#if GTEST_USES_POSIX_RE + + regex_t full_regex_; // For FullMatch(). + regex_t partial_regex_; // For PartialMatch(). + +#else // GTEST_USES_SIMPLE_RE + + const char* full_pattern_; // For FullMatch(); + +#endif +}; + +#endif // ::testing::internal::RE implementation + +// Formats a source file path and a line number as they would appear +// in an error message from the compiler used to compile this code. +GTEST_API_ ::std::string FormatFileLocation(const char* file, int line); + +// Formats a file location for compiler-independent XML output. +// Although this function is not platform dependent, we put it next to +// FormatFileLocation in order to contrast the two functions. +GTEST_API_ ::std::string FormatCompilerIndependentFileLocation(const char* file, + int line); + +// Defines logging utilities: +// GTEST_LOG_(severity) - logs messages at the specified severity level. The +// message itself is streamed into the macro. +// LogToStderr() - directs all log messages to stderr. +// FlushInfoLog() - flushes informational log messages. + +enum GTestLogSeverity { GTEST_INFO, GTEST_WARNING, GTEST_ERROR, GTEST_FATAL }; + +// Formats log entry severity, provides a stream object for streaming the +// log message, and terminates the message with a newline when going out of +// scope. +class GTEST_API_ GTestLog { + public: + GTestLog(GTestLogSeverity severity, const char* file, int line); + + // Flushes the buffers and, if severity is GTEST_FATAL, aborts the program. + ~GTestLog(); + + ::std::ostream& GetStream() { return ::std::cerr; } + + private: + const GTestLogSeverity severity_; + + GTestLog(const GTestLog&) = delete; + GTestLog& operator=(const GTestLog&) = delete; +}; + +#if !defined(GTEST_LOG_) + +#define GTEST_LOG_(severity) \ + ::testing::internal::GTestLog(::testing::internal::GTEST_##severity, \ + __FILE__, __LINE__) \ + .GetStream() + +inline void LogToStderr() {} +inline void FlushInfoLog() { fflush(nullptr); } + +#endif // !defined(GTEST_LOG_) + +#if !defined(GTEST_CHECK_) +// INTERNAL IMPLEMENTATION - DO NOT USE. +// +// GTEST_CHECK_ is an all-mode assert. It aborts the program if the condition +// is not satisfied. +// Synopsis: +// GTEST_CHECK_(boolean_condition); +// or +// GTEST_CHECK_(boolean_condition) << "Additional message"; +// +// This checks the condition and if the condition is not satisfied +// it prints message about the condition violation, including the +// condition itself, plus additional message streamed into it, if any, +// and then it aborts the program. It aborts the program irrespective of +// whether it is built in the debug mode or not. +#define GTEST_CHECK_(condition) \ + GTEST_AMBIGUOUS_ELSE_BLOCKER_ \ + if (::testing::internal::IsTrue(condition)) \ + ; \ + else \ + GTEST_LOG_(FATAL) << "Condition " #condition " failed. " +#endif // !defined(GTEST_CHECK_) + +// An all-mode assert to verify that the given POSIX-style function +// call returns 0 (indicating success). Known limitation: this +// doesn't expand to a balanced 'if' statement, so enclose the macro +// in {} if you need to use it as the only statement in an 'if' +// branch. +#define GTEST_CHECK_POSIX_SUCCESS_(posix_call) \ + if (const int gtest_error = (posix_call)) \ + GTEST_LOG_(FATAL) << #posix_call << "failed with error " << gtest_error + +// Transforms "T" into "const T&" according to standard reference collapsing +// rules (this is only needed as a backport for C++98 compilers that do not +// support reference collapsing). Specifically, it transforms: +// +// char ==> const char& +// const char ==> const char& +// char& ==> char& +// const char& ==> const char& +// +// Note that the non-const reference will not have "const" added. This is +// standard, and necessary so that "T" can always bind to "const T&". +template +struct ConstRef { + typedef const T& type; +}; +template +struct ConstRef { + typedef T& type; +}; + +// The argument T must depend on some template parameters. +#define GTEST_REFERENCE_TO_CONST_(T) \ + typename ::testing::internal::ConstRef::type + +// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE. +// +// Use ImplicitCast_ as a safe version of static_cast for upcasting in +// the type hierarchy (e.g. casting a Foo* to a SuperclassOfFoo* or a +// const Foo*). When you use ImplicitCast_, the compiler checks that +// the cast is safe. Such explicit ImplicitCast_s are necessary in +// surprisingly many situations where C++ demands an exact type match +// instead of an argument type convertible to a target type. +// +// The syntax for using ImplicitCast_ is the same as for static_cast: +// +// ImplicitCast_(expr) +// +// ImplicitCast_ would have been part of the C++ standard library, +// but the proposal was submitted too late. It will probably make +// its way into the language in the future. +// +// This relatively ugly name is intentional. It prevents clashes with +// similar functions users may have (e.g., implicit_cast). The internal +// namespace alone is not enough because the function can be found by ADL. +template +inline To ImplicitCast_(To x) { + return x; +} + +// When you upcast (that is, cast a pointer from type Foo to type +// SuperclassOfFoo), it's fine to use ImplicitCast_<>, since upcasts +// always succeed. When you downcast (that is, cast a pointer from +// type Foo to type SubclassOfFoo), static_cast<> isn't safe, because +// how do you know the pointer is really of type SubclassOfFoo? It +// could be a bare Foo, or of type DifferentSubclassOfFoo. Thus, +// when you downcast, you should use this macro. In debug mode, we +// use dynamic_cast<> to double-check the downcast is legal (we die +// if it's not). In normal mode, we do the efficient static_cast<> +// instead. Thus, it's important to test in debug mode to make sure +// the cast is legal! +// This is the only place in the code we should use dynamic_cast<>. +// In particular, you SHOULDN'T be using dynamic_cast<> in order to +// do RTTI (eg code like this: +// if (dynamic_cast(foo)) HandleASubclass1Object(foo); +// if (dynamic_cast(foo)) HandleASubclass2Object(foo); +// You should design the code some other way not to need this. +// +// This relatively ugly name is intentional. It prevents clashes with +// similar functions users may have (e.g., down_cast). The internal +// namespace alone is not enough because the function can be found by ADL. +template // use like this: DownCast_(foo); +inline To DownCast_(From* f) { // so we only accept pointers + // Ensures that To is a sub-type of From *. This test is here only + // for compile-time type checking, and has no overhead in an + // optimized build at run-time, as it will be optimized away + // completely. + GTEST_INTENTIONAL_CONST_COND_PUSH_() + if (false) { + GTEST_INTENTIONAL_CONST_COND_POP_() + const To to = nullptr; + ::testing::internal::ImplicitCast_(to); + } + +#if GTEST_HAS_RTTI + // RTTI: debug mode only! + GTEST_CHECK_(f == nullptr || dynamic_cast(f) != nullptr); +#endif + return static_cast(f); +} + +// Downcasts the pointer of type Base to Derived. +// Derived must be a subclass of Base. The parameter MUST +// point to a class of type Derived, not any subclass of it. +// When RTTI is available, the function performs a runtime +// check to enforce this. +template +Derived* CheckedDowncastToActualType(Base* base) { +#if GTEST_HAS_RTTI + GTEST_CHECK_(typeid(*base) == typeid(Derived)); +#endif + +#if GTEST_HAS_DOWNCAST_ + return ::down_cast(base); +#elif GTEST_HAS_RTTI + return dynamic_cast(base); // NOLINT +#else + return static_cast(base); // Poor man's downcast. +#endif +} + +#if GTEST_HAS_STREAM_REDIRECTION + +// Defines the stderr capturer: +// CaptureStdout - starts capturing stdout. +// GetCapturedStdout - stops capturing stdout and returns the captured string. +// CaptureStderr - starts capturing stderr. +// GetCapturedStderr - stops capturing stderr and returns the captured string. +// +GTEST_API_ void CaptureStdout(); +GTEST_API_ std::string GetCapturedStdout(); +GTEST_API_ void CaptureStderr(); +GTEST_API_ std::string GetCapturedStderr(); + +#endif // GTEST_HAS_STREAM_REDIRECTION +// Returns the size (in bytes) of a file. +GTEST_API_ size_t GetFileSize(FILE* file); + +// Reads the entire content of a file as a string. +GTEST_API_ std::string ReadEntireFile(FILE* file); + +// All command line arguments. +GTEST_API_ std::vector GetArgvs(); + +#if GTEST_HAS_DEATH_TEST + +std::vector GetInjectableArgvs(); +// Deprecated: pass the args vector by value instead. +void SetInjectableArgvs(const std::vector* new_argvs); +void SetInjectableArgvs(const std::vector& new_argvs); +void ClearInjectableArgvs(); + +#endif // GTEST_HAS_DEATH_TEST + +// Defines synchronization primitives. +#if GTEST_IS_THREADSAFE + +#if GTEST_OS_WINDOWS +// Provides leak-safe Windows kernel handle ownership. +// Used in death tests and in threading support. +class GTEST_API_ AutoHandle { + public: + // Assume that Win32 HANDLE type is equivalent to void*. Doing so allows us to + // avoid including in this header file. Including is + // undesirable because it defines a lot of symbols and macros that tend to + // conflict with client code. This assumption is verified by + // WindowsTypesTest.HANDLEIsVoidStar. + typedef void* Handle; + AutoHandle(); + explicit AutoHandle(Handle handle); + + ~AutoHandle(); + + Handle Get() const; + void Reset(); + void Reset(Handle handle); + + private: + // Returns true if and only if the handle is a valid handle object that can be + // closed. + bool IsCloseable() const; + + Handle handle_; + + AutoHandle(const AutoHandle&) = delete; + AutoHandle& operator=(const AutoHandle&) = delete; +}; +#endif + +#if GTEST_HAS_NOTIFICATION_ +// Notification has already been imported into the namespace. +// Nothing to do here. + +#else +GTEST_DISABLE_MSC_WARNINGS_PUSH_(4251 \ +/* class A needs to have dll-interface to be used by clients of class B */) + +// Allows a controller thread to pause execution of newly created +// threads until notified. Instances of this class must be created +// and destroyed in the controller thread. +// +// This class is only for testing Google Test's own constructs. Do not +// use it in user tests, either directly or indirectly. +// TODO(b/203539622): Replace unconditionally with absl::Notification. +class GTEST_API_ Notification { + public: + Notification() : notified_(false) {} + Notification(const Notification&) = delete; + Notification& operator=(const Notification&) = delete; + + // Notifies all threads created with this notification to start. Must + // be called from the controller thread. + void Notify() { + std::lock_guard lock(mu_); + notified_ = true; + cv_.notify_all(); + } + + // Blocks until the controller thread notifies. Must be called from a test + // thread. + void WaitForNotification() { + std::unique_lock lock(mu_); + cv_.wait(lock, [this]() { return notified_; }); + } + + private: + std::mutex mu_; + std::condition_variable cv_; + bool notified_; +}; +GTEST_DISABLE_MSC_WARNINGS_POP_() // 4251 +#endif // GTEST_HAS_NOTIFICATION_ + +// On MinGW, we can have both GTEST_OS_WINDOWS and GTEST_HAS_PTHREAD +// defined, but we don't want to use MinGW's pthreads implementation, which +// has conformance problems with some versions of the POSIX standard. +#if GTEST_HAS_PTHREAD && !GTEST_OS_WINDOWS_MINGW + +// As a C-function, ThreadFuncWithCLinkage cannot be templated itself. +// Consequently, it cannot select a correct instantiation of ThreadWithParam +// in order to call its Run(). Introducing ThreadWithParamBase as a +// non-templated base class for ThreadWithParam allows us to bypass this +// problem. +class ThreadWithParamBase { + public: + virtual ~ThreadWithParamBase() {} + virtual void Run() = 0; +}; + +// pthread_create() accepts a pointer to a function type with the C linkage. +// According to the Standard (7.5/1), function types with different linkages +// are different even if they are otherwise identical. Some compilers (for +// example, SunStudio) treat them as different types. Since class methods +// cannot be defined with C-linkage we need to define a free C-function to +// pass into pthread_create(). +extern "C" inline void* ThreadFuncWithCLinkage(void* thread) { + static_cast(thread)->Run(); + return nullptr; +} + +// Helper class for testing Google Test's multi-threading constructs. +// To use it, write: +// +// void ThreadFunc(int param) { /* Do things with param */ } +// Notification thread_can_start; +// ... +// // The thread_can_start parameter is optional; you can supply NULL. +// ThreadWithParam thread(&ThreadFunc, 5, &thread_can_start); +// thread_can_start.Notify(); +// +// These classes are only for testing Google Test's own constructs. Do +// not use them in user tests, either directly or indirectly. +template +class ThreadWithParam : public ThreadWithParamBase { + public: + typedef void UserThreadFunc(T); + + ThreadWithParam(UserThreadFunc* func, T param, Notification* thread_can_start) + : func_(func), + param_(param), + thread_can_start_(thread_can_start), + finished_(false) { + ThreadWithParamBase* const base = this; + // The thread can be created only after all fields except thread_ + // have been initialized. + GTEST_CHECK_POSIX_SUCCESS_( + pthread_create(&thread_, nullptr, &ThreadFuncWithCLinkage, base)); + } + ~ThreadWithParam() override { Join(); } + + void Join() { + if (!finished_) { + GTEST_CHECK_POSIX_SUCCESS_(pthread_join(thread_, nullptr)); + finished_ = true; + } + } + + void Run() override { + if (thread_can_start_ != nullptr) thread_can_start_->WaitForNotification(); + func_(param_); + } + + private: + UserThreadFunc* const func_; // User-supplied thread function. + const T param_; // User-supplied parameter to the thread function. + // When non-NULL, used to block execution until the controller thread + // notifies. + Notification* const thread_can_start_; + bool finished_; // true if and only if we know that the thread function has + // finished. + pthread_t thread_; // The native thread object. + + ThreadWithParam(const ThreadWithParam&) = delete; + ThreadWithParam& operator=(const ThreadWithParam&) = delete; +}; +#endif // !GTEST_OS_WINDOWS && GTEST_HAS_PTHREAD || + // GTEST_HAS_MUTEX_AND_THREAD_LOCAL_ + +#if GTEST_HAS_MUTEX_AND_THREAD_LOCAL_ +// Mutex and ThreadLocal have already been imported into the namespace. +// Nothing to do here. + +#elif GTEST_OS_WINDOWS && !GTEST_OS_WINDOWS_PHONE && !GTEST_OS_WINDOWS_RT + +// Mutex implements mutex on Windows platforms. It is used in conjunction +// with class MutexLock: +// +// Mutex mutex; +// ... +// MutexLock lock(&mutex); // Acquires the mutex and releases it at the +// // end of the current scope. +// +// A static Mutex *must* be defined or declared using one of the following +// macros: +// GTEST_DEFINE_STATIC_MUTEX_(g_some_mutex); +// GTEST_DECLARE_STATIC_MUTEX_(g_some_mutex); +// +// (A non-static Mutex is defined/declared in the usual way). +class GTEST_API_ Mutex { + public: + enum MutexType { kStatic = 0, kDynamic = 1 }; + // We rely on kStaticMutex being 0 as it is to what the linker initializes + // type_ in static mutexes. critical_section_ will be initialized lazily + // in ThreadSafeLazyInit(). + enum StaticConstructorSelector { kStaticMutex = 0 }; + + // This constructor intentionally does nothing. It relies on type_ being + // statically initialized to 0 (effectively setting it to kStatic) and on + // ThreadSafeLazyInit() to lazily initialize the rest of the members. + explicit Mutex(StaticConstructorSelector /*dummy*/) {} + + Mutex(); + ~Mutex(); + + void Lock(); + + void Unlock(); + + // Does nothing if the current thread holds the mutex. Otherwise, crashes + // with high probability. + void AssertHeld(); + + private: + // Initializes owner_thread_id_ and critical_section_ in static mutexes. + void ThreadSafeLazyInit(); + + // Per https://blogs.msdn.microsoft.com/oldnewthing/20040223-00/?p=40503, + // we assume that 0 is an invalid value for thread IDs. + unsigned int owner_thread_id_; + + // For static mutexes, we rely on these members being initialized to zeros + // by the linker. + MutexType type_; + long critical_section_init_phase_; // NOLINT + GTEST_CRITICAL_SECTION* critical_section_; + + Mutex(const Mutex&) = delete; + Mutex& operator=(const Mutex&) = delete; +}; + +#define GTEST_DECLARE_STATIC_MUTEX_(mutex) \ + extern ::testing::internal::Mutex mutex + +#define GTEST_DEFINE_STATIC_MUTEX_(mutex) \ + ::testing::internal::Mutex mutex(::testing::internal::Mutex::kStaticMutex) + +// We cannot name this class MutexLock because the ctor declaration would +// conflict with a macro named MutexLock, which is defined on some +// platforms. That macro is used as a defensive measure to prevent against +// inadvertent misuses of MutexLock like "MutexLock(&mu)" rather than +// "MutexLock l(&mu)". Hence the typedef trick below. +class GTestMutexLock { + public: + explicit GTestMutexLock(Mutex* mutex) : mutex_(mutex) { mutex_->Lock(); } + + ~GTestMutexLock() { mutex_->Unlock(); } + + private: + Mutex* const mutex_; + + GTestMutexLock(const GTestMutexLock&) = delete; + GTestMutexLock& operator=(const GTestMutexLock&) = delete; +}; + +typedef GTestMutexLock MutexLock; + +// Base class for ValueHolder. Allows a caller to hold and delete a value +// without knowing its type. +class ThreadLocalValueHolderBase { + public: + virtual ~ThreadLocalValueHolderBase() {} +}; + +// Provides a way for a thread to send notifications to a ThreadLocal +// regardless of its parameter type. +class ThreadLocalBase { + public: + // Creates a new ValueHolder object holding a default value passed to + // this ThreadLocal's constructor and returns it. It is the caller's + // responsibility not to call this when the ThreadLocal instance already + // has a value on the current thread. + virtual ThreadLocalValueHolderBase* NewValueForCurrentThread() const = 0; + + protected: + ThreadLocalBase() {} + virtual ~ThreadLocalBase() {} + + private: + ThreadLocalBase(const ThreadLocalBase&) = delete; + ThreadLocalBase& operator=(const ThreadLocalBase&) = delete; +}; + +// Maps a thread to a set of ThreadLocals that have values instantiated on that +// thread and notifies them when the thread exits. A ThreadLocal instance is +// expected to persist until all threads it has values on have terminated. +class GTEST_API_ ThreadLocalRegistry { + public: + // Registers thread_local_instance as having value on the current thread. + // Returns a value that can be used to identify the thread from other threads. + static ThreadLocalValueHolderBase* GetValueOnCurrentThread( + const ThreadLocalBase* thread_local_instance); + + // Invoked when a ThreadLocal instance is destroyed. + static void OnThreadLocalDestroyed( + const ThreadLocalBase* thread_local_instance); +}; + +class GTEST_API_ ThreadWithParamBase { + public: + void Join(); + + protected: + class Runnable { + public: + virtual ~Runnable() {} + virtual void Run() = 0; + }; + + ThreadWithParamBase(Runnable* runnable, Notification* thread_can_start); + virtual ~ThreadWithParamBase(); + + private: + AutoHandle thread_; +}; + +// Helper class for testing Google Test's multi-threading constructs. +template +class ThreadWithParam : public ThreadWithParamBase { + public: + typedef void UserThreadFunc(T); + + ThreadWithParam(UserThreadFunc* func, T param, Notification* thread_can_start) + : ThreadWithParamBase(new RunnableImpl(func, param), thread_can_start) {} + virtual ~ThreadWithParam() {} + + private: + class RunnableImpl : public Runnable { + public: + RunnableImpl(UserThreadFunc* func, T param) : func_(func), param_(param) {} + virtual ~RunnableImpl() {} + virtual void Run() { func_(param_); } + + private: + UserThreadFunc* const func_; + const T param_; + + RunnableImpl(const RunnableImpl&) = delete; + RunnableImpl& operator=(const RunnableImpl&) = delete; + }; + + ThreadWithParam(const ThreadWithParam&) = delete; + ThreadWithParam& operator=(const ThreadWithParam&) = delete; +}; + +// Implements thread-local storage on Windows systems. +// +// // Thread 1 +// ThreadLocal tl(100); // 100 is the default value for each thread. +// +// // Thread 2 +// tl.set(150); // Changes the value for thread 2 only. +// EXPECT_EQ(150, tl.get()); +// +// // Thread 1 +// EXPECT_EQ(100, tl.get()); // In thread 1, tl has the original value. +// tl.set(200); +// EXPECT_EQ(200, tl.get()); +// +// The template type argument T must have a public copy constructor. +// In addition, the default ThreadLocal constructor requires T to have +// a public default constructor. +// +// The users of a TheadLocal instance have to make sure that all but one +// threads (including the main one) using that instance have exited before +// destroying it. Otherwise, the per-thread objects managed for them by the +// ThreadLocal instance are not guaranteed to be destroyed on all platforms. +// +// Google Test only uses global ThreadLocal objects. That means they +// will die after main() has returned. Therefore, no per-thread +// object managed by Google Test will be leaked as long as all threads +// using Google Test have exited when main() returns. +template +class ThreadLocal : public ThreadLocalBase { + public: + ThreadLocal() : default_factory_(new DefaultValueHolderFactory()) {} + explicit ThreadLocal(const T& value) + : default_factory_(new InstanceValueHolderFactory(value)) {} + + ~ThreadLocal() override { ThreadLocalRegistry::OnThreadLocalDestroyed(this); } + + T* pointer() { return GetOrCreateValue(); } + const T* pointer() const { return GetOrCreateValue(); } + const T& get() const { return *pointer(); } + void set(const T& value) { *pointer() = value; } + + private: + // Holds a value of T. Can be deleted via its base class without the caller + // knowing the type of T. + class ValueHolder : public ThreadLocalValueHolderBase { + public: + ValueHolder() : value_() {} + explicit ValueHolder(const T& value) : value_(value) {} + + T* pointer() { return &value_; } + + private: + T value_; + ValueHolder(const ValueHolder&) = delete; + ValueHolder& operator=(const ValueHolder&) = delete; + }; + + T* GetOrCreateValue() const { + return static_cast( + ThreadLocalRegistry::GetValueOnCurrentThread(this)) + ->pointer(); + } + + ThreadLocalValueHolderBase* NewValueForCurrentThread() const override { + return default_factory_->MakeNewHolder(); + } + + class ValueHolderFactory { + public: + ValueHolderFactory() {} + virtual ~ValueHolderFactory() {} + virtual ValueHolder* MakeNewHolder() const = 0; + + private: + ValueHolderFactory(const ValueHolderFactory&) = delete; + ValueHolderFactory& operator=(const ValueHolderFactory&) = delete; + }; + + class DefaultValueHolderFactory : public ValueHolderFactory { + public: + DefaultValueHolderFactory() {} + ValueHolder* MakeNewHolder() const override { return new ValueHolder(); } + + private: + DefaultValueHolderFactory(const DefaultValueHolderFactory&) = delete; + DefaultValueHolderFactory& operator=(const DefaultValueHolderFactory&) = + delete; + }; + + class InstanceValueHolderFactory : public ValueHolderFactory { + public: + explicit InstanceValueHolderFactory(const T& value) : value_(value) {} + ValueHolder* MakeNewHolder() const override { + return new ValueHolder(value_); + } + + private: + const T value_; // The value for each thread. + + InstanceValueHolderFactory(const InstanceValueHolderFactory&) = delete; + InstanceValueHolderFactory& operator=(const InstanceValueHolderFactory&) = + delete; + }; + + std::unique_ptr default_factory_; + + ThreadLocal(const ThreadLocal&) = delete; + ThreadLocal& operator=(const ThreadLocal&) = delete; +}; + +#elif GTEST_HAS_PTHREAD + +// MutexBase and Mutex implement mutex on pthreads-based platforms. +class MutexBase { + public: + // Acquires this mutex. + void Lock() { + GTEST_CHECK_POSIX_SUCCESS_(pthread_mutex_lock(&mutex_)); + owner_ = pthread_self(); + has_owner_ = true; + } + + // Releases this mutex. + void Unlock() { + // Since the lock is being released the owner_ field should no longer be + // considered valid. We don't protect writing to has_owner_ here, as it's + // the caller's responsibility to ensure that the current thread holds the + // mutex when this is called. + has_owner_ = false; + GTEST_CHECK_POSIX_SUCCESS_(pthread_mutex_unlock(&mutex_)); + } + + // Does nothing if the current thread holds the mutex. Otherwise, crashes + // with high probability. + void AssertHeld() const { + GTEST_CHECK_(has_owner_ && pthread_equal(owner_, pthread_self())) + << "The current thread is not holding the mutex @" << this; + } + + // A static mutex may be used before main() is entered. It may even + // be used before the dynamic initialization stage. Therefore we + // must be able to initialize a static mutex object at link time. + // This means MutexBase has to be a POD and its member variables + // have to be public. + public: + pthread_mutex_t mutex_; // The underlying pthread mutex. + // has_owner_ indicates whether the owner_ field below contains a valid thread + // ID and is therefore safe to inspect (e.g., to use in pthread_equal()). All + // accesses to the owner_ field should be protected by a check of this field. + // An alternative might be to memset() owner_ to all zeros, but there's no + // guarantee that a zero'd pthread_t is necessarily invalid or even different + // from pthread_self(). + bool has_owner_; + pthread_t owner_; // The thread holding the mutex. +}; + +// Forward-declares a static mutex. +#define GTEST_DECLARE_STATIC_MUTEX_(mutex) \ + extern ::testing::internal::MutexBase mutex + +// Defines and statically (i.e. at link time) initializes a static mutex. +// The initialization list here does not explicitly initialize each field, +// instead relying on default initialization for the unspecified fields. In +// particular, the owner_ field (a pthread_t) is not explicitly initialized. +// This allows initialization to work whether pthread_t is a scalar or struct. +// The flag -Wmissing-field-initializers must not be specified for this to work. +#define GTEST_DEFINE_STATIC_MUTEX_(mutex) \ + ::testing::internal::MutexBase mutex = {PTHREAD_MUTEX_INITIALIZER, false, 0} + +// The Mutex class can only be used for mutexes created at runtime. It +// shares its API with MutexBase otherwise. +class Mutex : public MutexBase { + public: + Mutex() { + GTEST_CHECK_POSIX_SUCCESS_(pthread_mutex_init(&mutex_, nullptr)); + has_owner_ = false; + } + ~Mutex() { GTEST_CHECK_POSIX_SUCCESS_(pthread_mutex_destroy(&mutex_)); } + + private: + Mutex(const Mutex&) = delete; + Mutex& operator=(const Mutex&) = delete; +}; + +// We cannot name this class MutexLock because the ctor declaration would +// conflict with a macro named MutexLock, which is defined on some +// platforms. That macro is used as a defensive measure to prevent against +// inadvertent misuses of MutexLock like "MutexLock(&mu)" rather than +// "MutexLock l(&mu)". Hence the typedef trick below. +class GTestMutexLock { + public: + explicit GTestMutexLock(MutexBase* mutex) : mutex_(mutex) { mutex_->Lock(); } + + ~GTestMutexLock() { mutex_->Unlock(); } + + private: + MutexBase* const mutex_; + + GTestMutexLock(const GTestMutexLock&) = delete; + GTestMutexLock& operator=(const GTestMutexLock&) = delete; +}; + +typedef GTestMutexLock MutexLock; + +// Helpers for ThreadLocal. + +// pthread_key_create() requires DeleteThreadLocalValue() to have +// C-linkage. Therefore it cannot be templatized to access +// ThreadLocal. Hence the need for class +// ThreadLocalValueHolderBase. +class GTEST_API_ ThreadLocalValueHolderBase { + public: + virtual ~ThreadLocalValueHolderBase() {} +}; + +// Called by pthread to delete thread-local data stored by +// pthread_setspecific(). +extern "C" inline void DeleteThreadLocalValue(void* value_holder) { + delete static_cast(value_holder); +} + +// Implements thread-local storage on pthreads-based systems. +template +class GTEST_API_ ThreadLocal { + public: + ThreadLocal() + : key_(CreateKey()), default_factory_(new DefaultValueHolderFactory()) {} + explicit ThreadLocal(const T& value) + : key_(CreateKey()), + default_factory_(new InstanceValueHolderFactory(value)) {} + + ~ThreadLocal() { + // Destroys the managed object for the current thread, if any. + DeleteThreadLocalValue(pthread_getspecific(key_)); + + // Releases resources associated with the key. This will *not* + // delete managed objects for other threads. + GTEST_CHECK_POSIX_SUCCESS_(pthread_key_delete(key_)); + } + + T* pointer() { return GetOrCreateValue(); } + const T* pointer() const { return GetOrCreateValue(); } + const T& get() const { return *pointer(); } + void set(const T& value) { *pointer() = value; } + + private: + // Holds a value of type T. + class ValueHolder : public ThreadLocalValueHolderBase { + public: + ValueHolder() : value_() {} + explicit ValueHolder(const T& value) : value_(value) {} + + T* pointer() { return &value_; } + + private: + T value_; + ValueHolder(const ValueHolder&) = delete; + ValueHolder& operator=(const ValueHolder&) = delete; + }; + + static pthread_key_t CreateKey() { + pthread_key_t key; + // When a thread exits, DeleteThreadLocalValue() will be called on + // the object managed for that thread. + GTEST_CHECK_POSIX_SUCCESS_( + pthread_key_create(&key, &DeleteThreadLocalValue)); + return key; + } + + T* GetOrCreateValue() const { + ThreadLocalValueHolderBase* const holder = + static_cast(pthread_getspecific(key_)); + if (holder != nullptr) { + return CheckedDowncastToActualType(holder)->pointer(); + } + + ValueHolder* const new_holder = default_factory_->MakeNewHolder(); + ThreadLocalValueHolderBase* const holder_base = new_holder; + GTEST_CHECK_POSIX_SUCCESS_(pthread_setspecific(key_, holder_base)); + return new_holder->pointer(); + } + + class ValueHolderFactory { + public: + ValueHolderFactory() {} + virtual ~ValueHolderFactory() {} + virtual ValueHolder* MakeNewHolder() const = 0; + + private: + ValueHolderFactory(const ValueHolderFactory&) = delete; + ValueHolderFactory& operator=(const ValueHolderFactory&) = delete; + }; + + class DefaultValueHolderFactory : public ValueHolderFactory { + public: + DefaultValueHolderFactory() {} + ValueHolder* MakeNewHolder() const override { return new ValueHolder(); } + + private: + DefaultValueHolderFactory(const DefaultValueHolderFactory&) = delete; + DefaultValueHolderFactory& operator=(const DefaultValueHolderFactory&) = + delete; + }; + + class InstanceValueHolderFactory : public ValueHolderFactory { + public: + explicit InstanceValueHolderFactory(const T& value) : value_(value) {} + ValueHolder* MakeNewHolder() const override { + return new ValueHolder(value_); + } + + private: + const T value_; // The value for each thread. + + InstanceValueHolderFactory(const InstanceValueHolderFactory&) = delete; + InstanceValueHolderFactory& operator=(const InstanceValueHolderFactory&) = + delete; + }; + + // A key pthreads uses for looking up per-thread values. + const pthread_key_t key_; + std::unique_ptr default_factory_; + + ThreadLocal(const ThreadLocal&) = delete; + ThreadLocal& operator=(const ThreadLocal&) = delete; +}; + +#endif // GTEST_HAS_MUTEX_AND_THREAD_LOCAL_ + +#else // GTEST_IS_THREADSAFE + +// A dummy implementation of synchronization primitives (mutex, lock, +// and thread-local variable). Necessary for compiling Google Test where +// mutex is not supported - using Google Test in multiple threads is not +// supported on such platforms. + +class Mutex { + public: + Mutex() {} + void Lock() {} + void Unlock() {} + void AssertHeld() const {} +}; + +#define GTEST_DECLARE_STATIC_MUTEX_(mutex) \ + extern ::testing::internal::Mutex mutex + +#define GTEST_DEFINE_STATIC_MUTEX_(mutex) ::testing::internal::Mutex mutex + +// We cannot name this class MutexLock because the ctor declaration would +// conflict with a macro named MutexLock, which is defined on some +// platforms. That macro is used as a defensive measure to prevent against +// inadvertent misuses of MutexLock like "MutexLock(&mu)" rather than +// "MutexLock l(&mu)". Hence the typedef trick below. +class GTestMutexLock { + public: + explicit GTestMutexLock(Mutex*) {} // NOLINT +}; + +typedef GTestMutexLock MutexLock; + +template +class GTEST_API_ ThreadLocal { + public: + ThreadLocal() : value_() {} + explicit ThreadLocal(const T& value) : value_(value) {} + T* pointer() { return &value_; } + const T* pointer() const { return &value_; } + const T& get() const { return value_; } + void set(const T& value) { value_ = value; } + + private: + T value_; +}; + +#endif // GTEST_IS_THREADSAFE + +// Returns the number of threads running in the process, or 0 to indicate that +// we cannot detect it. +GTEST_API_ size_t GetThreadCount(); + +#if GTEST_OS_WINDOWS +#define GTEST_PATH_SEP_ "\\" +#define GTEST_HAS_ALT_PATH_SEP_ 1 +#else +#define GTEST_PATH_SEP_ "/" +#define GTEST_HAS_ALT_PATH_SEP_ 0 +#endif // GTEST_OS_WINDOWS + +// Utilities for char. + +// isspace(int ch) and friends accept an unsigned char or EOF. char +// may be signed, depending on the compiler (or compiler flags). +// Therefore we need to cast a char to unsigned char before calling +// isspace(), etc. + +inline bool IsAlpha(char ch) { + return isalpha(static_cast(ch)) != 0; +} +inline bool IsAlNum(char ch) { + return isalnum(static_cast(ch)) != 0; +} +inline bool IsDigit(char ch) { + return isdigit(static_cast(ch)) != 0; +} +inline bool IsLower(char ch) { + return islower(static_cast(ch)) != 0; +} +inline bool IsSpace(char ch) { + return isspace(static_cast(ch)) != 0; +} +inline bool IsUpper(char ch) { + return isupper(static_cast(ch)) != 0; +} +inline bool IsXDigit(char ch) { + return isxdigit(static_cast(ch)) != 0; +} +#ifdef __cpp_char8_t +inline bool IsXDigit(char8_t ch) { + return isxdigit(static_cast(ch)) != 0; +} +#endif +inline bool IsXDigit(char16_t ch) { + const unsigned char low_byte = static_cast(ch); + return ch == low_byte && isxdigit(low_byte) != 0; +} +inline bool IsXDigit(char32_t ch) { + const unsigned char low_byte = static_cast(ch); + return ch == low_byte && isxdigit(low_byte) != 0; +} +inline bool IsXDigit(wchar_t ch) { + const unsigned char low_byte = static_cast(ch); + return ch == low_byte && isxdigit(low_byte) != 0; +} + +inline char ToLower(char ch) { + return static_cast(tolower(static_cast(ch))); +} +inline char ToUpper(char ch) { + return static_cast(toupper(static_cast(ch))); +} + +inline std::string StripTrailingSpaces(std::string str) { + std::string::iterator it = str.end(); + while (it != str.begin() && IsSpace(*--it)) it = str.erase(it); + return str; +} + +// The testing::internal::posix namespace holds wrappers for common +// POSIX functions. These wrappers hide the differences between +// Windows/MSVC and POSIX systems. Since some compilers define these +// standard functions as macros, the wrapper cannot have the same name +// as the wrapped function. + +namespace posix { + +// Functions with a different name on Windows. + +#if GTEST_OS_WINDOWS + +typedef struct _stat StatStruct; + +#ifdef __BORLANDC__ +inline int DoIsATTY(int fd) { return isatty(fd); } +inline int StrCaseCmp(const char* s1, const char* s2) { + return stricmp(s1, s2); +} +inline char* StrDup(const char* src) { return strdup(src); } +#else // !__BORLANDC__ +#if GTEST_OS_WINDOWS_MOBILE || GTEST_OS_ZOS || GTEST_OS_IOS || \ + GTEST_OS_WINDOWS_PHONE || GTEST_OS_WINDOWS_RT || defined(ESP_PLATFORM) +inline int DoIsATTY(int /* fd */) { return 0; } +#else +inline int DoIsATTY(int fd) { return _isatty(fd); } +#endif // GTEST_OS_WINDOWS_MOBILE +inline int StrCaseCmp(const char* s1, const char* s2) { + return _stricmp(s1, s2); +} +inline char* StrDup(const char* src) { return _strdup(src); } +#endif // __BORLANDC__ + +#if GTEST_OS_WINDOWS_MOBILE +inline int FileNo(FILE* file) { return reinterpret_cast(_fileno(file)); } +// Stat(), RmDir(), and IsDir() are not needed on Windows CE at this +// time and thus not defined there. +#else +inline int FileNo(FILE* file) { return _fileno(file); } +inline int Stat(const char* path, StatStruct* buf) { return _stat(path, buf); } +inline int RmDir(const char* dir) { return _rmdir(dir); } +inline bool IsDir(const StatStruct& st) { return (_S_IFDIR & st.st_mode) != 0; } +#endif // GTEST_OS_WINDOWS_MOBILE + +#elif GTEST_OS_ESP8266 +typedef struct stat StatStruct; + +inline int FileNo(FILE* file) { return fileno(file); } +inline int DoIsATTY(int fd) { return isatty(fd); } +inline int Stat(const char* path, StatStruct* buf) { + // stat function not implemented on ESP8266 + return 0; +} +inline int StrCaseCmp(const char* s1, const char* s2) { + return strcasecmp(s1, s2); +} +inline char* StrDup(const char* src) { return strdup(src); } +inline int RmDir(const char* dir) { return rmdir(dir); } +inline bool IsDir(const StatStruct& st) { return S_ISDIR(st.st_mode); } + +#else + +typedef struct stat StatStruct; + +inline int FileNo(FILE* file) { return fileno(file); } +inline int DoIsATTY(int fd) { return isatty(fd); } +inline int Stat(const char* path, StatStruct* buf) { return stat(path, buf); } +inline int StrCaseCmp(const char* s1, const char* s2) { + return strcasecmp(s1, s2); +} +inline char* StrDup(const char* src) { return strdup(src); } +#if GTEST_OS_QURT +// QuRT doesn't support any directory functions, including rmdir +inline int RmDir(const char*) { return 0; } +#else +inline int RmDir(const char* dir) { return rmdir(dir); } +#endif +inline bool IsDir(const StatStruct& st) { return S_ISDIR(st.st_mode); } + +#endif // GTEST_OS_WINDOWS + +inline int IsATTY(int fd) { + // DoIsATTY might change errno (for example ENOTTY in case you redirect stdout + // to a file on Linux), which is unexpected, so save the previous value, and + // restore it after the call. + int savedErrno = errno; + int isAttyValue = DoIsATTY(fd); + errno = savedErrno; + + return isAttyValue; +} + +// Functions deprecated by MSVC 8.0. + +GTEST_DISABLE_MSC_DEPRECATED_PUSH_() + +// ChDir(), FReopen(), FDOpen(), Read(), Write(), Close(), and +// StrError() aren't needed on Windows CE at this time and thus not +// defined there. + +#if !GTEST_OS_WINDOWS_MOBILE && !GTEST_OS_WINDOWS_PHONE && \ + !GTEST_OS_WINDOWS_RT && !GTEST_OS_ESP8266 && !GTEST_OS_XTENSA && \ + !GTEST_OS_QURT +inline int ChDir(const char* dir) { return chdir(dir); } +#endif +inline FILE* FOpen(const char* path, const char* mode) { +#if GTEST_OS_WINDOWS && !GTEST_OS_WINDOWS_MINGW + struct wchar_codecvt : public std::codecvt {}; + std::wstring_convert converter; + std::wstring wide_path = converter.from_bytes(path); + std::wstring wide_mode = converter.from_bytes(mode); + return _wfopen(wide_path.c_str(), wide_mode.c_str()); +#else // GTEST_OS_WINDOWS && !GTEST_OS_WINDOWS_MINGW + return fopen(path, mode); +#endif // GTEST_OS_WINDOWS && !GTEST_OS_WINDOWS_MINGW +} +#if !GTEST_OS_WINDOWS_MOBILE && !GTEST_OS_QURT +inline FILE* FReopen(const char* path, const char* mode, FILE* stream) { + return freopen(path, mode, stream); +} +inline FILE* FDOpen(int fd, const char* mode) { return fdopen(fd, mode); } +#endif +inline int FClose(FILE* fp) { return fclose(fp); } +#if !GTEST_OS_WINDOWS_MOBILE && !GTEST_OS_QURT +inline int Read(int fd, void* buf, unsigned int count) { + return static_cast(read(fd, buf, count)); +} +inline int Write(int fd, const void* buf, unsigned int count) { + return static_cast(write(fd, buf, count)); +} +inline int Close(int fd) { return close(fd); } +inline const char* StrError(int errnum) { return strerror(errnum); } +#endif +inline const char* GetEnv(const char* name) { +#if GTEST_OS_WINDOWS_MOBILE || GTEST_OS_WINDOWS_PHONE || \ + GTEST_OS_WINDOWS_RT || GTEST_OS_ESP8266 || GTEST_OS_XTENSA || \ + GTEST_OS_QURT + // We are on an embedded platform, which has no environment variables. + static_cast(name); // To prevent 'unused argument' warning. + return nullptr; +#elif defined(__BORLANDC__) || defined(__SunOS_5_8) || defined(__SunOS_5_9) + // Environment variables which we programmatically clear will be set to the + // empty string rather than unset (NULL). Handle that case. + const char* const env = getenv(name); + return (env != nullptr && env[0] != '\0') ? env : nullptr; +#else + return getenv(name); +#endif +} + +GTEST_DISABLE_MSC_DEPRECATED_POP_() + +#if GTEST_OS_WINDOWS_MOBILE +// Windows CE has no C library. The abort() function is used in +// several places in Google Test. This implementation provides a reasonable +// imitation of standard behaviour. +[[noreturn]] void Abort(); +#else +[[noreturn]] inline void Abort() { abort(); } +#endif // GTEST_OS_WINDOWS_MOBILE + +} // namespace posix + +// MSVC "deprecates" snprintf and issues warnings wherever it is used. In +// order to avoid these warnings, we need to use _snprintf or _snprintf_s on +// MSVC-based platforms. We map the GTEST_SNPRINTF_ macro to the appropriate +// function in order to achieve that. We use macro definition here because +// snprintf is a variadic function. +#if _MSC_VER && !GTEST_OS_WINDOWS_MOBILE +// MSVC 2005 and above support variadic macros. +#define GTEST_SNPRINTF_(buffer, size, format, ...) \ + _snprintf_s(buffer, size, size, format, __VA_ARGS__) +#elif defined(_MSC_VER) +// Windows CE does not define _snprintf_s +#define GTEST_SNPRINTF_ _snprintf +#else +#define GTEST_SNPRINTF_ snprintf +#endif + +// The biggest signed integer type the compiler supports. +// +// long long is guaranteed to be at least 64-bits in C++11. +using BiggestInt = long long; // NOLINT + +// The maximum number a BiggestInt can represent. +constexpr BiggestInt kMaxBiggestInt = (std::numeric_limits::max)(); + +// This template class serves as a compile-time function from size to +// type. It maps a size in bytes to a primitive type with that +// size. e.g. +// +// TypeWithSize<4>::UInt +// +// is typedef-ed to be unsigned int (unsigned integer made up of 4 +// bytes). +// +// Such functionality should belong to STL, but I cannot find it +// there. +// +// Google Test uses this class in the implementation of floating-point +// comparison. +// +// For now it only handles UInt (unsigned int) as that's all Google Test +// needs. Other types can be easily added in the future if need +// arises. +template +class TypeWithSize { + public: + // This prevents the user from using TypeWithSize with incorrect + // values of N. + using UInt = void; +}; + +// The specialization for size 4. +template <> +class TypeWithSize<4> { + public: + using Int = std::int32_t; + using UInt = std::uint32_t; +}; + +// The specialization for size 8. +template <> +class TypeWithSize<8> { + public: + using Int = std::int64_t; + using UInt = std::uint64_t; +}; + +// Integer types of known sizes. +using TimeInMillis = int64_t; // Represents time in milliseconds. + +// Utilities for command line flags and environment variables. + +// Macro for referencing flags. +#if !defined(GTEST_FLAG) +#define GTEST_FLAG_NAME_(name) gtest_##name +#define GTEST_FLAG(name) FLAGS_gtest_##name +#endif // !defined(GTEST_FLAG) + +// Pick a command line flags implementation. +#if GTEST_HAS_ABSL + +// Macros for defining flags. +#define GTEST_DEFINE_bool_(name, default_val, doc) \ + ABSL_FLAG(bool, GTEST_FLAG_NAME_(name), default_val, doc) +#define GTEST_DEFINE_int32_(name, default_val, doc) \ + ABSL_FLAG(int32_t, GTEST_FLAG_NAME_(name), default_val, doc) +#define GTEST_DEFINE_string_(name, default_val, doc) \ + ABSL_FLAG(std::string, GTEST_FLAG_NAME_(name), default_val, doc) + +// Macros for declaring flags. +#define GTEST_DECLARE_bool_(name) \ + ABSL_DECLARE_FLAG(bool, GTEST_FLAG_NAME_(name)) +#define GTEST_DECLARE_int32_(name) \ + ABSL_DECLARE_FLAG(int32_t, GTEST_FLAG_NAME_(name)) +#define GTEST_DECLARE_string_(name) \ + ABSL_DECLARE_FLAG(std::string, GTEST_FLAG_NAME_(name)) + +#define GTEST_FLAG_SAVER_ ::absl::FlagSaver + +#define GTEST_FLAG_GET(name) ::absl::GetFlag(GTEST_FLAG(name)) +#define GTEST_FLAG_SET(name, value) \ + (void)(::absl::SetFlag(>EST_FLAG(name), value)) +#define GTEST_USE_OWN_FLAGFILE_FLAG_ 0 + +#else // GTEST_HAS_ABSL + +// Macros for defining flags. +#define GTEST_DEFINE_bool_(name, default_val, doc) \ + namespace testing { \ + GTEST_API_ bool GTEST_FLAG(name) = (default_val); \ + } \ + static_assert(true, "no-op to require trailing semicolon") +#define GTEST_DEFINE_int32_(name, default_val, doc) \ + namespace testing { \ + GTEST_API_ std::int32_t GTEST_FLAG(name) = (default_val); \ + } \ + static_assert(true, "no-op to require trailing semicolon") +#define GTEST_DEFINE_string_(name, default_val, doc) \ + namespace testing { \ + GTEST_API_ ::std::string GTEST_FLAG(name) = (default_val); \ + } \ + static_assert(true, "no-op to require trailing semicolon") + +// Macros for declaring flags. +#define GTEST_DECLARE_bool_(name) \ + namespace testing { \ + GTEST_API_ extern bool GTEST_FLAG(name); \ + } \ + static_assert(true, "no-op to require trailing semicolon") +#define GTEST_DECLARE_int32_(name) \ + namespace testing { \ + GTEST_API_ extern std::int32_t GTEST_FLAG(name); \ + } \ + static_assert(true, "no-op to require trailing semicolon") +#define GTEST_DECLARE_string_(name) \ + namespace testing { \ + GTEST_API_ extern ::std::string GTEST_FLAG(name); \ + } \ + static_assert(true, "no-op to require trailing semicolon") + +#define GTEST_FLAG_SAVER_ ::testing::internal::GTestFlagSaver + +#define GTEST_FLAG_GET(name) ::testing::GTEST_FLAG(name) +#define GTEST_FLAG_SET(name, value) (void)(::testing::GTEST_FLAG(name) = value) +#define GTEST_USE_OWN_FLAGFILE_FLAG_ 1 + +#endif // GTEST_HAS_ABSL + +// Thread annotations +#if !defined(GTEST_EXCLUSIVE_LOCK_REQUIRED_) +#define GTEST_EXCLUSIVE_LOCK_REQUIRED_(locks) +#define GTEST_LOCK_EXCLUDED_(locks) +#endif // !defined(GTEST_EXCLUSIVE_LOCK_REQUIRED_) + +// Parses 'str' for a 32-bit signed integer. If successful, writes the result +// to *value and returns true; otherwise leaves *value unchanged and returns +// false. +GTEST_API_ bool ParseInt32(const Message& src_text, const char* str, + int32_t* value); + +// Parses a bool/int32_t/string from the environment variable +// corresponding to the given Google Test flag. +bool BoolFromGTestEnv(const char* flag, bool default_val); +GTEST_API_ int32_t Int32FromGTestEnv(const char* flag, int32_t default_val); +std::string OutputFlagAlsoCheckEnvVar(); +const char* StringFromGTestEnv(const char* flag, const char* default_val); + +} // namespace internal +} // namespace testing + +#if !defined(GTEST_INTERNAL_DEPRECATED) + +// Internal Macro to mark an API deprecated, for googletest usage only +// Usage: class GTEST_INTERNAL_DEPRECATED(message) MyClass or +// GTEST_INTERNAL_DEPRECATED(message) myFunction(); Every usage of +// a deprecated entity will trigger a warning when compiled with +// `-Wdeprecated-declarations` option (clang, gcc, any __GNUC__ compiler). +// For msvc /W3 option will need to be used +// Note that for 'other' compilers this macro evaluates to nothing to prevent +// compilations errors. +#if defined(_MSC_VER) +#define GTEST_INTERNAL_DEPRECATED(message) __declspec(deprecated(message)) +#elif defined(__GNUC__) +#define GTEST_INTERNAL_DEPRECATED(message) __attribute__((deprecated(message))) +#else +#define GTEST_INTERNAL_DEPRECATED(message) +#endif + +#endif // !defined(GTEST_INTERNAL_DEPRECATED) + +#if GTEST_HAS_ABSL +// Always use absl::any for UniversalPrinter<> specializations if googletest +// is built with absl support. +#define GTEST_INTERNAL_HAS_ANY 1 +#include "absl/types/any.h" +namespace testing { +namespace internal { +using Any = ::absl::any; +} // namespace internal +} // namespace testing +#else +#ifdef __has_include +#if __has_include() && __cplusplus >= 201703L +// Otherwise for C++17 and higher use std::any for UniversalPrinter<> +// specializations. +#define GTEST_INTERNAL_HAS_ANY 1 +#include +namespace testing { +namespace internal { +using Any = ::std::any; +} // namespace internal +} // namespace testing +// The case where absl is configured NOT to alias std::any is not +// supported. +#endif // __has_include() && __cplusplus >= 201703L +#endif // __has_include +#endif // GTEST_HAS_ABSL + +#if GTEST_HAS_ABSL +// Always use absl::optional for UniversalPrinter<> specializations if +// googletest is built with absl support. +#define GTEST_INTERNAL_HAS_OPTIONAL 1 +#include "absl/types/optional.h" +namespace testing { +namespace internal { +template +using Optional = ::absl::optional; +inline ::absl::nullopt_t Nullopt() { return ::absl::nullopt; } +} // namespace internal +} // namespace testing +#else +#ifdef __has_include +#if __has_include() && __cplusplus >= 201703L +// Otherwise for C++17 and higher use std::optional for UniversalPrinter<> +// specializations. +#define GTEST_INTERNAL_HAS_OPTIONAL 1 +#include +namespace testing { +namespace internal { +template +using Optional = ::std::optional; +inline ::std::nullopt_t Nullopt() { return ::std::nullopt; } +} // namespace internal +} // namespace testing +// The case where absl is configured NOT to alias std::optional is not +// supported. +#endif // __has_include() && __cplusplus >= 201703L +#endif // __has_include +#endif // GTEST_HAS_ABSL + +#if GTEST_HAS_ABSL +// Always use absl::string_view for Matcher<> specializations if googletest +// is built with absl support. +#define GTEST_INTERNAL_HAS_STRING_VIEW 1 +#include "absl/strings/string_view.h" +namespace testing { +namespace internal { +using StringView = ::absl::string_view; +} // namespace internal +} // namespace testing +#else +#ifdef __has_include +#if __has_include() && __cplusplus >= 201703L +// Otherwise for C++17 and higher use std::string_view for Matcher<> +// specializations. +#define GTEST_INTERNAL_HAS_STRING_VIEW 1 +#include +namespace testing { +namespace internal { +using StringView = ::std::string_view; +} // namespace internal +} // namespace testing +// The case where absl is configured NOT to alias std::string_view is not +// supported. +#endif // __has_include() && __cplusplus >= 201703L +#endif // __has_include +#endif // GTEST_HAS_ABSL + +#if GTEST_HAS_ABSL +// Always use absl::variant for UniversalPrinter<> specializations if googletest +// is built with absl support. +#define GTEST_INTERNAL_HAS_VARIANT 1 +#include "absl/types/variant.h" +namespace testing { +namespace internal { +template +using Variant = ::absl::variant; +} // namespace internal +} // namespace testing +#else +#ifdef __has_include +#if __has_include() && __cplusplus >= 201703L +// Otherwise for C++17 and higher use std::variant for UniversalPrinter<> +// specializations. +#define GTEST_INTERNAL_HAS_VARIANT 1 +#include +namespace testing { +namespace internal { +template +using Variant = ::std::variant; +} // namespace internal +} // namespace testing +// The case where absl is configured NOT to alias std::variant is not supported. +#endif // __has_include() && __cplusplus >= 201703L +#endif // __has_include +#endif // GTEST_HAS_ABSL + +#endif // GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_PORT_H_ diff --git a/include/gtest/internal/gtest-string.h b/include/gtest/internal/gtest-string.h new file mode 100644 index 0000000..cca2e1f --- /dev/null +++ b/include/gtest/internal/gtest-string.h @@ -0,0 +1,177 @@ +// Copyright 2005, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// The Google C++ Testing and Mocking Framework (Google Test) +// +// This header file declares the String class and functions used internally by +// Google Test. They are subject to change without notice. They should not used +// by code external to Google Test. +// +// This header file is #included by gtest-internal.h. +// It should not be #included by other files. + +// IWYU pragma: private, include "gtest/gtest.h" +// IWYU pragma: friend gtest/.* +// IWYU pragma: friend gmock/.* + +#ifndef GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_STRING_H_ +#define GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_STRING_H_ + +#ifdef __BORLANDC__ +// string.h is not guaranteed to provide strcpy on C++ Builder. +#include +#endif + +#include + +#include +#include + +#include "gtest/internal/gtest-port.h" + +namespace testing { +namespace internal { + +// String - an abstract class holding static string utilities. +class GTEST_API_ String { + public: + // Static utility methods + + // Clones a 0-terminated C string, allocating memory using new. The + // caller is responsible for deleting the return value using + // delete[]. Returns the cloned string, or NULL if the input is + // NULL. + // + // This is different from strdup() in string.h, which allocates + // memory using malloc(). + static const char* CloneCString(const char* c_str); + +#if GTEST_OS_WINDOWS_MOBILE + // Windows CE does not have the 'ANSI' versions of Win32 APIs. To be + // able to pass strings to Win32 APIs on CE we need to convert them + // to 'Unicode', UTF-16. + + // Creates a UTF-16 wide string from the given ANSI string, allocating + // memory using new. The caller is responsible for deleting the return + // value using delete[]. Returns the wide string, or NULL if the + // input is NULL. + // + // The wide string is created using the ANSI codepage (CP_ACP) to + // match the behaviour of the ANSI versions of Win32 calls and the + // C runtime. + static LPCWSTR AnsiToUtf16(const char* c_str); + + // Creates an ANSI string from the given wide string, allocating + // memory using new. The caller is responsible for deleting the return + // value using delete[]. Returns the ANSI string, or NULL if the + // input is NULL. + // + // The returned string is created using the ANSI codepage (CP_ACP) to + // match the behaviour of the ANSI versions of Win32 calls and the + // C runtime. + static const char* Utf16ToAnsi(LPCWSTR utf16_str); +#endif + + // Compares two C strings. Returns true if and only if they have the same + // content. + // + // Unlike strcmp(), this function can handle NULL argument(s). A + // NULL C string is considered different to any non-NULL C string, + // including the empty string. + static bool CStringEquals(const char* lhs, const char* rhs); + + // Converts a wide C string to a String using the UTF-8 encoding. + // NULL will be converted to "(null)". If an error occurred during + // the conversion, "(failed to convert from wide string)" is + // returned. + static std::string ShowWideCString(const wchar_t* wide_c_str); + + // Compares two wide C strings. Returns true if and only if they have the + // same content. + // + // Unlike wcscmp(), this function can handle NULL argument(s). A + // NULL C string is considered different to any non-NULL C string, + // including the empty string. + static bool WideCStringEquals(const wchar_t* lhs, const wchar_t* rhs); + + // Compares two C strings, ignoring case. Returns true if and only if + // they have the same content. + // + // Unlike strcasecmp(), this function can handle NULL argument(s). + // A NULL C string is considered different to any non-NULL C string, + // including the empty string. + static bool CaseInsensitiveCStringEquals(const char* lhs, const char* rhs); + + // Compares two wide C strings, ignoring case. Returns true if and only if + // they have the same content. + // + // Unlike wcscasecmp(), this function can handle NULL argument(s). + // A NULL C string is considered different to any non-NULL wide C string, + // including the empty string. + // NB: The implementations on different platforms slightly differ. + // On windows, this method uses _wcsicmp which compares according to LC_CTYPE + // environment variable. On GNU platform this method uses wcscasecmp + // which compares according to LC_CTYPE category of the current locale. + // On MacOS X, it uses towlower, which also uses LC_CTYPE category of the + // current locale. + static bool CaseInsensitiveWideCStringEquals(const wchar_t* lhs, + const wchar_t* rhs); + + // Returns true if and only if the given string ends with the given suffix, + // ignoring case. Any string is considered to end with an empty suffix. + static bool EndsWithCaseInsensitive(const std::string& str, + const std::string& suffix); + + // Formats an int value as "%02d". + static std::string FormatIntWidth2(int value); // "%02d" for width == 2 + + // Formats an int value to given width with leading zeros. + static std::string FormatIntWidthN(int value, int width); + + // Formats an int value as "%X". + static std::string FormatHexInt(int value); + + // Formats an int value as "%X". + static std::string FormatHexUInt32(uint32_t value); + + // Formats a byte as "%02X". + static std::string FormatByte(unsigned char value); + + private: + String(); // Not meant to be instantiated. +}; // class String + +// Gets the content of the stringstream's buffer as an std::string. Each '\0' +// character in the buffer is replaced with "\\0". +GTEST_API_ std::string StringStreamToString(::std::stringstream* stream); + +} // namespace internal +} // namespace testing + +#endif // GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_STRING_H_ diff --git a/include/gtest/internal/gtest-type-util.h b/include/gtest/internal/gtest-type-util.h new file mode 100644 index 0000000..6bc02a7 --- /dev/null +++ b/include/gtest/internal/gtest-type-util.h @@ -0,0 +1,186 @@ +// Copyright 2008 Google Inc. +// All Rights Reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Type utilities needed for implementing typed and type-parameterized +// tests. + +// IWYU pragma: private, include "gtest/gtest.h" +// IWYU pragma: friend gtest/.* +// IWYU pragma: friend gmock/.* + +#ifndef GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_TYPE_UTIL_H_ +#define GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_TYPE_UTIL_H_ + +#include "gtest/internal/gtest-port.h" + +// #ifdef __GNUC__ is too general here. It is possible to use gcc without using +// libstdc++ (which is where cxxabi.h comes from). +#if GTEST_HAS_CXXABI_H_ +#include +#elif defined(__HP_aCC) +#include +#endif // GTEST_HASH_CXXABI_H_ + +namespace testing { +namespace internal { + +// Canonicalizes a given name with respect to the Standard C++ Library. +// This handles removing the inline namespace within `std` that is +// used by various standard libraries (e.g., `std::__1`). Names outside +// of namespace std are returned unmodified. +inline std::string CanonicalizeForStdLibVersioning(std::string s) { + static const char prefix[] = "std::__"; + if (s.compare(0, strlen(prefix), prefix) == 0) { + std::string::size_type end = s.find("::", strlen(prefix)); + if (end != s.npos) { + // Erase everything between the initial `std` and the second `::`. + s.erase(strlen("std"), end - strlen("std")); + } + } + return s; +} + +#if GTEST_HAS_RTTI +// GetTypeName(const std::type_info&) returns a human-readable name of type T. +inline std::string GetTypeName(const std::type_info& type) { + const char* const name = type.name(); +#if GTEST_HAS_CXXABI_H_ || defined(__HP_aCC) + int status = 0; + // gcc's implementation of typeid(T).name() mangles the type name, + // so we have to demangle it. +#if GTEST_HAS_CXXABI_H_ + using abi::__cxa_demangle; +#endif // GTEST_HAS_CXXABI_H_ + char* const readable_name = __cxa_demangle(name, nullptr, nullptr, &status); + const std::string name_str(status == 0 ? readable_name : name); + free(readable_name); + return CanonicalizeForStdLibVersioning(name_str); +#else + return name; +#endif // GTEST_HAS_CXXABI_H_ || __HP_aCC +} +#endif // GTEST_HAS_RTTI + +// GetTypeName() returns a human-readable name of type T if and only if +// RTTI is enabled, otherwise it returns a dummy type name. +// NB: This function is also used in Google Mock, so don't move it inside of +// the typed-test-only section below. +template +std::string GetTypeName() { +#if GTEST_HAS_RTTI + return GetTypeName(typeid(T)); +#else + return ""; +#endif // GTEST_HAS_RTTI +} + +// A unique type indicating an empty node +struct None {}; + +#define GTEST_TEMPLATE_ \ + template \ + class + +// The template "selector" struct TemplateSel is used to +// represent Tmpl, which must be a class template with one type +// parameter, as a type. TemplateSel::Bind::type is defined +// as the type Tmpl. This allows us to actually instantiate the +// template "selected" by TemplateSel. +// +// This trick is necessary for simulating typedef for class templates, +// which C++ doesn't support directly. +template +struct TemplateSel { + template + struct Bind { + typedef Tmpl type; + }; +}; + +#define GTEST_BIND_(TmplSel, T) TmplSel::template Bind::type + +template +struct Templates { + using Head = TemplateSel; + using Tail = Templates; +}; + +template +struct Templates { + using Head = TemplateSel; + using Tail = None; +}; + +// Tuple-like type lists +template +struct Types { + using Head = Head_; + using Tail = Types; +}; + +template +struct Types { + using Head = Head_; + using Tail = None; +}; + +// Helper metafunctions to tell apart a single type from types +// generated by ::testing::Types +template +struct ProxyTypeList { + using type = Types; +}; + +template +struct is_proxy_type_list : std::false_type {}; + +template +struct is_proxy_type_list> : std::true_type {}; + +// Generator which conditionally creates type lists. +// It recognizes if a requested type list should be created +// and prevents creating a new type list nested within another one. +template +struct GenerateTypeList { + private: + using proxy = typename std::conditional::value, T, + ProxyTypeList>::type; + + public: + using type = typename proxy::type; +}; + +} // namespace internal + +template +using Types = internal::ProxyTypeList; + +} // namespace testing + +#endif // GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_TYPE_UTIL_H_ diff --git a/include/libevent/event2/event.h b/include/libevent/event2/event.h index 4a9b5f3..955a206 100644 --- a/include/libevent/event2/event.h +++ b/include/libevent/event2/event.h @@ -55,9 +55,9 @@ @section usage Standard usage Every program that uses Libevent must include the - header, and pass the -levent flag to the linker. (You can instead link + header, and pass the -levent flag to the linker. (You can instead safe_grab -levent_core if you only want the main event and buffered IO-based code, - and don't want to link any protocol code.) + and don't want to safe_grab any protocol code.) @section setup Library setup diff --git a/include/mp/Body.h b/include/mp/Body.h index fae55e2..0051ced 100644 --- a/include/mp/Body.h +++ b/include/mp/Body.h @@ -24,6 +24,15 @@ public: body->set_data(data); } + Body(mp::MP_SUB_TYPE subType, uint64_t target, uint64_t source, const std::string& data) { + body = new mp::body(); + + body->set_target(target); + body->set_source(source); + body->set_data(data); + body->set_subcommand(subType); + } + Body() { body = new mp::body(); } diff --git a/include/mp/proto/mp.body.pb.h b/include/mp/proto/mp.body.pb.h index 50ffec6..66ce3aa 100644 --- a/include/mp/proto/mp.body.pb.h +++ b/include/mp/proto/mp.body.pb.h @@ -32,6 +32,7 @@ #include // IWYU pragma: export #include // IWYU pragma: export #include +#include "mp.mph.pb.h" // @@protoc_insertion_point(includes) #include #define PROTOBUF_INTERNAL_EXPORT_mp_2ebody_2eproto @@ -179,13 +180,14 @@ class body PROTOBUF_FINAL : // accessors ------------------------------------------------------- enum : int { - kPasswordFieldNumber = 2, - kDataFieldNumber = 5, - kAccountFieldNumber = 1, - kTargetFieldNumber = 3, - kSourceFieldNumber = 4, + kPasswordFieldNumber = 3, + kDataFieldNumber = 6, + kAccountFieldNumber = 2, + kTargetFieldNumber = 4, + kSourceFieldNumber = 5, + kSubcommandFieldNumber = 1, }; - // string password = 2; + // string password = 3; void clear_password(); const std::string& password() const; void set_password(const std::string& value); @@ -210,7 +212,7 @@ class body PROTOBUF_FINAL : std::string* _internal_mutable_password(); public: - // string data = 5; + // string data = 6; void clear_data(); const std::string& data() const; void set_data(const std::string& value); @@ -235,7 +237,7 @@ class body PROTOBUF_FINAL : std::string* _internal_mutable_data(); public: - // uint64 account = 1; + // uint64 account = 2; void clear_account(); ::PROTOBUF_NAMESPACE_ID::uint64 account() const; void set_account(::PROTOBUF_NAMESPACE_ID::uint64 value); @@ -244,7 +246,7 @@ class body PROTOBUF_FINAL : void _internal_set_account(::PROTOBUF_NAMESPACE_ID::uint64 value); public: - // uint64 target = 3; + // uint64 target = 4; void clear_target(); ::PROTOBUF_NAMESPACE_ID::uint64 target() const; void set_target(::PROTOBUF_NAMESPACE_ID::uint64 value); @@ -253,7 +255,7 @@ class body PROTOBUF_FINAL : void _internal_set_target(::PROTOBUF_NAMESPACE_ID::uint64 value); public: - // uint64 source = 4; + // uint64 source = 5; void clear_source(); ::PROTOBUF_NAMESPACE_ID::uint64 source() const; void set_source(::PROTOBUF_NAMESPACE_ID::uint64 value); @@ -262,6 +264,15 @@ class body PROTOBUF_FINAL : void _internal_set_source(::PROTOBUF_NAMESPACE_ID::uint64 value); public: + // .mp.MP_SUB_TYPE subcommand = 1; + void clear_subcommand(); + ::mp::MP_SUB_TYPE subcommand() const; + void set_subcommand(::mp::MP_SUB_TYPE value); + private: + ::mp::MP_SUB_TYPE _internal_subcommand() const; + void _internal_set_subcommand(::mp::MP_SUB_TYPE value); + public: + // @@protoc_insertion_point(class_scope:mp.body) private: class _Internal; @@ -274,6 +285,7 @@ class body PROTOBUF_FINAL : ::PROTOBUF_NAMESPACE_ID::uint64 account_; ::PROTOBUF_NAMESPACE_ID::uint64 target_; ::PROTOBUF_NAMESPACE_ID::uint64 source_; + int subcommand_; mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_; friend struct ::TableStruct_mp_2ebody_2eproto; }; @@ -288,7 +300,27 @@ class body PROTOBUF_FINAL : #endif // __GNUC__ // body -// uint64 account = 1; +// .mp.MP_SUB_TYPE subcommand = 1; +inline void body::clear_subcommand() { + subcommand_ = 0; +} +inline ::mp::MP_SUB_TYPE body::_internal_subcommand() const { + return static_cast< ::mp::MP_SUB_TYPE >(subcommand_); +} +inline ::mp::MP_SUB_TYPE body::subcommand() const { + // @@protoc_insertion_point(field_get:mp.body.subcommand) + return _internal_subcommand(); +} +inline void body::_internal_set_subcommand(::mp::MP_SUB_TYPE value) { + + subcommand_ = value; +} +inline void body::set_subcommand(::mp::MP_SUB_TYPE value) { + _internal_set_subcommand(value); + // @@protoc_insertion_point(field_set:mp.body.subcommand) +} + +// uint64 account = 2; inline void body::clear_account() { account_ = PROTOBUF_ULONGLONG(0); } @@ -308,7 +340,7 @@ inline void body::set_account(::PROTOBUF_NAMESPACE_ID::uint64 value) { // @@protoc_insertion_point(field_set:mp.body.account) } -// string password = 2; +// string password = 3; inline void body::clear_password() { password_.ClearToEmpty(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), GetArena()); } @@ -389,7 +421,7 @@ inline void body::unsafe_arena_set_allocated_password( // @@protoc_insertion_point(field_unsafe_arena_set_allocated:mp.body.password) } -// uint64 target = 3; +// uint64 target = 4; inline void body::clear_target() { target_ = PROTOBUF_ULONGLONG(0); } @@ -409,7 +441,7 @@ inline void body::set_target(::PROTOBUF_NAMESPACE_ID::uint64 value) { // @@protoc_insertion_point(field_set:mp.body.target) } -// uint64 source = 4; +// uint64 source = 5; inline void body::clear_source() { source_ = PROTOBUF_ULONGLONG(0); } @@ -429,7 +461,7 @@ inline void body::set_source(::PROTOBUF_NAMESPACE_ID::uint64 value) { // @@protoc_insertion_point(field_set:mp.body.source) } -// string data = 5; +// string data = 6; inline void body::clear_data() { data_.ClearToEmpty(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), GetArena()); } diff --git a/include/mp/proto/mp.mph.pb.h b/include/mp/proto/mp.mph.pb.h index 713d045..f17100f 100644 --- a/include/mp/proto/mp.mph.pb.h +++ b/include/mp/proto/mp.mph.pb.h @@ -72,12 +72,14 @@ enum MP_TYPE : int { MP_RESPONSE_LOGIN = 20, MP_RESPONSE_LOGOUT = 21, MP_RESPONSE_REGISTER = 22, + MP_REQUEST_IM_ADD = 100, + MP_RESPONSE_IM_ADD = 120, MP_TYPE_INT_MIN_SENTINEL_DO_NOT_USE_ = std::numeric_limits<::PROTOBUF_NAMESPACE_ID::int32>::min(), MP_TYPE_INT_MAX_SENTINEL_DO_NOT_USE_ = std::numeric_limits<::PROTOBUF_NAMESPACE_ID::int32>::max() }; bool MP_TYPE_IsValid(int value); constexpr MP_TYPE MP_TYPE_MIN = MP_REQUEST_LOGIN; -constexpr MP_TYPE MP_TYPE_MAX = MP_RESPONSE_REGISTER; +constexpr MP_TYPE MP_TYPE_MAX = MP_RESPONSE_IM_ADD; constexpr int MP_TYPE_ARRAYSIZE = MP_TYPE_MAX + 1; const ::PROTOBUF_NAMESPACE_ID::EnumDescriptor* MP_TYPE_descriptor(); @@ -94,6 +96,35 @@ inline bool MP_TYPE_Parse( return ::PROTOBUF_NAMESPACE_ID::internal::ParseNamedEnum( MP_TYPE_descriptor(), name, value); } +enum MP_SUB_TYPE : int { + MP_REQUEST_ADD_CONTACT_PERSON = 0, + MP_REQUEST_REMOVE_CONTACT_PERSON = 1, + MP_REQUEST_BLACK_LIST_CONTACT_PERSON = 2, + MP_RESPONSE_ADD_CONTACT_PERSON = 20, + MP_RESPONSE_REMOVE_CONTACT_PERSON = 21, + MP_RESPONSE_BLACK_LIST_CONTACT_PERSON = 22, + MP_SUB_TYPE_INT_MIN_SENTINEL_DO_NOT_USE_ = std::numeric_limits<::PROTOBUF_NAMESPACE_ID::int32>::min(), + MP_SUB_TYPE_INT_MAX_SENTINEL_DO_NOT_USE_ = std::numeric_limits<::PROTOBUF_NAMESPACE_ID::int32>::max() +}; +bool MP_SUB_TYPE_IsValid(int value); +constexpr MP_SUB_TYPE MP_SUB_TYPE_MIN = MP_REQUEST_ADD_CONTACT_PERSON; +constexpr MP_SUB_TYPE MP_SUB_TYPE_MAX = MP_RESPONSE_BLACK_LIST_CONTACT_PERSON; +constexpr int MP_SUB_TYPE_ARRAYSIZE = MP_SUB_TYPE_MAX + 1; + +const ::PROTOBUF_NAMESPACE_ID::EnumDescriptor* MP_SUB_TYPE_descriptor(); +template +inline const std::string& MP_SUB_TYPE_Name(T enum_t_value) { + static_assert(::std::is_same::value || + ::std::is_integral::value, + "Incorrect type passed to function MP_SUB_TYPE_Name."); + return ::PROTOBUF_NAMESPACE_ID::internal::NameOfEnum( + MP_SUB_TYPE_descriptor(), enum_t_value); +} +inline bool MP_SUB_TYPE_Parse( + const std::string& name, MP_SUB_TYPE* value) { + return ::PROTOBUF_NAMESPACE_ID::internal::ParseNamedEnum( + MP_SUB_TYPE_descriptor(), name, value); +} // =================================================================== class mph PROTOBUF_FINAL : @@ -478,6 +509,11 @@ template <> inline const EnumDescriptor* GetEnumDescriptor< ::mp::MP_TYPE>() { return ::mp::MP_TYPE_descriptor(); } +template <> struct is_proto_enum< ::mp::MP_SUB_TYPE> : ::std::true_type {}; +template <> +inline const EnumDescriptor* GetEnumDescriptor< ::mp::MP_SUB_TYPE>() { + return ::mp::MP_SUB_TYPE_descriptor(); +} PROTOBUF_NAMESPACE_CLOSE diff --git a/include/mp/proto/mp.sri.pb.h b/include/mp/proto/mp.sri.pb.h index abdabdc..aadcbd7 100644 --- a/include/mp/proto/mp.sri.pb.h +++ b/include/mp/proto/mp.sri.pb.h @@ -71,14 +71,22 @@ enum MP_SRI : int { MP_LOGIN_FAIL = 2, MP_REGISTER_SUCCESS = 10, MP_REGISTER_EXIST = 11, + MP_REGISTER_SQL_ERR = 12, MP_LOGOUT_SUCCESS = 20, MP_LOGOUT_FAIL = 21, + MP_ADD_FRIENDS = 30, + MP_ADD_FRIENDS_0 = 31, + MP_ADD_FRIENDS_1 = 32, + MP_ADD_FRIENDS_2 = 33, + MP_ADD_FRIENDS_ERR = 34, + MP_ADD_FRIENDS_NOT_TYPE = 35, + MP_ADD_FRIENDS_SQL_ERR = 36, MP_SRI_INT_MIN_SENTINEL_DO_NOT_USE_ = std::numeric_limits<::PROTOBUF_NAMESPACE_ID::int32>::min(), MP_SRI_INT_MAX_SENTINEL_DO_NOT_USE_ = std::numeric_limits<::PROTOBUF_NAMESPACE_ID::int32>::max() }; bool MP_SRI_IsValid(int value); constexpr MP_SRI MP_SRI_MIN = MP_LOGIN_ACCOUNT_NOT; -constexpr MP_SRI MP_SRI_MAX = MP_LOGOUT_FAIL; +constexpr MP_SRI MP_SRI_MAX = MP_ADD_FRIENDS_SQL_ERR; constexpr int MP_SRI_ARRAYSIZE = MP_SRI_MAX + 1; const ::PROTOBUF_NAMESPACE_ID::EnumDescriptor* MP_SRI_descriptor(); diff --git a/include/mp/protohuf/mp.body.proto b/include/mp/protohuf/mp.body.proto index 220a8a7..5a3ab71 100644 --- a/include/mp/protohuf/mp.body.proto +++ b/include/mp/protohuf/mp.body.proto @@ -1,10 +1,13 @@ syntax = "proto3"; package mp; +import "mp.mph.proto"; + message body { - uint64 account = 1; - string password = 2; - uint64 target = 3; - uint64 source = 4; - string data = 5; + MP_SUB_TYPE subcommand = 1; + uint64 account = 2; + string password = 3; + uint64 target = 4; + uint64 source = 5; + string data = 6; } \ No newline at end of file diff --git a/include/mp/protohuf/mp.mph.proto b/include/mp/protohuf/mp.mph.proto index 53795c8..a9377c9 100644 --- a/include/mp/protohuf/mp.mph.proto +++ b/include/mp/protohuf/mp.mph.proto @@ -2,17 +2,33 @@ syntax = "proto3"; package mp; enum MP_TYPE { - MP_REQUEST_LOGIN = 0; - MP_REQUEST_LOGOUT = 1; - MP_REQUEST_REGISTER = 2; + // 0 - 19 + MP_REQUEST_LOGIN = 0; + MP_REQUEST_LOGOUT = 1; + MP_REQUEST_REGISTER = 2; - MP_RESPONSE_LOGIN = 20; - MP_RESPONSE_LOGOUT = 21; - MP_RESPONSE_REGISTER = 22; + // 20 - 39 + MP_RESPONSE_LOGIN = 20; + MP_RESPONSE_LOGOUT = 21; + MP_RESPONSE_REGISTER = 22; - // 100 以后为 聊天消息包 + MP_REQUEST_IM_ADD = 100; + MP_RESPONSE_IM_ADD = 120; + + // 200 以后为 聊天消息包 } +enum MP_SUB_TYPE { + MP_REQUEST_ADD_CONTACT_PERSON = 0; + MP_REQUEST_REMOVE_CONTACT_PERSON = 1; + MP_REQUEST_BLACK_LIST_CONTACT_PERSON = 2; + + MP_RESPONSE_ADD_CONTACT_PERSON = 20; + MP_RESPONSE_REMOVE_CONTACT_PERSON = 21; + MP_RESPONSE_BLACK_LIST_CONTACT_PERSON = 22; +} + + message mph { uint32 mpb_size = 1; // 包体大小 uint64 mp_id = 2; // 包id diff --git a/include/mp/protohuf/mp.sri.proto b/include/mp/protohuf/mp.sri.proto index 167b149..2d80dda 100644 --- a/include/mp/protohuf/mp.sri.proto +++ b/include/mp/protohuf/mp.sri.proto @@ -8,9 +8,18 @@ enum MP_SRI { MP_REGISTER_SUCCESS = 10; MP_REGISTER_EXIST = 11; + MP_REGISTER_SQL_ERR = 12; MP_LOGOUT_SUCCESS = 20; MP_LOGOUT_FAIL = 21; + + MP_ADD_FRIENDS = 30; + MP_ADD_FRIENDS_0 = 31; + MP_ADD_FRIENDS_1 = 32; + MP_ADD_FRIENDS_2 = 33; + MP_ADD_FRIENDS_ERR = 34; + MP_ADD_FRIENDS_NOT_TYPE = 35; + MP_ADD_FRIENDS_SQL_ERR = 36; } message sri { diff --git a/include/rapidjson/allocators.h b/include/rapidjson/allocators.h new file mode 100644 index 0000000..35650af --- /dev/null +++ b/include/rapidjson/allocators.h @@ -0,0 +1,693 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// 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 RAPIDJSON_ALLOCATORS_H_ +#define RAPIDJSON_ALLOCATORS_H_ + +#include "rapidjson.h" +#include "internal/meta.h" + +#include +#include + +#if RAPIDJSON_HAS_CXX11 +#include +#endif + +RAPIDJSON_NAMESPACE_BEGIN + +/////////////////////////////////////////////////////////////////////////////// +// Allocator + +/*! \class rapidjson::Allocator + \brief Concept for allocating, resizing and freeing memory block. + + Note that Malloc() and Realloc() are non-static but Free() is static. + + So if an allocator need to support Free(), it needs to put its pointer in + the header of memory block. + +\code +concept Allocator { + static const bool kNeedFree; //!< Whether this allocator needs to call Free(). + + // Allocate a memory block. + // \param size of the memory block in bytes. + // \returns pointer to the memory block. + void* Malloc(size_t size); + + // Resize a memory block. + // \param originalPtr The pointer to current memory block. Null pointer is permitted. + // \param originalSize The current size in bytes. (Design issue: since some allocator may not book-keep this, explicitly pass to it can save memory.) + // \param newSize the new size in bytes. + void* Realloc(void* originalPtr, size_t originalSize, size_t newSize); + + // Free a memory block. + // \param pointer to the memory block. Null pointer is permitted. + static void Free(void *ptr); +}; +\endcode +*/ + + +/*! \def RAPIDJSON_ALLOCATOR_DEFAULT_CHUNK_CAPACITY + \ingroup RAPIDJSON_CONFIG + \brief User-defined kDefaultChunkCapacity definition. + + User can define this as any \c size that is a power of 2. +*/ + +#ifndef RAPIDJSON_ALLOCATOR_DEFAULT_CHUNK_CAPACITY +#define RAPIDJSON_ALLOCATOR_DEFAULT_CHUNK_CAPACITY (64 * 1024) +#endif + + +/////////////////////////////////////////////////////////////////////////////// +// CrtAllocator + +//! C-runtime library allocator. +/*! This class is just wrapper for standard C library memory routines. + \note implements Allocator concept +*/ +class CrtAllocator { +public: + static const bool kNeedFree = true; + void* Malloc(size_t size) { + if (size) // behavior of malloc(0) is implementation defined. + return RAPIDJSON_MALLOC(size); + else + return NULL; // standardize to returning NULL. + } + void* Realloc(void* originalPtr, size_t originalSize, size_t newSize) { + (void)originalSize; + if (newSize == 0) { + RAPIDJSON_FREE(originalPtr); + return NULL; + } + return RAPIDJSON_REALLOC(originalPtr, newSize); + } + static void Free(void *ptr) RAPIDJSON_NOEXCEPT { RAPIDJSON_FREE(ptr); } + + bool operator==(const CrtAllocator&) const RAPIDJSON_NOEXCEPT { + return true; + } + bool operator!=(const CrtAllocator&) const RAPIDJSON_NOEXCEPT { + return false; + } +}; + +/////////////////////////////////////////////////////////////////////////////// +// MemoryPoolAllocator + +//! Default memory allocator used by the parser and DOM. +/*! This allocator allocate memory blocks from pre-allocated memory chunks. + + It does not free memory blocks. And Realloc() only allocate new memory. + + The memory chunks are allocated by BaseAllocator, which is CrtAllocator by default. + + User may also supply a buffer as the first chunk. + + If the user-buffer is full then additional chunks are allocated by BaseAllocator. + + The user-buffer is not deallocated by this allocator. + + \tparam BaseAllocator the allocator type for allocating memory chunks. Default is CrtAllocator. + \note implements Allocator concept +*/ +template +class MemoryPoolAllocator { + //! Chunk header for perpending to each chunk. + /*! Chunks are stored as a singly linked list. + */ + struct ChunkHeader { + size_t capacity; //!< Capacity of the chunk in bytes (excluding the header itself). + size_t size; //!< Current size of allocated memory in bytes. + ChunkHeader *next; //!< Next chunk in the linked list. + }; + + struct SharedData { + ChunkHeader *chunkHead; //!< Head of the chunk linked-list. Only the head chunk serves allocation. + BaseAllocator* ownBaseAllocator; //!< base allocator created by this object. + size_t refcount; + bool ownBuffer; + }; + + static const size_t SIZEOF_SHARED_DATA = RAPIDJSON_ALIGN(sizeof(SharedData)); + static const size_t SIZEOF_CHUNK_HEADER = RAPIDJSON_ALIGN(sizeof(ChunkHeader)); + + static inline ChunkHeader *GetChunkHead(SharedData *shared) + { + return reinterpret_cast(reinterpret_cast(shared) + SIZEOF_SHARED_DATA); + } + static inline uint8_t *GetChunkBuffer(SharedData *shared) + { + return reinterpret_cast(shared->chunkHead) + SIZEOF_CHUNK_HEADER; + } + + static const size_t kDefaultChunkCapacity = RAPIDJSON_ALLOCATOR_DEFAULT_CHUNK_CAPACITY; //!< Default chunk capacity. + +public: + static const bool kNeedFree = false; //!< Tell users that no need to call Free() with this allocator. (concept Allocator) + static const bool kRefCounted = true; //!< Tell users that this allocator is reference counted on copy + + //! Constructor with chunkSize. + /*! \param chunkSize The size of memory chunk. The default is kDefaultChunkSize. + \param baseAllocator The allocator for allocating memory chunks. + */ + explicit + MemoryPoolAllocator(size_t chunkSize = kDefaultChunkCapacity, BaseAllocator* baseAllocator = 0) : + chunk_capacity_(chunkSize), + baseAllocator_(baseAllocator ? baseAllocator : RAPIDJSON_NEW(BaseAllocator)()), + shared_(static_cast(baseAllocator_ ? baseAllocator_->Malloc(SIZEOF_SHARED_DATA + SIZEOF_CHUNK_HEADER) : 0)) + { + RAPIDJSON_ASSERT(baseAllocator_ != 0); + RAPIDJSON_ASSERT(shared_ != 0); + if (baseAllocator) { + shared_->ownBaseAllocator = 0; + } + else { + shared_->ownBaseAllocator = baseAllocator_; + } + shared_->chunkHead = GetChunkHead(shared_); + shared_->chunkHead->capacity = 0; + shared_->chunkHead->size = 0; + shared_->chunkHead->next = 0; + shared_->ownBuffer = true; + shared_->refcount = 1; + } + + //! Constructor with user-supplied buffer. + /*! The user buffer will be used firstly. When it is full, memory pool allocates new chunk with chunk size. + + The user buffer will not be deallocated when this allocator is destructed. + + \param buffer User supplied buffer. + \param size Size of the buffer in bytes. It must at least larger than sizeof(ChunkHeader). + \param chunkSize The size of memory chunk. The default is kDefaultChunkSize. + \param baseAllocator The allocator for allocating memory chunks. + */ + MemoryPoolAllocator(void *buffer, size_t size, size_t chunkSize = kDefaultChunkCapacity, BaseAllocator* baseAllocator = 0) : + chunk_capacity_(chunkSize), + baseAllocator_(baseAllocator), + shared_(static_cast(AlignBuffer(buffer, size))) + { + RAPIDJSON_ASSERT(size >= SIZEOF_SHARED_DATA + SIZEOF_CHUNK_HEADER); + shared_->chunkHead = GetChunkHead(shared_); + shared_->chunkHead->capacity = size - SIZEOF_SHARED_DATA - SIZEOF_CHUNK_HEADER; + shared_->chunkHead->size = 0; + shared_->chunkHead->next = 0; + shared_->ownBaseAllocator = 0; + shared_->ownBuffer = false; + shared_->refcount = 1; + } + + MemoryPoolAllocator(const MemoryPoolAllocator& rhs) RAPIDJSON_NOEXCEPT : + chunk_capacity_(rhs.chunk_capacity_), + baseAllocator_(rhs.baseAllocator_), + shared_(rhs.shared_) + { + RAPIDJSON_NOEXCEPT_ASSERT(shared_->refcount > 0); + ++shared_->refcount; + } + MemoryPoolAllocator& operator=(const MemoryPoolAllocator& rhs) RAPIDJSON_NOEXCEPT + { + RAPIDJSON_NOEXCEPT_ASSERT(rhs.shared_->refcount > 0); + ++rhs.shared_->refcount; + this->~MemoryPoolAllocator(); + baseAllocator_ = rhs.baseAllocator_; + chunk_capacity_ = rhs.chunk_capacity_; + shared_ = rhs.shared_; + return *this; + } + +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS + MemoryPoolAllocator(MemoryPoolAllocator&& rhs) RAPIDJSON_NOEXCEPT : + chunk_capacity_(rhs.chunk_capacity_), + baseAllocator_(rhs.baseAllocator_), + shared_(rhs.shared_) + { + RAPIDJSON_NOEXCEPT_ASSERT(rhs.shared_->refcount > 0); + rhs.shared_ = 0; + } + MemoryPoolAllocator& operator=(MemoryPoolAllocator&& rhs) RAPIDJSON_NOEXCEPT + { + RAPIDJSON_NOEXCEPT_ASSERT(rhs.shared_->refcount > 0); + this->~MemoryPoolAllocator(); + baseAllocator_ = rhs.baseAllocator_; + chunk_capacity_ = rhs.chunk_capacity_; + shared_ = rhs.shared_; + rhs.shared_ = 0; + return *this; + } +#endif + + //! Destructor. + /*! This deallocates all memory chunks, excluding the user-supplied buffer. + */ + ~MemoryPoolAllocator() RAPIDJSON_NOEXCEPT { + if (!shared_) { + // do nothing if moved + return; + } + if (shared_->refcount > 1) { + --shared_->refcount; + return; + } + Clear(); + BaseAllocator *a = shared_->ownBaseAllocator; + if (shared_->ownBuffer) { + baseAllocator_->Free(shared_); + } + RAPIDJSON_DELETE(a); + } + + //! Deallocates all memory chunks, excluding the first/user one. + void Clear() RAPIDJSON_NOEXCEPT { + RAPIDJSON_NOEXCEPT_ASSERT(shared_->refcount > 0); + for (;;) { + ChunkHeader* c = shared_->chunkHead; + if (!c->next) { + break; + } + shared_->chunkHead = c->next; + baseAllocator_->Free(c); + } + shared_->chunkHead->size = 0; + } + + //! Computes the total capacity of allocated memory chunks. + /*! \return total capacity in bytes. + */ + size_t Capacity() const RAPIDJSON_NOEXCEPT { + RAPIDJSON_NOEXCEPT_ASSERT(shared_->refcount > 0); + size_t capacity = 0; + for (ChunkHeader* c = shared_->chunkHead; c != 0; c = c->next) + capacity += c->capacity; + return capacity; + } + + //! Computes the memory blocks allocated. + /*! \return total used bytes. + */ + size_t Size() const RAPIDJSON_NOEXCEPT { + RAPIDJSON_NOEXCEPT_ASSERT(shared_->refcount > 0); + size_t size = 0; + for (ChunkHeader* c = shared_->chunkHead; c != 0; c = c->next) + size += c->size; + return size; + } + + //! Whether the allocator is shared. + /*! \return true or false. + */ + bool Shared() const RAPIDJSON_NOEXCEPT { + RAPIDJSON_NOEXCEPT_ASSERT(shared_->refcount > 0); + return shared_->refcount > 1; + } + + //! Allocates a memory block. (concept Allocator) + void* Malloc(size_t size) { + RAPIDJSON_NOEXCEPT_ASSERT(shared_->refcount > 0); + if (!size) + return NULL; + + size = RAPIDJSON_ALIGN(size); + if (RAPIDJSON_UNLIKELY(shared_->chunkHead->size + size > shared_->chunkHead->capacity)) + if (!AddChunk(chunk_capacity_ > size ? chunk_capacity_ : size)) + return NULL; + + void *buffer = GetChunkBuffer(shared_) + shared_->chunkHead->size; + shared_->chunkHead->size += size; + return buffer; + } + + //! Resizes a memory block (concept Allocator) + void* Realloc(void* originalPtr, size_t originalSize, size_t newSize) { + if (originalPtr == 0) + return Malloc(newSize); + + RAPIDJSON_NOEXCEPT_ASSERT(shared_->refcount > 0); + if (newSize == 0) + return NULL; + + originalSize = RAPIDJSON_ALIGN(originalSize); + newSize = RAPIDJSON_ALIGN(newSize); + + // Do not shrink if new size is smaller than original + if (originalSize >= newSize) + return originalPtr; + + // Simply expand it if it is the last allocation and there is sufficient space + if (originalPtr == GetChunkBuffer(shared_) + shared_->chunkHead->size - originalSize) { + size_t increment = static_cast(newSize - originalSize); + if (shared_->chunkHead->size + increment <= shared_->chunkHead->capacity) { + shared_->chunkHead->size += increment; + return originalPtr; + } + } + + // Realloc process: allocate and copy memory, do not free original buffer. + if (void* newBuffer = Malloc(newSize)) { + if (originalSize) + std::memcpy(newBuffer, originalPtr, originalSize); + return newBuffer; + } + else + return NULL; + } + + //! Frees a memory block (concept Allocator) + static void Free(void *ptr) RAPIDJSON_NOEXCEPT { (void)ptr; } // Do nothing + + //! Compare (equality) with another MemoryPoolAllocator + bool operator==(const MemoryPoolAllocator& rhs) const RAPIDJSON_NOEXCEPT { + RAPIDJSON_NOEXCEPT_ASSERT(shared_->refcount > 0); + RAPIDJSON_NOEXCEPT_ASSERT(rhs.shared_->refcount > 0); + return shared_ == rhs.shared_; + } + //! Compare (inequality) with another MemoryPoolAllocator + bool operator!=(const MemoryPoolAllocator& rhs) const RAPIDJSON_NOEXCEPT { + return !operator==(rhs); + } + +private: + //! Creates a new chunk. + /*! \param capacity Capacity of the chunk in bytes. + \return true if success. + */ + bool AddChunk(size_t capacity) { + if (!baseAllocator_) + shared_->ownBaseAllocator = baseAllocator_ = RAPIDJSON_NEW(BaseAllocator)(); + if (ChunkHeader* chunk = static_cast(baseAllocator_->Malloc(SIZEOF_CHUNK_HEADER + capacity))) { + chunk->capacity = capacity; + chunk->size = 0; + chunk->next = shared_->chunkHead; + shared_->chunkHead = chunk; + return true; + } + else + return false; + } + + static inline void* AlignBuffer(void* buf, size_t &size) + { + RAPIDJSON_NOEXCEPT_ASSERT(buf != 0); + const uintptr_t mask = sizeof(void*) - 1; + const uintptr_t ubuf = reinterpret_cast(buf); + if (RAPIDJSON_UNLIKELY(ubuf & mask)) { + const uintptr_t abuf = (ubuf + mask) & ~mask; + RAPIDJSON_ASSERT(size >= abuf - ubuf); + buf = reinterpret_cast(abuf); + size -= abuf - ubuf; + } + return buf; + } + + size_t chunk_capacity_; //!< The minimum capacity of chunk when they are allocated. + BaseAllocator* baseAllocator_; //!< base allocator for allocating memory chunks. + SharedData *shared_; //!< The shared data of the allocator +}; + +namespace internal { + template + struct IsRefCounted : + public FalseType + { }; + template + struct IsRefCounted::Type> : + public TrueType + { }; +} + +template +inline T* Realloc(A& a, T* old_p, size_t old_n, size_t new_n) +{ + RAPIDJSON_NOEXCEPT_ASSERT(old_n <= (std::numeric_limits::max)() / sizeof(T) && new_n <= (std::numeric_limits::max)() / sizeof(T)); + return static_cast(a.Realloc(old_p, old_n * sizeof(T), new_n * sizeof(T))); +} + +template +inline T *Malloc(A& a, size_t n = 1) +{ + return Realloc(a, NULL, 0, n); +} + +template +inline void Free(A& a, T *p, size_t n = 1) +{ + static_cast(Realloc(a, p, n, 0)); +} + +#ifdef __GNUC__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(effc++) // std::allocator can safely be inherited +#endif + +template +class StdAllocator : + public std::allocator +{ + typedef std::allocator allocator_type; +#if RAPIDJSON_HAS_CXX11 + typedef std::allocator_traits traits_type; +#else + typedef allocator_type traits_type; +#endif + +public: + typedef BaseAllocator BaseAllocatorType; + + StdAllocator() RAPIDJSON_NOEXCEPT : + allocator_type(), + baseAllocator_() + { } + + StdAllocator(const StdAllocator& rhs) RAPIDJSON_NOEXCEPT : + allocator_type(rhs), + baseAllocator_(rhs.baseAllocator_) + { } + + template + StdAllocator(const StdAllocator& rhs) RAPIDJSON_NOEXCEPT : + allocator_type(rhs), + baseAllocator_(rhs.baseAllocator_) + { } + +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS + StdAllocator(StdAllocator&& rhs) RAPIDJSON_NOEXCEPT : + allocator_type(std::move(rhs)), + baseAllocator_(std::move(rhs.baseAllocator_)) + { } +#endif +#if RAPIDJSON_HAS_CXX11 + using propagate_on_container_move_assignment = std::true_type; + using propagate_on_container_swap = std::true_type; +#endif + + /* implicit */ + StdAllocator(const BaseAllocator& allocator) RAPIDJSON_NOEXCEPT : + allocator_type(), + baseAllocator_(allocator) + { } + + ~StdAllocator() RAPIDJSON_NOEXCEPT + { } + + template + struct rebind { + typedef StdAllocator other; + }; + + typedef typename traits_type::size_type size_type; + typedef typename traits_type::difference_type difference_type; + + typedef typename traits_type::value_type value_type; + typedef typename traits_type::pointer pointer; + typedef typename traits_type::const_pointer const_pointer; + +#if RAPIDJSON_HAS_CXX11 + + typedef typename std::add_lvalue_reference::type &reference; + typedef typename std::add_lvalue_reference::type>::type &const_reference; + + pointer address(reference r) const RAPIDJSON_NOEXCEPT + { + return std::addressof(r); + } + const_pointer address(const_reference r) const RAPIDJSON_NOEXCEPT + { + return std::addressof(r); + } + + size_type max_size() const RAPIDJSON_NOEXCEPT + { + return traits_type::max_size(*this); + } + + template + void construct(pointer p, Args&&... args) + { + traits_type::construct(*this, p, std::forward(args)...); + } + void destroy(pointer p) + { + traits_type::destroy(*this, p); + } + +#else // !RAPIDJSON_HAS_CXX11 + + typedef typename allocator_type::reference reference; + typedef typename allocator_type::const_reference const_reference; + + pointer address(reference r) const RAPIDJSON_NOEXCEPT + { + return allocator_type::address(r); + } + const_pointer address(const_reference r) const RAPIDJSON_NOEXCEPT + { + return allocator_type::address(r); + } + + size_type max_size() const RAPIDJSON_NOEXCEPT + { + return allocator_type::max_size(); + } + + void construct(pointer p, const_reference r) + { + allocator_type::construct(p, r); + } + void destroy(pointer p) + { + allocator_type::destroy(p); + } + +#endif // !RAPIDJSON_HAS_CXX11 + + template + U* allocate(size_type n = 1, const void* = 0) + { + return RAPIDJSON_NAMESPACE::Malloc(baseAllocator_, n); + } + template + void deallocate(U* p, size_type n = 1) + { + RAPIDJSON_NAMESPACE::Free(baseAllocator_, p, n); + } + + pointer allocate(size_type n = 1, const void* = 0) + { + return allocate(n); + } + void deallocate(pointer p, size_type n = 1) + { + deallocate(p, n); + } + +#if RAPIDJSON_HAS_CXX11 + using is_always_equal = std::is_empty; +#endif + + template + bool operator==(const StdAllocator& rhs) const RAPIDJSON_NOEXCEPT + { + return baseAllocator_ == rhs.baseAllocator_; + } + template + bool operator!=(const StdAllocator& rhs) const RAPIDJSON_NOEXCEPT + { + return !operator==(rhs); + } + + //! rapidjson Allocator concept + static const bool kNeedFree = BaseAllocator::kNeedFree; + static const bool kRefCounted = internal::IsRefCounted::Value; + void* Malloc(size_t size) + { + return baseAllocator_.Malloc(size); + } + void* Realloc(void* originalPtr, size_t originalSize, size_t newSize) + { + return baseAllocator_.Realloc(originalPtr, originalSize, newSize); + } + static void Free(void *ptr) RAPIDJSON_NOEXCEPT + { + BaseAllocator::Free(ptr); + } + +private: + template + friend class StdAllocator; // access to StdAllocator.* + + BaseAllocator baseAllocator_; +}; + +#if !RAPIDJSON_HAS_CXX17 // std::allocator deprecated in C++17 +template +class StdAllocator : + public std::allocator +{ + typedef std::allocator allocator_type; + +public: + typedef BaseAllocator BaseAllocatorType; + + StdAllocator() RAPIDJSON_NOEXCEPT : + allocator_type(), + baseAllocator_() + { } + + StdAllocator(const StdAllocator& rhs) RAPIDJSON_NOEXCEPT : + allocator_type(rhs), + baseAllocator_(rhs.baseAllocator_) + { } + + template + StdAllocator(const StdAllocator& rhs) RAPIDJSON_NOEXCEPT : + allocator_type(rhs), + baseAllocator_(rhs.baseAllocator_) + { } + + /* implicit */ + StdAllocator(const BaseAllocator& baseAllocator) RAPIDJSON_NOEXCEPT : + allocator_type(), + baseAllocator_(baseAllocator) + { } + + ~StdAllocator() RAPIDJSON_NOEXCEPT + { } + + template + struct rebind { + typedef StdAllocator other; + }; + + typedef typename allocator_type::value_type value_type; + +private: + template + friend class StdAllocator; // access to StdAllocator.* + + BaseAllocator baseAllocator_; +}; +#endif + +#ifdef __GNUC__ +RAPIDJSON_DIAG_POP +#endif + +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_ENCODINGS_H_ diff --git a/include/rapidjson/cursorstreamwrapper.h b/include/rapidjson/cursorstreamwrapper.h new file mode 100644 index 0000000..fd6513d --- /dev/null +++ b/include/rapidjson/cursorstreamwrapper.h @@ -0,0 +1,78 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// 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 RAPIDJSON_CURSORSTREAMWRAPPER_H_ +#define RAPIDJSON_CURSORSTREAMWRAPPER_H_ + +#include "stream.h" + +#if defined(__GNUC__) +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(effc++) +#endif + +#if defined(_MSC_VER) && _MSC_VER <= 1800 +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(4702) // unreachable code +RAPIDJSON_DIAG_OFF(4512) // assignment operator could not be generated +#endif + +RAPIDJSON_NAMESPACE_BEGIN + + +//! Cursor stream wrapper for counting line and column number if error exists. +/*! + \tparam InputStream Any stream that implements Stream Concept +*/ +template > +class CursorStreamWrapper : public GenericStreamWrapper { +public: + typedef typename Encoding::Ch Ch; + + CursorStreamWrapper(InputStream& is): + GenericStreamWrapper(is), line_(1), col_(0) {} + + // counting line and column number + Ch Take() { + Ch ch = this->is_.Take(); + if(ch == '\n') { + line_ ++; + col_ = 0; + } else { + col_ ++; + } + return ch; + } + + //! Get the error line number, if error exists. + size_t GetLine() const { return line_; } + //! Get the error column number, if error exists. + size_t GetColumn() const { return col_; } + +private: + size_t line_; //!< Current Line + size_t col_; //!< Current Column +}; + +#if defined(_MSC_VER) && _MSC_VER <= 1800 +RAPIDJSON_DIAG_POP +#endif + +#if defined(__GNUC__) +RAPIDJSON_DIAG_POP +#endif + +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_CURSORSTREAMWRAPPER_H_ diff --git a/include/rapidjson/document.h b/include/rapidjson/document.h new file mode 100644 index 0000000..2cd9a70 --- /dev/null +++ b/include/rapidjson/document.h @@ -0,0 +1,3043 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// 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 RAPIDJSON_DOCUMENT_H_ +#define RAPIDJSON_DOCUMENT_H_ + +/*! \file document.h */ + +#include "reader.h" +#include "internal/meta.h" +#include "internal/strfunc.h" +#include "memorystream.h" +#include "encodedstream.h" +#include // placement new +#include +#ifdef __cpp_lib_three_way_comparison +#include +#endif + +RAPIDJSON_DIAG_PUSH +#ifdef __clang__ +RAPIDJSON_DIAG_OFF(padded) +RAPIDJSON_DIAG_OFF(switch-enum) +RAPIDJSON_DIAG_OFF(c++98-compat) +#elif defined(_MSC_VER) +RAPIDJSON_DIAG_OFF(4127) // conditional expression is constant +RAPIDJSON_DIAG_OFF(4244) // conversion from kXxxFlags to 'uint16_t', possible loss of data +#endif + +#ifdef __GNUC__ +RAPIDJSON_DIAG_OFF(effc++) +#endif // __GNUC__ + +#ifdef GetObject +// see https://github.com/Tencent/rapidjson/issues/1448 +// a former included windows.h might have defined a macro called GetObject, which affects +// GetObject defined here. This ensures the macro does not get applied +#pragma push_macro("GetObject") +#define RAPIDJSON_WINDOWS_GETOBJECT_WORKAROUND_APPLIED +#undef GetObject +#endif + +#ifndef RAPIDJSON_NOMEMBERITERATORCLASS +#include // std::random_access_iterator_tag +#endif + +#if RAPIDJSON_USE_MEMBERSMAP +#include // std::multimap +#endif + +RAPIDJSON_NAMESPACE_BEGIN + +// Forward declaration. +template +class GenericValue; + +template +class GenericDocument; + +/*! \def RAPIDJSON_DEFAULT_ALLOCATOR + \ingroup RAPIDJSON_CONFIG + \brief Allows to choose default allocator. + + User can define this to use CrtAllocator or MemoryPoolAllocator. +*/ +#ifndef RAPIDJSON_DEFAULT_ALLOCATOR +#define RAPIDJSON_DEFAULT_ALLOCATOR ::RAPIDJSON_NAMESPACE::MemoryPoolAllocator<::RAPIDJSON_NAMESPACE::CrtAllocator> +#endif + +/*! \def RAPIDJSON_DEFAULT_STACK_ALLOCATOR + \ingroup RAPIDJSON_CONFIG + \brief Allows to choose default stack allocator for Document. + + User can define this to use CrtAllocator or MemoryPoolAllocator. +*/ +#ifndef RAPIDJSON_DEFAULT_STACK_ALLOCATOR +#define RAPIDJSON_DEFAULT_STACK_ALLOCATOR ::RAPIDJSON_NAMESPACE::CrtAllocator +#endif + +/*! \def RAPIDJSON_VALUE_DEFAULT_OBJECT_CAPACITY + \ingroup RAPIDJSON_CONFIG + \brief User defined kDefaultObjectCapacity value. + + User can define this as any natural number. +*/ +#ifndef RAPIDJSON_VALUE_DEFAULT_OBJECT_CAPACITY +// number of objects that rapidjson::Value allocates memory for by default +#define RAPIDJSON_VALUE_DEFAULT_OBJECT_CAPACITY 16 +#endif + +/*! \def RAPIDJSON_VALUE_DEFAULT_ARRAY_CAPACITY + \ingroup RAPIDJSON_CONFIG + \brief User defined kDefaultArrayCapacity value. + + User can define this as any natural number. +*/ +#ifndef RAPIDJSON_VALUE_DEFAULT_ARRAY_CAPACITY +// number of array elements that rapidjson::Value allocates memory for by default +#define RAPIDJSON_VALUE_DEFAULT_ARRAY_CAPACITY 16 +#endif + +//! Name-value pair in a JSON object value. +/*! + This class was internal to GenericValue. It used to be a inner struct. + But a compiler (IBM XL C/C++ for AIX) have reported to have problem with that so it moved as a namespace scope struct. + https://code.google.com/p/rapidjson/issues/detail?id=64 +*/ +template +class GenericMember { +public: + GenericValue name; //!< name of member (must be a string) + GenericValue value; //!< value of member. + +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS + //! Move constructor in C++11 + GenericMember(GenericMember&& rhs) RAPIDJSON_NOEXCEPT + : name(std::move(rhs.name)), + value(std::move(rhs.value)) + { + } + + //! Move assignment in C++11 + GenericMember& operator=(GenericMember&& rhs) RAPIDJSON_NOEXCEPT { + return *this = static_cast(rhs); + } +#endif + + //! Assignment with move semantics. + /*! \param rhs Source of the assignment. Its name and value will become a null value after assignment. + */ + GenericMember& operator=(GenericMember& rhs) RAPIDJSON_NOEXCEPT { + if (RAPIDJSON_LIKELY(this != &rhs)) { + name = rhs.name; + value = rhs.value; + } + return *this; + } + + // swap() for std::sort() and other potential use in STL. + friend inline void swap(GenericMember& a, GenericMember& b) RAPIDJSON_NOEXCEPT { + a.name.Swap(b.name); + a.value.Swap(b.value); + } + +private: + //! Copy constructor is not permitted. + GenericMember(const GenericMember& rhs); +}; + +/////////////////////////////////////////////////////////////////////////////// +// GenericMemberIterator + +#ifndef RAPIDJSON_NOMEMBERITERATORCLASS + +//! (Constant) member iterator for a JSON object value +/*! + \tparam Const Is this a constant iterator? + \tparam Encoding Encoding of the value. (Even non-string values need to have the same encoding in a document) + \tparam Allocator Allocator type for allocating memory of object, array and string. + + This class implements a Random Access Iterator for GenericMember elements + of a GenericValue, see ISO/IEC 14882:2003(E) C++ standard, 24.1 [lib.iterator.requirements]. + + \note This iterator implementation is mainly intended to avoid implicit + conversions from iterator values to \c NULL, + e.g. from GenericValue::FindMember. + + \note Define \c RAPIDJSON_NOMEMBERITERATORCLASS to fall back to a + pointer-based implementation, if your platform doesn't provide + the C++ header. + + \see GenericMember, GenericValue::MemberIterator, GenericValue::ConstMemberIterator + */ +template +class GenericMemberIterator { + + friend class GenericValue; + template friend class GenericMemberIterator; + + typedef GenericMember PlainType; + typedef typename internal::MaybeAddConst::Type ValueType; + +public: + //! Iterator type itself + typedef GenericMemberIterator Iterator; + //! Constant iterator type + typedef GenericMemberIterator ConstIterator; + //! Non-constant iterator type + typedef GenericMemberIterator NonConstIterator; + + /** \name std::iterator_traits support */ + //@{ + typedef ValueType value_type; + typedef ValueType * pointer; + typedef ValueType & reference; + typedef std::ptrdiff_t difference_type; + typedef std::random_access_iterator_tag iterator_category; + //@} + + //! Pointer to (const) GenericMember + typedef pointer Pointer; + //! Reference to (const) GenericMember + typedef reference Reference; + //! Signed integer type (e.g. \c ptrdiff_t) + typedef difference_type DifferenceType; + + //! Default constructor (singular value) + /*! Creates an iterator pointing to no element. + \note All operations, except for comparisons, are undefined on such values. + */ + GenericMemberIterator() : ptr_() {} + + //! Iterator conversions to more const + /*! + \param it (Non-const) iterator to copy from + + Allows the creation of an iterator from another GenericMemberIterator + that is "less const". Especially, creating a non-constant iterator + from a constant iterator are disabled: + \li const -> non-const (not ok) + \li const -> const (ok) + \li non-const -> const (ok) + \li non-const -> non-const (ok) + + \note If the \c Const template parameter is already \c false, this + constructor effectively defines a regular copy-constructor. + Otherwise, the copy constructor is implicitly defined. + */ + GenericMemberIterator(const NonConstIterator & it) : ptr_(it.ptr_) {} + Iterator& operator=(const NonConstIterator & it) { ptr_ = it.ptr_; return *this; } + + //! @name stepping + //@{ + Iterator& operator++(){ ++ptr_; return *this; } + Iterator& operator--(){ --ptr_; return *this; } + Iterator operator++(int){ Iterator old(*this); ++ptr_; return old; } + Iterator operator--(int){ Iterator old(*this); --ptr_; return old; } + //@} + + //! @name increment/decrement + //@{ + Iterator operator+(DifferenceType n) const { return Iterator(ptr_+n); } + Iterator operator-(DifferenceType n) const { return Iterator(ptr_-n); } + + Iterator& operator+=(DifferenceType n) { ptr_+=n; return *this; } + Iterator& operator-=(DifferenceType n) { ptr_-=n; return *this; } + //@} + + //! @name relations + //@{ + template bool operator==(const GenericMemberIterator& that) const { return ptr_ == that.ptr_; } + template bool operator!=(const GenericMemberIterator& that) const { return ptr_ != that.ptr_; } + template bool operator<=(const GenericMemberIterator& that) const { return ptr_ <= that.ptr_; } + template bool operator>=(const GenericMemberIterator& that) const { return ptr_ >= that.ptr_; } + template bool operator< (const GenericMemberIterator& that) const { return ptr_ < that.ptr_; } + template bool operator> (const GenericMemberIterator& that) const { return ptr_ > that.ptr_; } + +#ifdef __cpp_lib_three_way_comparison + template std::strong_ordering operator<=>(const GenericMemberIterator& that) const { return ptr_ <=> that.ptr_; } +#endif + //@} + + //! @name dereference + //@{ + Reference operator*() const { return *ptr_; } + Pointer operator->() const { return ptr_; } + Reference operator[](DifferenceType n) const { return ptr_[n]; } + //@} + + //! Distance + DifferenceType operator-(ConstIterator that) const { return ptr_-that.ptr_; } + +private: + //! Internal constructor from plain pointer + explicit GenericMemberIterator(Pointer p) : ptr_(p) {} + + Pointer ptr_; //!< raw pointer +}; + +#else // RAPIDJSON_NOMEMBERITERATORCLASS + +// class-based member iterator implementation disabled, use plain pointers + +template +class GenericMemberIterator; + +//! non-const GenericMemberIterator +template +class GenericMemberIterator { +public: + //! use plain pointer as iterator type + typedef GenericMember* Iterator; +}; +//! const GenericMemberIterator +template +class GenericMemberIterator { +public: + //! use plain const pointer as iterator type + typedef const GenericMember* Iterator; +}; + +#endif // RAPIDJSON_NOMEMBERITERATORCLASS + +/////////////////////////////////////////////////////////////////////////////// +// GenericStringRef + +//! Reference to a constant string (not taking a copy) +/*! + \tparam CharType character type of the string + + This helper class is used to automatically infer constant string + references for string literals, especially from \c const \b (!) + character arrays. + + The main use is for creating JSON string values without copying the + source string via an \ref Allocator. This requires that the referenced + string pointers have a sufficient lifetime, which exceeds the lifetime + of the associated GenericValue. + + \b Example + \code + Value v("foo"); // ok, no need to copy & calculate length + const char foo[] = "foo"; + v.SetString(foo); // ok + + const char* bar = foo; + // Value x(bar); // not ok, can't rely on bar's lifetime + Value x(StringRef(bar)); // lifetime explicitly guaranteed by user + Value y(StringRef(bar, 3)); // ok, explicitly pass length + \endcode + + \see StringRef, GenericValue::SetString +*/ +template +struct GenericStringRef { + typedef CharType Ch; //!< character type of the string + + //! Create string reference from \c const character array +#ifndef __clang__ // -Wdocumentation + /*! + This constructor implicitly creates a constant string reference from + a \c const character array. It has better performance than + \ref StringRef(const CharType*) by inferring the string \ref length + from the array length, and also supports strings containing null + characters. + + \tparam N length of the string, automatically inferred + + \param str Constant character array, lifetime assumed to be longer + than the use of the string in e.g. a GenericValue + + \post \ref s == str + + \note Constant complexity. + \note There is a hidden, private overload to disallow references to + non-const character arrays to be created via this constructor. + By this, e.g. function-scope arrays used to be filled via + \c snprintf are excluded from consideration. + In such cases, the referenced string should be \b copied to the + GenericValue instead. + */ +#endif + template + GenericStringRef(const CharType (&str)[N]) RAPIDJSON_NOEXCEPT + : s(str), length(N-1) {} + + //! Explicitly create string reference from \c const character pointer +#ifndef __clang__ // -Wdocumentation + /*! + This constructor can be used to \b explicitly create a reference to + a constant string pointer. + + \see StringRef(const CharType*) + + \param str Constant character pointer, lifetime assumed to be longer + than the use of the string in e.g. a GenericValue + + \post \ref s == str + + \note There is a hidden, private overload to disallow references to + non-const character arrays to be created via this constructor. + By this, e.g. function-scope arrays used to be filled via + \c snprintf are excluded from consideration. + In such cases, the referenced string should be \b copied to the + GenericValue instead. + */ +#endif + explicit GenericStringRef(const CharType* str) + : s(str), length(NotNullStrLen(str)) {} + + //! Create constant string reference from pointer and length +#ifndef __clang__ // -Wdocumentation + /*! \param str constant string, lifetime assumed to be longer than the use of the string in e.g. a GenericValue + \param len length of the string, excluding the trailing NULL terminator + + \post \ref s == str && \ref length == len + \note Constant complexity. + */ +#endif + GenericStringRef(const CharType* str, SizeType len) + : s(RAPIDJSON_LIKELY(str) ? str : emptyString), length(len) { RAPIDJSON_ASSERT(str != 0 || len == 0u); } + + GenericStringRef(const GenericStringRef& rhs) : s(rhs.s), length(rhs.length) {} + + //! implicit conversion to plain CharType pointer + operator const Ch *() const { return s; } + + const Ch* const s; //!< plain CharType pointer + const SizeType length; //!< length of the string (excluding the trailing NULL terminator) + +private: + SizeType NotNullStrLen(const CharType* str) { + RAPIDJSON_ASSERT(str != 0); + return internal::StrLen(str); + } + + /// Empty string - used when passing in a NULL pointer + static const Ch emptyString[]; + + //! Disallow construction from non-const array + template + GenericStringRef(CharType (&str)[N]) /* = delete */; + //! Copy assignment operator not permitted - immutable type + GenericStringRef& operator=(const GenericStringRef& rhs) /* = delete */; +}; + +template +const CharType GenericStringRef::emptyString[] = { CharType() }; + +//! Mark a character pointer as constant string +/*! Mark a plain character pointer as a "string literal". This function + can be used to avoid copying a character string to be referenced as a + value in a JSON GenericValue object, if the string's lifetime is known + to be valid long enough. + \tparam CharType Character type of the string + \param str Constant string, lifetime assumed to be longer than the use of the string in e.g. a GenericValue + \return GenericStringRef string reference object + \relatesalso GenericStringRef + + \see GenericValue::GenericValue(StringRefType), GenericValue::operator=(StringRefType), GenericValue::SetString(StringRefType), GenericValue::PushBack(StringRefType, Allocator&), GenericValue::AddMember +*/ +template +inline GenericStringRef StringRef(const CharType* str) { + return GenericStringRef(str); +} + +//! Mark a character pointer as constant string +/*! Mark a plain character pointer as a "string literal". This function + can be used to avoid copying a character string to be referenced as a + value in a JSON GenericValue object, if the string's lifetime is known + to be valid long enough. + + This version has better performance with supplied length, and also + supports string containing null characters. + + \tparam CharType character type of the string + \param str Constant string, lifetime assumed to be longer than the use of the string in e.g. a GenericValue + \param length The length of source string. + \return GenericStringRef string reference object + \relatesalso GenericStringRef +*/ +template +inline GenericStringRef StringRef(const CharType* str, size_t length) { + return GenericStringRef(str, SizeType(length)); +} + +#if RAPIDJSON_HAS_STDSTRING +//! Mark a string object as constant string +/*! Mark a string object (e.g. \c std::string) as a "string literal". + This function can be used to avoid copying a string to be referenced as a + value in a JSON GenericValue object, if the string's lifetime is known + to be valid long enough. + + \tparam CharType character type of the string + \param str Constant string, lifetime assumed to be longer than the use of the string in e.g. a GenericValue + \return GenericStringRef string reference object + \relatesalso GenericStringRef + \note Requires the definition of the preprocessor symbol \ref RAPIDJSON_HAS_STDSTRING. +*/ +template +inline GenericStringRef StringRef(const std::basic_string& str) { + return GenericStringRef(str.data(), SizeType(str.size())); +} +#endif + +/////////////////////////////////////////////////////////////////////////////// +// GenericValue type traits +namespace internal { + +template +struct IsGenericValueImpl : FalseType {}; + +// select candidates according to nested encoding and allocator types +template struct IsGenericValueImpl::Type, typename Void::Type> + : IsBaseOf, T>::Type {}; + +// helper to match arbitrary GenericValue instantiations, including derived classes +template struct IsGenericValue : IsGenericValueImpl::Type {}; + +} // namespace internal + +/////////////////////////////////////////////////////////////////////////////// +// TypeHelper + +namespace internal { + +template +struct TypeHelper {}; + +template +struct TypeHelper { + static bool Is(const ValueType& v) { return v.IsBool(); } + static bool Get(const ValueType& v) { return v.GetBool(); } + static ValueType& Set(ValueType& v, bool data) { return v.SetBool(data); } + static ValueType& Set(ValueType& v, bool data, typename ValueType::AllocatorType&) { return v.SetBool(data); } +}; + +template +struct TypeHelper { + static bool Is(const ValueType& v) { return v.IsInt(); } + static int Get(const ValueType& v) { return v.GetInt(); } + static ValueType& Set(ValueType& v, int data) { return v.SetInt(data); } + static ValueType& Set(ValueType& v, int data, typename ValueType::AllocatorType&) { return v.SetInt(data); } +}; + +template +struct TypeHelper { + static bool Is(const ValueType& v) { return v.IsUint(); } + static unsigned Get(const ValueType& v) { return v.GetUint(); } + static ValueType& Set(ValueType& v, unsigned data) { return v.SetUint(data); } + static ValueType& Set(ValueType& v, unsigned data, typename ValueType::AllocatorType&) { return v.SetUint(data); } +}; + +#ifdef _MSC_VER +RAPIDJSON_STATIC_ASSERT(sizeof(long) == sizeof(int)); +template +struct TypeHelper { + static bool Is(const ValueType& v) { return v.IsInt(); } + static long Get(const ValueType& v) { return v.GetInt(); } + static ValueType& Set(ValueType& v, long data) { return v.SetInt(data); } + static ValueType& Set(ValueType& v, long data, typename ValueType::AllocatorType&) { return v.SetInt(data); } +}; + +RAPIDJSON_STATIC_ASSERT(sizeof(unsigned long) == sizeof(unsigned)); +template +struct TypeHelper { + static bool Is(const ValueType& v) { return v.IsUint(); } + static unsigned long Get(const ValueType& v) { return v.GetUint(); } + static ValueType& Set(ValueType& v, unsigned long data) { return v.SetUint(data); } + static ValueType& Set(ValueType& v, unsigned long data, typename ValueType::AllocatorType&) { return v.SetUint(data); } +}; +#endif + +template +struct TypeHelper { + static bool Is(const ValueType& v) { return v.IsInt64(); } + static int64_t Get(const ValueType& v) { return v.GetInt64(); } + static ValueType& Set(ValueType& v, int64_t data) { return v.SetInt64(data); } + static ValueType& Set(ValueType& v, int64_t data, typename ValueType::AllocatorType&) { return v.SetInt64(data); } +}; + +template +struct TypeHelper { + static bool Is(const ValueType& v) { return v.IsUint64(); } + static uint64_t Get(const ValueType& v) { return v.GetUint64(); } + static ValueType& Set(ValueType& v, uint64_t data) { return v.SetUint64(data); } + static ValueType& Set(ValueType& v, uint64_t data, typename ValueType::AllocatorType&) { return v.SetUint64(data); } +}; + +template +struct TypeHelper { + static bool Is(const ValueType& v) { return v.IsDouble(); } + static double Get(const ValueType& v) { return v.GetDouble(); } + static ValueType& Set(ValueType& v, double data) { return v.SetDouble(data); } + static ValueType& Set(ValueType& v, double data, typename ValueType::AllocatorType&) { return v.SetDouble(data); } +}; + +template +struct TypeHelper { + static bool Is(const ValueType& v) { return v.IsFloat(); } + static float Get(const ValueType& v) { return v.GetFloat(); } + static ValueType& Set(ValueType& v, float data) { return v.SetFloat(data); } + static ValueType& Set(ValueType& v, float data, typename ValueType::AllocatorType&) { return v.SetFloat(data); } +}; + +template +struct TypeHelper { + typedef const typename ValueType::Ch* StringType; + static bool Is(const ValueType& v) { return v.IsString(); } + static StringType Get(const ValueType& v) { return v.GetString(); } + static ValueType& Set(ValueType& v, const StringType data) { return v.SetString(typename ValueType::StringRefType(data)); } + static ValueType& Set(ValueType& v, const StringType data, typename ValueType::AllocatorType& a) { return v.SetString(data, a); } +}; + +#if RAPIDJSON_HAS_STDSTRING +template +struct TypeHelper > { + typedef std::basic_string StringType; + static bool Is(const ValueType& v) { return v.IsString(); } + static StringType Get(const ValueType& v) { return StringType(v.GetString(), v.GetStringLength()); } + static ValueType& Set(ValueType& v, const StringType& data, typename ValueType::AllocatorType& a) { return v.SetString(data, a); } +}; +#endif + +template +struct TypeHelper { + typedef typename ValueType::Array ArrayType; + static bool Is(const ValueType& v) { return v.IsArray(); } + static ArrayType Get(ValueType& v) { return v.GetArray(); } + static ValueType& Set(ValueType& v, ArrayType data) { return v = data; } + static ValueType& Set(ValueType& v, ArrayType data, typename ValueType::AllocatorType&) { return v = data; } +}; + +template +struct TypeHelper { + typedef typename ValueType::ConstArray ArrayType; + static bool Is(const ValueType& v) { return v.IsArray(); } + static ArrayType Get(const ValueType& v) { return v.GetArray(); } +}; + +template +struct TypeHelper { + typedef typename ValueType::Object ObjectType; + static bool Is(const ValueType& v) { return v.IsObject(); } + static ObjectType Get(ValueType& v) { return v.GetObject(); } + static ValueType& Set(ValueType& v, ObjectType data) { return v = data; } + static ValueType& Set(ValueType& v, ObjectType data, typename ValueType::AllocatorType&) { return v = data; } +}; + +template +struct TypeHelper { + typedef typename ValueType::ConstObject ObjectType; + static bool Is(const ValueType& v) { return v.IsObject(); } + static ObjectType Get(const ValueType& v) { return v.GetObject(); } +}; + +} // namespace internal + +// Forward declarations +template class GenericArray; +template class GenericObject; + +/////////////////////////////////////////////////////////////////////////////// +// GenericValue + +//! Represents a JSON value. Use Value for UTF8 encoding and default allocator. +/*! + A JSON value can be one of 7 types. This class is a variant type supporting + these types. + + Use the Value if UTF8 and default allocator + + \tparam Encoding Encoding of the value. (Even non-string values need to have the same encoding in a document) + \tparam Allocator Allocator type for allocating memory of object, array and string. +*/ +template +class GenericValue { +public: + //! Name-value pair in an object. + typedef GenericMember Member; + typedef Encoding EncodingType; //!< Encoding type from template parameter. + typedef Allocator AllocatorType; //!< Allocator type from template parameter. + typedef typename Encoding::Ch Ch; //!< Character type derived from Encoding. + typedef GenericStringRef StringRefType; //!< Reference to a constant string + typedef typename GenericMemberIterator::Iterator MemberIterator; //!< Member iterator for iterating in object. + typedef typename GenericMemberIterator::Iterator ConstMemberIterator; //!< Constant member iterator for iterating in object. + typedef GenericValue* ValueIterator; //!< Value iterator for iterating in array. + typedef const GenericValue* ConstValueIterator; //!< Constant value iterator for iterating in array. + typedef GenericValue ValueType; //!< Value type of itself. + typedef GenericArray Array; + typedef GenericArray ConstArray; + typedef GenericObject Object; + typedef GenericObject ConstObject; + + //!@name Constructors and destructor. + //@{ + + //! Default constructor creates a null value. + GenericValue() RAPIDJSON_NOEXCEPT : data_() { data_.f.flags = kNullFlag; } + +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS + //! Move constructor in C++11 + GenericValue(GenericValue&& rhs) RAPIDJSON_NOEXCEPT : data_(rhs.data_) { + rhs.data_.f.flags = kNullFlag; // give up contents + } +#endif + +private: + //! Copy constructor is not permitted. + GenericValue(const GenericValue& rhs); + +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS + //! Moving from a GenericDocument is not permitted. + template + GenericValue(GenericDocument&& rhs); + + //! Move assignment from a GenericDocument is not permitted. + template + GenericValue& operator=(GenericDocument&& rhs); +#endif + +public: + + //! Constructor with JSON value type. + /*! This creates a Value of specified type with default content. + \param type Type of the value. + \note Default content for number is zero. + */ + explicit GenericValue(Type type) RAPIDJSON_NOEXCEPT : data_() { + static const uint16_t defaultFlags[] = { + kNullFlag, kFalseFlag, kTrueFlag, kObjectFlag, kArrayFlag, kShortStringFlag, + kNumberAnyFlag + }; + RAPIDJSON_NOEXCEPT_ASSERT(type >= kNullType && type <= kNumberType); + data_.f.flags = defaultFlags[type]; + + // Use ShortString to store empty string. + if (type == kStringType) + data_.ss.SetLength(0); + } + + //! Explicit copy constructor (with allocator) + /*! Creates a copy of a Value by using the given Allocator + \tparam SourceAllocator allocator of \c rhs + \param rhs Value to copy from (read-only) + \param allocator Allocator for allocating copied elements and buffers. Commonly use GenericDocument::GetAllocator(). + \param copyConstStrings Force copying of constant strings (e.g. referencing an in-situ buffer) + \see CopyFrom() + */ + template + GenericValue(const GenericValue& rhs, Allocator& allocator, bool copyConstStrings = false) { + switch (rhs.GetType()) { + case kObjectType: + DoCopyMembers(rhs, allocator, copyConstStrings); + break; + case kArrayType: { + SizeType count = rhs.data_.a.size; + GenericValue* le = reinterpret_cast(allocator.Malloc(count * sizeof(GenericValue))); + const GenericValue* re = rhs.GetElementsPointer(); + for (SizeType i = 0; i < count; i++) + new (&le[i]) GenericValue(re[i], allocator, copyConstStrings); + data_.f.flags = kArrayFlag; + data_.a.size = data_.a.capacity = count; + SetElementsPointer(le); + } + break; + case kStringType: + if (rhs.data_.f.flags == kConstStringFlag && !copyConstStrings) { + data_.f.flags = rhs.data_.f.flags; + data_ = *reinterpret_cast(&rhs.data_); + } + else + SetStringRaw(StringRef(rhs.GetString(), rhs.GetStringLength()), allocator); + break; + default: + data_.f.flags = rhs.data_.f.flags; + data_ = *reinterpret_cast(&rhs.data_); + break; + } + } + + //! Constructor for boolean value. + /*! \param b Boolean value + \note This constructor is limited to \em real boolean values and rejects + implicitly converted types like arbitrary pointers. Use an explicit cast + to \c bool, if you want to construct a boolean JSON value in such cases. + */ +#ifndef RAPIDJSON_DOXYGEN_RUNNING // hide SFINAE from Doxygen + template + explicit GenericValue(T b, RAPIDJSON_ENABLEIF((internal::IsSame))) RAPIDJSON_NOEXCEPT // See #472 +#else + explicit GenericValue(bool b) RAPIDJSON_NOEXCEPT +#endif + : data_() { + // safe-guard against failing SFINAE + RAPIDJSON_STATIC_ASSERT((internal::IsSame::Value)); + data_.f.flags = b ? kTrueFlag : kFalseFlag; + } + + //! Constructor for int value. + explicit GenericValue(int i) RAPIDJSON_NOEXCEPT : data_() { + data_.n.i64 = i; + data_.f.flags = (i >= 0) ? (kNumberIntFlag | kUintFlag | kUint64Flag) : kNumberIntFlag; + } + + //! Constructor for unsigned value. + explicit GenericValue(unsigned u) RAPIDJSON_NOEXCEPT : data_() { + data_.n.u64 = u; + data_.f.flags = (u & 0x80000000) ? kNumberUintFlag : (kNumberUintFlag | kIntFlag | kInt64Flag); + } + + //! Constructor for int64_t value. + explicit GenericValue(int64_t i64) RAPIDJSON_NOEXCEPT : data_() { + data_.n.i64 = i64; + data_.f.flags = kNumberInt64Flag; + if (i64 >= 0) { + data_.f.flags |= kNumberUint64Flag; + if (!(static_cast(i64) & RAPIDJSON_UINT64_C2(0xFFFFFFFF, 0x00000000))) + data_.f.flags |= kUintFlag; + if (!(static_cast(i64) & RAPIDJSON_UINT64_C2(0xFFFFFFFF, 0x80000000))) + data_.f.flags |= kIntFlag; + } + else if (i64 >= static_cast(RAPIDJSON_UINT64_C2(0xFFFFFFFF, 0x80000000))) + data_.f.flags |= kIntFlag; + } + + //! Constructor for uint64_t value. + explicit GenericValue(uint64_t u64) RAPIDJSON_NOEXCEPT : data_() { + data_.n.u64 = u64; + data_.f.flags = kNumberUint64Flag; + if (!(u64 & RAPIDJSON_UINT64_C2(0x80000000, 0x00000000))) + data_.f.flags |= kInt64Flag; + if (!(u64 & RAPIDJSON_UINT64_C2(0xFFFFFFFF, 0x00000000))) + data_.f.flags |= kUintFlag; + if (!(u64 & RAPIDJSON_UINT64_C2(0xFFFFFFFF, 0x80000000))) + data_.f.flags |= kIntFlag; + } + + //! Constructor for double value. + explicit GenericValue(double d) RAPIDJSON_NOEXCEPT : data_() { data_.n.d = d; data_.f.flags = kNumberDoubleFlag; } + + //! Constructor for float value. + explicit GenericValue(float f) RAPIDJSON_NOEXCEPT : data_() { data_.n.d = static_cast(f); data_.f.flags = kNumberDoubleFlag; } + + //! Constructor for constant string (i.e. do not make a copy of string) + GenericValue(const Ch* s, SizeType length) RAPIDJSON_NOEXCEPT : data_() { SetStringRaw(StringRef(s, length)); } + + //! Constructor for constant string (i.e. do not make a copy of string) + explicit GenericValue(StringRefType s) RAPIDJSON_NOEXCEPT : data_() { SetStringRaw(s); } + + //! Constructor for copy-string (i.e. do make a copy of string) + GenericValue(const Ch* s, SizeType length, Allocator& allocator) : data_() { SetStringRaw(StringRef(s, length), allocator); } + + //! Constructor for copy-string (i.e. do make a copy of string) + GenericValue(const Ch*s, Allocator& allocator) : data_() { SetStringRaw(StringRef(s), allocator); } + +#if RAPIDJSON_HAS_STDSTRING + //! Constructor for copy-string from a string object (i.e. do make a copy of string) + /*! \note Requires the definition of the preprocessor symbol \ref RAPIDJSON_HAS_STDSTRING. + */ + GenericValue(const std::basic_string& s, Allocator& allocator) : data_() { SetStringRaw(StringRef(s), allocator); } +#endif + + //! Constructor for Array. + /*! + \param a An array obtained by \c GetArray(). + \note \c Array is always pass-by-value. + \note the source array is moved into this value and the sourec array becomes empty. + */ + GenericValue(Array a) RAPIDJSON_NOEXCEPT : data_(a.value_.data_) { + a.value_.data_ = Data(); + a.value_.data_.f.flags = kArrayFlag; + } + + //! Constructor for Object. + /*! + \param o An object obtained by \c GetObject(). + \note \c Object is always pass-by-value. + \note the source object is moved into this value and the sourec object becomes empty. + */ + GenericValue(Object o) RAPIDJSON_NOEXCEPT : data_(o.value_.data_) { + o.value_.data_ = Data(); + o.value_.data_.f.flags = kObjectFlag; + } + + //! Destructor. + /*! Need to destruct elements of array, members of object, or copy-string. + */ + ~GenericValue() { + // With RAPIDJSON_USE_MEMBERSMAP, the maps need to be destroyed to release + // their Allocator if it's refcounted (e.g. MemoryPoolAllocator). + if (Allocator::kNeedFree || (RAPIDJSON_USE_MEMBERSMAP+0 && + internal::IsRefCounted::Value)) { + switch(data_.f.flags) { + case kArrayFlag: + { + GenericValue* e = GetElementsPointer(); + for (GenericValue* v = e; v != e + data_.a.size; ++v) + v->~GenericValue(); + if (Allocator::kNeedFree) { // Shortcut by Allocator's trait + Allocator::Free(e); + } + } + break; + + case kObjectFlag: + DoFreeMembers(); + break; + + case kCopyStringFlag: + if (Allocator::kNeedFree) { // Shortcut by Allocator's trait + Allocator::Free(const_cast(GetStringPointer())); + } + break; + + default: + break; // Do nothing for other types. + } + } + } + + //@} + + //!@name Assignment operators + //@{ + + //! Assignment with move semantics. + /*! \param rhs Source of the assignment. It will become a null value after assignment. + */ + GenericValue& operator=(GenericValue& rhs) RAPIDJSON_NOEXCEPT { + if (RAPIDJSON_LIKELY(this != &rhs)) { + // Can't destroy "this" before assigning "rhs", otherwise "rhs" + // could be used after free if it's an sub-Value of "this", + // hence the temporary danse. + GenericValue temp; + temp.RawAssign(rhs); + this->~GenericValue(); + RawAssign(temp); + } + return *this; + } + +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS + //! Move assignment in C++11 + GenericValue& operator=(GenericValue&& rhs) RAPIDJSON_NOEXCEPT { + return *this = rhs.Move(); + } +#endif + + //! Assignment of constant string reference (no copy) + /*! \param str Constant string reference to be assigned + \note This overload is needed to avoid clashes with the generic primitive type assignment overload below. + \see GenericStringRef, operator=(T) + */ + GenericValue& operator=(StringRefType str) RAPIDJSON_NOEXCEPT { + GenericValue s(str); + return *this = s; + } + + //! Assignment with primitive types. + /*! \tparam T Either \ref Type, \c int, \c unsigned, \c int64_t, \c uint64_t + \param value The value to be assigned. + + \note The source type \c T explicitly disallows all pointer types, + especially (\c const) \ref Ch*. This helps avoiding implicitly + referencing character strings with insufficient lifetime, use + \ref SetString(const Ch*, Allocator&) (for copying) or + \ref StringRef() (to explicitly mark the pointer as constant) instead. + All other pointer types would implicitly convert to \c bool, + use \ref SetBool() instead. + */ + template + RAPIDJSON_DISABLEIF_RETURN((internal::IsPointer), (GenericValue&)) + operator=(T value) { + GenericValue v(value); + return *this = v; + } + + //! Deep-copy assignment from Value + /*! Assigns a \b copy of the Value to the current Value object + \tparam SourceAllocator Allocator type of \c rhs + \param rhs Value to copy from (read-only) + \param allocator Allocator to use for copying + \param copyConstStrings Force copying of constant strings (e.g. referencing an in-situ buffer) + */ + template + GenericValue& CopyFrom(const GenericValue& rhs, Allocator& allocator, bool copyConstStrings = false) { + RAPIDJSON_ASSERT(static_cast(this) != static_cast(&rhs)); + this->~GenericValue(); + new (this) GenericValue(rhs, allocator, copyConstStrings); + return *this; + } + + //! Exchange the contents of this value with those of other. + /*! + \param other Another value. + \note Constant complexity. + */ + GenericValue& Swap(GenericValue& other) RAPIDJSON_NOEXCEPT { + GenericValue temp; + temp.RawAssign(*this); + RawAssign(other); + other.RawAssign(temp); + return *this; + } + + //! free-standing swap function helper + /*! + Helper function to enable support for common swap implementation pattern based on \c std::swap: + \code + void swap(MyClass& a, MyClass& b) { + using std::swap; + swap(a.value, b.value); + // ... + } + \endcode + \see Swap() + */ + friend inline void swap(GenericValue& a, GenericValue& b) RAPIDJSON_NOEXCEPT { a.Swap(b); } + + //! Prepare Value for move semantics + /*! \return *this */ + GenericValue& Move() RAPIDJSON_NOEXCEPT { return *this; } + //@} + + //!@name Equal-to and not-equal-to operators + //@{ + //! Equal-to operator + /*! + \note If an object contains duplicated named member, comparing equality with any object is always \c false. + \note Complexity is quadratic in Object's member number and linear for the rest (number of all values in the subtree and total lengths of all strings). + */ + template + bool operator==(const GenericValue& rhs) const { + typedef GenericValue RhsType; + if (GetType() != rhs.GetType()) + return false; + + switch (GetType()) { + case kObjectType: // Warning: O(n^2) inner-loop + if (data_.o.size != rhs.data_.o.size) + return false; + for (ConstMemberIterator lhsMemberItr = MemberBegin(); lhsMemberItr != MemberEnd(); ++lhsMemberItr) { + typename RhsType::ConstMemberIterator rhsMemberItr = rhs.FindMember(lhsMemberItr->name); + if (rhsMemberItr == rhs.MemberEnd() || lhsMemberItr->value != rhsMemberItr->value) + return false; + } + return true; + + case kArrayType: + if (data_.a.size != rhs.data_.a.size) + return false; + for (SizeType i = 0; i < data_.a.size; i++) + if ((*this)[i] != rhs[i]) + return false; + return true; + + case kStringType: + return StringEqual(rhs); + + case kNumberType: + if (IsDouble() || rhs.IsDouble()) { + double a = GetDouble(); // May convert from integer to double. + double b = rhs.GetDouble(); // Ditto + return a >= b && a <= b; // Prevent -Wfloat-equal + } + else + return data_.n.u64 == rhs.data_.n.u64; + + default: + return true; + } + } + + //! Equal-to operator with const C-string pointer + bool operator==(const Ch* rhs) const { return *this == GenericValue(StringRef(rhs)); } + +#if RAPIDJSON_HAS_STDSTRING + //! Equal-to operator with string object + /*! \note Requires the definition of the preprocessor symbol \ref RAPIDJSON_HAS_STDSTRING. + */ + bool operator==(const std::basic_string& rhs) const { return *this == GenericValue(StringRef(rhs)); } +#endif + + //! Equal-to operator with primitive types + /*! \tparam T Either \ref Type, \c int, \c unsigned, \c int64_t, \c uint64_t, \c double, \c true, \c false + */ + template RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr,internal::IsGenericValue >), (bool)) operator==(const T& rhs) const { return *this == GenericValue(rhs); } + +#ifndef __cpp_impl_three_way_comparison + //! Not-equal-to operator + /*! \return !(*this == rhs) + */ + template + bool operator!=(const GenericValue& rhs) const { return !(*this == rhs); } + + //! Not-equal-to operator with const C-string pointer + bool operator!=(const Ch* rhs) const { return !(*this == rhs); } + + //! Not-equal-to operator with arbitrary types + /*! \return !(*this == rhs) + */ + template RAPIDJSON_DISABLEIF_RETURN((internal::IsGenericValue), (bool)) operator!=(const T& rhs) const { return !(*this == rhs); } + + //! Equal-to operator with arbitrary types (symmetric version) + /*! \return (rhs == lhs) + */ + template friend RAPIDJSON_DISABLEIF_RETURN((internal::IsGenericValue), (bool)) operator==(const T& lhs, const GenericValue& rhs) { return rhs == lhs; } + + //! Not-Equal-to operator with arbitrary types (symmetric version) + /*! \return !(rhs == lhs) + */ + template friend RAPIDJSON_DISABLEIF_RETURN((internal::IsGenericValue), (bool)) operator!=(const T& lhs, const GenericValue& rhs) { return !(rhs == lhs); } + //@} +#endif + + //!@name Type + //@{ + + Type GetType() const { return static_cast(data_.f.flags & kTypeMask); } + bool IsNull() const { return data_.f.flags == kNullFlag; } + bool IsFalse() const { return data_.f.flags == kFalseFlag; } + bool IsTrue() const { return data_.f.flags == kTrueFlag; } + bool IsBool() const { return (data_.f.flags & kBoolFlag) != 0; } + bool IsObject() const { return data_.f.flags == kObjectFlag; } + bool IsArray() const { return data_.f.flags == kArrayFlag; } + bool IsNumber() const { return (data_.f.flags & kNumberFlag) != 0; } + bool IsInt() const { return (data_.f.flags & kIntFlag) != 0; } + bool IsUint() const { return (data_.f.flags & kUintFlag) != 0; } + bool IsInt64() const { return (data_.f.flags & kInt64Flag) != 0; } + bool IsUint64() const { return (data_.f.flags & kUint64Flag) != 0; } + bool IsDouble() const { return (data_.f.flags & kDoubleFlag) != 0; } + bool IsString() const { return (data_.f.flags & kStringFlag) != 0; } + + // Checks whether a number can be losslessly converted to a double. + bool IsLosslessDouble() const { + if (!IsNumber()) return false; + if (IsUint64()) { + uint64_t u = GetUint64(); + volatile double d = static_cast(u); + return (d >= 0.0) + && (d < static_cast((std::numeric_limits::max)())) + && (u == static_cast(d)); + } + if (IsInt64()) { + int64_t i = GetInt64(); + volatile double d = static_cast(i); + return (d >= static_cast((std::numeric_limits::min)())) + && (d < static_cast((std::numeric_limits::max)())) + && (i == static_cast(d)); + } + return true; // double, int, uint are always lossless + } + + // Checks whether a number is a float (possible lossy). + bool IsFloat() const { + if ((data_.f.flags & kDoubleFlag) == 0) + return false; + double d = GetDouble(); + return d >= -3.4028234e38 && d <= 3.4028234e38; + } + // Checks whether a number can be losslessly converted to a float. + bool IsLosslessFloat() const { + if (!IsNumber()) return false; + double a = GetDouble(); + if (a < static_cast(-(std::numeric_limits::max)()) + || a > static_cast((std::numeric_limits::max)())) + return false; + double b = static_cast(static_cast(a)); + return a >= b && a <= b; // Prevent -Wfloat-equal + } + + //@} + + //!@name Null + //@{ + + GenericValue& SetNull() { this->~GenericValue(); new (this) GenericValue(); return *this; } + + //@} + + //!@name Bool + //@{ + + bool GetBool() const { RAPIDJSON_ASSERT(IsBool()); return data_.f.flags == kTrueFlag; } + //!< Set boolean value + /*! \post IsBool() == true */ + GenericValue& SetBool(bool b) { this->~GenericValue(); new (this) GenericValue(b); return *this; } + + //@} + + //!@name Object + //@{ + + //! Set this value as an empty object. + /*! \post IsObject() == true */ + GenericValue& SetObject() { this->~GenericValue(); new (this) GenericValue(kObjectType); return *this; } + + //! Get the number of members in the object. + SizeType MemberCount() const { RAPIDJSON_ASSERT(IsObject()); return data_.o.size; } + + //! Get the capacity of object. + SizeType MemberCapacity() const { RAPIDJSON_ASSERT(IsObject()); return data_.o.capacity; } + + //! Check whether the object is empty. + bool ObjectEmpty() const { RAPIDJSON_ASSERT(IsObject()); return data_.o.size == 0; } + + //! Get a value from an object associated with the name. + /*! \pre IsObject() == true + \tparam T Either \c Ch or \c const \c Ch (template used for disambiguation with \ref operator[](SizeType)) + \note In version 0.1x, if the member is not found, this function returns a null value. This makes issue 7. + Since 0.2, if the name is not correct, it will assert. + If user is unsure whether a member exists, user should use HasMember() first. + A better approach is to use FindMember(). + \note Linear time complexity. + */ + template + RAPIDJSON_DISABLEIF_RETURN((internal::NotExpr::Type, Ch> >),(GenericValue&)) operator[](T* name) { + GenericValue n(StringRef(name)); + return (*this)[n]; + } + template + RAPIDJSON_DISABLEIF_RETURN((internal::NotExpr::Type, Ch> >),(const GenericValue&)) operator[](T* name) const { return const_cast(*this)[name]; } + + //! Get a value from an object associated with the name. + /*! \pre IsObject() == true + \tparam SourceAllocator Allocator of the \c name value + + \note Compared to \ref operator[](T*), this version is faster because it does not need a StrLen(). + And it can also handle strings with embedded null characters. + + \note Linear time complexity. + */ + template + GenericValue& operator[](const GenericValue& name) { + MemberIterator member = FindMember(name); + if (member != MemberEnd()) + return member->value; + else { + RAPIDJSON_ASSERT(false); // see above note + +#if RAPIDJSON_HAS_CXX11 + // Use thread-local storage to prevent races between threads. + // Use static buffer and placement-new to prevent destruction, with + // alignas() to ensure proper alignment. + alignas(GenericValue) thread_local static char buffer[sizeof(GenericValue)]; + return *new (buffer) GenericValue(); +#elif defined(_MSC_VER) && _MSC_VER < 1900 + // There's no way to solve both thread locality and proper alignment + // simultaneously. + __declspec(thread) static char buffer[sizeof(GenericValue)]; + return *new (buffer) GenericValue(); +#elif defined(__GNUC__) || defined(__clang__) + // This will generate -Wexit-time-destructors in clang, but that's + // better than having under-alignment. + __thread static GenericValue buffer; + return buffer; +#else + // Don't know what compiler this is, so don't know how to ensure + // thread-locality. + static GenericValue buffer; + return buffer; +#endif + } + } + template + const GenericValue& operator[](const GenericValue& name) const { return const_cast(*this)[name]; } + +#if RAPIDJSON_HAS_STDSTRING + //! Get a value from an object associated with name (string object). + GenericValue& operator[](const std::basic_string& name) { return (*this)[GenericValue(StringRef(name))]; } + const GenericValue& operator[](const std::basic_string& name) const { return (*this)[GenericValue(StringRef(name))]; } +#endif + + //! Const member iterator + /*! \pre IsObject() == true */ + ConstMemberIterator MemberBegin() const { RAPIDJSON_ASSERT(IsObject()); return ConstMemberIterator(GetMembersPointer()); } + //! Const \em past-the-end member iterator + /*! \pre IsObject() == true */ + ConstMemberIterator MemberEnd() const { RAPIDJSON_ASSERT(IsObject()); return ConstMemberIterator(GetMembersPointer() + data_.o.size); } + //! Member iterator + /*! \pre IsObject() == true */ + MemberIterator MemberBegin() { RAPIDJSON_ASSERT(IsObject()); return MemberIterator(GetMembersPointer()); } + //! \em Past-the-end member iterator + /*! \pre IsObject() == true */ + MemberIterator MemberEnd() { RAPIDJSON_ASSERT(IsObject()); return MemberIterator(GetMembersPointer() + data_.o.size); } + + //! Request the object to have enough capacity to store members. + /*! \param newCapacity The capacity that the object at least need to have. + \param allocator Allocator for reallocating memory. It must be the same one as used before. Commonly use GenericDocument::GetAllocator(). + \return The value itself for fluent API. + \note Linear time complexity. + */ + GenericValue& MemberReserve(SizeType newCapacity, Allocator &allocator) { + RAPIDJSON_ASSERT(IsObject()); + DoReserveMembers(newCapacity, allocator); + return *this; + } + + //! Check whether a member exists in the object. + /*! + \param name Member name to be searched. + \pre IsObject() == true + \return Whether a member with that name exists. + \note It is better to use FindMember() directly if you need the obtain the value as well. + \note Linear time complexity. + */ + bool HasMember(const Ch* name) const { return FindMember(name) != MemberEnd(); } + +#if RAPIDJSON_HAS_STDSTRING + //! Check whether a member exists in the object with string object. + /*! + \param name Member name to be searched. + \pre IsObject() == true + \return Whether a member with that name exists. + \note It is better to use FindMember() directly if you need the obtain the value as well. + \note Linear time complexity. + */ + bool HasMember(const std::basic_string& name) const { return FindMember(name) != MemberEnd(); } +#endif + + //! Check whether a member exists in the object with GenericValue name. + /*! + This version is faster because it does not need a StrLen(). It can also handle string with null character. + \param name Member name to be searched. + \pre IsObject() == true + \return Whether a member with that name exists. + \note It is better to use FindMember() directly if you need the obtain the value as well. + \note Linear time complexity. + */ + template + bool HasMember(const GenericValue& name) const { return FindMember(name) != MemberEnd(); } + + //! Find member by name. + /*! + \param name Member name to be searched. + \pre IsObject() == true + \return Iterator to member, if it exists. + Otherwise returns \ref MemberEnd(). + + \note Earlier versions of Rapidjson returned a \c NULL pointer, in case + the requested member doesn't exist. For consistency with e.g. + \c std::map, this has been changed to MemberEnd() now. + \note Linear time complexity. + */ + MemberIterator FindMember(const Ch* name) { + GenericValue n(StringRef(name)); + return FindMember(n); + } + + ConstMemberIterator FindMember(const Ch* name) const { return const_cast(*this).FindMember(name); } + + //! Find member by name. + /*! + This version is faster because it does not need a StrLen(). It can also handle string with null character. + \param name Member name to be searched. + \pre IsObject() == true + \return Iterator to member, if it exists. + Otherwise returns \ref MemberEnd(). + + \note Earlier versions of Rapidjson returned a \c NULL pointer, in case + the requested member doesn't exist. For consistency with e.g. + \c std::map, this has been changed to MemberEnd() now. + \note Linear time complexity. + */ + template + MemberIterator FindMember(const GenericValue& name) { + RAPIDJSON_ASSERT(IsObject()); + RAPIDJSON_ASSERT(name.IsString()); + return DoFindMember(name); + } + template ConstMemberIterator FindMember(const GenericValue& name) const { return const_cast(*this).FindMember(name); } + +#if RAPIDJSON_HAS_STDSTRING + //! Find member by string object name. + /*! + \param name Member name to be searched. + \pre IsObject() == true + \return Iterator to member, if it exists. + Otherwise returns \ref MemberEnd(). + */ + MemberIterator FindMember(const std::basic_string& name) { return FindMember(GenericValue(StringRef(name))); } + ConstMemberIterator FindMember(const std::basic_string& name) const { return FindMember(GenericValue(StringRef(name))); } +#endif + + //! Add a member (name-value pair) to the object. + /*! \param name A string value as name of member. + \param value Value of any type. + \param allocator Allocator for reallocating memory. It must be the same one as used before. Commonly use GenericDocument::GetAllocator(). + \return The value itself for fluent API. + \note The ownership of \c name and \c value will be transferred to this object on success. + \pre IsObject() && name.IsString() + \post name.IsNull() && value.IsNull() + \note Amortized Constant time complexity. + */ + GenericValue& AddMember(GenericValue& name, GenericValue& value, Allocator& allocator) { + RAPIDJSON_ASSERT(IsObject()); + RAPIDJSON_ASSERT(name.IsString()); + DoAddMember(name, value, allocator); + return *this; + } + + //! Add a constant string value as member (name-value pair) to the object. + /*! \param name A string value as name of member. + \param value constant string reference as value of member. + \param allocator Allocator for reallocating memory. It must be the same one as used before. Commonly use GenericDocument::GetAllocator(). + \return The value itself for fluent API. + \pre IsObject() + \note This overload is needed to avoid clashes with the generic primitive type AddMember(GenericValue&,T,Allocator&) overload below. + \note Amortized Constant time complexity. + */ + GenericValue& AddMember(GenericValue& name, StringRefType value, Allocator& allocator) { + GenericValue v(value); + return AddMember(name, v, allocator); + } + +#if RAPIDJSON_HAS_STDSTRING + //! Add a string object as member (name-value pair) to the object. + /*! \param name A string value as name of member. + \param value constant string reference as value of member. + \param allocator Allocator for reallocating memory. It must be the same one as used before. Commonly use GenericDocument::GetAllocator(). + \return The value itself for fluent API. + \pre IsObject() + \note This overload is needed to avoid clashes with the generic primitive type AddMember(GenericValue&,T,Allocator&) overload below. + \note Amortized Constant time complexity. + */ + GenericValue& AddMember(GenericValue& name, std::basic_string& value, Allocator& allocator) { + GenericValue v(value, allocator); + return AddMember(name, v, allocator); + } +#endif + + //! Add any primitive value as member (name-value pair) to the object. + /*! \tparam T Either \ref Type, \c int, \c unsigned, \c int64_t, \c uint64_t + \param name A string value as name of member. + \param value Value of primitive type \c T as value of member + \param allocator Allocator for reallocating memory. Commonly use GenericDocument::GetAllocator(). + \return The value itself for fluent API. + \pre IsObject() + + \note The source type \c T explicitly disallows all pointer types, + especially (\c const) \ref Ch*. This helps avoiding implicitly + referencing character strings with insufficient lifetime, use + \ref AddMember(StringRefType, GenericValue&, Allocator&) or \ref + AddMember(StringRefType, StringRefType, Allocator&). + All other pointer types would implicitly convert to \c bool, + use an explicit cast instead, if needed. + \note Amortized Constant time complexity. + */ + template + RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (GenericValue&)) + AddMember(GenericValue& name, T value, Allocator& allocator) { + GenericValue v(value); + return AddMember(name, v, allocator); + } + +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS + GenericValue& AddMember(GenericValue&& name, GenericValue&& value, Allocator& allocator) { + return AddMember(name, value, allocator); + } + GenericValue& AddMember(GenericValue&& name, GenericValue& value, Allocator& allocator) { + return AddMember(name, value, allocator); + } + GenericValue& AddMember(GenericValue& name, GenericValue&& value, Allocator& allocator) { + return AddMember(name, value, allocator); + } + GenericValue& AddMember(StringRefType name, GenericValue&& value, Allocator& allocator) { + GenericValue n(name); + return AddMember(n, value, allocator); + } +#endif // RAPIDJSON_HAS_CXX11_RVALUE_REFS + + + //! Add a member (name-value pair) to the object. + /*! \param name A constant string reference as name of member. + \param value Value of any type. + \param allocator Allocator for reallocating memory. It must be the same one as used before. Commonly use GenericDocument::GetAllocator(). + \return The value itself for fluent API. + \note The ownership of \c value will be transferred to this object on success. + \pre IsObject() + \post value.IsNull() + \note Amortized Constant time complexity. + */ + GenericValue& AddMember(StringRefType name, GenericValue& value, Allocator& allocator) { + GenericValue n(name); + return AddMember(n, value, allocator); + } + + //! Add a constant string value as member (name-value pair) to the object. + /*! \param name A constant string reference as name of member. + \param value constant string reference as value of member. + \param allocator Allocator for reallocating memory. It must be the same one as used before. Commonly use GenericDocument::GetAllocator(). + \return The value itself for fluent API. + \pre IsObject() + \note This overload is needed to avoid clashes with the generic primitive type AddMember(StringRefType,T,Allocator&) overload below. + \note Amortized Constant time complexity. + */ + GenericValue& AddMember(StringRefType name, StringRefType value, Allocator& allocator) { + GenericValue v(value); + return AddMember(name, v, allocator); + } + + //! Add any primitive value as member (name-value pair) to the object. + /*! \tparam T Either \ref Type, \c int, \c unsigned, \c int64_t, \c uint64_t + \param name A constant string reference as name of member. + \param value Value of primitive type \c T as value of member + \param allocator Allocator for reallocating memory. Commonly use GenericDocument::GetAllocator(). + \return The value itself for fluent API. + \pre IsObject() + + \note The source type \c T explicitly disallows all pointer types, + especially (\c const) \ref Ch*. This helps avoiding implicitly + referencing character strings with insufficient lifetime, use + \ref AddMember(StringRefType, GenericValue&, Allocator&) or \ref + AddMember(StringRefType, StringRefType, Allocator&). + All other pointer types would implicitly convert to \c bool, + use an explicit cast instead, if needed. + \note Amortized Constant time complexity. + */ + template + RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (GenericValue&)) + AddMember(StringRefType name, T value, Allocator& allocator) { + GenericValue n(name); + return AddMember(n, value, allocator); + } + + //! Remove all members in the object. + /*! This function do not deallocate memory in the object, i.e. the capacity is unchanged. + \note Linear time complexity. + */ + void RemoveAllMembers() { + RAPIDJSON_ASSERT(IsObject()); + DoClearMembers(); + } + + //! Remove a member in object by its name. + /*! \param name Name of member to be removed. + \return Whether the member existed. + \note This function may reorder the object members. Use \ref + EraseMember(ConstMemberIterator) if you need to preserve the + relative order of the remaining members. + \note Linear time complexity. + */ + bool RemoveMember(const Ch* name) { + GenericValue n(StringRef(name)); + return RemoveMember(n); + } + +#if RAPIDJSON_HAS_STDSTRING + bool RemoveMember(const std::basic_string& name) { return RemoveMember(GenericValue(StringRef(name))); } +#endif + + template + bool RemoveMember(const GenericValue& name) { + MemberIterator m = FindMember(name); + if (m != MemberEnd()) { + RemoveMember(m); + return true; + } + else + return false; + } + + //! Remove a member in object by iterator. + /*! \param m member iterator (obtained by FindMember() or MemberBegin()). + \return the new iterator after removal. + \note This function may reorder the object members. Use \ref + EraseMember(ConstMemberIterator) if you need to preserve the + relative order of the remaining members. + \note Constant time complexity. + */ + MemberIterator RemoveMember(MemberIterator m) { + RAPIDJSON_ASSERT(IsObject()); + RAPIDJSON_ASSERT(data_.o.size > 0); + RAPIDJSON_ASSERT(GetMembersPointer() != 0); + RAPIDJSON_ASSERT(m >= MemberBegin() && m < MemberEnd()); + return DoRemoveMember(m); + } + + //! Remove a member from an object by iterator. + /*! \param pos iterator to the member to remove + \pre IsObject() == true && \ref MemberBegin() <= \c pos < \ref MemberEnd() + \return Iterator following the removed element. + If the iterator \c pos refers to the last element, the \ref MemberEnd() iterator is returned. + \note This function preserves the relative order of the remaining object + members. If you do not need this, use the more efficient \ref RemoveMember(MemberIterator). + \note Linear time complexity. + */ + MemberIterator EraseMember(ConstMemberIterator pos) { + return EraseMember(pos, pos +1); + } + + //! Remove members in the range [first, last) from an object. + /*! \param first iterator to the first member to remove + \param last iterator following the last member to remove + \pre IsObject() == true && \ref MemberBegin() <= \c first <= \c last <= \ref MemberEnd() + \return Iterator following the last removed element. + \note This function preserves the relative order of the remaining object + members. + \note Linear time complexity. + */ + MemberIterator EraseMember(ConstMemberIterator first, ConstMemberIterator last) { + RAPIDJSON_ASSERT(IsObject()); + RAPIDJSON_ASSERT(data_.o.size > 0); + RAPIDJSON_ASSERT(GetMembersPointer() != 0); + RAPIDJSON_ASSERT(first >= MemberBegin()); + RAPIDJSON_ASSERT(first <= last); + RAPIDJSON_ASSERT(last <= MemberEnd()); + return DoEraseMembers(first, last); + } + + //! Erase a member in object by its name. + /*! \param name Name of member to be removed. + \return Whether the member existed. + \note Linear time complexity. + */ + bool EraseMember(const Ch* name) { + GenericValue n(StringRef(name)); + return EraseMember(n); + } + +#if RAPIDJSON_HAS_STDSTRING + bool EraseMember(const std::basic_string& name) { return EraseMember(GenericValue(StringRef(name))); } +#endif + + template + bool EraseMember(const GenericValue& name) { + MemberIterator m = FindMember(name); + if (m != MemberEnd()) { + EraseMember(m); + return true; + } + else + return false; + } + + Object GetObject() { RAPIDJSON_ASSERT(IsObject()); return Object(*this); } + Object GetObj() { RAPIDJSON_ASSERT(IsObject()); return Object(*this); } + ConstObject GetObject() const { RAPIDJSON_ASSERT(IsObject()); return ConstObject(*this); } + ConstObject GetObj() const { RAPIDJSON_ASSERT(IsObject()); return ConstObject(*this); } + + //@} + + //!@name Array + //@{ + + //! Set this value as an empty array. + /*! \post IsArray == true */ + GenericValue& SetArray() { this->~GenericValue(); new (this) GenericValue(kArrayType); return *this; } + + //! Get the number of elements in array. + SizeType Size() const { RAPIDJSON_ASSERT(IsArray()); return data_.a.size; } + + //! Get the capacity of array. + SizeType Capacity() const { RAPIDJSON_ASSERT(IsArray()); return data_.a.capacity; } + + //! Check whether the array is empty. + bool Empty() const { RAPIDJSON_ASSERT(IsArray()); return data_.a.size == 0; } + + //! Remove all elements in the array. + /*! This function do not deallocate memory in the array, i.e. the capacity is unchanged. + \note Linear time complexity. + */ + void Clear() { + RAPIDJSON_ASSERT(IsArray()); + GenericValue* e = GetElementsPointer(); + for (GenericValue* v = e; v != e + data_.a.size; ++v) + v->~GenericValue(); + data_.a.size = 0; + } + + //! Get an element from array by index. + /*! \pre IsArray() == true + \param index Zero-based index of element. + \see operator[](T*) + */ + GenericValue& operator[](SizeType index) { + RAPIDJSON_ASSERT(IsArray()); + RAPIDJSON_ASSERT(index < data_.a.size); + return GetElementsPointer()[index]; + } + const GenericValue& operator[](SizeType index) const { return const_cast(*this)[index]; } + + //! Element iterator + /*! \pre IsArray() == true */ + ValueIterator Begin() { RAPIDJSON_ASSERT(IsArray()); return GetElementsPointer(); } + //! \em Past-the-end element iterator + /*! \pre IsArray() == true */ + ValueIterator End() { RAPIDJSON_ASSERT(IsArray()); return GetElementsPointer() + data_.a.size; } + //! Constant element iterator + /*! \pre IsArray() == true */ + ConstValueIterator Begin() const { return const_cast(*this).Begin(); } + //! Constant \em past-the-end element iterator + /*! \pre IsArray() == true */ + ConstValueIterator End() const { return const_cast(*this).End(); } + + //! Request the array to have enough capacity to store elements. + /*! \param newCapacity The capacity that the array at least need to have. + \param allocator Allocator for reallocating memory. It must be the same one as used before. Commonly use GenericDocument::GetAllocator(). + \return The value itself for fluent API. + \note Linear time complexity. + */ + GenericValue& Reserve(SizeType newCapacity, Allocator &allocator) { + RAPIDJSON_ASSERT(IsArray()); + if (newCapacity > data_.a.capacity) { + SetElementsPointer(reinterpret_cast(allocator.Realloc(GetElementsPointer(), data_.a.capacity * sizeof(GenericValue), newCapacity * sizeof(GenericValue)))); + data_.a.capacity = newCapacity; + } + return *this; + } + + //! Append a GenericValue at the end of the array. + /*! \param value Value to be appended. + \param allocator Allocator for reallocating memory. It must be the same one as used before. Commonly use GenericDocument::GetAllocator(). + \pre IsArray() == true + \post value.IsNull() == true + \return The value itself for fluent API. + \note The ownership of \c value will be transferred to this array on success. + \note If the number of elements to be appended is known, calls Reserve() once first may be more efficient. + \note Amortized constant time complexity. + */ + GenericValue& PushBack(GenericValue& value, Allocator& allocator) { + RAPIDJSON_ASSERT(IsArray()); + if (data_.a.size >= data_.a.capacity) + Reserve(data_.a.capacity == 0 ? kDefaultArrayCapacity : (data_.a.capacity + (data_.a.capacity + 1) / 2), allocator); + GetElementsPointer()[data_.a.size++].RawAssign(value); + return *this; + } + +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS + GenericValue& PushBack(GenericValue&& value, Allocator& allocator) { + return PushBack(value, allocator); + } +#endif // RAPIDJSON_HAS_CXX11_RVALUE_REFS + + //! Append a constant string reference at the end of the array. + /*! \param value Constant string reference to be appended. + \param allocator Allocator for reallocating memory. It must be the same one used previously. Commonly use GenericDocument::GetAllocator(). + \pre IsArray() == true + \return The value itself for fluent API. + \note If the number of elements to be appended is known, calls Reserve() once first may be more efficient. + \note Amortized constant time complexity. + \see GenericStringRef + */ + GenericValue& PushBack(StringRefType value, Allocator& allocator) { + return (*this).template PushBack(value, allocator); + } + + //! Append a primitive value at the end of the array. + /*! \tparam T Either \ref Type, \c int, \c unsigned, \c int64_t, \c uint64_t + \param value Value of primitive type T to be appended. + \param allocator Allocator for reallocating memory. It must be the same one as used before. Commonly use GenericDocument::GetAllocator(). + \pre IsArray() == true + \return The value itself for fluent API. + \note If the number of elements to be appended is known, calls Reserve() once first may be more efficient. + + \note The source type \c T explicitly disallows all pointer types, + especially (\c const) \ref Ch*. This helps avoiding implicitly + referencing character strings with insufficient lifetime, use + \ref PushBack(GenericValue&, Allocator&) or \ref + PushBack(StringRefType, Allocator&). + All other pointer types would implicitly convert to \c bool, + use an explicit cast instead, if needed. + \note Amortized constant time complexity. + */ + template + RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (GenericValue&)) + PushBack(T value, Allocator& allocator) { + GenericValue v(value); + return PushBack(v, allocator); + } + + //! Remove the last element in the array. + /*! + \note Constant time complexity. + */ + GenericValue& PopBack() { + RAPIDJSON_ASSERT(IsArray()); + RAPIDJSON_ASSERT(!Empty()); + GetElementsPointer()[--data_.a.size].~GenericValue(); + return *this; + } + + //! Remove an element of array by iterator. + /*! + \param pos iterator to the element to remove + \pre IsArray() == true && \ref Begin() <= \c pos < \ref End() + \return Iterator following the removed element. If the iterator pos refers to the last element, the End() iterator is returned. + \note Linear time complexity. + */ + ValueIterator Erase(ConstValueIterator pos) { + return Erase(pos, pos + 1); + } + + //! Remove elements in the range [first, last) of the array. + /*! + \param first iterator to the first element to remove + \param last iterator following the last element to remove + \pre IsArray() == true && \ref Begin() <= \c first <= \c last <= \ref End() + \return Iterator following the last removed element. + \note Linear time complexity. + */ + ValueIterator Erase(ConstValueIterator first, ConstValueIterator last) { + RAPIDJSON_ASSERT(IsArray()); + RAPIDJSON_ASSERT(data_.a.size > 0); + RAPIDJSON_ASSERT(GetElementsPointer() != 0); + RAPIDJSON_ASSERT(first >= Begin()); + RAPIDJSON_ASSERT(first <= last); + RAPIDJSON_ASSERT(last <= End()); + ValueIterator pos = Begin() + (first - Begin()); + for (ValueIterator itr = pos; itr != last; ++itr) + itr->~GenericValue(); + std::memmove(static_cast(pos), last, static_cast(End() - last) * sizeof(GenericValue)); + data_.a.size -= static_cast(last - first); + return pos; + } + + Array GetArray() { RAPIDJSON_ASSERT(IsArray()); return Array(*this); } + ConstArray GetArray() const { RAPIDJSON_ASSERT(IsArray()); return ConstArray(*this); } + + //@} + + //!@name Number + //@{ + + int GetInt() const { RAPIDJSON_ASSERT(data_.f.flags & kIntFlag); return data_.n.i.i; } + unsigned GetUint() const { RAPIDJSON_ASSERT(data_.f.flags & kUintFlag); return data_.n.u.u; } + int64_t GetInt64() const { RAPIDJSON_ASSERT(data_.f.flags & kInt64Flag); return data_.n.i64; } + uint64_t GetUint64() const { RAPIDJSON_ASSERT(data_.f.flags & kUint64Flag); return data_.n.u64; } + + //! Get the value as double type. + /*! \note If the value is 64-bit integer type, it may lose precision. Use \c IsLosslessDouble() to check whether the converison is lossless. + */ + double GetDouble() const { + RAPIDJSON_ASSERT(IsNumber()); + if ((data_.f.flags & kDoubleFlag) != 0) return data_.n.d; // exact type, no conversion. + if ((data_.f.flags & kIntFlag) != 0) return data_.n.i.i; // int -> double + if ((data_.f.flags & kUintFlag) != 0) return data_.n.u.u; // unsigned -> double + if ((data_.f.flags & kInt64Flag) != 0) return static_cast(data_.n.i64); // int64_t -> double (may lose precision) + RAPIDJSON_ASSERT((data_.f.flags & kUint64Flag) != 0); return static_cast(data_.n.u64); // uint64_t -> double (may lose precision) + } + + //! Get the value as float type. + /*! \note If the value is 64-bit integer type, it may lose precision. Use \c IsLosslessFloat() to check whether the converison is lossless. + */ + float GetFloat() const { + return static_cast(GetDouble()); + } + + GenericValue& SetInt(int i) { this->~GenericValue(); new (this) GenericValue(i); return *this; } + GenericValue& SetUint(unsigned u) { this->~GenericValue(); new (this) GenericValue(u); return *this; } + GenericValue& SetInt64(int64_t i64) { this->~GenericValue(); new (this) GenericValue(i64); return *this; } + GenericValue& SetUint64(uint64_t u64) { this->~GenericValue(); new (this) GenericValue(u64); return *this; } + GenericValue& SetDouble(double d) { this->~GenericValue(); new (this) GenericValue(d); return *this; } + GenericValue& SetFloat(float f) { this->~GenericValue(); new (this) GenericValue(static_cast(f)); return *this; } + + //@} + + //!@name String + //@{ + + const Ch* GetString() const { RAPIDJSON_ASSERT(IsString()); return DataString(data_); } + + //! Get the length of string. + /*! Since rapidjson permits "\\u0000" in the json string, strlen(v.GetString()) may not equal to v.GetStringLength(). + */ + SizeType GetStringLength() const { RAPIDJSON_ASSERT(IsString()); return DataStringLength(data_); } + + //! Set this value as a string without copying source string. + /*! This version has better performance with supplied length, and also support string containing null character. + \param s source string pointer. + \param length The length of source string, excluding the trailing null terminator. + \return The value itself for fluent API. + \post IsString() == true && GetString() == s && GetStringLength() == length + \see SetString(StringRefType) + */ + GenericValue& SetString(const Ch* s, SizeType length) { return SetString(StringRef(s, length)); } + + //! Set this value as a string without copying source string. + /*! \param s source string reference + \return The value itself for fluent API. + \post IsString() == true && GetString() == s && GetStringLength() == s.length + */ + GenericValue& SetString(StringRefType s) { this->~GenericValue(); SetStringRaw(s); return *this; } + + //! Set this value as a string by copying from source string. + /*! This version has better performance with supplied length, and also support string containing null character. + \param s source string. + \param length The length of source string, excluding the trailing null terminator. + \param allocator Allocator for allocating copied buffer. Commonly use GenericDocument::GetAllocator(). + \return The value itself for fluent API. + \post IsString() == true && GetString() != s && strcmp(GetString(),s) == 0 && GetStringLength() == length + */ + GenericValue& SetString(const Ch* s, SizeType length, Allocator& allocator) { return SetString(StringRef(s, length), allocator); } + + //! Set this value as a string by copying from source string. + /*! \param s source string. + \param allocator Allocator for allocating copied buffer. Commonly use GenericDocument::GetAllocator(). + \return The value itself for fluent API. + \post IsString() == true && GetString() != s && strcmp(GetString(),s) == 0 && GetStringLength() == length + */ + GenericValue& SetString(const Ch* s, Allocator& allocator) { return SetString(StringRef(s), allocator); } + + //! Set this value as a string by copying from source string. + /*! \param s source string reference + \param allocator Allocator for allocating copied buffer. Commonly use GenericDocument::GetAllocator(). + \return The value itself for fluent API. + \post IsString() == true && GetString() != s.s && strcmp(GetString(),s) == 0 && GetStringLength() == length + */ + GenericValue& SetString(StringRefType s, Allocator& allocator) { this->~GenericValue(); SetStringRaw(s, allocator); return *this; } + +#if RAPIDJSON_HAS_STDSTRING + //! Set this value as a string by copying from source string. + /*! \param s source string. + \param allocator Allocator for allocating copied buffer. Commonly use GenericDocument::GetAllocator(). + \return The value itself for fluent API. + \post IsString() == true && GetString() != s.data() && strcmp(GetString(),s.data() == 0 && GetStringLength() == s.size() + \note Requires the definition of the preprocessor symbol \ref RAPIDJSON_HAS_STDSTRING. + */ + GenericValue& SetString(const std::basic_string& s, Allocator& allocator) { return SetString(StringRef(s), allocator); } +#endif + + //@} + + //!@name Array + //@{ + + //! Templated version for checking whether this value is type T. + /*! + \tparam T Either \c bool, \c int, \c unsigned, \c int64_t, \c uint64_t, \c double, \c float, \c const \c char*, \c std::basic_string + */ + template + bool Is() const { return internal::TypeHelper::Is(*this); } + + template + T Get() const { return internal::TypeHelper::Get(*this); } + + template + T Get() { return internal::TypeHelper::Get(*this); } + + template + ValueType& Set(const T& data) { return internal::TypeHelper::Set(*this, data); } + + template + ValueType& Set(const T& data, AllocatorType& allocator) { return internal::TypeHelper::Set(*this, data, allocator); } + + //@} + + //! Generate events of this value to a Handler. + /*! This function adopts the GoF visitor pattern. + Typical usage is to output this JSON value as JSON text via Writer, which is a Handler. + It can also be used to deep clone this value via GenericDocument, which is also a Handler. + \tparam Handler type of handler. + \param handler An object implementing concept Handler. + */ + template + bool Accept(Handler& handler) const { + switch(GetType()) { + case kNullType: return handler.Null(); + case kFalseType: return handler.Bool(false); + case kTrueType: return handler.Bool(true); + + case kObjectType: + if (RAPIDJSON_UNLIKELY(!handler.StartObject())) + return false; + for (ConstMemberIterator m = MemberBegin(); m != MemberEnd(); ++m) { + RAPIDJSON_ASSERT(m->name.IsString()); // User may change the type of name by MemberIterator. + if (RAPIDJSON_UNLIKELY(!handler.Key(m->name.GetString(), m->name.GetStringLength(), (m->name.data_.f.flags & kCopyFlag) != 0))) + return false; + if (RAPIDJSON_UNLIKELY(!m->value.Accept(handler))) + return false; + } + return handler.EndObject(data_.o.size); + + case kArrayType: + if (RAPIDJSON_UNLIKELY(!handler.StartArray())) + return false; + for (ConstValueIterator v = Begin(); v != End(); ++v) + if (RAPIDJSON_UNLIKELY(!v->Accept(handler))) + return false; + return handler.EndArray(data_.a.size); + + case kStringType: + return handler.String(GetString(), GetStringLength(), (data_.f.flags & kCopyFlag) != 0); + + default: + RAPIDJSON_ASSERT(GetType() == kNumberType); + if (IsDouble()) return handler.Double(data_.n.d); + else if (IsInt()) return handler.Int(data_.n.i.i); + else if (IsUint()) return handler.Uint(data_.n.u.u); + else if (IsInt64()) return handler.Int64(data_.n.i64); + else return handler.Uint64(data_.n.u64); + } + } + +private: + template friend class GenericValue; + template friend class GenericDocument; + + enum { + kBoolFlag = 0x0008, + kNumberFlag = 0x0010, + kIntFlag = 0x0020, + kUintFlag = 0x0040, + kInt64Flag = 0x0080, + kUint64Flag = 0x0100, + kDoubleFlag = 0x0200, + kStringFlag = 0x0400, + kCopyFlag = 0x0800, + kInlineStrFlag = 0x1000, + + // Initial flags of different types. + kNullFlag = kNullType, + // These casts are added to suppress the warning on MSVC about bitwise operations between enums of different types. + kTrueFlag = static_cast(kTrueType) | static_cast(kBoolFlag), + kFalseFlag = static_cast(kFalseType) | static_cast(kBoolFlag), + kNumberIntFlag = static_cast(kNumberType) | static_cast(kNumberFlag | kIntFlag | kInt64Flag), + kNumberUintFlag = static_cast(kNumberType) | static_cast(kNumberFlag | kUintFlag | kUint64Flag | kInt64Flag), + kNumberInt64Flag = static_cast(kNumberType) | static_cast(kNumberFlag | kInt64Flag), + kNumberUint64Flag = static_cast(kNumberType) | static_cast(kNumberFlag | kUint64Flag), + kNumberDoubleFlag = static_cast(kNumberType) | static_cast(kNumberFlag | kDoubleFlag), + kNumberAnyFlag = static_cast(kNumberType) | static_cast(kNumberFlag | kIntFlag | kInt64Flag | kUintFlag | kUint64Flag | kDoubleFlag), + kConstStringFlag = static_cast(kStringType) | static_cast(kStringFlag), + kCopyStringFlag = static_cast(kStringType) | static_cast(kStringFlag | kCopyFlag), + kShortStringFlag = static_cast(kStringType) | static_cast(kStringFlag | kCopyFlag | kInlineStrFlag), + kObjectFlag = kObjectType, + kArrayFlag = kArrayType, + + kTypeMask = 0x07 + }; + + static const SizeType kDefaultArrayCapacity = RAPIDJSON_VALUE_DEFAULT_ARRAY_CAPACITY; + static const SizeType kDefaultObjectCapacity = RAPIDJSON_VALUE_DEFAULT_OBJECT_CAPACITY; + + struct Flag { +#if RAPIDJSON_48BITPOINTER_OPTIMIZATION + char payload[sizeof(SizeType) * 2 + 6]; // 2 x SizeType + lower 48-bit pointer +#elif RAPIDJSON_64BIT + char payload[sizeof(SizeType) * 2 + sizeof(void*) + 6]; // 6 padding bytes +#else + char payload[sizeof(SizeType) * 2 + sizeof(void*) + 2]; // 2 padding bytes +#endif + uint16_t flags; + }; + + struct String { + SizeType length; + SizeType hashcode; //!< reserved + const Ch* str; + }; // 12 bytes in 32-bit mode, 16 bytes in 64-bit mode + + // implementation detail: ShortString can represent zero-terminated strings up to MaxSize chars + // (excluding the terminating zero) and store a value to determine the length of the contained + // string in the last character str[LenPos] by storing "MaxSize - length" there. If the string + // to store has the maximal length of MaxSize then str[LenPos] will be 0 and therefore act as + // the string terminator as well. For getting the string length back from that value just use + // "MaxSize - str[LenPos]". + // This allows to store 13-chars strings in 32-bit mode, 21-chars strings in 64-bit mode, + // 13-chars strings for RAPIDJSON_48BITPOINTER_OPTIMIZATION=1 inline (for `UTF8`-encoded strings). + struct ShortString { + enum { MaxChars = sizeof(static_cast(0)->payload) / sizeof(Ch), MaxSize = MaxChars - 1, LenPos = MaxSize }; + Ch str[MaxChars]; + + inline static bool Usable(SizeType len) { return (MaxSize >= len); } + inline void SetLength(SizeType len) { str[LenPos] = static_cast(MaxSize - len); } + inline SizeType GetLength() const { return static_cast(MaxSize - str[LenPos]); } + }; // at most as many bytes as "String" above => 12 bytes in 32-bit mode, 16 bytes in 64-bit mode + + // By using proper binary layout, retrieval of different integer types do not need conversions. + union Number { +#if RAPIDJSON_ENDIAN == RAPIDJSON_LITTLEENDIAN + struct I { + int i; + char padding[4]; + }i; + struct U { + unsigned u; + char padding2[4]; + }u; +#else + struct I { + char padding[4]; + int i; + }i; + struct U { + char padding2[4]; + unsigned u; + }u; +#endif + int64_t i64; + uint64_t u64; + double d; + }; // 8 bytes + + struct ObjectData { + SizeType size; + SizeType capacity; + Member* members; + }; // 12 bytes in 32-bit mode, 16 bytes in 64-bit mode + + struct ArrayData { + SizeType size; + SizeType capacity; + GenericValue* elements; + }; // 12 bytes in 32-bit mode, 16 bytes in 64-bit mode + + union Data { + String s; + ShortString ss; + Number n; + ObjectData o; + ArrayData a; + Flag f; + }; // 16 bytes in 32-bit mode, 24 bytes in 64-bit mode, 16 bytes in 64-bit with RAPIDJSON_48BITPOINTER_OPTIMIZATION + + static RAPIDJSON_FORCEINLINE const Ch* DataString(const Data& data) { + return (data.f.flags & kInlineStrFlag) ? data.ss.str : RAPIDJSON_GETPOINTER(Ch, data.s.str); + } + static RAPIDJSON_FORCEINLINE SizeType DataStringLength(const Data& data) { + return (data.f.flags & kInlineStrFlag) ? data.ss.GetLength() : data.s.length; + } + + RAPIDJSON_FORCEINLINE const Ch* GetStringPointer() const { return RAPIDJSON_GETPOINTER(Ch, data_.s.str); } + RAPIDJSON_FORCEINLINE const Ch* SetStringPointer(const Ch* str) { return RAPIDJSON_SETPOINTER(Ch, data_.s.str, str); } + RAPIDJSON_FORCEINLINE GenericValue* GetElementsPointer() const { return RAPIDJSON_GETPOINTER(GenericValue, data_.a.elements); } + RAPIDJSON_FORCEINLINE GenericValue* SetElementsPointer(GenericValue* elements) { return RAPIDJSON_SETPOINTER(GenericValue, data_.a.elements, elements); } + RAPIDJSON_FORCEINLINE Member* GetMembersPointer() const { return RAPIDJSON_GETPOINTER(Member, data_.o.members); } + RAPIDJSON_FORCEINLINE Member* SetMembersPointer(Member* members) { return RAPIDJSON_SETPOINTER(Member, data_.o.members, members); } + +#if RAPIDJSON_USE_MEMBERSMAP + + struct MapTraits { + struct Less { + bool operator()(const Data& s1, const Data& s2) const { + SizeType n1 = DataStringLength(s1), n2 = DataStringLength(s2); + int cmp = std::memcmp(DataString(s1), DataString(s2), sizeof(Ch) * (n1 < n2 ? n1 : n2)); + return cmp < 0 || (cmp == 0 && n1 < n2); + } + }; + typedef std::pair Pair; + typedef std::multimap > Map; + typedef typename Map::iterator Iterator; + }; + typedef typename MapTraits::Map Map; + typedef typename MapTraits::Less MapLess; + typedef typename MapTraits::Pair MapPair; + typedef typename MapTraits::Iterator MapIterator; + + // + // Layout of the members' map/array, re(al)located according to the needed capacity: + // + // {Map*}<>{capacity}<>{Member[capacity]}<>{MapIterator[capacity]} + // + // (where <> stands for the RAPIDJSON_ALIGN-ment, if needed) + // + + static RAPIDJSON_FORCEINLINE size_t GetMapLayoutSize(SizeType capacity) { + return RAPIDJSON_ALIGN(sizeof(Map*)) + + RAPIDJSON_ALIGN(sizeof(SizeType)) + + RAPIDJSON_ALIGN(capacity * sizeof(Member)) + + capacity * sizeof(MapIterator); + } + + static RAPIDJSON_FORCEINLINE SizeType &GetMapCapacity(Map* &map) { + return *reinterpret_cast(reinterpret_cast(&map) + + RAPIDJSON_ALIGN(sizeof(Map*))); + } + + static RAPIDJSON_FORCEINLINE Member* GetMapMembers(Map* &map) { + return reinterpret_cast(reinterpret_cast(&map) + + RAPIDJSON_ALIGN(sizeof(Map*)) + + RAPIDJSON_ALIGN(sizeof(SizeType))); + } + + static RAPIDJSON_FORCEINLINE MapIterator* GetMapIterators(Map* &map) { + return reinterpret_cast(reinterpret_cast(&map) + + RAPIDJSON_ALIGN(sizeof(Map*)) + + RAPIDJSON_ALIGN(sizeof(SizeType)) + + RAPIDJSON_ALIGN(GetMapCapacity(map) * sizeof(Member))); + } + + static RAPIDJSON_FORCEINLINE Map* &GetMap(Member* members) { + RAPIDJSON_ASSERT(members != 0); + return *reinterpret_cast(reinterpret_cast(members) - + RAPIDJSON_ALIGN(sizeof(SizeType)) - + RAPIDJSON_ALIGN(sizeof(Map*))); + } + + // Some compilers' debug mechanisms want all iterators to be destroyed, for their accounting.. + RAPIDJSON_FORCEINLINE MapIterator DropMapIterator(MapIterator& rhs) { +#if RAPIDJSON_HAS_CXX11 + MapIterator ret = std::move(rhs); +#else + MapIterator ret = rhs; +#endif + rhs.~MapIterator(); + return ret; + } + + Map* &DoReallocMap(Map** oldMap, SizeType newCapacity, Allocator& allocator) { + Map **newMap = static_cast(allocator.Malloc(GetMapLayoutSize(newCapacity))); + GetMapCapacity(*newMap) = newCapacity; + if (!oldMap) { + *newMap = new (allocator.Malloc(sizeof(Map))) Map(MapLess(), allocator); + } + else { + *newMap = *oldMap; + size_t count = (*oldMap)->size(); + std::memcpy(static_cast(GetMapMembers(*newMap)), + static_cast(GetMapMembers(*oldMap)), + count * sizeof(Member)); + MapIterator *oldIt = GetMapIterators(*oldMap), + *newIt = GetMapIterators(*newMap); + while (count--) { + new (&newIt[count]) MapIterator(DropMapIterator(oldIt[count])); + } + Allocator::Free(oldMap); + } + return *newMap; + } + + RAPIDJSON_FORCEINLINE Member* DoAllocMembers(SizeType capacity, Allocator& allocator) { + return GetMapMembers(DoReallocMap(0, capacity, allocator)); + } + + void DoReserveMembers(SizeType newCapacity, Allocator& allocator) { + ObjectData& o = data_.o; + if (newCapacity > o.capacity) { + Member* oldMembers = GetMembersPointer(); + Map **oldMap = oldMembers ? &GetMap(oldMembers) : 0, + *&newMap = DoReallocMap(oldMap, newCapacity, allocator); + RAPIDJSON_SETPOINTER(Member, o.members, GetMapMembers(newMap)); + o.capacity = newCapacity; + } + } + + template + MemberIterator DoFindMember(const GenericValue& name) { + if (Member* members = GetMembersPointer()) { + Map* &map = GetMap(members); + MapIterator mit = map->find(reinterpret_cast(name.data_)); + if (mit != map->end()) { + return MemberIterator(&members[mit->second]); + } + } + return MemberEnd(); + } + + void DoClearMembers() { + if (Member* members = GetMembersPointer()) { + Map* &map = GetMap(members); + MapIterator* mit = GetMapIterators(map); + for (SizeType i = 0; i < data_.o.size; i++) { + map->erase(DropMapIterator(mit[i])); + members[i].~Member(); + } + data_.o.size = 0; + } + } + + void DoFreeMembers() { + if (Member* members = GetMembersPointer()) { + GetMap(members)->~Map(); + for (SizeType i = 0; i < data_.o.size; i++) { + members[i].~Member(); + } + if (Allocator::kNeedFree) { // Shortcut by Allocator's trait + Map** map = &GetMap(members); + Allocator::Free(*map); + Allocator::Free(map); + } + } + } + +#else // !RAPIDJSON_USE_MEMBERSMAP + + RAPIDJSON_FORCEINLINE Member* DoAllocMembers(SizeType capacity, Allocator& allocator) { + return Malloc(allocator, capacity); + } + + void DoReserveMembers(SizeType newCapacity, Allocator& allocator) { + ObjectData& o = data_.o; + if (newCapacity > o.capacity) { + Member* newMembers = Realloc(allocator, GetMembersPointer(), o.capacity, newCapacity); + RAPIDJSON_SETPOINTER(Member, o.members, newMembers); + o.capacity = newCapacity; + } + } + + template + MemberIterator DoFindMember(const GenericValue& name) { + MemberIterator member = MemberBegin(); + for ( ; member != MemberEnd(); ++member) + if (name.StringEqual(member->name)) + break; + return member; + } + + void DoClearMembers() { + for (MemberIterator m = MemberBegin(); m != MemberEnd(); ++m) + m->~Member(); + data_.o.size = 0; + } + + void DoFreeMembers() { + for (MemberIterator m = MemberBegin(); m != MemberEnd(); ++m) + m->~Member(); + Allocator::Free(GetMembersPointer()); + } + +#endif // !RAPIDJSON_USE_MEMBERSMAP + + void DoAddMember(GenericValue& name, GenericValue& value, Allocator& allocator) { + ObjectData& o = data_.o; + if (o.size >= o.capacity) + DoReserveMembers(o.capacity ? (o.capacity + (o.capacity + 1) / 2) : kDefaultObjectCapacity, allocator); + Member* members = GetMembersPointer(); + Member* m = members + o.size; + m->name.RawAssign(name); + m->value.RawAssign(value); +#if RAPIDJSON_USE_MEMBERSMAP + Map* &map = GetMap(members); + MapIterator* mit = GetMapIterators(map); + new (&mit[o.size]) MapIterator(map->insert(MapPair(m->name.data_, o.size))); +#endif + ++o.size; + } + + MemberIterator DoRemoveMember(MemberIterator m) { + ObjectData& o = data_.o; + Member* members = GetMembersPointer(); +#if RAPIDJSON_USE_MEMBERSMAP + Map* &map = GetMap(members); + MapIterator* mit = GetMapIterators(map); + SizeType mpos = static_cast(&*m - members); + map->erase(DropMapIterator(mit[mpos])); +#endif + MemberIterator last(members + (o.size - 1)); + if (o.size > 1 && m != last) { +#if RAPIDJSON_USE_MEMBERSMAP + new (&mit[mpos]) MapIterator(DropMapIterator(mit[&*last - members])); + mit[mpos]->second = mpos; +#endif + *m = *last; // Move the last one to this place + } + else { + m->~Member(); // Only one left, just destroy + } + --o.size; + return m; + } + + MemberIterator DoEraseMembers(ConstMemberIterator first, ConstMemberIterator last) { + ObjectData& o = data_.o; + MemberIterator beg = MemberBegin(), + pos = beg + (first - beg), + end = MemberEnd(); +#if RAPIDJSON_USE_MEMBERSMAP + Map* &map = GetMap(GetMembersPointer()); + MapIterator* mit = GetMapIterators(map); +#endif + for (MemberIterator itr = pos; itr != last; ++itr) { +#if RAPIDJSON_USE_MEMBERSMAP + map->erase(DropMapIterator(mit[itr - beg])); +#endif + itr->~Member(); + } +#if RAPIDJSON_USE_MEMBERSMAP + if (first != last) { + // Move remaining members/iterators + MemberIterator next = pos + (last - first); + for (MemberIterator itr = pos; next != end; ++itr, ++next) { + std::memcpy(static_cast(&*itr), &*next, sizeof(Member)); + SizeType mpos = static_cast(itr - beg); + new (&mit[mpos]) MapIterator(DropMapIterator(mit[next - beg])); + mit[mpos]->second = mpos; + } + } +#else + std::memmove(static_cast(&*pos), &*last, + static_cast(end - last) * sizeof(Member)); +#endif + o.size -= static_cast(last - first); + return pos; + } + + template + void DoCopyMembers(const GenericValue& rhs, Allocator& allocator, bool copyConstStrings) { + RAPIDJSON_ASSERT(rhs.GetType() == kObjectType); + + data_.f.flags = kObjectFlag; + SizeType count = rhs.data_.o.size; + Member* lm = DoAllocMembers(count, allocator); + const typename GenericValue::Member* rm = rhs.GetMembersPointer(); +#if RAPIDJSON_USE_MEMBERSMAP + Map* &map = GetMap(lm); + MapIterator* mit = GetMapIterators(map); +#endif + for (SizeType i = 0; i < count; i++) { + new (&lm[i].name) GenericValue(rm[i].name, allocator, copyConstStrings); + new (&lm[i].value) GenericValue(rm[i].value, allocator, copyConstStrings); +#if RAPIDJSON_USE_MEMBERSMAP + new (&mit[i]) MapIterator(map->insert(MapPair(lm[i].name.data_, i))); +#endif + } + data_.o.size = data_.o.capacity = count; + SetMembersPointer(lm); + } + + // Initialize this value as array with initial data, without calling destructor. + void SetArrayRaw(GenericValue* values, SizeType count, Allocator& allocator) { + data_.f.flags = kArrayFlag; + if (count) { + GenericValue* e = static_cast(allocator.Malloc(count * sizeof(GenericValue))); + SetElementsPointer(e); + std::memcpy(static_cast(e), values, count * sizeof(GenericValue)); + } + else + SetElementsPointer(0); + data_.a.size = data_.a.capacity = count; + } + + //! Initialize this value as object with initial data, without calling destructor. + void SetObjectRaw(Member* members, SizeType count, Allocator& allocator) { + data_.f.flags = kObjectFlag; + if (count) { + Member* m = DoAllocMembers(count, allocator); + SetMembersPointer(m); + std::memcpy(static_cast(m), members, count * sizeof(Member)); +#if RAPIDJSON_USE_MEMBERSMAP + Map* &map = GetMap(m); + MapIterator* mit = GetMapIterators(map); + for (SizeType i = 0; i < count; i++) { + new (&mit[i]) MapIterator(map->insert(MapPair(m[i].name.data_, i))); + } +#endif + } + else + SetMembersPointer(0); + data_.o.size = data_.o.capacity = count; + } + + //! Initialize this value as constant string, without calling destructor. + void SetStringRaw(StringRefType s) RAPIDJSON_NOEXCEPT { + data_.f.flags = kConstStringFlag; + SetStringPointer(s); + data_.s.length = s.length; + } + + //! Initialize this value as copy string with initial data, without calling destructor. + void SetStringRaw(StringRefType s, Allocator& allocator) { + Ch* str = 0; + if (ShortString::Usable(s.length)) { + data_.f.flags = kShortStringFlag; + data_.ss.SetLength(s.length); + str = data_.ss.str; + } else { + data_.f.flags = kCopyStringFlag; + data_.s.length = s.length; + str = static_cast(allocator.Malloc((s.length + 1) * sizeof(Ch))); + SetStringPointer(str); + } + std::memcpy(str, s, s.length * sizeof(Ch)); + str[s.length] = '\0'; + } + + //! Assignment without calling destructor + void RawAssign(GenericValue& rhs) RAPIDJSON_NOEXCEPT { + data_ = rhs.data_; + // data_.f.flags = rhs.data_.f.flags; + rhs.data_.f.flags = kNullFlag; + } + + template + bool StringEqual(const GenericValue& rhs) const { + RAPIDJSON_ASSERT(IsString()); + RAPIDJSON_ASSERT(rhs.IsString()); + + const SizeType len1 = GetStringLength(); + const SizeType len2 = rhs.GetStringLength(); + if(len1 != len2) { return false; } + + const Ch* const str1 = GetString(); + const Ch* const str2 = rhs.GetString(); + if(str1 == str2) { return true; } // fast path for constant string + + return (std::memcmp(str1, str2, sizeof(Ch) * len1) == 0); + } + + Data data_; +}; + +//! GenericValue with UTF8 encoding +typedef GenericValue > Value; + +/////////////////////////////////////////////////////////////////////////////// +// GenericDocument + +//! A document for parsing JSON text as DOM. +/*! + \note implements Handler concept + \tparam Encoding Encoding for both parsing and string storage. + \tparam Allocator Allocator for allocating memory for the DOM + \tparam StackAllocator Allocator for allocating memory for stack during parsing. + \warning Although GenericDocument inherits from GenericValue, the API does \b not provide any virtual functions, especially no virtual destructor. To avoid memory leaks, do not \c delete a GenericDocument object via a pointer to a GenericValue. +*/ +template +class GenericDocument : public GenericValue { +public: + typedef typename Encoding::Ch Ch; //!< Character type derived from Encoding. + typedef GenericValue ValueType; //!< Value type of the document. + typedef Allocator AllocatorType; //!< Allocator type from template parameter. + typedef StackAllocator StackAllocatorType; //!< StackAllocator type from template parameter. + + //! Constructor + /*! Creates an empty document of specified type. + \param type Mandatory type of object to create. + \param allocator Optional allocator for allocating memory. + \param stackCapacity Optional initial capacity of stack in bytes. + \param stackAllocator Optional allocator for allocating memory for stack. + */ + explicit GenericDocument(Type type, Allocator* allocator = 0, size_t stackCapacity = kDefaultStackCapacity, StackAllocator* stackAllocator = 0) : + GenericValue(type), allocator_(allocator), ownAllocator_(0), stack_(stackAllocator, stackCapacity), parseResult_() + { + if (!allocator_) + ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator)(); + } + + //! Constructor + /*! Creates an empty document which type is Null. + \param allocator Optional allocator for allocating memory. + \param stackCapacity Optional initial capacity of stack in bytes. + \param stackAllocator Optional allocator for allocating memory for stack. + */ + GenericDocument(Allocator* allocator = 0, size_t stackCapacity = kDefaultStackCapacity, StackAllocator* stackAllocator = 0) : + allocator_(allocator), ownAllocator_(0), stack_(stackAllocator, stackCapacity), parseResult_() + { + if (!allocator_) + ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator)(); + } + +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS + //! Move constructor in C++11 + GenericDocument(GenericDocument&& rhs) RAPIDJSON_NOEXCEPT + : ValueType(std::forward(rhs)), // explicit cast to avoid prohibited move from Document + allocator_(rhs.allocator_), + ownAllocator_(rhs.ownAllocator_), + stack_(std::move(rhs.stack_)), + parseResult_(rhs.parseResult_) + { + rhs.allocator_ = 0; + rhs.ownAllocator_ = 0; + rhs.parseResult_ = ParseResult(); + } +#endif + + ~GenericDocument() { + // Clear the ::ValueType before ownAllocator is destroyed, ~ValueType() + // runs last and may access its elements or members which would be freed + // with an allocator like MemoryPoolAllocator (CrtAllocator does not + // free its data when destroyed, but MemoryPoolAllocator does). + if (ownAllocator_) { + ValueType::SetNull(); + } + Destroy(); + } + +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS + //! Move assignment in C++11 + GenericDocument& operator=(GenericDocument&& rhs) RAPIDJSON_NOEXCEPT + { + // The cast to ValueType is necessary here, because otherwise it would + // attempt to call GenericValue's templated assignment operator. + ValueType::operator=(std::forward(rhs)); + + // Calling the destructor here would prematurely call stack_'s destructor + Destroy(); + + allocator_ = rhs.allocator_; + ownAllocator_ = rhs.ownAllocator_; + stack_ = std::move(rhs.stack_); + parseResult_ = rhs.parseResult_; + + rhs.allocator_ = 0; + rhs.ownAllocator_ = 0; + rhs.parseResult_ = ParseResult(); + + return *this; + } +#endif + + //! Exchange the contents of this document with those of another. + /*! + \param rhs Another document. + \note Constant complexity. + \see GenericValue::Swap + */ + GenericDocument& Swap(GenericDocument& rhs) RAPIDJSON_NOEXCEPT { + ValueType::Swap(rhs); + stack_.Swap(rhs.stack_); + internal::Swap(allocator_, rhs.allocator_); + internal::Swap(ownAllocator_, rhs.ownAllocator_); + internal::Swap(parseResult_, rhs.parseResult_); + return *this; + } + + // Allow Swap with ValueType. + // Refer to Effective C++ 3rd Edition/Item 33: Avoid hiding inherited names. + using ValueType::Swap; + + //! free-standing swap function helper + /*! + Helper function to enable support for common swap implementation pattern based on \c std::swap: + \code + void swap(MyClass& a, MyClass& b) { + using std::swap; + swap(a.doc, b.doc); + // ... + } + \endcode + \see Swap() + */ + friend inline void swap(GenericDocument& a, GenericDocument& b) RAPIDJSON_NOEXCEPT { a.Swap(b); } + + //! Populate this document by a generator which produces SAX events. + /*! \tparam Generator A functor with bool f(Handler) prototype. + \param g Generator functor which sends SAX events to the parameter. + \return The document itself for fluent API. + */ + template + GenericDocument& Populate(Generator& g) { + ClearStackOnExit scope(*this); + if (g(*this)) { + RAPIDJSON_ASSERT(stack_.GetSize() == sizeof(ValueType)); // Got one and only one root object + ValueType::operator=(*stack_.template Pop(1));// Move value from stack to document + } + return *this; + } + + //!@name Parse from stream + //!@{ + + //! Parse JSON text from an input stream (with Encoding conversion) + /*! \tparam parseFlags Combination of \ref ParseFlag. + \tparam SourceEncoding Encoding of input stream + \tparam InputStream Type of input stream, implementing Stream concept + \param is Input stream to be parsed. + \return The document itself for fluent API. + */ + template + GenericDocument& ParseStream(InputStream& is) { + GenericReader reader( + stack_.HasAllocator() ? &stack_.GetAllocator() : 0); + ClearStackOnExit scope(*this); + parseResult_ = reader.template Parse(is, *this); + if (parseResult_) { + RAPIDJSON_ASSERT(stack_.GetSize() == sizeof(ValueType)); // Got one and only one root object + ValueType::operator=(*stack_.template Pop(1));// Move value from stack to document + } + return *this; + } + + //! Parse JSON text from an input stream + /*! \tparam parseFlags Combination of \ref ParseFlag. + \tparam InputStream Type of input stream, implementing Stream concept + \param is Input stream to be parsed. + \return The document itself for fluent API. + */ + template + GenericDocument& ParseStream(InputStream& is) { + return ParseStream(is); + } + + //! Parse JSON text from an input stream (with \ref kParseDefaultFlags) + /*! \tparam InputStream Type of input stream, implementing Stream concept + \param is Input stream to be parsed. + \return The document itself for fluent API. + */ + template + GenericDocument& ParseStream(InputStream& is) { + return ParseStream(is); + } + //!@} + + //!@name Parse in-place from mutable string + //!@{ + + //! Parse JSON text from a mutable string + /*! \tparam parseFlags Combination of \ref ParseFlag. + \param str Mutable zero-terminated string to be parsed. + \return The document itself for fluent API. + */ + template + GenericDocument& ParseInsitu(Ch* str) { + GenericInsituStringStream s(str); + return ParseStream(s); + } + + //! Parse JSON text from a mutable string (with \ref kParseDefaultFlags) + /*! \param str Mutable zero-terminated string to be parsed. + \return The document itself for fluent API. + */ + GenericDocument& ParseInsitu(Ch* str) { + return ParseInsitu(str); + } + //!@} + + //!@name Parse from read-only string + //!@{ + + //! Parse JSON text from a read-only string (with Encoding conversion) + /*! \tparam parseFlags Combination of \ref ParseFlag (must not contain \ref kParseInsituFlag). + \tparam SourceEncoding Transcoding from input Encoding + \param str Read-only zero-terminated string to be parsed. + */ + template + GenericDocument& Parse(const typename SourceEncoding::Ch* str) { + RAPIDJSON_ASSERT(!(parseFlags & kParseInsituFlag)); + GenericStringStream s(str); + return ParseStream(s); + } + + //! Parse JSON text from a read-only string + /*! \tparam parseFlags Combination of \ref ParseFlag (must not contain \ref kParseInsituFlag). + \param str Read-only zero-terminated string to be parsed. + */ + template + GenericDocument& Parse(const Ch* str) { + return Parse(str); + } + + //! Parse JSON text from a read-only string (with \ref kParseDefaultFlags) + /*! \param str Read-only zero-terminated string to be parsed. + */ + GenericDocument& Parse(const Ch* str) { + return Parse(str); + } + + template + GenericDocument& Parse(const typename SourceEncoding::Ch* str, size_t length) { + RAPIDJSON_ASSERT(!(parseFlags & kParseInsituFlag)); + MemoryStream ms(reinterpret_cast(str), length * sizeof(typename SourceEncoding::Ch)); + EncodedInputStream is(ms); + ParseStream(is); + return *this; + } + + template + GenericDocument& Parse(const Ch* str, size_t length) { + return Parse(str, length); + } + + GenericDocument& Parse(const Ch* str, size_t length) { + return Parse(str, length); + } + +#if RAPIDJSON_HAS_STDSTRING + template + GenericDocument& Parse(const std::basic_string& str) { + // c_str() is constant complexity according to standard. Should be faster than Parse(const char*, size_t) + return Parse(str.c_str()); + } + + template + GenericDocument& Parse(const std::basic_string& str) { + return Parse(str.c_str()); + } + + GenericDocument& Parse(const std::basic_string& str) { + return Parse(str); + } +#endif // RAPIDJSON_HAS_STDSTRING + + //!@} + + //!@name Handling parse errors + //!@{ + + //! Whether a parse error has occurred in the last parsing. + bool HasParseError() const { return parseResult_.IsError(); } + + //! Get the \ref ParseErrorCode of last parsing. + ParseErrorCode GetParseError() const { return parseResult_.Code(); } + + //! Get the position of last parsing error in input, 0 otherwise. + size_t GetErrorOffset() const { return parseResult_.Offset(); } + + //! Implicit conversion to get the last parse result +#ifndef __clang // -Wdocumentation + /*! \return \ref ParseResult of the last parse operation + + \code + Document doc; + ParseResult ok = doc.Parse(json); + if (!ok) + printf( "JSON parse error: %s (%u)\n", GetParseError_En(ok.Code()), ok.Offset()); + \endcode + */ +#endif + operator ParseResult() const { return parseResult_; } + //!@} + + //! Get the allocator of this document. + Allocator& GetAllocator() { + RAPIDJSON_ASSERT(allocator_); + return *allocator_; + } + + //! Get the capacity of stack in bytes. + size_t GetStackCapacity() const { return stack_.GetCapacity(); } + +private: + // clear stack on any exit from ParseStream, e.g. due to exception + struct ClearStackOnExit { + explicit ClearStackOnExit(GenericDocument& d) : d_(d) {} + ~ClearStackOnExit() { d_.ClearStack(); } + private: + ClearStackOnExit(const ClearStackOnExit&); + ClearStackOnExit& operator=(const ClearStackOnExit&); + GenericDocument& d_; + }; + + // callers of the following private Handler functions + // template friend class GenericReader; // for parsing + template friend class GenericValue; // for deep copying + +public: + // Implementation of Handler + bool Null() { new (stack_.template Push()) ValueType(); return true; } + bool Bool(bool b) { new (stack_.template Push()) ValueType(b); return true; } + bool Int(int i) { new (stack_.template Push()) ValueType(i); return true; } + bool Uint(unsigned i) { new (stack_.template Push()) ValueType(i); return true; } + bool Int64(int64_t i) { new (stack_.template Push()) ValueType(i); return true; } + bool Uint64(uint64_t i) { new (stack_.template Push()) ValueType(i); return true; } + bool Double(double d) { new (stack_.template Push()) ValueType(d); return true; } + + bool RawNumber(const Ch* str, SizeType length, bool copy) { + if (copy) + new (stack_.template Push()) ValueType(str, length, GetAllocator()); + else + new (stack_.template Push()) ValueType(str, length); + return true; + } + + bool String(const Ch* str, SizeType length, bool copy) { + if (copy) + new (stack_.template Push()) ValueType(str, length, GetAllocator()); + else + new (stack_.template Push()) ValueType(str, length); + return true; + } + + bool StartObject() { new (stack_.template Push()) ValueType(kObjectType); return true; } + + bool Key(const Ch* str, SizeType length, bool copy) { return String(str, length, copy); } + + bool EndObject(SizeType memberCount) { + typename ValueType::Member* members = stack_.template Pop(memberCount); + stack_.template Top()->SetObjectRaw(members, memberCount, GetAllocator()); + return true; + } + + bool StartArray() { new (stack_.template Push()) ValueType(kArrayType); return true; } + + bool EndArray(SizeType elementCount) { + ValueType* elements = stack_.template Pop(elementCount); + stack_.template Top()->SetArrayRaw(elements, elementCount, GetAllocator()); + return true; + } + +private: + //! Prohibit copying + GenericDocument(const GenericDocument&); + //! Prohibit assignment + GenericDocument& operator=(const GenericDocument&); + + void ClearStack() { + if (Allocator::kNeedFree) + while (stack_.GetSize() > 0) // Here assumes all elements in stack array are GenericValue (Member is actually 2 GenericValue objects) + (stack_.template Pop(1))->~ValueType(); + else + stack_.Clear(); + stack_.ShrinkToFit(); + } + + void Destroy() { + RAPIDJSON_DELETE(ownAllocator_); + } + + static const size_t kDefaultStackCapacity = 1024; + Allocator* allocator_; + Allocator* ownAllocator_; + internal::Stack stack_; + ParseResult parseResult_; +}; + +//! GenericDocument with UTF8 encoding +typedef GenericDocument > Document; + + +//! Helper class for accessing Value of array type. +/*! + Instance of this helper class is obtained by \c GenericValue::GetArray(). + In addition to all APIs for array type, it provides range-based for loop if \c RAPIDJSON_HAS_CXX11_RANGE_FOR=1. +*/ +template +class GenericArray { +public: + typedef GenericArray ConstArray; + typedef GenericArray Array; + typedef ValueT PlainType; + typedef typename internal::MaybeAddConst::Type ValueType; + typedef ValueType* ValueIterator; // This may be const or non-const iterator + typedef const ValueT* ConstValueIterator; + typedef typename ValueType::AllocatorType AllocatorType; + typedef typename ValueType::StringRefType StringRefType; + + template + friend class GenericValue; + + GenericArray(const GenericArray& rhs) : value_(rhs.value_) {} + GenericArray& operator=(const GenericArray& rhs) { value_ = rhs.value_; return *this; } + ~GenericArray() {} + + operator ValueType&() const { return value_; } + SizeType Size() const { return value_.Size(); } + SizeType Capacity() const { return value_.Capacity(); } + bool Empty() const { return value_.Empty(); } + void Clear() const { value_.Clear(); } + ValueType& operator[](SizeType index) const { return value_[index]; } + ValueIterator Begin() const { return value_.Begin(); } + ValueIterator End() const { return value_.End(); } + GenericArray Reserve(SizeType newCapacity, AllocatorType &allocator) const { value_.Reserve(newCapacity, allocator); return *this; } + GenericArray PushBack(ValueType& value, AllocatorType& allocator) const { value_.PushBack(value, allocator); return *this; } +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS + GenericArray PushBack(ValueType&& value, AllocatorType& allocator) const { value_.PushBack(value, allocator); return *this; } +#endif // RAPIDJSON_HAS_CXX11_RVALUE_REFS + GenericArray PushBack(StringRefType value, AllocatorType& allocator) const { value_.PushBack(value, allocator); return *this; } + template RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (const GenericArray&)) PushBack(T value, AllocatorType& allocator) const { value_.PushBack(value, allocator); return *this; } + GenericArray PopBack() const { value_.PopBack(); return *this; } + ValueIterator Erase(ConstValueIterator pos) const { return value_.Erase(pos); } + ValueIterator Erase(ConstValueIterator first, ConstValueIterator last) const { return value_.Erase(first, last); } + +#if RAPIDJSON_HAS_CXX11_RANGE_FOR + ValueIterator begin() const { return value_.Begin(); } + ValueIterator end() const { return value_.End(); } +#endif + +private: + GenericArray(); + GenericArray(ValueType& value) : value_(value) {} + ValueType& value_; +}; + +//! Helper class for accessing Value of object type. +/*! + Instance of this helper class is obtained by \c GenericValue::GetObject(). + In addition to all APIs for array type, it provides range-based for loop if \c RAPIDJSON_HAS_CXX11_RANGE_FOR=1. +*/ +template +class GenericObject { +public: + typedef GenericObject ConstObject; + typedef GenericObject Object; + typedef ValueT PlainType; + typedef typename internal::MaybeAddConst::Type ValueType; + typedef GenericMemberIterator MemberIterator; // This may be const or non-const iterator + typedef GenericMemberIterator ConstMemberIterator; + typedef typename ValueType::AllocatorType AllocatorType; + typedef typename ValueType::StringRefType StringRefType; + typedef typename ValueType::EncodingType EncodingType; + typedef typename ValueType::Ch Ch; + + template + friend class GenericValue; + + GenericObject(const GenericObject& rhs) : value_(rhs.value_) {} + GenericObject& operator=(const GenericObject& rhs) { value_ = rhs.value_; return *this; } + ~GenericObject() {} + + operator ValueType&() const { return value_; } + SizeType MemberCount() const { return value_.MemberCount(); } + SizeType MemberCapacity() const { return value_.MemberCapacity(); } + bool ObjectEmpty() const { return value_.ObjectEmpty(); } + template ValueType& operator[](T* name) const { return value_[name]; } + template ValueType& operator[](const GenericValue& name) const { return value_[name]; } +#if RAPIDJSON_HAS_STDSTRING + ValueType& operator[](const std::basic_string& name) const { return value_[name]; } +#endif + MemberIterator MemberBegin() const { return value_.MemberBegin(); } + MemberIterator MemberEnd() const { return value_.MemberEnd(); } + GenericObject MemberReserve(SizeType newCapacity, AllocatorType &allocator) const { value_.MemberReserve(newCapacity, allocator); return *this; } + bool HasMember(const Ch* name) const { return value_.HasMember(name); } +#if RAPIDJSON_HAS_STDSTRING + bool HasMember(const std::basic_string& name) const { return value_.HasMember(name); } +#endif + template bool HasMember(const GenericValue& name) const { return value_.HasMember(name); } + MemberIterator FindMember(const Ch* name) const { return value_.FindMember(name); } + template MemberIterator FindMember(const GenericValue& name) const { return value_.FindMember(name); } +#if RAPIDJSON_HAS_STDSTRING + MemberIterator FindMember(const std::basic_string& name) const { return value_.FindMember(name); } +#endif + GenericObject AddMember(ValueType& name, ValueType& value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; } + GenericObject AddMember(ValueType& name, StringRefType value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; } +#if RAPIDJSON_HAS_STDSTRING + GenericObject AddMember(ValueType& name, std::basic_string& value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; } +#endif + template RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (ValueType&)) AddMember(ValueType& name, T value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; } +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS + GenericObject AddMember(ValueType&& name, ValueType&& value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; } + GenericObject AddMember(ValueType&& name, ValueType& value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; } + GenericObject AddMember(ValueType& name, ValueType&& value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; } + GenericObject AddMember(StringRefType name, ValueType&& value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; } +#endif // RAPIDJSON_HAS_CXX11_RVALUE_REFS + GenericObject AddMember(StringRefType name, ValueType& value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; } + GenericObject AddMember(StringRefType name, StringRefType value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; } + template RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (GenericObject)) AddMember(StringRefType name, T value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; } + void RemoveAllMembers() { value_.RemoveAllMembers(); } + bool RemoveMember(const Ch* name) const { return value_.RemoveMember(name); } +#if RAPIDJSON_HAS_STDSTRING + bool RemoveMember(const std::basic_string& name) const { return value_.RemoveMember(name); } +#endif + template bool RemoveMember(const GenericValue& name) const { return value_.RemoveMember(name); } + MemberIterator RemoveMember(MemberIterator m) const { return value_.RemoveMember(m); } + MemberIterator EraseMember(ConstMemberIterator pos) const { return value_.EraseMember(pos); } + MemberIterator EraseMember(ConstMemberIterator first, ConstMemberIterator last) const { return value_.EraseMember(first, last); } + bool EraseMember(const Ch* name) const { return value_.EraseMember(name); } +#if RAPIDJSON_HAS_STDSTRING + bool EraseMember(const std::basic_string& name) const { return EraseMember(ValueType(StringRef(name))); } +#endif + template bool EraseMember(const GenericValue& name) const { return value_.EraseMember(name); } + +#if RAPIDJSON_HAS_CXX11_RANGE_FOR + MemberIterator begin() const { return value_.MemberBegin(); } + MemberIterator end() const { return value_.MemberEnd(); } +#endif + +private: + GenericObject(); + GenericObject(ValueType& value) : value_(value) {} + ValueType& value_; +}; + +RAPIDJSON_NAMESPACE_END +RAPIDJSON_DIAG_POP + +#ifdef RAPIDJSON_WINDOWS_GETOBJECT_WORKAROUND_APPLIED +#pragma pop_macro("GetObject") +#undef RAPIDJSON_WINDOWS_GETOBJECT_WORKAROUND_APPLIED +#endif + +#endif // RAPIDJSON_DOCUMENT_H_ diff --git a/include/rapidjson/encodedstream.h b/include/rapidjson/encodedstream.h new file mode 100644 index 0000000..cf046b8 --- /dev/null +++ b/include/rapidjson/encodedstream.h @@ -0,0 +1,299 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// 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 RAPIDJSON_ENCODEDSTREAM_H_ +#define RAPIDJSON_ENCODEDSTREAM_H_ + +#include "stream.h" +#include "memorystream.h" + +#ifdef __GNUC__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(effc++) +#endif + +#ifdef __clang__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(padded) +#endif + +RAPIDJSON_NAMESPACE_BEGIN + +//! Input byte stream wrapper with a statically bound encoding. +/*! + \tparam Encoding The interpretation of encoding of the stream. Either UTF8, UTF16LE, UTF16BE, UTF32LE, UTF32BE. + \tparam InputByteStream Type of input byte stream. For example, FileReadStream. +*/ +template +class EncodedInputStream { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); +public: + typedef typename Encoding::Ch Ch; + + EncodedInputStream(InputByteStream& is) : is_(is) { + current_ = Encoding::TakeBOM(is_); + } + + Ch Peek() const { return current_; } + Ch Take() { Ch c = current_; current_ = Encoding::Take(is_); return c; } + size_t Tell() const { return is_.Tell(); } + + // Not implemented + void Put(Ch) { RAPIDJSON_ASSERT(false); } + void Flush() { RAPIDJSON_ASSERT(false); } + Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; } + size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; } + +private: + EncodedInputStream(const EncodedInputStream&); + EncodedInputStream& operator=(const EncodedInputStream&); + + InputByteStream& is_; + Ch current_; +}; + +//! Specialized for UTF8 MemoryStream. +template <> +class EncodedInputStream, MemoryStream> { +public: + typedef UTF8<>::Ch Ch; + + EncodedInputStream(MemoryStream& is) : is_(is) { + if (static_cast(is_.Peek()) == 0xEFu) is_.Take(); + if (static_cast(is_.Peek()) == 0xBBu) is_.Take(); + if (static_cast(is_.Peek()) == 0xBFu) is_.Take(); + } + Ch Peek() const { return is_.Peek(); } + Ch Take() { return is_.Take(); } + size_t Tell() const { return is_.Tell(); } + + // Not implemented + void Put(Ch) {} + void Flush() {} + Ch* PutBegin() { return 0; } + size_t PutEnd(Ch*) { return 0; } + + MemoryStream& is_; + +private: + EncodedInputStream(const EncodedInputStream&); + EncodedInputStream& operator=(const EncodedInputStream&); +}; + +//! Output byte stream wrapper with statically bound encoding. +/*! + \tparam Encoding The interpretation of encoding of the stream. Either UTF8, UTF16LE, UTF16BE, UTF32LE, UTF32BE. + \tparam OutputByteStream Type of input byte stream. For example, FileWriteStream. +*/ +template +class EncodedOutputStream { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); +public: + typedef typename Encoding::Ch Ch; + + EncodedOutputStream(OutputByteStream& os, bool putBOM = true) : os_(os) { + if (putBOM) + Encoding::PutBOM(os_); + } + + void Put(Ch c) { Encoding::Put(os_, c); } + void Flush() { os_.Flush(); } + + // Not implemented + Ch Peek() const { RAPIDJSON_ASSERT(false); return 0;} + Ch Take() { RAPIDJSON_ASSERT(false); return 0;} + size_t Tell() const { RAPIDJSON_ASSERT(false); return 0; } + Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; } + size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; } + +private: + EncodedOutputStream(const EncodedOutputStream&); + EncodedOutputStream& operator=(const EncodedOutputStream&); + + OutputByteStream& os_; +}; + +#define RAPIDJSON_ENCODINGS_FUNC(x) UTF8::x, UTF16LE::x, UTF16BE::x, UTF32LE::x, UTF32BE::x + +//! Input stream wrapper with dynamically bound encoding and automatic encoding detection. +/*! + \tparam CharType Type of character for reading. + \tparam InputByteStream type of input byte stream to be wrapped. +*/ +template +class AutoUTFInputStream { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); +public: + typedef CharType Ch; + + //! Constructor. + /*! + \param is input stream to be wrapped. + \param type UTF encoding type if it is not detected from the stream. + */ + AutoUTFInputStream(InputByteStream& is, UTFType type = kUTF8) : is_(&is), type_(type), hasBOM_(false) { + RAPIDJSON_ASSERT(type >= kUTF8 && type <= kUTF32BE); + DetectType(); + static const TakeFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(Take) }; + takeFunc_ = f[type_]; + current_ = takeFunc_(*is_); + } + + UTFType GetType() const { return type_; } + bool HasBOM() const { return hasBOM_; } + + Ch Peek() const { return current_; } + Ch Take() { Ch c = current_; current_ = takeFunc_(*is_); return c; } + size_t Tell() const { return is_->Tell(); } + + // Not implemented + void Put(Ch) { RAPIDJSON_ASSERT(false); } + void Flush() { RAPIDJSON_ASSERT(false); } + Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; } + size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; } + +private: + AutoUTFInputStream(const AutoUTFInputStream&); + AutoUTFInputStream& operator=(const AutoUTFInputStream&); + + // Detect encoding type with BOM or RFC 4627 + void DetectType() { + // BOM (Byte Order Mark): + // 00 00 FE FF UTF-32BE + // FF FE 00 00 UTF-32LE + // FE FF UTF-16BE + // FF FE UTF-16LE + // EF BB BF UTF-8 + + const unsigned char* c = reinterpret_cast(is_->Peek4()); + if (!c) + return; + + unsigned bom = static_cast(c[0] | (c[1] << 8) | (c[2] << 16) | (c[3] << 24)); + hasBOM_ = false; + if (bom == 0xFFFE0000) { type_ = kUTF32BE; hasBOM_ = true; is_->Take(); is_->Take(); is_->Take(); is_->Take(); } + else if (bom == 0x0000FEFF) { type_ = kUTF32LE; hasBOM_ = true; is_->Take(); is_->Take(); is_->Take(); is_->Take(); } + else if ((bom & 0xFFFF) == 0xFFFE) { type_ = kUTF16BE; hasBOM_ = true; is_->Take(); is_->Take(); } + else if ((bom & 0xFFFF) == 0xFEFF) { type_ = kUTF16LE; hasBOM_ = true; is_->Take(); is_->Take(); } + else if ((bom & 0xFFFFFF) == 0xBFBBEF) { type_ = kUTF8; hasBOM_ = true; is_->Take(); is_->Take(); is_->Take(); } + + // RFC 4627: Section 3 + // "Since the first two characters of a JSON text will always be ASCII + // characters [RFC0020], it is possible to determine whether an octet + // stream is UTF-8, UTF-16 (BE or LE), or UTF-32 (BE or LE) by looking + // at the pattern of nulls in the first four octets." + // 00 00 00 xx UTF-32BE + // 00 xx 00 xx UTF-16BE + // xx 00 00 00 UTF-32LE + // xx 00 xx 00 UTF-16LE + // xx xx xx xx UTF-8 + + if (!hasBOM_) { + int pattern = (c[0] ? 1 : 0) | (c[1] ? 2 : 0) | (c[2] ? 4 : 0) | (c[3] ? 8 : 0); + switch (pattern) { + case 0x08: type_ = kUTF32BE; break; + case 0x0A: type_ = kUTF16BE; break; + case 0x01: type_ = kUTF32LE; break; + case 0x05: type_ = kUTF16LE; break; + case 0x0F: type_ = kUTF8; break; + default: break; // Use type defined by user. + } + } + + // Runtime check whether the size of character type is sufficient. It only perform checks with assertion. + if (type_ == kUTF16LE || type_ == kUTF16BE) RAPIDJSON_ASSERT(sizeof(Ch) >= 2); + if (type_ == kUTF32LE || type_ == kUTF32BE) RAPIDJSON_ASSERT(sizeof(Ch) >= 4); + } + + typedef Ch (*TakeFunc)(InputByteStream& is); + InputByteStream* is_; + UTFType type_; + Ch current_; + TakeFunc takeFunc_; + bool hasBOM_; +}; + +//! Output stream wrapper with dynamically bound encoding and automatic encoding detection. +/*! + \tparam CharType Type of character for writing. + \tparam OutputByteStream type of output byte stream to be wrapped. +*/ +template +class AutoUTFOutputStream { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); +public: + typedef CharType Ch; + + //! Constructor. + /*! + \param os output stream to be wrapped. + \param type UTF encoding type. + \param putBOM Whether to write BOM at the beginning of the stream. + */ + AutoUTFOutputStream(OutputByteStream& os, UTFType type, bool putBOM) : os_(&os), type_(type) { + RAPIDJSON_ASSERT(type >= kUTF8 && type <= kUTF32BE); + + // Runtime check whether the size of character type is sufficient. It only perform checks with assertion. + if (type_ == kUTF16LE || type_ == kUTF16BE) RAPIDJSON_ASSERT(sizeof(Ch) >= 2); + if (type_ == kUTF32LE || type_ == kUTF32BE) RAPIDJSON_ASSERT(sizeof(Ch) >= 4); + + static const PutFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(Put) }; + putFunc_ = f[type_]; + + if (putBOM) + PutBOM(); + } + + UTFType GetType() const { return type_; } + + void Put(Ch c) { putFunc_(*os_, c); } + void Flush() { os_->Flush(); } + + // Not implemented + Ch Peek() const { RAPIDJSON_ASSERT(false); return 0;} + Ch Take() { RAPIDJSON_ASSERT(false); return 0;} + size_t Tell() const { RAPIDJSON_ASSERT(false); return 0; } + Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; } + size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; } + +private: + AutoUTFOutputStream(const AutoUTFOutputStream&); + AutoUTFOutputStream& operator=(const AutoUTFOutputStream&); + + void PutBOM() { + typedef void (*PutBOMFunc)(OutputByteStream&); + static const PutBOMFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(PutBOM) }; + f[type_](*os_); + } + + typedef void (*PutFunc)(OutputByteStream&, Ch); + + OutputByteStream* os_; + UTFType type_; + PutFunc putFunc_; +}; + +#undef RAPIDJSON_ENCODINGS_FUNC + +RAPIDJSON_NAMESPACE_END + +#ifdef __clang__ +RAPIDJSON_DIAG_POP +#endif + +#ifdef __GNUC__ +RAPIDJSON_DIAG_POP +#endif + +#endif // RAPIDJSON_FILESTREAM_H_ diff --git a/include/rapidjson/encodings.h b/include/rapidjson/encodings.h new file mode 100644 index 0000000..50ad18b --- /dev/null +++ b/include/rapidjson/encodings.h @@ -0,0 +1,716 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// 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 RAPIDJSON_ENCODINGS_H_ +#define RAPIDJSON_ENCODINGS_H_ + +#include "rapidjson.h" + +#if defined(_MSC_VER) && !defined(__clang__) +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(4244) // conversion from 'type1' to 'type2', possible loss of data +RAPIDJSON_DIAG_OFF(4702) // unreachable code +#elif defined(__GNUC__) +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(effc++) +RAPIDJSON_DIAG_OFF(overflow) +#endif + +RAPIDJSON_NAMESPACE_BEGIN + +/////////////////////////////////////////////////////////////////////////////// +// Encoding + +/*! \class rapidjson::Encoding + \brief Concept for encoding of Unicode characters. + +\code +concept Encoding { + typename Ch; //! Type of character. A "character" is actually a code unit in unicode's definition. + + enum { supportUnicode = 1 }; // or 0 if not supporting unicode + + //! \brief Encode a Unicode codepoint to an output stream. + //! \param os Output stream. + //! \param codepoint An unicode codepoint, ranging from 0x0 to 0x10FFFF inclusively. + template + static void Encode(OutputStream& os, unsigned codepoint); + + //! \brief Decode a Unicode codepoint from an input stream. + //! \param is Input stream. + //! \param codepoint Output of the unicode codepoint. + //! \return true if a valid codepoint can be decoded from the stream. + template + static bool Decode(InputStream& is, unsigned* codepoint); + + //! \brief Validate one Unicode codepoint from an encoded stream. + //! \param is Input stream to obtain codepoint. + //! \param os Output for copying one codepoint. + //! \return true if it is valid. + //! \note This function just validating and copying the codepoint without actually decode it. + template + static bool Validate(InputStream& is, OutputStream& os); + + // The following functions are deal with byte streams. + + //! Take a character from input byte stream, skip BOM if exist. + template + static CharType TakeBOM(InputByteStream& is); + + //! Take a character from input byte stream. + template + static Ch Take(InputByteStream& is); + + //! Put BOM to output byte stream. + template + static void PutBOM(OutputByteStream& os); + + //! Put a character to output byte stream. + template + static void Put(OutputByteStream& os, Ch c); +}; +\endcode +*/ + +/////////////////////////////////////////////////////////////////////////////// +// UTF8 + +//! UTF-8 encoding. +/*! http://en.wikipedia.org/wiki/UTF-8 + http://tools.ietf.org/html/rfc3629 + \tparam CharType Code unit for storing 8-bit UTF-8 data. Default is char. + \note implements Encoding concept +*/ +template +struct UTF8 { + typedef CharType Ch; + + enum { supportUnicode = 1 }; + + template + static void Encode(OutputStream& os, unsigned codepoint) { + if (codepoint <= 0x7F) + os.Put(static_cast(codepoint & 0xFF)); + else if (codepoint <= 0x7FF) { + os.Put(static_cast(0xC0 | ((codepoint >> 6) & 0xFF))); + os.Put(static_cast(0x80 | ((codepoint & 0x3F)))); + } + else if (codepoint <= 0xFFFF) { + os.Put(static_cast(0xE0 | ((codepoint >> 12) & 0xFF))); + os.Put(static_cast(0x80 | ((codepoint >> 6) & 0x3F))); + os.Put(static_cast(0x80 | (codepoint & 0x3F))); + } + else { + RAPIDJSON_ASSERT(codepoint <= 0x10FFFF); + os.Put(static_cast(0xF0 | ((codepoint >> 18) & 0xFF))); + os.Put(static_cast(0x80 | ((codepoint >> 12) & 0x3F))); + os.Put(static_cast(0x80 | ((codepoint >> 6) & 0x3F))); + os.Put(static_cast(0x80 | (codepoint & 0x3F))); + } + } + + template + static void EncodeUnsafe(OutputStream& os, unsigned codepoint) { + if (codepoint <= 0x7F) + PutUnsafe(os, static_cast(codepoint & 0xFF)); + else if (codepoint <= 0x7FF) { + PutUnsafe(os, static_cast(0xC0 | ((codepoint >> 6) & 0xFF))); + PutUnsafe(os, static_cast(0x80 | ((codepoint & 0x3F)))); + } + else if (codepoint <= 0xFFFF) { + PutUnsafe(os, static_cast(0xE0 | ((codepoint >> 12) & 0xFF))); + PutUnsafe(os, static_cast(0x80 | ((codepoint >> 6) & 0x3F))); + PutUnsafe(os, static_cast(0x80 | (codepoint & 0x3F))); + } + else { + RAPIDJSON_ASSERT(codepoint <= 0x10FFFF); + PutUnsafe(os, static_cast(0xF0 | ((codepoint >> 18) & 0xFF))); + PutUnsafe(os, static_cast(0x80 | ((codepoint >> 12) & 0x3F))); + PutUnsafe(os, static_cast(0x80 | ((codepoint >> 6) & 0x3F))); + PutUnsafe(os, static_cast(0x80 | (codepoint & 0x3F))); + } + } + + template + static bool Decode(InputStream& is, unsigned* codepoint) { +#define RAPIDJSON_COPY() c = is.Take(); *codepoint = (*codepoint << 6) | (static_cast(c) & 0x3Fu) +#define RAPIDJSON_TRANS(mask) result &= ((GetRange(static_cast(c)) & mask) != 0) +#define RAPIDJSON_TAIL() RAPIDJSON_COPY(); RAPIDJSON_TRANS(0x70) + typename InputStream::Ch c = is.Take(); + if (!(c & 0x80)) { + *codepoint = static_cast(c); + return true; + } + + unsigned char type = GetRange(static_cast(c)); + if (type >= 32) { + *codepoint = 0; + } else { + *codepoint = (0xFFu >> type) & static_cast(c); + } + bool result = true; + switch (type) { + case 2: RAPIDJSON_TAIL(); return result; + case 3: RAPIDJSON_TAIL(); RAPIDJSON_TAIL(); return result; + case 4: RAPIDJSON_COPY(); RAPIDJSON_TRANS(0x50); RAPIDJSON_TAIL(); return result; + case 5: RAPIDJSON_COPY(); RAPIDJSON_TRANS(0x10); RAPIDJSON_TAIL(); RAPIDJSON_TAIL(); return result; + case 6: RAPIDJSON_TAIL(); RAPIDJSON_TAIL(); RAPIDJSON_TAIL(); return result; + case 10: RAPIDJSON_COPY(); RAPIDJSON_TRANS(0x20); RAPIDJSON_TAIL(); return result; + case 11: RAPIDJSON_COPY(); RAPIDJSON_TRANS(0x60); RAPIDJSON_TAIL(); RAPIDJSON_TAIL(); return result; + default: return false; + } +#undef RAPIDJSON_COPY +#undef RAPIDJSON_TRANS +#undef RAPIDJSON_TAIL + } + + template + static bool Validate(InputStream& is, OutputStream& os) { +#define RAPIDJSON_COPY() os.Put(c = is.Take()) +#define RAPIDJSON_TRANS(mask) result &= ((GetRange(static_cast(c)) & mask) != 0) +#define RAPIDJSON_TAIL() RAPIDJSON_COPY(); RAPIDJSON_TRANS(0x70) + Ch c; + RAPIDJSON_COPY(); + if (!(c & 0x80)) + return true; + + bool result = true; + switch (GetRange(static_cast(c))) { + case 2: RAPIDJSON_TAIL(); return result; + case 3: RAPIDJSON_TAIL(); RAPIDJSON_TAIL(); return result; + case 4: RAPIDJSON_COPY(); RAPIDJSON_TRANS(0x50); RAPIDJSON_TAIL(); return result; + case 5: RAPIDJSON_COPY(); RAPIDJSON_TRANS(0x10); RAPIDJSON_TAIL(); RAPIDJSON_TAIL(); return result; + case 6: RAPIDJSON_TAIL(); RAPIDJSON_TAIL(); RAPIDJSON_TAIL(); return result; + case 10: RAPIDJSON_COPY(); RAPIDJSON_TRANS(0x20); RAPIDJSON_TAIL(); return result; + case 11: RAPIDJSON_COPY(); RAPIDJSON_TRANS(0x60); RAPIDJSON_TAIL(); RAPIDJSON_TAIL(); return result; + default: return false; + } +#undef RAPIDJSON_COPY +#undef RAPIDJSON_TRANS +#undef RAPIDJSON_TAIL + } + + static unsigned char GetRange(unsigned char c) { + // Referring to DFA of http://bjoern.hoehrmann.de/utf-8/decoder/dfa/ + // With new mapping 1 -> 0x10, 7 -> 0x20, 9 -> 0x40, such that AND operation can test multiple types. + static const unsigned char type[] = { + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10, + 0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40, + 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, + 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, + 8,8,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 10,3,3,3,3,3,3,3,3,3,3,3,3,4,3,3, 11,6,6,6,5,8,8,8,8,8,8,8,8,8,8,8, + }; + return type[c]; + } + + template + static CharType TakeBOM(InputByteStream& is) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); + typename InputByteStream::Ch c = Take(is); + if (static_cast(c) != 0xEFu) return c; + c = is.Take(); + if (static_cast(c) != 0xBBu) return c; + c = is.Take(); + if (static_cast(c) != 0xBFu) return c; + c = is.Take(); + return c; + } + + template + static Ch Take(InputByteStream& is) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); + return static_cast(is.Take()); + } + + template + static void PutBOM(OutputByteStream& os) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); + os.Put(static_cast(0xEFu)); + os.Put(static_cast(0xBBu)); + os.Put(static_cast(0xBFu)); + } + + template + static void Put(OutputByteStream& os, Ch c) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); + os.Put(static_cast(c)); + } +}; + +/////////////////////////////////////////////////////////////////////////////// +// UTF16 + +//! UTF-16 encoding. +/*! http://en.wikipedia.org/wiki/UTF-16 + http://tools.ietf.org/html/rfc2781 + \tparam CharType Type for storing 16-bit UTF-16 data. Default is wchar_t. C++11 may use char16_t instead. + \note implements Encoding concept + + \note For in-memory access, no need to concern endianness. The code units and code points are represented by CPU's endianness. + For streaming, use UTF16LE and UTF16BE, which handle endianness. +*/ +template +struct UTF16 { + typedef CharType Ch; + RAPIDJSON_STATIC_ASSERT(sizeof(Ch) >= 2); + + enum { supportUnicode = 1 }; + + template + static void Encode(OutputStream& os, unsigned codepoint) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputStream::Ch) >= 2); + if (codepoint <= 0xFFFF) { + RAPIDJSON_ASSERT(codepoint < 0xD800 || codepoint > 0xDFFF); // Code point itself cannot be surrogate pair + os.Put(static_cast(codepoint)); + } + else { + RAPIDJSON_ASSERT(codepoint <= 0x10FFFF); + unsigned v = codepoint - 0x10000; + os.Put(static_cast((v >> 10) | 0xD800)); + os.Put(static_cast((v & 0x3FF) | 0xDC00)); + } + } + + + template + static void EncodeUnsafe(OutputStream& os, unsigned codepoint) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputStream::Ch) >= 2); + if (codepoint <= 0xFFFF) { + RAPIDJSON_ASSERT(codepoint < 0xD800 || codepoint > 0xDFFF); // Code point itself cannot be surrogate pair + PutUnsafe(os, static_cast(codepoint)); + } + else { + RAPIDJSON_ASSERT(codepoint <= 0x10FFFF); + unsigned v = codepoint - 0x10000; + PutUnsafe(os, static_cast((v >> 10) | 0xD800)); + PutUnsafe(os, static_cast((v & 0x3FF) | 0xDC00)); + } + } + + template + static bool Decode(InputStream& is, unsigned* codepoint) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputStream::Ch) >= 2); + typename InputStream::Ch c = is.Take(); + if (c < 0xD800 || c > 0xDFFF) { + *codepoint = static_cast(c); + return true; + } + else if (c <= 0xDBFF) { + *codepoint = (static_cast(c) & 0x3FF) << 10; + c = is.Take(); + *codepoint |= (static_cast(c) & 0x3FF); + *codepoint += 0x10000; + return c >= 0xDC00 && c <= 0xDFFF; + } + return false; + } + + template + static bool Validate(InputStream& is, OutputStream& os) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputStream::Ch) >= 2); + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputStream::Ch) >= 2); + typename InputStream::Ch c; + os.Put(static_cast(c = is.Take())); + if (c < 0xD800 || c > 0xDFFF) + return true; + else if (c <= 0xDBFF) { + os.Put(c = is.Take()); + return c >= 0xDC00 && c <= 0xDFFF; + } + return false; + } +}; + +//! UTF-16 little endian encoding. +template +struct UTF16LE : UTF16 { + template + static CharType TakeBOM(InputByteStream& is) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); + CharType c = Take(is); + return static_cast(c) == 0xFEFFu ? Take(is) : c; + } + + template + static CharType Take(InputByteStream& is) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); + unsigned c = static_cast(is.Take()); + c |= static_cast(static_cast(is.Take())) << 8; + return static_cast(c); + } + + template + static void PutBOM(OutputByteStream& os) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); + os.Put(static_cast(0xFFu)); + os.Put(static_cast(0xFEu)); + } + + template + static void Put(OutputByteStream& os, CharType c) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); + os.Put(static_cast(static_cast(c) & 0xFFu)); + os.Put(static_cast((static_cast(c) >> 8) & 0xFFu)); + } +}; + +//! UTF-16 big endian encoding. +template +struct UTF16BE : UTF16 { + template + static CharType TakeBOM(InputByteStream& is) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); + CharType c = Take(is); + return static_cast(c) == 0xFEFFu ? Take(is) : c; + } + + template + static CharType Take(InputByteStream& is) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); + unsigned c = static_cast(static_cast(is.Take())) << 8; + c |= static_cast(static_cast(is.Take())); + return static_cast(c); + } + + template + static void PutBOM(OutputByteStream& os) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); + os.Put(static_cast(0xFEu)); + os.Put(static_cast(0xFFu)); + } + + template + static void Put(OutputByteStream& os, CharType c) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); + os.Put(static_cast((static_cast(c) >> 8) & 0xFFu)); + os.Put(static_cast(static_cast(c) & 0xFFu)); + } +}; + +/////////////////////////////////////////////////////////////////////////////// +// UTF32 + +//! UTF-32 encoding. +/*! http://en.wikipedia.org/wiki/UTF-32 + \tparam CharType Type for storing 32-bit UTF-32 data. Default is unsigned. C++11 may use char32_t instead. + \note implements Encoding concept + + \note For in-memory access, no need to concern endianness. The code units and code points are represented by CPU's endianness. + For streaming, use UTF32LE and UTF32BE, which handle endianness. +*/ +template +struct UTF32 { + typedef CharType Ch; + RAPIDJSON_STATIC_ASSERT(sizeof(Ch) >= 4); + + enum { supportUnicode = 1 }; + + template + static void Encode(OutputStream& os, unsigned codepoint) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputStream::Ch) >= 4); + RAPIDJSON_ASSERT(codepoint <= 0x10FFFF); + os.Put(codepoint); + } + + template + static void EncodeUnsafe(OutputStream& os, unsigned codepoint) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputStream::Ch) >= 4); + RAPIDJSON_ASSERT(codepoint <= 0x10FFFF); + PutUnsafe(os, codepoint); + } + + template + static bool Decode(InputStream& is, unsigned* codepoint) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputStream::Ch) >= 4); + Ch c = is.Take(); + *codepoint = c; + return c <= 0x10FFFF; + } + + template + static bool Validate(InputStream& is, OutputStream& os) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputStream::Ch) >= 4); + Ch c; + os.Put(c = is.Take()); + return c <= 0x10FFFF; + } +}; + +//! UTF-32 little endian enocoding. +template +struct UTF32LE : UTF32 { + template + static CharType TakeBOM(InputByteStream& is) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); + CharType c = Take(is); + return static_cast(c) == 0x0000FEFFu ? Take(is) : c; + } + + template + static CharType Take(InputByteStream& is) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); + unsigned c = static_cast(is.Take()); + c |= static_cast(static_cast(is.Take())) << 8; + c |= static_cast(static_cast(is.Take())) << 16; + c |= static_cast(static_cast(is.Take())) << 24; + return static_cast(c); + } + + template + static void PutBOM(OutputByteStream& os) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); + os.Put(static_cast(0xFFu)); + os.Put(static_cast(0xFEu)); + os.Put(static_cast(0x00u)); + os.Put(static_cast(0x00u)); + } + + template + static void Put(OutputByteStream& os, CharType c) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); + os.Put(static_cast(c & 0xFFu)); + os.Put(static_cast((c >> 8) & 0xFFu)); + os.Put(static_cast((c >> 16) & 0xFFu)); + os.Put(static_cast((c >> 24) & 0xFFu)); + } +}; + +//! UTF-32 big endian encoding. +template +struct UTF32BE : UTF32 { + template + static CharType TakeBOM(InputByteStream& is) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); + CharType c = Take(is); + return static_cast(c) == 0x0000FEFFu ? Take(is) : c; + } + + template + static CharType Take(InputByteStream& is) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); + unsigned c = static_cast(static_cast(is.Take())) << 24; + c |= static_cast(static_cast(is.Take())) << 16; + c |= static_cast(static_cast(is.Take())) << 8; + c |= static_cast(static_cast(is.Take())); + return static_cast(c); + } + + template + static void PutBOM(OutputByteStream& os) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); + os.Put(static_cast(0x00u)); + os.Put(static_cast(0x00u)); + os.Put(static_cast(0xFEu)); + os.Put(static_cast(0xFFu)); + } + + template + static void Put(OutputByteStream& os, CharType c) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); + os.Put(static_cast((c >> 24) & 0xFFu)); + os.Put(static_cast((c >> 16) & 0xFFu)); + os.Put(static_cast((c >> 8) & 0xFFu)); + os.Put(static_cast(c & 0xFFu)); + } +}; + +/////////////////////////////////////////////////////////////////////////////// +// ASCII + +//! ASCII encoding. +/*! http://en.wikipedia.org/wiki/ASCII + \tparam CharType Code unit for storing 7-bit ASCII data. Default is char. + \note implements Encoding concept +*/ +template +struct ASCII { + typedef CharType Ch; + + enum { supportUnicode = 0 }; + + template + static void Encode(OutputStream& os, unsigned codepoint) { + RAPIDJSON_ASSERT(codepoint <= 0x7F); + os.Put(static_cast(codepoint & 0xFF)); + } + + template + static void EncodeUnsafe(OutputStream& os, unsigned codepoint) { + RAPIDJSON_ASSERT(codepoint <= 0x7F); + PutUnsafe(os, static_cast(codepoint & 0xFF)); + } + + template + static bool Decode(InputStream& is, unsigned* codepoint) { + uint8_t c = static_cast(is.Take()); + *codepoint = c; + return c <= 0X7F; + } + + template + static bool Validate(InputStream& is, OutputStream& os) { + uint8_t c = static_cast(is.Take()); + os.Put(static_cast(c)); + return c <= 0x7F; + } + + template + static CharType TakeBOM(InputByteStream& is) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); + uint8_t c = static_cast(Take(is)); + return static_cast(c); + } + + template + static Ch Take(InputByteStream& is) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); + return static_cast(is.Take()); + } + + template + static void PutBOM(OutputByteStream& os) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); + (void)os; + } + + template + static void Put(OutputByteStream& os, Ch c) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); + os.Put(static_cast(c)); + } +}; + +/////////////////////////////////////////////////////////////////////////////// +// AutoUTF + +//! Runtime-specified UTF encoding type of a stream. +enum UTFType { + kUTF8 = 0, //!< UTF-8. + kUTF16LE = 1, //!< UTF-16 little endian. + kUTF16BE = 2, //!< UTF-16 big endian. + kUTF32LE = 3, //!< UTF-32 little endian. + kUTF32BE = 4 //!< UTF-32 big endian. +}; + +//! Dynamically select encoding according to stream's runtime-specified UTF encoding type. +/*! \note This class can be used with AutoUTFInputtStream and AutoUTFOutputStream, which provides GetType(). +*/ +template +struct AutoUTF { + typedef CharType Ch; + + enum { supportUnicode = 1 }; + +#define RAPIDJSON_ENCODINGS_FUNC(x) UTF8::x, UTF16LE::x, UTF16BE::x, UTF32LE::x, UTF32BE::x + + template + static RAPIDJSON_FORCEINLINE void Encode(OutputStream& os, unsigned codepoint) { + typedef void (*EncodeFunc)(OutputStream&, unsigned); + static const EncodeFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(Encode) }; + (*f[os.GetType()])(os, codepoint); + } + + template + static RAPIDJSON_FORCEINLINE void EncodeUnsafe(OutputStream& os, unsigned codepoint) { + typedef void (*EncodeFunc)(OutputStream&, unsigned); + static const EncodeFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(EncodeUnsafe) }; + (*f[os.GetType()])(os, codepoint); + } + + template + static RAPIDJSON_FORCEINLINE bool Decode(InputStream& is, unsigned* codepoint) { + typedef bool (*DecodeFunc)(InputStream&, unsigned*); + static const DecodeFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(Decode) }; + return (*f[is.GetType()])(is, codepoint); + } + + template + static RAPIDJSON_FORCEINLINE bool Validate(InputStream& is, OutputStream& os) { + typedef bool (*ValidateFunc)(InputStream&, OutputStream&); + static const ValidateFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(Validate) }; + return (*f[is.GetType()])(is, os); + } + +#undef RAPIDJSON_ENCODINGS_FUNC +}; + +/////////////////////////////////////////////////////////////////////////////// +// Transcoder + +//! Encoding conversion. +template +struct Transcoder { + //! Take one Unicode codepoint from source encoding, convert it to target encoding and put it to the output stream. + template + static RAPIDJSON_FORCEINLINE bool Transcode(InputStream& is, OutputStream& os) { + unsigned codepoint; + if (!SourceEncoding::Decode(is, &codepoint)) + return false; + TargetEncoding::Encode(os, codepoint); + return true; + } + + template + static RAPIDJSON_FORCEINLINE bool TranscodeUnsafe(InputStream& is, OutputStream& os) { + unsigned codepoint; + if (!SourceEncoding::Decode(is, &codepoint)) + return false; + TargetEncoding::EncodeUnsafe(os, codepoint); + return true; + } + + //! Validate one Unicode codepoint from an encoded stream. + template + static RAPIDJSON_FORCEINLINE bool Validate(InputStream& is, OutputStream& os) { + return Transcode(is, os); // Since source/target encoding is different, must transcode. + } +}; + +// Forward declaration. +template +inline void PutUnsafe(Stream& stream, typename Stream::Ch c); + +//! Specialization of Transcoder with same source and target encoding. +template +struct Transcoder { + template + static RAPIDJSON_FORCEINLINE bool Transcode(InputStream& is, OutputStream& os) { + os.Put(is.Take()); // Just copy one code unit. This semantic is different from primary template class. + return true; + } + + template + static RAPIDJSON_FORCEINLINE bool TranscodeUnsafe(InputStream& is, OutputStream& os) { + PutUnsafe(os, is.Take()); // Just copy one code unit. This semantic is different from primary template class. + return true; + } + + template + static RAPIDJSON_FORCEINLINE bool Validate(InputStream& is, OutputStream& os) { + return Encoding::Validate(is, os); // source/target encoding are the same + } +}; + +RAPIDJSON_NAMESPACE_END + +#if defined(__GNUC__) || (defined(_MSC_VER) && !defined(__clang__)) +RAPIDJSON_DIAG_POP +#endif + +#endif // RAPIDJSON_ENCODINGS_H_ diff --git a/include/rapidjson/error/en.h b/include/rapidjson/error/en.h new file mode 100644 index 0000000..c87b04e --- /dev/null +++ b/include/rapidjson/error/en.h @@ -0,0 +1,176 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// 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 RAPIDJSON_ERROR_EN_H_ +#define RAPIDJSON_ERROR_EN_H_ + +#include "error.h" + +#ifdef __clang__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(switch-enum) +RAPIDJSON_DIAG_OFF(covered-switch-default) +#endif + +RAPIDJSON_NAMESPACE_BEGIN + +//! Maps error code of parsing into error message. +/*! + \ingroup RAPIDJSON_ERRORS + \param parseErrorCode Error code obtained in parsing. + \return the error message. + \note User can make a copy of this function for localization. + Using switch-case is safer for future modification of error codes. +*/ +inline const RAPIDJSON_ERROR_CHARTYPE* GetParseError_En(ParseErrorCode parseErrorCode) { + switch (parseErrorCode) { + case kParseErrorNone: return RAPIDJSON_ERROR_STRING("No error."); + + case kParseErrorDocumentEmpty: return RAPIDJSON_ERROR_STRING("The document is empty."); + case kParseErrorDocumentRootNotSingular: return RAPIDJSON_ERROR_STRING("The document root must not be followed by other values."); + + case kParseErrorValueInvalid: return RAPIDJSON_ERROR_STRING("Invalid value."); + + case kParseErrorObjectMissName: return RAPIDJSON_ERROR_STRING("Missing a name for object member."); + case kParseErrorObjectMissColon: return RAPIDJSON_ERROR_STRING("Missing a colon after a name of object member."); + case kParseErrorObjectMissCommaOrCurlyBracket: return RAPIDJSON_ERROR_STRING("Missing a comma or '}' after an object member."); + + case kParseErrorArrayMissCommaOrSquareBracket: return RAPIDJSON_ERROR_STRING("Missing a comma or ']' after an array element."); + + case kParseErrorStringUnicodeEscapeInvalidHex: return RAPIDJSON_ERROR_STRING("Incorrect hex digit after \\u escape in string."); + case kParseErrorStringUnicodeSurrogateInvalid: return RAPIDJSON_ERROR_STRING("The surrogate pair in string is invalid."); + case kParseErrorStringEscapeInvalid: return RAPIDJSON_ERROR_STRING("Invalid escape character in string."); + case kParseErrorStringMissQuotationMark: return RAPIDJSON_ERROR_STRING("Missing a closing quotation mark in string."); + case kParseErrorStringInvalidEncoding: return RAPIDJSON_ERROR_STRING("Invalid encoding in string."); + + case kParseErrorNumberTooBig: return RAPIDJSON_ERROR_STRING("Number too big to be stored in double."); + case kParseErrorNumberMissFraction: return RAPIDJSON_ERROR_STRING("Miss fraction part in number."); + case kParseErrorNumberMissExponent: return RAPIDJSON_ERROR_STRING("Miss exponent in number."); + + case kParseErrorTermination: return RAPIDJSON_ERROR_STRING("Terminate parsing due to Handler error."); + case kParseErrorUnspecificSyntaxError: return RAPIDJSON_ERROR_STRING("Unspecific syntax error."); + + default: return RAPIDJSON_ERROR_STRING("Unknown error."); + } +} + +//! Maps error code of validation into error message. +/*! + \ingroup RAPIDJSON_ERRORS + \param validateErrorCode Error code obtained from validator. + \return the error message. + \note User can make a copy of this function for localization. + Using switch-case is safer for future modification of error codes. +*/ +inline const RAPIDJSON_ERROR_CHARTYPE* GetValidateError_En(ValidateErrorCode validateErrorCode) { + switch (validateErrorCode) { + case kValidateErrors: return RAPIDJSON_ERROR_STRING("One or more validation errors have occurred"); + case kValidateErrorNone: return RAPIDJSON_ERROR_STRING("No error."); + + case kValidateErrorMultipleOf: return RAPIDJSON_ERROR_STRING("Number '%actual' is not a multiple of the 'multipleOf' value '%expected'."); + case kValidateErrorMaximum: return RAPIDJSON_ERROR_STRING("Number '%actual' is greater than the 'maximum' value '%expected'."); + case kValidateErrorExclusiveMaximum: return RAPIDJSON_ERROR_STRING("Number '%actual' is greater than or equal to the 'exclusiveMaximum' value '%expected'."); + case kValidateErrorMinimum: return RAPIDJSON_ERROR_STRING("Number '%actual' is less than the 'minimum' value '%expected'."); + case kValidateErrorExclusiveMinimum: return RAPIDJSON_ERROR_STRING("Number '%actual' is less than or equal to the 'exclusiveMinimum' value '%expected'."); + + case kValidateErrorMaxLength: return RAPIDJSON_ERROR_STRING("String '%actual' is longer than the 'maxLength' value '%expected'."); + case kValidateErrorMinLength: return RAPIDJSON_ERROR_STRING("String '%actual' is shorter than the 'minLength' value '%expected'."); + case kValidateErrorPattern: return RAPIDJSON_ERROR_STRING("String '%actual' does not match the 'pattern' regular expression."); + + case kValidateErrorMaxItems: return RAPIDJSON_ERROR_STRING("Array of length '%actual' is longer than the 'maxItems' value '%expected'."); + case kValidateErrorMinItems: return RAPIDJSON_ERROR_STRING("Array of length '%actual' is shorter than the 'minItems' value '%expected'."); + case kValidateErrorUniqueItems: return RAPIDJSON_ERROR_STRING("Array has duplicate items at indices '%duplicates' but 'uniqueItems' is true."); + case kValidateErrorAdditionalItems: return RAPIDJSON_ERROR_STRING("Array has an additional item at index '%disallowed' that is not allowed by the schema."); + + case kValidateErrorMaxProperties: return RAPIDJSON_ERROR_STRING("Object has '%actual' members which is more than 'maxProperties' value '%expected'."); + case kValidateErrorMinProperties: return RAPIDJSON_ERROR_STRING("Object has '%actual' members which is less than 'minProperties' value '%expected'."); + case kValidateErrorRequired: return RAPIDJSON_ERROR_STRING("Object is missing the following members required by the schema: '%missing'."); + case kValidateErrorAdditionalProperties: return RAPIDJSON_ERROR_STRING("Object has an additional member '%disallowed' that is not allowed by the schema."); + case kValidateErrorPatternProperties: return RAPIDJSON_ERROR_STRING("Object has 'patternProperties' that are not allowed by the schema."); + case kValidateErrorDependencies: return RAPIDJSON_ERROR_STRING("Object has missing property or schema dependencies, refer to following errors."); + + case kValidateErrorEnum: return RAPIDJSON_ERROR_STRING("Property has a value that is not one of its allowed enumerated values."); + case kValidateErrorType: return RAPIDJSON_ERROR_STRING("Property has a type '%actual' that is not in the following list: '%expected'."); + + case kValidateErrorOneOf: return RAPIDJSON_ERROR_STRING("Property did not match any of the sub-schemas specified by 'oneOf', refer to following errors."); + case kValidateErrorOneOfMatch: return RAPIDJSON_ERROR_STRING("Property matched more than one of the sub-schemas specified by 'oneOf', indices '%matches'."); + case kValidateErrorAllOf: return RAPIDJSON_ERROR_STRING("Property did not match all of the sub-schemas specified by 'allOf', refer to following errors."); + case kValidateErrorAnyOf: return RAPIDJSON_ERROR_STRING("Property did not match any of the sub-schemas specified by 'anyOf', refer to following errors."); + case kValidateErrorNot: return RAPIDJSON_ERROR_STRING("Property matched the sub-schema specified by 'not'."); + + case kValidateErrorReadOnly: return RAPIDJSON_ERROR_STRING("Property is read-only but has been provided when validation is for writing."); + case kValidateErrorWriteOnly: return RAPIDJSON_ERROR_STRING("Property is write-only but has been provided when validation is for reading."); + + default: return RAPIDJSON_ERROR_STRING("Unknown error."); + } +} + +//! Maps error code of schema document compilation into error message. +/*! + \ingroup RAPIDJSON_ERRORS + \param schemaErrorCode Error code obtained from compiling the schema document. + \return the error message. + \note User can make a copy of this function for localization. + Using switch-case is safer for future modification of error codes. +*/ + inline const RAPIDJSON_ERROR_CHARTYPE* GetSchemaError_En(SchemaErrorCode schemaErrorCode) { + switch (schemaErrorCode) { + case kSchemaErrorNone: return RAPIDJSON_ERROR_STRING("No error."); + + case kSchemaErrorStartUnknown: return RAPIDJSON_ERROR_STRING("Pointer '%value' to start of schema does not resolve to a location in the document."); + case kSchemaErrorRefPlainName: return RAPIDJSON_ERROR_STRING("$ref fragment '%value' must be a JSON pointer."); + case kSchemaErrorRefInvalid: return RAPIDJSON_ERROR_STRING("$ref must not be an empty string."); + case kSchemaErrorRefPointerInvalid: return RAPIDJSON_ERROR_STRING("$ref fragment '%value' is not a valid JSON pointer at offset '%offset'."); + case kSchemaErrorRefUnknown: return RAPIDJSON_ERROR_STRING("$ref '%value' does not resolve to a location in the target document."); + case kSchemaErrorRefCyclical: return RAPIDJSON_ERROR_STRING("$ref '%value' is cyclical."); + case kSchemaErrorRefNoRemoteProvider: return RAPIDJSON_ERROR_STRING("$ref is remote but there is no remote provider."); + case kSchemaErrorRefNoRemoteSchema: return RAPIDJSON_ERROR_STRING("$ref '%value' is remote but the remote provider did not return a schema."); + case kSchemaErrorRegexInvalid: return RAPIDJSON_ERROR_STRING("Invalid regular expression '%value' in 'pattern' or 'patternProperties'."); + case kSchemaErrorSpecUnknown: return RAPIDJSON_ERROR_STRING("JSON schema draft or OpenAPI version is not recognized."); + case kSchemaErrorSpecUnsupported: return RAPIDJSON_ERROR_STRING("JSON schema draft or OpenAPI version is not supported."); + case kSchemaErrorSpecIllegal: return RAPIDJSON_ERROR_STRING("Both JSON schema draft and OpenAPI version found in document."); + case kSchemaErrorReadOnlyAndWriteOnly: return RAPIDJSON_ERROR_STRING("Property must not be both 'readOnly' and 'writeOnly'."); + + default: return RAPIDJSON_ERROR_STRING("Unknown error."); + } + } + +//! Maps error code of pointer parse into error message. +/*! + \ingroup RAPIDJSON_ERRORS + \param pointerParseErrorCode Error code obtained from pointer parse. + \return the error message. + \note User can make a copy of this function for localization. + Using switch-case is safer for future modification of error codes. +*/ +inline const RAPIDJSON_ERROR_CHARTYPE* GetPointerParseError_En(PointerParseErrorCode pointerParseErrorCode) { + switch (pointerParseErrorCode) { + case kPointerParseErrorNone: return RAPIDJSON_ERROR_STRING("No error."); + + case kPointerParseErrorTokenMustBeginWithSolidus: return RAPIDJSON_ERROR_STRING("A token must begin with a '/'."); + case kPointerParseErrorInvalidEscape: return RAPIDJSON_ERROR_STRING("Invalid escape."); + case kPointerParseErrorInvalidPercentEncoding: return RAPIDJSON_ERROR_STRING("Invalid percent encoding in URI fragment."); + case kPointerParseErrorCharacterMustPercentEncode: return RAPIDJSON_ERROR_STRING("A character must be percent encoded in a URI fragment."); + + default: return RAPIDJSON_ERROR_STRING("Unknown error."); + } +} + +RAPIDJSON_NAMESPACE_END + +#ifdef __clang__ +RAPIDJSON_DIAG_POP +#endif + +#endif // RAPIDJSON_ERROR_EN_H_ diff --git a/include/rapidjson/error/error.h b/include/rapidjson/error/error.h new file mode 100644 index 0000000..cae345d --- /dev/null +++ b/include/rapidjson/error/error.h @@ -0,0 +1,285 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// 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 RAPIDJSON_ERROR_ERROR_H_ +#define RAPIDJSON_ERROR_ERROR_H_ + +#include "../rapidjson.h" + +#ifdef __clang__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(padded) +#endif + +/*! \file error.h */ + +/*! \defgroup RAPIDJSON_ERRORS RapidJSON error handling */ + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_ERROR_CHARTYPE + +//! Character type of error messages. +/*! \ingroup RAPIDJSON_ERRORS + The default character type is \c char. + On Windows, user can define this macro as \c TCHAR for supporting both + unicode/non-unicode settings. +*/ +#ifndef RAPIDJSON_ERROR_CHARTYPE +#define RAPIDJSON_ERROR_CHARTYPE char +#endif + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_ERROR_STRING + +//! Macro for converting string literal to \ref RAPIDJSON_ERROR_CHARTYPE[]. +/*! \ingroup RAPIDJSON_ERRORS + By default this conversion macro does nothing. + On Windows, user can define this macro as \c _T(x) for supporting both + unicode/non-unicode settings. +*/ +#ifndef RAPIDJSON_ERROR_STRING +#define RAPIDJSON_ERROR_STRING(x) x +#endif + +RAPIDJSON_NAMESPACE_BEGIN + +/////////////////////////////////////////////////////////////////////////////// +// ParseErrorCode + +//! Error code of parsing. +/*! \ingroup RAPIDJSON_ERRORS + \see GenericReader::Parse, GenericReader::GetParseErrorCode +*/ +enum ParseErrorCode { + kParseErrorNone = 0, //!< No error. + + kParseErrorDocumentEmpty, //!< The document is empty. + kParseErrorDocumentRootNotSingular, //!< The document root must not follow by other values. + + kParseErrorValueInvalid, //!< Invalid value. + + kParseErrorObjectMissName, //!< Missing a name for object member. + kParseErrorObjectMissColon, //!< Missing a colon after a name of object member. + kParseErrorObjectMissCommaOrCurlyBracket, //!< Missing a comma or '}' after an object member. + + kParseErrorArrayMissCommaOrSquareBracket, //!< Missing a comma or ']' after an array element. + + kParseErrorStringUnicodeEscapeInvalidHex, //!< Incorrect hex digit after \\u escape in string. + kParseErrorStringUnicodeSurrogateInvalid, //!< The surrogate pair in string is invalid. + kParseErrorStringEscapeInvalid, //!< Invalid escape character in string. + kParseErrorStringMissQuotationMark, //!< Missing a closing quotation mark in string. + kParseErrorStringInvalidEncoding, //!< Invalid encoding in string. + + kParseErrorNumberTooBig, //!< Number too big to be stored in double. + kParseErrorNumberMissFraction, //!< Miss fraction part in number. + kParseErrorNumberMissExponent, //!< Miss exponent in number. + + kParseErrorTermination, //!< Parsing was terminated. + kParseErrorUnspecificSyntaxError //!< Unspecific syntax error. +}; + +//! Result of parsing (wraps ParseErrorCode) +/*! + \ingroup RAPIDJSON_ERRORS + \code + Document doc; + ParseResult ok = doc.Parse("[42]"); + if (!ok) { + fprintf(stderr, "JSON parse error: %s (%u)", + GetParseError_En(ok.Code()), ok.Offset()); + exit(EXIT_FAILURE); + } + \endcode + \see GenericReader::Parse, GenericDocument::Parse +*/ +struct ParseResult { + //!! Unspecified boolean type + typedef bool (ParseResult::*BooleanType)() const; +public: + //! Default constructor, no error. + ParseResult() : code_(kParseErrorNone), offset_(0) {} + //! Constructor to set an error. + ParseResult(ParseErrorCode code, size_t offset) : code_(code), offset_(offset) {} + + //! Get the error code. + ParseErrorCode Code() const { return code_; } + //! Get the error offset, if \ref IsError(), 0 otherwise. + size_t Offset() const { return offset_; } + + //! Explicit conversion to \c bool, returns \c true, iff !\ref IsError(). + operator BooleanType() const { return !IsError() ? &ParseResult::IsError : NULL; } + //! Whether the result is an error. + bool IsError() const { return code_ != kParseErrorNone; } + + bool operator==(const ParseResult& that) const { return code_ == that.code_; } + bool operator==(ParseErrorCode code) const { return code_ == code; } + friend bool operator==(ParseErrorCode code, const ParseResult & err) { return code == err.code_; } + + bool operator!=(const ParseResult& that) const { return !(*this == that); } + bool operator!=(ParseErrorCode code) const { return !(*this == code); } + friend bool operator!=(ParseErrorCode code, const ParseResult & err) { return err != code; } + + //! Reset error code. + void Clear() { Set(kParseErrorNone); } + //! Update error code and offset. + void Set(ParseErrorCode code, size_t offset = 0) { code_ = code; offset_ = offset; } + +private: + ParseErrorCode code_; + size_t offset_; +}; + +//! Function pointer type of GetParseError(). +/*! \ingroup RAPIDJSON_ERRORS + + This is the prototype for \c GetParseError_X(), where \c X is a locale. + User can dynamically change locale in runtime, e.g.: +\code + GetParseErrorFunc GetParseError = GetParseError_En; // or whatever + const RAPIDJSON_ERROR_CHARTYPE* s = GetParseError(document.GetParseErrorCode()); +\endcode +*/ +typedef const RAPIDJSON_ERROR_CHARTYPE* (*GetParseErrorFunc)(ParseErrorCode); + +/////////////////////////////////////////////////////////////////////////////// +// ValidateErrorCode + +//! Error codes when validating. +/*! \ingroup RAPIDJSON_ERRORS + \see GenericSchemaValidator +*/ +enum ValidateErrorCode { + kValidateErrors = -1, //!< Top level error code when kValidateContinueOnErrorsFlag set. + kValidateErrorNone = 0, //!< No error. + + kValidateErrorMultipleOf, //!< Number is not a multiple of the 'multipleOf' value. + kValidateErrorMaximum, //!< Number is greater than the 'maximum' value. + kValidateErrorExclusiveMaximum, //!< Number is greater than or equal to the 'maximum' value. + kValidateErrorMinimum, //!< Number is less than the 'minimum' value. + kValidateErrorExclusiveMinimum, //!< Number is less than or equal to the 'minimum' value. + + kValidateErrorMaxLength, //!< String is longer than the 'maxLength' value. + kValidateErrorMinLength, //!< String is longer than the 'maxLength' value. + kValidateErrorPattern, //!< String does not match the 'pattern' regular expression. + + kValidateErrorMaxItems, //!< Array is longer than the 'maxItems' value. + kValidateErrorMinItems, //!< Array is shorter than the 'minItems' value. + kValidateErrorUniqueItems, //!< Array has duplicate items but 'uniqueItems' is true. + kValidateErrorAdditionalItems, //!< Array has additional items that are not allowed by the schema. + + kValidateErrorMaxProperties, //!< Object has more members than 'maxProperties' value. + kValidateErrorMinProperties, //!< Object has less members than 'minProperties' value. + kValidateErrorRequired, //!< Object is missing one or more members required by the schema. + kValidateErrorAdditionalProperties, //!< Object has additional members that are not allowed by the schema. + kValidateErrorPatternProperties, //!< See other errors. + kValidateErrorDependencies, //!< Object has missing property or schema dependencies. + + kValidateErrorEnum, //!< Property has a value that is not one of its allowed enumerated values. + kValidateErrorType, //!< Property has a type that is not allowed by the schema. + + kValidateErrorOneOf, //!< Property did not match any of the sub-schemas specified by 'oneOf'. + kValidateErrorOneOfMatch, //!< Property matched more than one of the sub-schemas specified by 'oneOf'. + kValidateErrorAllOf, //!< Property did not match all of the sub-schemas specified by 'allOf'. + kValidateErrorAnyOf, //!< Property did not match any of the sub-schemas specified by 'anyOf'. + kValidateErrorNot, //!< Property matched the sub-schema specified by 'not'. + + kValidateErrorReadOnly, //!< Property is read-only but has been provided when validation is for writing + kValidateErrorWriteOnly //!< Property is write-only but has been provided when validation is for reading +}; + +//! Function pointer type of GetValidateError(). +/*! \ingroup RAPIDJSON_ERRORS + + This is the prototype for \c GetValidateError_X(), where \c X is a locale. + User can dynamically change locale in runtime, e.g.: +\code + GetValidateErrorFunc GetValidateError = GetValidateError_En; // or whatever + const RAPIDJSON_ERROR_CHARTYPE* s = GetValidateError(validator.GetInvalidSchemaCode()); +\endcode +*/ +typedef const RAPIDJSON_ERROR_CHARTYPE* (*GetValidateErrorFunc)(ValidateErrorCode); + +/////////////////////////////////////////////////////////////////////////////// +// SchemaErrorCode + +//! Error codes when validating. +/*! \ingroup RAPIDJSON_ERRORS + \see GenericSchemaValidator +*/ +enum SchemaErrorCode { + kSchemaErrorNone = 0, //!< No error. + + kSchemaErrorStartUnknown, //!< Pointer to start of schema does not resolve to a location in the document + kSchemaErrorRefPlainName, //!< $ref fragment must be a JSON pointer + kSchemaErrorRefInvalid, //!< $ref must not be an empty string + kSchemaErrorRefPointerInvalid, //!< $ref fragment is not a valid JSON pointer at offset + kSchemaErrorRefUnknown, //!< $ref does not resolve to a location in the target document + kSchemaErrorRefCyclical, //!< $ref is cyclical + kSchemaErrorRefNoRemoteProvider, //!< $ref is remote but there is no remote provider + kSchemaErrorRefNoRemoteSchema, //!< $ref is remote but the remote provider did not return a schema + kSchemaErrorRegexInvalid, //!< Invalid regular expression in 'pattern' or 'patternProperties' + kSchemaErrorSpecUnknown, //!< JSON schema draft or OpenAPI version is not recognized + kSchemaErrorSpecUnsupported, //!< JSON schema draft or OpenAPI version is not supported + kSchemaErrorSpecIllegal, //!< Both JSON schema draft and OpenAPI version found in document + kSchemaErrorReadOnlyAndWriteOnly //!< Property must not be both 'readOnly' and 'writeOnly' +}; + +//! Function pointer type of GetSchemaError(). +/*! \ingroup RAPIDJSON_ERRORS + + This is the prototype for \c GetSchemaError_X(), where \c X is a locale. + User can dynamically change locale in runtime, e.g.: +\code + GetSchemaErrorFunc GetSchemaError = GetSchemaError_En; // or whatever + const RAPIDJSON_ERROR_CHARTYPE* s = GetSchemaError(validator.GetInvalidSchemaCode()); +\endcode +*/ +typedef const RAPIDJSON_ERROR_CHARTYPE* (*GetSchemaErrorFunc)(SchemaErrorCode); + +/////////////////////////////////////////////////////////////////////////////// +// PointerParseErrorCode + +//! Error code of JSON pointer parsing. +/*! \ingroup RAPIDJSON_ERRORS + \see GenericPointer::GenericPointer, GenericPointer::GetParseErrorCode +*/ +enum PointerParseErrorCode { + kPointerParseErrorNone = 0, //!< The parse is successful + + kPointerParseErrorTokenMustBeginWithSolidus, //!< A token must begin with a '/' + kPointerParseErrorInvalidEscape, //!< Invalid escape + kPointerParseErrorInvalidPercentEncoding, //!< Invalid percent encoding in URI fragment + kPointerParseErrorCharacterMustPercentEncode //!< A character must percent encoded in URI fragment +}; + +//! Function pointer type of GetPointerParseError(). +/*! \ingroup RAPIDJSON_ERRORS + + This is the prototype for \c GetPointerParseError_X(), where \c X is a locale. + User can dynamically change locale in runtime, e.g.: +\code + GetPointerParseErrorFunc GetPointerParseError = GetPointerParseError_En; // or whatever + const RAPIDJSON_ERROR_CHARTYPE* s = GetPointerParseError(pointer.GetParseErrorCode()); +\endcode +*/ +typedef const RAPIDJSON_ERROR_CHARTYPE* (*GetPointerParseErrorFunc)(PointerParseErrorCode); + + +RAPIDJSON_NAMESPACE_END + +#ifdef __clang__ +RAPIDJSON_DIAG_POP +#endif + +#endif // RAPIDJSON_ERROR_ERROR_H_ diff --git a/include/rapidjson/filereadstream.h b/include/rapidjson/filereadstream.h new file mode 100644 index 0000000..f8bb43c --- /dev/null +++ b/include/rapidjson/filereadstream.h @@ -0,0 +1,99 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// 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 RAPIDJSON_FILEREADSTREAM_H_ +#define RAPIDJSON_FILEREADSTREAM_H_ + +#include "stream.h" +#include + +#ifdef __clang__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(padded) +RAPIDJSON_DIAG_OFF(unreachable-code) +RAPIDJSON_DIAG_OFF(missing-noreturn) +#endif + +RAPIDJSON_NAMESPACE_BEGIN + +//! File byte stream for input using fread(). +/*! + \note implements Stream concept +*/ +class FileReadStream { +public: + typedef char Ch; //!< Character type (byte). + + //! Constructor. + /*! + \param fp File pointer opened for read. + \param buffer user-supplied buffer. + \param bufferSize size of buffer in bytes. Must >=4 bytes. + */ + FileReadStream(std::FILE* fp, char* buffer, size_t bufferSize) : fp_(fp), buffer_(buffer), bufferSize_(bufferSize), bufferLast_(0), current_(buffer_), readCount_(0), count_(0), eof_(false) { + RAPIDJSON_ASSERT(fp_ != 0); + RAPIDJSON_ASSERT(bufferSize >= 4); + Read(); + } + + Ch Peek() const { return *current_; } + Ch Take() { Ch c = *current_; Read(); return c; } + size_t Tell() const { return count_ + static_cast(current_ - buffer_); } + + // Not implemented + void Put(Ch) { RAPIDJSON_ASSERT(false); } + void Flush() { RAPIDJSON_ASSERT(false); } + Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; } + size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; } + + // For encoding detection only. + const Ch* Peek4() const { + return (current_ + 4 - !eof_ <= bufferLast_) ? current_ : 0; + } + +private: + void Read() { + if (current_ < bufferLast_) + ++current_; + else if (!eof_) { + count_ += readCount_; + readCount_ = std::fread(buffer_, 1, bufferSize_, fp_); + bufferLast_ = buffer_ + readCount_ - 1; + current_ = buffer_; + + if (readCount_ < bufferSize_) { + buffer_[readCount_] = '\0'; + ++bufferLast_; + eof_ = true; + } + } + } + + std::FILE* fp_; + Ch *buffer_; + size_t bufferSize_; + Ch *bufferLast_; + Ch *current_; + size_t readCount_; + size_t count_; //!< Number of characters read + bool eof_; +}; + +RAPIDJSON_NAMESPACE_END + +#ifdef __clang__ +RAPIDJSON_DIAG_POP +#endif + +#endif // RAPIDJSON_FILESTREAM_H_ diff --git a/include/rapidjson/filewritestream.h b/include/rapidjson/filewritestream.h new file mode 100644 index 0000000..5d89588 --- /dev/null +++ b/include/rapidjson/filewritestream.h @@ -0,0 +1,104 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// 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 RAPIDJSON_FILEWRITESTREAM_H_ +#define RAPIDJSON_FILEWRITESTREAM_H_ + +#include "stream.h" +#include + +#ifdef __clang__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(unreachable-code) +#endif + +RAPIDJSON_NAMESPACE_BEGIN + +//! Wrapper of C file stream for output using fwrite(). +/*! + \note implements Stream concept +*/ +class FileWriteStream { +public: + typedef char Ch; //!< Character type. Only support char. + + FileWriteStream(std::FILE* fp, char* buffer, size_t bufferSize) : fp_(fp), buffer_(buffer), bufferEnd_(buffer + bufferSize), current_(buffer_) { + RAPIDJSON_ASSERT(fp_ != 0); + } + + void Put(char c) { + if (current_ >= bufferEnd_) + Flush(); + + *current_++ = c; + } + + void PutN(char c, size_t n) { + size_t avail = static_cast(bufferEnd_ - current_); + while (n > avail) { + std::memset(current_, c, avail); + current_ += avail; + Flush(); + n -= avail; + avail = static_cast(bufferEnd_ - current_); + } + + if (n > 0) { + std::memset(current_, c, n); + current_ += n; + } + } + + void Flush() { + if (current_ != buffer_) { + size_t result = std::fwrite(buffer_, 1, static_cast(current_ - buffer_), fp_); + if (result < static_cast(current_ - buffer_)) { + // failure deliberately ignored at this time + // added to avoid warn_unused_result build errors + } + current_ = buffer_; + } + } + + // Not implemented + char Peek() const { RAPIDJSON_ASSERT(false); return 0; } + char Take() { RAPIDJSON_ASSERT(false); return 0; } + size_t Tell() const { RAPIDJSON_ASSERT(false); return 0; } + char* PutBegin() { RAPIDJSON_ASSERT(false); return 0; } + size_t PutEnd(char*) { RAPIDJSON_ASSERT(false); return 0; } + +private: + // Prohibit copy constructor & assignment operator. + FileWriteStream(const FileWriteStream&); + FileWriteStream& operator=(const FileWriteStream&); + + std::FILE* fp_; + char *buffer_; + char *bufferEnd_; + char *current_; +}; + +//! Implement specialized version of PutN() with memset() for better performance. +template<> +inline void PutN(FileWriteStream& stream, char c, size_t n) { + stream.PutN(c, n); +} + +RAPIDJSON_NAMESPACE_END + +#ifdef __clang__ +RAPIDJSON_DIAG_POP +#endif + +#endif // RAPIDJSON_FILESTREAM_H_ diff --git a/include/rapidjson/fwd.h b/include/rapidjson/fwd.h new file mode 100644 index 0000000..d62f77f --- /dev/null +++ b/include/rapidjson/fwd.h @@ -0,0 +1,151 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// 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 RAPIDJSON_FWD_H_ +#define RAPIDJSON_FWD_H_ + +#include "rapidjson.h" + +RAPIDJSON_NAMESPACE_BEGIN + +// encodings.h + +template struct UTF8; +template struct UTF16; +template struct UTF16BE; +template struct UTF16LE; +template struct UTF32; +template struct UTF32BE; +template struct UTF32LE; +template struct ASCII; +template struct AutoUTF; + +template +struct Transcoder; + +// allocators.h + +class CrtAllocator; + +template +class MemoryPoolAllocator; + +// stream.h + +template +struct GenericStringStream; + +typedef GenericStringStream > StringStream; + +template +struct GenericInsituStringStream; + +typedef GenericInsituStringStream > InsituStringStream; + +// stringbuffer.h + +template +class GenericStringBuffer; + +typedef GenericStringBuffer, CrtAllocator> StringBuffer; + +// filereadstream.h + +class FileReadStream; + +// filewritestream.h + +class FileWriteStream; + +// memorybuffer.h + +template +struct GenericMemoryBuffer; + +typedef GenericMemoryBuffer MemoryBuffer; + +// memorystream.h + +struct MemoryStream; + +// reader.h + +template +struct BaseReaderHandler; + +template +class GenericReader; + +typedef GenericReader, UTF8, CrtAllocator> Reader; + +// writer.h + +template +class Writer; + +// prettywriter.h + +template +class PrettyWriter; + +// document.h + +template +class GenericMember; + +template +class GenericMemberIterator; + +template +struct GenericStringRef; + +template +class GenericValue; + +typedef GenericValue, MemoryPoolAllocator > Value; + +template +class GenericDocument; + +typedef GenericDocument, MemoryPoolAllocator, CrtAllocator> Document; + +// pointer.h + +template +class GenericPointer; + +typedef GenericPointer Pointer; + +// schema.h + +template +class IGenericRemoteSchemaDocumentProvider; + +template +class GenericSchemaDocument; + +typedef GenericSchemaDocument SchemaDocument; +typedef IGenericRemoteSchemaDocumentProvider IRemoteSchemaDocumentProvider; + +template < + typename SchemaDocumentType, + typename OutputHandler, + typename StateAllocator> +class GenericSchemaValidator; + +typedef GenericSchemaValidator, void>, CrtAllocator> SchemaValidator; + +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_RAPIDJSONFWD_H_ diff --git a/include/rapidjson/internal/biginteger.h b/include/rapidjson/internal/biginteger.h new file mode 100644 index 0000000..af48738 --- /dev/null +++ b/include/rapidjson/internal/biginteger.h @@ -0,0 +1,297 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// 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 RAPIDJSON_BIGINTEGER_H_ +#define RAPIDJSON_BIGINTEGER_H_ + +#include "../rapidjson.h" + +#if defined(_MSC_VER) && !defined(__INTEL_COMPILER) && defined(_M_AMD64) +#include // for _umul128 +#if !defined(_ARM64EC_) +#pragma intrinsic(_umul128) +#else +#pragma comment(lib,"softintrin") +#endif +#endif + +RAPIDJSON_NAMESPACE_BEGIN +namespace internal { + +class BigInteger { +public: + typedef uint64_t Type; + + BigInteger(const BigInteger& rhs) : count_(rhs.count_) { + std::memcpy(digits_, rhs.digits_, count_ * sizeof(Type)); + } + + explicit BigInteger(uint64_t u) : count_(1) { + digits_[0] = u; + } + + template + BigInteger(const Ch* decimals, size_t length) : count_(1) { + RAPIDJSON_ASSERT(length > 0); + digits_[0] = 0; + size_t i = 0; + const size_t kMaxDigitPerIteration = 19; // 2^64 = 18446744073709551616 > 10^19 + while (length >= kMaxDigitPerIteration) { + AppendDecimal64(decimals + i, decimals + i + kMaxDigitPerIteration); + length -= kMaxDigitPerIteration; + i += kMaxDigitPerIteration; + } + + if (length > 0) + AppendDecimal64(decimals + i, decimals + i + length); + } + + BigInteger& operator=(const BigInteger &rhs) + { + if (this != &rhs) { + count_ = rhs.count_; + std::memcpy(digits_, rhs.digits_, count_ * sizeof(Type)); + } + return *this; + } + + BigInteger& operator=(uint64_t u) { + digits_[0] = u; + count_ = 1; + return *this; + } + + BigInteger& operator+=(uint64_t u) { + Type backup = digits_[0]; + digits_[0] += u; + for (size_t i = 0; i < count_ - 1; i++) { + if (digits_[i] >= backup) + return *this; // no carry + backup = digits_[i + 1]; + digits_[i + 1] += 1; + } + + // Last carry + if (digits_[count_ - 1] < backup) + PushBack(1); + + return *this; + } + + BigInteger& operator*=(uint64_t u) { + if (u == 0) return *this = 0; + if (u == 1) return *this; + if (*this == 1) return *this = u; + + uint64_t k = 0; + for (size_t i = 0; i < count_; i++) { + uint64_t hi; + digits_[i] = MulAdd64(digits_[i], u, k, &hi); + k = hi; + } + + if (k > 0) + PushBack(k); + + return *this; + } + + BigInteger& operator*=(uint32_t u) { + if (u == 0) return *this = 0; + if (u == 1) return *this; + if (*this == 1) return *this = u; + + uint64_t k = 0; + for (size_t i = 0; i < count_; i++) { + const uint64_t c = digits_[i] >> 32; + const uint64_t d = digits_[i] & 0xFFFFFFFF; + const uint64_t uc = u * c; + const uint64_t ud = u * d; + const uint64_t p0 = ud + k; + const uint64_t p1 = uc + (p0 >> 32); + digits_[i] = (p0 & 0xFFFFFFFF) | (p1 << 32); + k = p1 >> 32; + } + + if (k > 0) + PushBack(k); + + return *this; + } + + BigInteger& operator<<=(size_t shift) { + if (IsZero() || shift == 0) return *this; + + size_t offset = shift / kTypeBit; + size_t interShift = shift % kTypeBit; + RAPIDJSON_ASSERT(count_ + offset <= kCapacity); + + if (interShift == 0) { + std::memmove(digits_ + offset, digits_, count_ * sizeof(Type)); + count_ += offset; + } + else { + digits_[count_] = 0; + for (size_t i = count_; i > 0; i--) + digits_[i + offset] = (digits_[i] << interShift) | (digits_[i - 1] >> (kTypeBit - interShift)); + digits_[offset] = digits_[0] << interShift; + count_ += offset; + if (digits_[count_]) + count_++; + } + + std::memset(digits_, 0, offset * sizeof(Type)); + + return *this; + } + + bool operator==(const BigInteger& rhs) const { + return count_ == rhs.count_ && std::memcmp(digits_, rhs.digits_, count_ * sizeof(Type)) == 0; + } + + bool operator==(const Type rhs) const { + return count_ == 1 && digits_[0] == rhs; + } + + BigInteger& MultiplyPow5(unsigned exp) { + static const uint32_t kPow5[12] = { + 5, + 5 * 5, + 5 * 5 * 5, + 5 * 5 * 5 * 5, + 5 * 5 * 5 * 5 * 5, + 5 * 5 * 5 * 5 * 5 * 5, + 5 * 5 * 5 * 5 * 5 * 5 * 5, + 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5, + 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5, + 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5, + 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5, + 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 + }; + if (exp == 0) return *this; + for (; exp >= 27; exp -= 27) *this *= RAPIDJSON_UINT64_C2(0X6765C793, 0XFA10079D); // 5^27 + for (; exp >= 13; exp -= 13) *this *= static_cast(1220703125u); // 5^13 + if (exp > 0) *this *= kPow5[exp - 1]; + return *this; + } + + // Compute absolute difference of this and rhs. + // Assume this != rhs + bool Difference(const BigInteger& rhs, BigInteger* out) const { + int cmp = Compare(rhs); + RAPIDJSON_ASSERT(cmp != 0); + const BigInteger *a, *b; // Makes a > b + bool ret; + if (cmp < 0) { a = &rhs; b = this; ret = true; } + else { a = this; b = &rhs; ret = false; } + + Type borrow = 0; + for (size_t i = 0; i < a->count_; i++) { + Type d = a->digits_[i] - borrow; + if (i < b->count_) + d -= b->digits_[i]; + borrow = (d > a->digits_[i]) ? 1 : 0; + out->digits_[i] = d; + if (d != 0) + out->count_ = i + 1; + } + + return ret; + } + + int Compare(const BigInteger& rhs) const { + if (count_ != rhs.count_) + return count_ < rhs.count_ ? -1 : 1; + + for (size_t i = count_; i-- > 0;) + if (digits_[i] != rhs.digits_[i]) + return digits_[i] < rhs.digits_[i] ? -1 : 1; + + return 0; + } + + size_t GetCount() const { return count_; } + Type GetDigit(size_t index) const { RAPIDJSON_ASSERT(index < count_); return digits_[index]; } + bool IsZero() const { return count_ == 1 && digits_[0] == 0; } + +private: + template + void AppendDecimal64(const Ch* begin, const Ch* end) { + uint64_t u = ParseUint64(begin, end); + if (IsZero()) + *this = u; + else { + unsigned exp = static_cast(end - begin); + (MultiplyPow5(exp) <<= exp) += u; // *this = *this * 10^exp + u + } + } + + void PushBack(Type digit) { + RAPIDJSON_ASSERT(count_ < kCapacity); + digits_[count_++] = digit; + } + + template + static uint64_t ParseUint64(const Ch* begin, const Ch* end) { + uint64_t r = 0; + for (const Ch* p = begin; p != end; ++p) { + RAPIDJSON_ASSERT(*p >= Ch('0') && *p <= Ch('9')); + r = r * 10u + static_cast(*p - Ch('0')); + } + return r; + } + + // Assume a * b + k < 2^128 + static uint64_t MulAdd64(uint64_t a, uint64_t b, uint64_t k, uint64_t* outHigh) { +#if defined(_MSC_VER) && defined(_M_AMD64) + uint64_t low = _umul128(a, b, outHigh) + k; + if (low < k) + (*outHigh)++; + return low; +#elif (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)) && defined(__x86_64__) + __extension__ typedef unsigned __int128 uint128; + uint128 p = static_cast(a) * static_cast(b); + p += k; + *outHigh = static_cast(p >> 64); + return static_cast(p); +#else + const uint64_t a0 = a & 0xFFFFFFFF, a1 = a >> 32, b0 = b & 0xFFFFFFFF, b1 = b >> 32; + uint64_t x0 = a0 * b0, x1 = a0 * b1, x2 = a1 * b0, x3 = a1 * b1; + x1 += (x0 >> 32); // can't give carry + x1 += x2; + if (x1 < x2) + x3 += (static_cast(1) << 32); + uint64_t lo = (x1 << 32) + (x0 & 0xFFFFFFFF); + uint64_t hi = x3 + (x1 >> 32); + + lo += k; + if (lo < k) + hi++; + *outHigh = hi; + return lo; +#endif + } + + static const size_t kBitCount = 3328; // 64bit * 54 > 10^1000 + static const size_t kCapacity = kBitCount / sizeof(Type); + static const size_t kTypeBit = sizeof(Type) * 8; + + Type digits_[kCapacity]; + size_t count_; +}; + +} // namespace internal +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_BIGINTEGER_H_ diff --git a/include/rapidjson/internal/clzll.h b/include/rapidjson/internal/clzll.h new file mode 100644 index 0000000..8fc5118 --- /dev/null +++ b/include/rapidjson/internal/clzll.h @@ -0,0 +1,71 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// 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 RAPIDJSON_CLZLL_H_ +#define RAPIDJSON_CLZLL_H_ + +#include "../rapidjson.h" + +#if defined(_MSC_VER) && !defined(UNDER_CE) +#include +#if defined(_WIN64) +#pragma intrinsic(_BitScanReverse64) +#else +#pragma intrinsic(_BitScanReverse) +#endif +#endif + +RAPIDJSON_NAMESPACE_BEGIN +namespace internal { + +inline uint32_t clzll(uint64_t x) { + // Passing 0 to __builtin_clzll is UB in GCC and results in an + // infinite loop in the software implementation. + RAPIDJSON_ASSERT(x != 0); + +#if defined(_MSC_VER) && !defined(UNDER_CE) + unsigned long r = 0; +#if defined(_WIN64) + _BitScanReverse64(&r, x); +#else + // Scan the high 32 bits. + if (_BitScanReverse(&r, static_cast(x >> 32))) + return 63 - (r + 32); + + // Scan the low 32 bits. + _BitScanReverse(&r, static_cast(x & 0xFFFFFFFF)); +#endif // _WIN64 + + return 63 - r; +#elif (defined(__GNUC__) && __GNUC__ >= 4) || RAPIDJSON_HAS_BUILTIN(__builtin_clzll) + // __builtin_clzll wrapper + return static_cast(__builtin_clzll(x)); +#else + // naive version + uint32_t r = 0; + while (!(x & (static_cast(1) << 63))) { + x <<= 1; + ++r; + } + + return r; +#endif // _MSC_VER +} + +#define RAPIDJSON_CLZLL RAPIDJSON_NAMESPACE::internal::clzll + +} // namespace internal +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_CLZLL_H_ diff --git a/include/rapidjson/internal/diyfp.h b/include/rapidjson/internal/diyfp.h new file mode 100644 index 0000000..f7d4653 --- /dev/null +++ b/include/rapidjson/internal/diyfp.h @@ -0,0 +1,261 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// 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. + +// This is a C++ header-only implementation of Grisu2 algorithm from the publication: +// Loitsch, Florian. "Printing floating-point numbers quickly and accurately with +// integers." ACM Sigplan Notices 45.6 (2010): 233-243. + +#ifndef RAPIDJSON_DIYFP_H_ +#define RAPIDJSON_DIYFP_H_ + +#include "../rapidjson.h" +#include "clzll.h" +#include + +#if defined(_MSC_VER) && defined(_M_AMD64) && !defined(__INTEL_COMPILER) +#include +#if !defined(_ARM64EC_) +#pragma intrinsic(_umul128) +#else +#pragma comment(lib,"softintrin") +#endif +#endif + +RAPIDJSON_NAMESPACE_BEGIN +namespace internal { + +#ifdef __GNUC__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(effc++) +#endif + +#ifdef __clang__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(padded) +#endif + +struct DiyFp { + DiyFp() : f(), e() {} + + DiyFp(uint64_t fp, int exp) : f(fp), e(exp) {} + + explicit DiyFp(double d) { + union { + double d; + uint64_t u64; + } u = { d }; + + int biased_e = static_cast((u.u64 & kDpExponentMask) >> kDpSignificandSize); + uint64_t significand = (u.u64 & kDpSignificandMask); + if (biased_e != 0) { + f = significand + kDpHiddenBit; + e = biased_e - kDpExponentBias; + } + else { + f = significand; + e = kDpMinExponent + 1; + } + } + + DiyFp operator-(const DiyFp& rhs) const { + return DiyFp(f - rhs.f, e); + } + + DiyFp operator*(const DiyFp& rhs) const { +#if defined(_MSC_VER) && defined(_M_AMD64) + uint64_t h; + uint64_t l = _umul128(f, rhs.f, &h); + if (l & (uint64_t(1) << 63)) // rounding + h++; + return DiyFp(h, e + rhs.e + 64); +#elif (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)) && defined(__x86_64__) + __extension__ typedef unsigned __int128 uint128; + uint128 p = static_cast(f) * static_cast(rhs.f); + uint64_t h = static_cast(p >> 64); + uint64_t l = static_cast(p); + if (l & (uint64_t(1) << 63)) // rounding + h++; + return DiyFp(h, e + rhs.e + 64); +#else + const uint64_t M32 = 0xFFFFFFFF; + const uint64_t a = f >> 32; + const uint64_t b = f & M32; + const uint64_t c = rhs.f >> 32; + const uint64_t d = rhs.f & M32; + const uint64_t ac = a * c; + const uint64_t bc = b * c; + const uint64_t ad = a * d; + const uint64_t bd = b * d; + uint64_t tmp = (bd >> 32) + (ad & M32) + (bc & M32); + tmp += 1U << 31; /// mult_round + return DiyFp(ac + (ad >> 32) + (bc >> 32) + (tmp >> 32), e + rhs.e + 64); +#endif + } + + DiyFp Normalize() const { + int s = static_cast(clzll(f)); + return DiyFp(f << s, e - s); + } + + DiyFp NormalizeBoundary() const { + DiyFp res = *this; + while (!(res.f & (kDpHiddenBit << 1))) { + res.f <<= 1; + res.e--; + } + res.f <<= (kDiySignificandSize - kDpSignificandSize - 2); + res.e = res.e - (kDiySignificandSize - kDpSignificandSize - 2); + return res; + } + + void NormalizedBoundaries(DiyFp* minus, DiyFp* plus) const { + DiyFp pl = DiyFp((f << 1) + 1, e - 1).NormalizeBoundary(); + DiyFp mi = (f == kDpHiddenBit) ? DiyFp((f << 2) - 1, e - 2) : DiyFp((f << 1) - 1, e - 1); + mi.f <<= mi.e - pl.e; + mi.e = pl.e; + *plus = pl; + *minus = mi; + } + + double ToDouble() const { + union { + double d; + uint64_t u64; + }u; + RAPIDJSON_ASSERT(f <= kDpHiddenBit + kDpSignificandMask); + if (e < kDpDenormalExponent) { + // Underflow. + return 0.0; + } + if (e >= kDpMaxExponent) { + // Overflow. + return std::numeric_limits::infinity(); + } + const uint64_t be = (e == kDpDenormalExponent && (f & kDpHiddenBit) == 0) ? 0 : + static_cast(e + kDpExponentBias); + u.u64 = (f & kDpSignificandMask) | (be << kDpSignificandSize); + return u.d; + } + + static const int kDiySignificandSize = 64; + static const int kDpSignificandSize = 52; + static const int kDpExponentBias = 0x3FF + kDpSignificandSize; + static const int kDpMaxExponent = 0x7FF - kDpExponentBias; + static const int kDpMinExponent = -kDpExponentBias; + static const int kDpDenormalExponent = -kDpExponentBias + 1; + static const uint64_t kDpExponentMask = RAPIDJSON_UINT64_C2(0x7FF00000, 0x00000000); + static const uint64_t kDpSignificandMask = RAPIDJSON_UINT64_C2(0x000FFFFF, 0xFFFFFFFF); + static const uint64_t kDpHiddenBit = RAPIDJSON_UINT64_C2(0x00100000, 0x00000000); + + uint64_t f; + int e; +}; + +inline DiyFp GetCachedPowerByIndex(size_t index) { + // 10^-348, 10^-340, ..., 10^340 + static const uint64_t kCachedPowers_F[] = { + RAPIDJSON_UINT64_C2(0xfa8fd5a0, 0x081c0288), RAPIDJSON_UINT64_C2(0xbaaee17f, 0xa23ebf76), + RAPIDJSON_UINT64_C2(0x8b16fb20, 0x3055ac76), RAPIDJSON_UINT64_C2(0xcf42894a, 0x5dce35ea), + RAPIDJSON_UINT64_C2(0x9a6bb0aa, 0x55653b2d), RAPIDJSON_UINT64_C2(0xe61acf03, 0x3d1a45df), + RAPIDJSON_UINT64_C2(0xab70fe17, 0xc79ac6ca), RAPIDJSON_UINT64_C2(0xff77b1fc, 0xbebcdc4f), + RAPIDJSON_UINT64_C2(0xbe5691ef, 0x416bd60c), RAPIDJSON_UINT64_C2(0x8dd01fad, 0x907ffc3c), + RAPIDJSON_UINT64_C2(0xd3515c28, 0x31559a83), RAPIDJSON_UINT64_C2(0x9d71ac8f, 0xada6c9b5), + RAPIDJSON_UINT64_C2(0xea9c2277, 0x23ee8bcb), RAPIDJSON_UINT64_C2(0xaecc4991, 0x4078536d), + RAPIDJSON_UINT64_C2(0x823c1279, 0x5db6ce57), RAPIDJSON_UINT64_C2(0xc2109436, 0x4dfb5637), + RAPIDJSON_UINT64_C2(0x9096ea6f, 0x3848984f), RAPIDJSON_UINT64_C2(0xd77485cb, 0x25823ac7), + RAPIDJSON_UINT64_C2(0xa086cfcd, 0x97bf97f4), RAPIDJSON_UINT64_C2(0xef340a98, 0x172aace5), + RAPIDJSON_UINT64_C2(0xb23867fb, 0x2a35b28e), RAPIDJSON_UINT64_C2(0x84c8d4df, 0xd2c63f3b), + RAPIDJSON_UINT64_C2(0xc5dd4427, 0x1ad3cdba), RAPIDJSON_UINT64_C2(0x936b9fce, 0xbb25c996), + RAPIDJSON_UINT64_C2(0xdbac6c24, 0x7d62a584), RAPIDJSON_UINT64_C2(0xa3ab6658, 0x0d5fdaf6), + RAPIDJSON_UINT64_C2(0xf3e2f893, 0xdec3f126), RAPIDJSON_UINT64_C2(0xb5b5ada8, 0xaaff80b8), + RAPIDJSON_UINT64_C2(0x87625f05, 0x6c7c4a8b), RAPIDJSON_UINT64_C2(0xc9bcff60, 0x34c13053), + RAPIDJSON_UINT64_C2(0x964e858c, 0x91ba2655), RAPIDJSON_UINT64_C2(0xdff97724, 0x70297ebd), + RAPIDJSON_UINT64_C2(0xa6dfbd9f, 0xb8e5b88f), RAPIDJSON_UINT64_C2(0xf8a95fcf, 0x88747d94), + RAPIDJSON_UINT64_C2(0xb9447093, 0x8fa89bcf), RAPIDJSON_UINT64_C2(0x8a08f0f8, 0xbf0f156b), + RAPIDJSON_UINT64_C2(0xcdb02555, 0x653131b6), RAPIDJSON_UINT64_C2(0x993fe2c6, 0xd07b7fac), + RAPIDJSON_UINT64_C2(0xe45c10c4, 0x2a2b3b06), RAPIDJSON_UINT64_C2(0xaa242499, 0x697392d3), + RAPIDJSON_UINT64_C2(0xfd87b5f2, 0x8300ca0e), RAPIDJSON_UINT64_C2(0xbce50864, 0x92111aeb), + RAPIDJSON_UINT64_C2(0x8cbccc09, 0x6f5088cc), RAPIDJSON_UINT64_C2(0xd1b71758, 0xe219652c), + RAPIDJSON_UINT64_C2(0x9c400000, 0x00000000), RAPIDJSON_UINT64_C2(0xe8d4a510, 0x00000000), + RAPIDJSON_UINT64_C2(0xad78ebc5, 0xac620000), RAPIDJSON_UINT64_C2(0x813f3978, 0xf8940984), + RAPIDJSON_UINT64_C2(0xc097ce7b, 0xc90715b3), RAPIDJSON_UINT64_C2(0x8f7e32ce, 0x7bea5c70), + RAPIDJSON_UINT64_C2(0xd5d238a4, 0xabe98068), RAPIDJSON_UINT64_C2(0x9f4f2726, 0x179a2245), + RAPIDJSON_UINT64_C2(0xed63a231, 0xd4c4fb27), RAPIDJSON_UINT64_C2(0xb0de6538, 0x8cc8ada8), + RAPIDJSON_UINT64_C2(0x83c7088e, 0x1aab65db), RAPIDJSON_UINT64_C2(0xc45d1df9, 0x42711d9a), + RAPIDJSON_UINT64_C2(0x924d692c, 0xa61be758), RAPIDJSON_UINT64_C2(0xda01ee64, 0x1a708dea), + RAPIDJSON_UINT64_C2(0xa26da399, 0x9aef774a), RAPIDJSON_UINT64_C2(0xf209787b, 0xb47d6b85), + RAPIDJSON_UINT64_C2(0xb454e4a1, 0x79dd1877), RAPIDJSON_UINT64_C2(0x865b8692, 0x5b9bc5c2), + RAPIDJSON_UINT64_C2(0xc83553c5, 0xc8965d3d), RAPIDJSON_UINT64_C2(0x952ab45c, 0xfa97a0b3), + RAPIDJSON_UINT64_C2(0xde469fbd, 0x99a05fe3), RAPIDJSON_UINT64_C2(0xa59bc234, 0xdb398c25), + RAPIDJSON_UINT64_C2(0xf6c69a72, 0xa3989f5c), RAPIDJSON_UINT64_C2(0xb7dcbf53, 0x54e9bece), + RAPIDJSON_UINT64_C2(0x88fcf317, 0xf22241e2), RAPIDJSON_UINT64_C2(0xcc20ce9b, 0xd35c78a5), + RAPIDJSON_UINT64_C2(0x98165af3, 0x7b2153df), RAPIDJSON_UINT64_C2(0xe2a0b5dc, 0x971f303a), + RAPIDJSON_UINT64_C2(0xa8d9d153, 0x5ce3b396), RAPIDJSON_UINT64_C2(0xfb9b7cd9, 0xa4a7443c), + RAPIDJSON_UINT64_C2(0xbb764c4c, 0xa7a44410), RAPIDJSON_UINT64_C2(0x8bab8eef, 0xb6409c1a), + RAPIDJSON_UINT64_C2(0xd01fef10, 0xa657842c), RAPIDJSON_UINT64_C2(0x9b10a4e5, 0xe9913129), + RAPIDJSON_UINT64_C2(0xe7109bfb, 0xa19c0c9d), RAPIDJSON_UINT64_C2(0xac2820d9, 0x623bf429), + RAPIDJSON_UINT64_C2(0x80444b5e, 0x7aa7cf85), RAPIDJSON_UINT64_C2(0xbf21e440, 0x03acdd2d), + RAPIDJSON_UINT64_C2(0x8e679c2f, 0x5e44ff8f), RAPIDJSON_UINT64_C2(0xd433179d, 0x9c8cb841), + RAPIDJSON_UINT64_C2(0x9e19db92, 0xb4e31ba9), RAPIDJSON_UINT64_C2(0xeb96bf6e, 0xbadf77d9), + RAPIDJSON_UINT64_C2(0xaf87023b, 0x9bf0ee6b) + }; + static const int16_t kCachedPowers_E[] = { + -1220, -1193, -1166, -1140, -1113, -1087, -1060, -1034, -1007, -980, + -954, -927, -901, -874, -847, -821, -794, -768, -741, -715, + -688, -661, -635, -608, -582, -555, -529, -502, -475, -449, + -422, -396, -369, -343, -316, -289, -263, -236, -210, -183, + -157, -130, -103, -77, -50, -24, 3, 30, 56, 83, + 109, 136, 162, 189, 216, 242, 269, 295, 322, 348, + 375, 402, 428, 455, 481, 508, 534, 561, 588, 614, + 641, 667, 694, 720, 747, 774, 800, 827, 853, 880, + 907, 933, 960, 986, 1013, 1039, 1066 + }; + RAPIDJSON_ASSERT(index < 87); + return DiyFp(kCachedPowers_F[index], kCachedPowers_E[index]); +} + +inline DiyFp GetCachedPower(int e, int* K) { + + //int k = static_cast(ceil((-61 - e) * 0.30102999566398114)) + 374; + double dk = (-61 - e) * 0.30102999566398114 + 347; // dk must be positive, so can do ceiling in positive + int k = static_cast(dk); + if (dk - k > 0.0) + k++; + + unsigned index = static_cast((k >> 3) + 1); + *K = -(-348 + static_cast(index << 3)); // decimal exponent no need lookup table + + return GetCachedPowerByIndex(index); +} + +inline DiyFp GetCachedPower10(int exp, int *outExp) { + RAPIDJSON_ASSERT(exp >= -348); + unsigned index = static_cast(exp + 348) / 8u; + *outExp = -348 + static_cast(index) * 8; + return GetCachedPowerByIndex(index); +} + +#ifdef __GNUC__ +RAPIDJSON_DIAG_POP +#endif + +#ifdef __clang__ +RAPIDJSON_DIAG_POP +RAPIDJSON_DIAG_OFF(padded) +#endif + +} // namespace internal +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_DIYFP_H_ diff --git a/include/rapidjson/internal/dtoa.h b/include/rapidjson/internal/dtoa.h new file mode 100644 index 0000000..cd45672 --- /dev/null +++ b/include/rapidjson/internal/dtoa.h @@ -0,0 +1,249 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// 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. + +// This is a C++ header-only implementation of Grisu2 algorithm from the publication: +// Loitsch, Florian. "Printing floating-point numbers quickly and accurately with +// integers." ACM Sigplan Notices 45.6 (2010): 233-243. + +#ifndef RAPIDJSON_DTOA_ +#define RAPIDJSON_DTOA_ + +#include "itoa.h" // GetDigitsLut() +#include "diyfp.h" +#include "ieee754.h" + +RAPIDJSON_NAMESPACE_BEGIN +namespace internal { + +#ifdef __GNUC__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(effc++) +RAPIDJSON_DIAG_OFF(array-bounds) // some gcc versions generate wrong warnings https://gcc.gnu.org/bugzilla/show_bug.cgi?id=59124 +#endif + +inline void GrisuRound(char* buffer, int len, uint64_t delta, uint64_t rest, uint64_t ten_kappa, uint64_t wp_w) { + while (rest < wp_w && delta - rest >= ten_kappa && + (rest + ten_kappa < wp_w || /// closer + wp_w - rest > rest + ten_kappa - wp_w)) { + buffer[len - 1]--; + rest += ten_kappa; + } +} + +inline int CountDecimalDigit32(uint32_t n) { + // Simple pure C++ implementation was faster than __builtin_clz version in this situation. + if (n < 10) return 1; + if (n < 100) return 2; + if (n < 1000) return 3; + if (n < 10000) return 4; + if (n < 100000) return 5; + if (n < 1000000) return 6; + if (n < 10000000) return 7; + if (n < 100000000) return 8; + // Will not reach 10 digits in DigitGen() + //if (n < 1000000000) return 9; + //return 10; + return 9; +} + +inline void DigitGen(const DiyFp& W, const DiyFp& Mp, uint64_t delta, char* buffer, int* len, int* K) { + static const uint64_t kPow10[] = { 1ULL, 10ULL, 100ULL, 1000ULL, 10000ULL, 100000ULL, 1000000ULL, 10000000ULL, 100000000ULL, + 1000000000ULL, 10000000000ULL, 100000000000ULL, 1000000000000ULL, + 10000000000000ULL, 100000000000000ULL, 1000000000000000ULL, + 10000000000000000ULL, 100000000000000000ULL, 1000000000000000000ULL, + 10000000000000000000ULL }; + const DiyFp one(uint64_t(1) << -Mp.e, Mp.e); + const DiyFp wp_w = Mp - W; + uint32_t p1 = static_cast(Mp.f >> -one.e); + uint64_t p2 = Mp.f & (one.f - 1); + int kappa = CountDecimalDigit32(p1); // kappa in [0, 9] + *len = 0; + + while (kappa > 0) { + uint32_t d = 0; + switch (kappa) { + case 9: d = p1 / 100000000; p1 %= 100000000; break; + case 8: d = p1 / 10000000; p1 %= 10000000; break; + case 7: d = p1 / 1000000; p1 %= 1000000; break; + case 6: d = p1 / 100000; p1 %= 100000; break; + case 5: d = p1 / 10000; p1 %= 10000; break; + case 4: d = p1 / 1000; p1 %= 1000; break; + case 3: d = p1 / 100; p1 %= 100; break; + case 2: d = p1 / 10; p1 %= 10; break; + case 1: d = p1; p1 = 0; break; + default:; + } + if (d || *len) + buffer[(*len)++] = static_cast('0' + static_cast(d)); + kappa--; + uint64_t tmp = (static_cast(p1) << -one.e) + p2; + if (tmp <= delta) { + *K += kappa; + GrisuRound(buffer, *len, delta, tmp, kPow10[kappa] << -one.e, wp_w.f); + return; + } + } + + // kappa = 0 + for (;;) { + p2 *= 10; + delta *= 10; + char d = static_cast(p2 >> -one.e); + if (d || *len) + buffer[(*len)++] = static_cast('0' + d); + p2 &= one.f - 1; + kappa--; + if (p2 < delta) { + *K += kappa; + int index = -kappa; + GrisuRound(buffer, *len, delta, p2, one.f, wp_w.f * (index < 20 ? kPow10[index] : 0)); + return; + } + } +} + +inline void Grisu2(double value, char* buffer, int* length, int* K) { + const DiyFp v(value); + DiyFp w_m, w_p; + v.NormalizedBoundaries(&w_m, &w_p); + + const DiyFp c_mk = GetCachedPower(w_p.e, K); + const DiyFp W = v.Normalize() * c_mk; + DiyFp Wp = w_p * c_mk; + DiyFp Wm = w_m * c_mk; + Wm.f++; + Wp.f--; + DigitGen(W, Wp, Wp.f - Wm.f, buffer, length, K); +} + +inline char* WriteExponent(int K, char* buffer) { + if (K < 0) { + *buffer++ = '-'; + K = -K; + } + + if (K >= 100) { + *buffer++ = static_cast('0' + static_cast(K / 100)); + K %= 100; + const char* d = GetDigitsLut() + K * 2; + *buffer++ = d[0]; + *buffer++ = d[1]; + } + else if (K >= 10) { + const char* d = GetDigitsLut() + K * 2; + *buffer++ = d[0]; + *buffer++ = d[1]; + } + else + *buffer++ = static_cast('0' + static_cast(K)); + + return buffer; +} + +inline char* Prettify(char* buffer, int length, int k, int maxDecimalPlaces) { + const int kk = length + k; // 10^(kk-1) <= v < 10^kk + + if (0 <= k && kk <= 21) { + // 1234e7 -> 12340000000 + for (int i = length; i < kk; i++) + buffer[i] = '0'; + buffer[kk] = '.'; + buffer[kk + 1] = '0'; + return &buffer[kk + 2]; + } + else if (0 < kk && kk <= 21) { + // 1234e-2 -> 12.34 + std::memmove(&buffer[kk + 1], &buffer[kk], static_cast(length - kk)); + buffer[kk] = '.'; + if (0 > k + maxDecimalPlaces) { + // When maxDecimalPlaces = 2, 1.2345 -> 1.23, 1.102 -> 1.1 + // Remove extra trailing zeros (at least one) after truncation. + for (int i = kk + maxDecimalPlaces; i > kk + 1; i--) + if (buffer[i] != '0') + return &buffer[i + 1]; + return &buffer[kk + 2]; // Reserve one zero + } + else + return &buffer[length + 1]; + } + else if (-6 < kk && kk <= 0) { + // 1234e-6 -> 0.001234 + const int offset = 2 - kk; + std::memmove(&buffer[offset], &buffer[0], static_cast(length)); + buffer[0] = '0'; + buffer[1] = '.'; + for (int i = 2; i < offset; i++) + buffer[i] = '0'; + if (length - kk > maxDecimalPlaces) { + // When maxDecimalPlaces = 2, 0.123 -> 0.12, 0.102 -> 0.1 + // Remove extra trailing zeros (at least one) after truncation. + for (int i = maxDecimalPlaces + 1; i > 2; i--) + if (buffer[i] != '0') + return &buffer[i + 1]; + return &buffer[3]; // Reserve one zero + } + else + return &buffer[length + offset]; + } + else if (kk < -maxDecimalPlaces) { + // Truncate to zero + buffer[0] = '0'; + buffer[1] = '.'; + buffer[2] = '0'; + return &buffer[3]; + } + else if (length == 1) { + // 1e30 + buffer[1] = 'e'; + return WriteExponent(kk - 1, &buffer[2]); + } + else { + // 1234e30 -> 1.234e33 + std::memmove(&buffer[2], &buffer[1], static_cast(length - 1)); + buffer[1] = '.'; + buffer[length + 1] = 'e'; + return WriteExponent(kk - 1, &buffer[0 + length + 2]); + } +} + +inline char* dtoa(double value, char* buffer, int maxDecimalPlaces = 324) { + RAPIDJSON_ASSERT(maxDecimalPlaces >= 1); + Double d(value); + if (d.IsZero()) { + if (d.Sign()) + *buffer++ = '-'; // -0.0, Issue #289 + buffer[0] = '0'; + buffer[1] = '.'; + buffer[2] = '0'; + return &buffer[3]; + } + else { + if (value < 0) { + *buffer++ = '-'; + value = -value; + } + int length, K; + Grisu2(value, buffer, &length, &K); + return Prettify(buffer, length, K, maxDecimalPlaces); + } +} + +#ifdef __GNUC__ +RAPIDJSON_DIAG_POP +#endif + +} // namespace internal +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_DTOA_ diff --git a/include/rapidjson/internal/ieee754.h b/include/rapidjson/internal/ieee754.h new file mode 100644 index 0000000..68c9e96 --- /dev/null +++ b/include/rapidjson/internal/ieee754.h @@ -0,0 +1,78 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// 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 RAPIDJSON_IEEE754_ +#define RAPIDJSON_IEEE754_ + +#include "../rapidjson.h" + +RAPIDJSON_NAMESPACE_BEGIN +namespace internal { + +class Double { +public: + Double() {} + Double(double d) : d_(d) {} + Double(uint64_t u) : u_(u) {} + + double Value() const { return d_; } + uint64_t Uint64Value() const { return u_; } + + double NextPositiveDouble() const { + RAPIDJSON_ASSERT(!Sign()); + return Double(u_ + 1).Value(); + } + + bool Sign() const { return (u_ & kSignMask) != 0; } + uint64_t Significand() const { return u_ & kSignificandMask; } + int Exponent() const { return static_cast(((u_ & kExponentMask) >> kSignificandSize) - kExponentBias); } + + bool IsNan() const { return (u_ & kExponentMask) == kExponentMask && Significand() != 0; } + bool IsInf() const { return (u_ & kExponentMask) == kExponentMask && Significand() == 0; } + bool IsNanOrInf() const { return (u_ & kExponentMask) == kExponentMask; } + bool IsNormal() const { return (u_ & kExponentMask) != 0 || Significand() == 0; } + bool IsZero() const { return (u_ & (kExponentMask | kSignificandMask)) == 0; } + + uint64_t IntegerSignificand() const { return IsNormal() ? Significand() | kHiddenBit : Significand(); } + int IntegerExponent() const { return (IsNormal() ? Exponent() : kDenormalExponent) - kSignificandSize; } + uint64_t ToBias() const { return (u_ & kSignMask) ? ~u_ + 1 : u_ | kSignMask; } + + static int EffectiveSignificandSize(int order) { + if (order >= -1021) + return 53; + else if (order <= -1074) + return 0; + else + return order + 1074; + } + +private: + static const int kSignificandSize = 52; + static const int kExponentBias = 0x3FF; + static const int kDenormalExponent = 1 - kExponentBias; + static const uint64_t kSignMask = RAPIDJSON_UINT64_C2(0x80000000, 0x00000000); + static const uint64_t kExponentMask = RAPIDJSON_UINT64_C2(0x7FF00000, 0x00000000); + static const uint64_t kSignificandMask = RAPIDJSON_UINT64_C2(0x000FFFFF, 0xFFFFFFFF); + static const uint64_t kHiddenBit = RAPIDJSON_UINT64_C2(0x00100000, 0x00000000); + + union { + double d_; + uint64_t u_; + }; +}; + +} // namespace internal +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_IEEE754_ diff --git a/include/rapidjson/internal/itoa.h b/include/rapidjson/internal/itoa.h new file mode 100644 index 0000000..9fe8c93 --- /dev/null +++ b/include/rapidjson/internal/itoa.h @@ -0,0 +1,308 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// 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 RAPIDJSON_ITOA_ +#define RAPIDJSON_ITOA_ + +#include "../rapidjson.h" + +RAPIDJSON_NAMESPACE_BEGIN +namespace internal { + +inline const char* GetDigitsLut() { + static const char cDigitsLut[200] = { + '0','0','0','1','0','2','0','3','0','4','0','5','0','6','0','7','0','8','0','9', + '1','0','1','1','1','2','1','3','1','4','1','5','1','6','1','7','1','8','1','9', + '2','0','2','1','2','2','2','3','2','4','2','5','2','6','2','7','2','8','2','9', + '3','0','3','1','3','2','3','3','3','4','3','5','3','6','3','7','3','8','3','9', + '4','0','4','1','4','2','4','3','4','4','4','5','4','6','4','7','4','8','4','9', + '5','0','5','1','5','2','5','3','5','4','5','5','5','6','5','7','5','8','5','9', + '6','0','6','1','6','2','6','3','6','4','6','5','6','6','6','7','6','8','6','9', + '7','0','7','1','7','2','7','3','7','4','7','5','7','6','7','7','7','8','7','9', + '8','0','8','1','8','2','8','3','8','4','8','5','8','6','8','7','8','8','8','9', + '9','0','9','1','9','2','9','3','9','4','9','5','9','6','9','7','9','8','9','9' + }; + return cDigitsLut; +} + +inline char* u32toa(uint32_t value, char* buffer) { + RAPIDJSON_ASSERT(buffer != 0); + + const char* cDigitsLut = GetDigitsLut(); + + if (value < 10000) { + const uint32_t d1 = (value / 100) << 1; + const uint32_t d2 = (value % 100) << 1; + + if (value >= 1000) + *buffer++ = cDigitsLut[d1]; + if (value >= 100) + *buffer++ = cDigitsLut[d1 + 1]; + if (value >= 10) + *buffer++ = cDigitsLut[d2]; + *buffer++ = cDigitsLut[d2 + 1]; + } + else if (value < 100000000) { + // value = bbbbcccc + const uint32_t b = value / 10000; + const uint32_t c = value % 10000; + + const uint32_t d1 = (b / 100) << 1; + const uint32_t d2 = (b % 100) << 1; + + const uint32_t d3 = (c / 100) << 1; + const uint32_t d4 = (c % 100) << 1; + + if (value >= 10000000) + *buffer++ = cDigitsLut[d1]; + if (value >= 1000000) + *buffer++ = cDigitsLut[d1 + 1]; + if (value >= 100000) + *buffer++ = cDigitsLut[d2]; + *buffer++ = cDigitsLut[d2 + 1]; + + *buffer++ = cDigitsLut[d3]; + *buffer++ = cDigitsLut[d3 + 1]; + *buffer++ = cDigitsLut[d4]; + *buffer++ = cDigitsLut[d4 + 1]; + } + else { + // value = aabbbbcccc in decimal + + const uint32_t a = value / 100000000; // 1 to 42 + value %= 100000000; + + if (a >= 10) { + const unsigned i = a << 1; + *buffer++ = cDigitsLut[i]; + *buffer++ = cDigitsLut[i + 1]; + } + else + *buffer++ = static_cast('0' + static_cast(a)); + + const uint32_t b = value / 10000; // 0 to 9999 + const uint32_t c = value % 10000; // 0 to 9999 + + const uint32_t d1 = (b / 100) << 1; + const uint32_t d2 = (b % 100) << 1; + + const uint32_t d3 = (c / 100) << 1; + const uint32_t d4 = (c % 100) << 1; + + *buffer++ = cDigitsLut[d1]; + *buffer++ = cDigitsLut[d1 + 1]; + *buffer++ = cDigitsLut[d2]; + *buffer++ = cDigitsLut[d2 + 1]; + *buffer++ = cDigitsLut[d3]; + *buffer++ = cDigitsLut[d3 + 1]; + *buffer++ = cDigitsLut[d4]; + *buffer++ = cDigitsLut[d4 + 1]; + } + return buffer; +} + +inline char* i32toa(int32_t value, char* buffer) { + RAPIDJSON_ASSERT(buffer != 0); + uint32_t u = static_cast(value); + if (value < 0) { + *buffer++ = '-'; + u = ~u + 1; + } + + return u32toa(u, buffer); +} + +inline char* u64toa(uint64_t value, char* buffer) { + RAPIDJSON_ASSERT(buffer != 0); + const char* cDigitsLut = GetDigitsLut(); + const uint64_t kTen8 = 100000000; + const uint64_t kTen9 = kTen8 * 10; + const uint64_t kTen10 = kTen8 * 100; + const uint64_t kTen11 = kTen8 * 1000; + const uint64_t kTen12 = kTen8 * 10000; + const uint64_t kTen13 = kTen8 * 100000; + const uint64_t kTen14 = kTen8 * 1000000; + const uint64_t kTen15 = kTen8 * 10000000; + const uint64_t kTen16 = kTen8 * kTen8; + + if (value < kTen8) { + uint32_t v = static_cast(value); + if (v < 10000) { + const uint32_t d1 = (v / 100) << 1; + const uint32_t d2 = (v % 100) << 1; + + if (v >= 1000) + *buffer++ = cDigitsLut[d1]; + if (v >= 100) + *buffer++ = cDigitsLut[d1 + 1]; + if (v >= 10) + *buffer++ = cDigitsLut[d2]; + *buffer++ = cDigitsLut[d2 + 1]; + } + else { + // value = bbbbcccc + const uint32_t b = v / 10000; + const uint32_t c = v % 10000; + + const uint32_t d1 = (b / 100) << 1; + const uint32_t d2 = (b % 100) << 1; + + const uint32_t d3 = (c / 100) << 1; + const uint32_t d4 = (c % 100) << 1; + + if (value >= 10000000) + *buffer++ = cDigitsLut[d1]; + if (value >= 1000000) + *buffer++ = cDigitsLut[d1 + 1]; + if (value >= 100000) + *buffer++ = cDigitsLut[d2]; + *buffer++ = cDigitsLut[d2 + 1]; + + *buffer++ = cDigitsLut[d3]; + *buffer++ = cDigitsLut[d3 + 1]; + *buffer++ = cDigitsLut[d4]; + *buffer++ = cDigitsLut[d4 + 1]; + } + } + else if (value < kTen16) { + const uint32_t v0 = static_cast(value / kTen8); + const uint32_t v1 = static_cast(value % kTen8); + + const uint32_t b0 = v0 / 10000; + const uint32_t c0 = v0 % 10000; + + const uint32_t d1 = (b0 / 100) << 1; + const uint32_t d2 = (b0 % 100) << 1; + + const uint32_t d3 = (c0 / 100) << 1; + const uint32_t d4 = (c0 % 100) << 1; + + const uint32_t b1 = v1 / 10000; + const uint32_t c1 = v1 % 10000; + + const uint32_t d5 = (b1 / 100) << 1; + const uint32_t d6 = (b1 % 100) << 1; + + const uint32_t d7 = (c1 / 100) << 1; + const uint32_t d8 = (c1 % 100) << 1; + + if (value >= kTen15) + *buffer++ = cDigitsLut[d1]; + if (value >= kTen14) + *buffer++ = cDigitsLut[d1 + 1]; + if (value >= kTen13) + *buffer++ = cDigitsLut[d2]; + if (value >= kTen12) + *buffer++ = cDigitsLut[d2 + 1]; + if (value >= kTen11) + *buffer++ = cDigitsLut[d3]; + if (value >= kTen10) + *buffer++ = cDigitsLut[d3 + 1]; + if (value >= kTen9) + *buffer++ = cDigitsLut[d4]; + + *buffer++ = cDigitsLut[d4 + 1]; + *buffer++ = cDigitsLut[d5]; + *buffer++ = cDigitsLut[d5 + 1]; + *buffer++ = cDigitsLut[d6]; + *buffer++ = cDigitsLut[d6 + 1]; + *buffer++ = cDigitsLut[d7]; + *buffer++ = cDigitsLut[d7 + 1]; + *buffer++ = cDigitsLut[d8]; + *buffer++ = cDigitsLut[d8 + 1]; + } + else { + const uint32_t a = static_cast(value / kTen16); // 1 to 1844 + value %= kTen16; + + if (a < 10) + *buffer++ = static_cast('0' + static_cast(a)); + else if (a < 100) { + const uint32_t i = a << 1; + *buffer++ = cDigitsLut[i]; + *buffer++ = cDigitsLut[i + 1]; + } + else if (a < 1000) { + *buffer++ = static_cast('0' + static_cast(a / 100)); + + const uint32_t i = (a % 100) << 1; + *buffer++ = cDigitsLut[i]; + *buffer++ = cDigitsLut[i + 1]; + } + else { + const uint32_t i = (a / 100) << 1; + const uint32_t j = (a % 100) << 1; + *buffer++ = cDigitsLut[i]; + *buffer++ = cDigitsLut[i + 1]; + *buffer++ = cDigitsLut[j]; + *buffer++ = cDigitsLut[j + 1]; + } + + const uint32_t v0 = static_cast(value / kTen8); + const uint32_t v1 = static_cast(value % kTen8); + + const uint32_t b0 = v0 / 10000; + const uint32_t c0 = v0 % 10000; + + const uint32_t d1 = (b0 / 100) << 1; + const uint32_t d2 = (b0 % 100) << 1; + + const uint32_t d3 = (c0 / 100) << 1; + const uint32_t d4 = (c0 % 100) << 1; + + const uint32_t b1 = v1 / 10000; + const uint32_t c1 = v1 % 10000; + + const uint32_t d5 = (b1 / 100) << 1; + const uint32_t d6 = (b1 % 100) << 1; + + const uint32_t d7 = (c1 / 100) << 1; + const uint32_t d8 = (c1 % 100) << 1; + + *buffer++ = cDigitsLut[d1]; + *buffer++ = cDigitsLut[d1 + 1]; + *buffer++ = cDigitsLut[d2]; + *buffer++ = cDigitsLut[d2 + 1]; + *buffer++ = cDigitsLut[d3]; + *buffer++ = cDigitsLut[d3 + 1]; + *buffer++ = cDigitsLut[d4]; + *buffer++ = cDigitsLut[d4 + 1]; + *buffer++ = cDigitsLut[d5]; + *buffer++ = cDigitsLut[d5 + 1]; + *buffer++ = cDigitsLut[d6]; + *buffer++ = cDigitsLut[d6 + 1]; + *buffer++ = cDigitsLut[d7]; + *buffer++ = cDigitsLut[d7 + 1]; + *buffer++ = cDigitsLut[d8]; + *buffer++ = cDigitsLut[d8 + 1]; + } + + return buffer; +} + +inline char* i64toa(int64_t value, char* buffer) { + RAPIDJSON_ASSERT(buffer != 0); + uint64_t u = static_cast(value); + if (value < 0) { + *buffer++ = '-'; + u = ~u + 1; + } + + return u64toa(u, buffer); +} + +} // namespace internal +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_ITOA_ diff --git a/include/rapidjson/internal/meta.h b/include/rapidjson/internal/meta.h new file mode 100644 index 0000000..27092dc --- /dev/null +++ b/include/rapidjson/internal/meta.h @@ -0,0 +1,186 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// 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 RAPIDJSON_INTERNAL_META_H_ +#define RAPIDJSON_INTERNAL_META_H_ + +#include "../rapidjson.h" + +#ifdef __GNUC__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(effc++) +#endif + +#if defined(_MSC_VER) && !defined(__clang__) +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(6334) +#endif + +#if RAPIDJSON_HAS_CXX11_TYPETRAITS +#include +#endif + +//@cond RAPIDJSON_INTERNAL +RAPIDJSON_NAMESPACE_BEGIN +namespace internal { + +// Helper to wrap/convert arbitrary types to void, useful for arbitrary type matching +template struct Void { typedef void Type; }; + +/////////////////////////////////////////////////////////////////////////////// +// BoolType, TrueType, FalseType +// +template struct BoolType { + static const bool Value = Cond; + typedef BoolType Type; +}; +typedef BoolType TrueType; +typedef BoolType FalseType; + + +/////////////////////////////////////////////////////////////////////////////// +// SelectIf, BoolExpr, NotExpr, AndExpr, OrExpr +// + +template struct SelectIfImpl { template struct Apply { typedef T1 Type; }; }; +template <> struct SelectIfImpl { template struct Apply { typedef T2 Type; }; }; +template struct SelectIfCond : SelectIfImpl::template Apply {}; +template struct SelectIf : SelectIfCond {}; + +template struct AndExprCond : FalseType {}; +template <> struct AndExprCond : TrueType {}; +template struct OrExprCond : TrueType {}; +template <> struct OrExprCond : FalseType {}; + +template struct BoolExpr : SelectIf::Type {}; +template struct NotExpr : SelectIf::Type {}; +template struct AndExpr : AndExprCond::Type {}; +template struct OrExpr : OrExprCond::Type {}; + + +/////////////////////////////////////////////////////////////////////////////// +// AddConst, MaybeAddConst, RemoveConst +template struct AddConst { typedef const T Type; }; +template struct MaybeAddConst : SelectIfCond {}; +template struct RemoveConst { typedef T Type; }; +template struct RemoveConst { typedef T Type; }; + + +/////////////////////////////////////////////////////////////////////////////// +// IsSame, IsConst, IsMoreConst, IsPointer +// +template struct IsSame : FalseType {}; +template struct IsSame : TrueType {}; + +template struct IsConst : FalseType {}; +template struct IsConst : TrueType {}; + +template +struct IsMoreConst + : AndExpr::Type, typename RemoveConst::Type>, + BoolType::Value >= IsConst::Value> >::Type {}; + +template struct IsPointer : FalseType {}; +template struct IsPointer : TrueType {}; + +/////////////////////////////////////////////////////////////////////////////// +// IsBaseOf +// +#if RAPIDJSON_HAS_CXX11_TYPETRAITS + +template struct IsBaseOf + : BoolType< ::std::is_base_of::value> {}; + +#else // simplified version adopted from Boost + +template struct IsBaseOfImpl { + RAPIDJSON_STATIC_ASSERT(sizeof(B) != 0); + RAPIDJSON_STATIC_ASSERT(sizeof(D) != 0); + + typedef char (&Yes)[1]; + typedef char (&No) [2]; + + template + static Yes Check(const D*, T); + static No Check(const B*, int); + + struct Host { + operator const B*() const; + operator const D*(); + }; + + enum { Value = (sizeof(Check(Host(), 0)) == sizeof(Yes)) }; +}; + +template struct IsBaseOf + : OrExpr, BoolExpr > >::Type {}; + +#endif // RAPIDJSON_HAS_CXX11_TYPETRAITS + + +////////////////////////////////////////////////////////////////////////// +// EnableIf / DisableIf +// +template struct EnableIfCond { typedef T Type; }; +template struct EnableIfCond { /* empty */ }; + +template struct DisableIfCond { typedef T Type; }; +template struct DisableIfCond { /* empty */ }; + +template +struct EnableIf : EnableIfCond {}; + +template +struct DisableIf : DisableIfCond {}; + +// SFINAE helpers +struct SfinaeTag {}; +template struct RemoveSfinaeTag; +template struct RemoveSfinaeTag { typedef T Type; }; + +#define RAPIDJSON_REMOVEFPTR_(type) \ + typename ::RAPIDJSON_NAMESPACE::internal::RemoveSfinaeTag \ + < ::RAPIDJSON_NAMESPACE::internal::SfinaeTag&(*) type>::Type + +#define RAPIDJSON_ENABLEIF(cond) \ + typename ::RAPIDJSON_NAMESPACE::internal::EnableIf \ + ::Type * = NULL + +#define RAPIDJSON_DISABLEIF(cond) \ + typename ::RAPIDJSON_NAMESPACE::internal::DisableIf \ + ::Type * = NULL + +#define RAPIDJSON_ENABLEIF_RETURN(cond,returntype) \ + typename ::RAPIDJSON_NAMESPACE::internal::EnableIf \ + ::Type + +#define RAPIDJSON_DISABLEIF_RETURN(cond,returntype) \ + typename ::RAPIDJSON_NAMESPACE::internal::DisableIf \ + ::Type + +} // namespace internal +RAPIDJSON_NAMESPACE_END +//@endcond + +#if defined(_MSC_VER) && !defined(__clang__) +RAPIDJSON_DIAG_POP +#endif + +#ifdef __GNUC__ +RAPIDJSON_DIAG_POP +#endif + +#endif // RAPIDJSON_INTERNAL_META_H_ diff --git a/include/rapidjson/internal/pow10.h b/include/rapidjson/internal/pow10.h new file mode 100644 index 0000000..eae1a43 --- /dev/null +++ b/include/rapidjson/internal/pow10.h @@ -0,0 +1,55 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// 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 RAPIDJSON_POW10_ +#define RAPIDJSON_POW10_ + +#include "../rapidjson.h" + +RAPIDJSON_NAMESPACE_BEGIN +namespace internal { + +//! Computes integer powers of 10 in double (10.0^n). +/*! This function uses lookup table for fast and accurate results. + \param n non-negative exponent. Must <= 308. + \return 10.0^n +*/ +inline double Pow10(int n) { + static const double e[] = { // 1e-0...1e308: 309 * 8 bytes = 2472 bytes + 1e+0, + 1e+1, 1e+2, 1e+3, 1e+4, 1e+5, 1e+6, 1e+7, 1e+8, 1e+9, 1e+10, 1e+11, 1e+12, 1e+13, 1e+14, 1e+15, 1e+16, 1e+17, 1e+18, 1e+19, 1e+20, + 1e+21, 1e+22, 1e+23, 1e+24, 1e+25, 1e+26, 1e+27, 1e+28, 1e+29, 1e+30, 1e+31, 1e+32, 1e+33, 1e+34, 1e+35, 1e+36, 1e+37, 1e+38, 1e+39, 1e+40, + 1e+41, 1e+42, 1e+43, 1e+44, 1e+45, 1e+46, 1e+47, 1e+48, 1e+49, 1e+50, 1e+51, 1e+52, 1e+53, 1e+54, 1e+55, 1e+56, 1e+57, 1e+58, 1e+59, 1e+60, + 1e+61, 1e+62, 1e+63, 1e+64, 1e+65, 1e+66, 1e+67, 1e+68, 1e+69, 1e+70, 1e+71, 1e+72, 1e+73, 1e+74, 1e+75, 1e+76, 1e+77, 1e+78, 1e+79, 1e+80, + 1e+81, 1e+82, 1e+83, 1e+84, 1e+85, 1e+86, 1e+87, 1e+88, 1e+89, 1e+90, 1e+91, 1e+92, 1e+93, 1e+94, 1e+95, 1e+96, 1e+97, 1e+98, 1e+99, 1e+100, + 1e+101,1e+102,1e+103,1e+104,1e+105,1e+106,1e+107,1e+108,1e+109,1e+110,1e+111,1e+112,1e+113,1e+114,1e+115,1e+116,1e+117,1e+118,1e+119,1e+120, + 1e+121,1e+122,1e+123,1e+124,1e+125,1e+126,1e+127,1e+128,1e+129,1e+130,1e+131,1e+132,1e+133,1e+134,1e+135,1e+136,1e+137,1e+138,1e+139,1e+140, + 1e+141,1e+142,1e+143,1e+144,1e+145,1e+146,1e+147,1e+148,1e+149,1e+150,1e+151,1e+152,1e+153,1e+154,1e+155,1e+156,1e+157,1e+158,1e+159,1e+160, + 1e+161,1e+162,1e+163,1e+164,1e+165,1e+166,1e+167,1e+168,1e+169,1e+170,1e+171,1e+172,1e+173,1e+174,1e+175,1e+176,1e+177,1e+178,1e+179,1e+180, + 1e+181,1e+182,1e+183,1e+184,1e+185,1e+186,1e+187,1e+188,1e+189,1e+190,1e+191,1e+192,1e+193,1e+194,1e+195,1e+196,1e+197,1e+198,1e+199,1e+200, + 1e+201,1e+202,1e+203,1e+204,1e+205,1e+206,1e+207,1e+208,1e+209,1e+210,1e+211,1e+212,1e+213,1e+214,1e+215,1e+216,1e+217,1e+218,1e+219,1e+220, + 1e+221,1e+222,1e+223,1e+224,1e+225,1e+226,1e+227,1e+228,1e+229,1e+230,1e+231,1e+232,1e+233,1e+234,1e+235,1e+236,1e+237,1e+238,1e+239,1e+240, + 1e+241,1e+242,1e+243,1e+244,1e+245,1e+246,1e+247,1e+248,1e+249,1e+250,1e+251,1e+252,1e+253,1e+254,1e+255,1e+256,1e+257,1e+258,1e+259,1e+260, + 1e+261,1e+262,1e+263,1e+264,1e+265,1e+266,1e+267,1e+268,1e+269,1e+270,1e+271,1e+272,1e+273,1e+274,1e+275,1e+276,1e+277,1e+278,1e+279,1e+280, + 1e+281,1e+282,1e+283,1e+284,1e+285,1e+286,1e+287,1e+288,1e+289,1e+290,1e+291,1e+292,1e+293,1e+294,1e+295,1e+296,1e+297,1e+298,1e+299,1e+300, + 1e+301,1e+302,1e+303,1e+304,1e+305,1e+306,1e+307,1e+308 + }; + RAPIDJSON_ASSERT(n >= 0 && n <= 308); + return e[n]; +} + +} // namespace internal +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_POW10_ diff --git a/include/rapidjson/internal/regex.h b/include/rapidjson/internal/regex.h new file mode 100644 index 0000000..6446c40 --- /dev/null +++ b/include/rapidjson/internal/regex.h @@ -0,0 +1,739 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// 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 RAPIDJSON_INTERNAL_REGEX_H_ +#define RAPIDJSON_INTERNAL_REGEX_H_ + +#include "../allocators.h" +#include "../stream.h" +#include "stack.h" + +#ifdef __clang__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(padded) +RAPIDJSON_DIAG_OFF(switch-enum) +#elif defined(_MSC_VER) +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(4512) // assignment operator could not be generated +#endif + +#ifdef __GNUC__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(effc++) +#endif + +#ifndef RAPIDJSON_REGEX_VERBOSE +#define RAPIDJSON_REGEX_VERBOSE 0 +#endif + +RAPIDJSON_NAMESPACE_BEGIN +namespace internal { + +/////////////////////////////////////////////////////////////////////////////// +// DecodedStream + +template +class DecodedStream { +public: + DecodedStream(SourceStream& ss) : ss_(ss), codepoint_() { Decode(); } + unsigned Peek() { return codepoint_; } + unsigned Take() { + unsigned c = codepoint_; + if (c) // No further decoding when '\0' + Decode(); + return c; + } + +private: + void Decode() { + if (!Encoding::Decode(ss_, &codepoint_)) + codepoint_ = 0; + } + + SourceStream& ss_; + unsigned codepoint_; +}; + +/////////////////////////////////////////////////////////////////////////////// +// GenericRegex + +static const SizeType kRegexInvalidState = ~SizeType(0); //!< Represents an invalid index in GenericRegex::State::out, out1 +static const SizeType kRegexInvalidRange = ~SizeType(0); + +template +class GenericRegexSearch; + +//! Regular expression engine with subset of ECMAscript grammar. +/*! + Supported regular expression syntax: + - \c ab Concatenation + - \c a|b Alternation + - \c a? Zero or one + - \c a* Zero or more + - \c a+ One or more + - \c a{3} Exactly 3 times + - \c a{3,} At least 3 times + - \c a{3,5} 3 to 5 times + - \c (ab) Grouping + - \c ^a At the beginning + - \c a$ At the end + - \c . Any character + - \c [abc] Character classes + - \c [a-c] Character class range + - \c [a-z0-9_] Character class combination + - \c [^abc] Negated character classes + - \c [^a-c] Negated character class range + - \c [\b] Backspace (U+0008) + - \c \\| \\\\ ... Escape characters + - \c \\f Form feed (U+000C) + - \c \\n Line feed (U+000A) + - \c \\r Carriage return (U+000D) + - \c \\t Tab (U+0009) + - \c \\v Vertical tab (U+000B) + + \note This is a Thompson NFA engine, implemented with reference to + Cox, Russ. "Regular Expression Matching Can Be Simple And Fast (but is slow in Java, Perl, PHP, Python, Ruby,...).", + https://swtch.com/~rsc/regexp/regexp1.html +*/ +template +class GenericRegex { +public: + typedef Encoding EncodingType; + typedef typename Encoding::Ch Ch; + template friend class GenericRegexSearch; + + GenericRegex(const Ch* source, Allocator* allocator = 0) : + ownAllocator_(allocator ? 0 : RAPIDJSON_NEW(Allocator)()), allocator_(allocator ? allocator : ownAllocator_), + states_(allocator_, 256), ranges_(allocator_, 256), root_(kRegexInvalidState), stateCount_(), rangeCount_(), + anchorBegin_(), anchorEnd_() + { + GenericStringStream ss(source); + DecodedStream, Encoding> ds(ss); + Parse(ds); + } + + ~GenericRegex() + { + RAPIDJSON_DELETE(ownAllocator_); + } + + bool IsValid() const { + return root_ != kRegexInvalidState; + } + +private: + enum Operator { + kZeroOrOne, + kZeroOrMore, + kOneOrMore, + kConcatenation, + kAlternation, + kLeftParenthesis + }; + + static const unsigned kAnyCharacterClass = 0xFFFFFFFF; //!< For '.' + static const unsigned kRangeCharacterClass = 0xFFFFFFFE; + static const unsigned kRangeNegationFlag = 0x80000000; + + struct Range { + unsigned start; // + unsigned end; + SizeType next; + }; + + struct State { + SizeType out; //!< Equals to kInvalid for matching state + SizeType out1; //!< Equals to non-kInvalid for split + SizeType rangeStart; + unsigned codepoint; + }; + + struct Frag { + Frag(SizeType s, SizeType o, SizeType m) : start(s), out(o), minIndex(m) {} + SizeType start; + SizeType out; //!< link-list of all output states + SizeType minIndex; + }; + + State& GetState(SizeType index) { + RAPIDJSON_ASSERT(index < stateCount_); + return states_.template Bottom()[index]; + } + + const State& GetState(SizeType index) const { + RAPIDJSON_ASSERT(index < stateCount_); + return states_.template Bottom()[index]; + } + + Range& GetRange(SizeType index) { + RAPIDJSON_ASSERT(index < rangeCount_); + return ranges_.template Bottom()[index]; + } + + const Range& GetRange(SizeType index) const { + RAPIDJSON_ASSERT(index < rangeCount_); + return ranges_.template Bottom()[index]; + } + + template + void Parse(DecodedStream& ds) { + Stack operandStack(allocator_, 256); // Frag + Stack operatorStack(allocator_, 256); // Operator + Stack atomCountStack(allocator_, 256); // unsigned (Atom per parenthesis) + + *atomCountStack.template Push() = 0; + + unsigned codepoint; + while (ds.Peek() != 0) { + switch (codepoint = ds.Take()) { + case '^': + anchorBegin_ = true; + break; + + case '$': + anchorEnd_ = true; + break; + + case '|': + while (!operatorStack.Empty() && *operatorStack.template Top() < kAlternation) + if (!Eval(operandStack, *operatorStack.template Pop(1))) + return; + *operatorStack.template Push() = kAlternation; + *atomCountStack.template Top() = 0; + break; + + case '(': + *operatorStack.template Push() = kLeftParenthesis; + *atomCountStack.template Push() = 0; + break; + + case ')': + while (!operatorStack.Empty() && *operatorStack.template Top() != kLeftParenthesis) + if (!Eval(operandStack, *operatorStack.template Pop(1))) + return; + if (operatorStack.Empty()) + return; + operatorStack.template Pop(1); + atomCountStack.template Pop(1); + ImplicitConcatenation(atomCountStack, operatorStack); + break; + + case '?': + if (!Eval(operandStack, kZeroOrOne)) + return; + break; + + case '*': + if (!Eval(operandStack, kZeroOrMore)) + return; + break; + + case '+': + if (!Eval(operandStack, kOneOrMore)) + return; + break; + + case '{': + { + unsigned n, m; + if (!ParseUnsigned(ds, &n)) + return; + + if (ds.Peek() == ',') { + ds.Take(); + if (ds.Peek() == '}') + m = kInfinityQuantifier; + else if (!ParseUnsigned(ds, &m) || m < n) + return; + } + else + m = n; + + if (!EvalQuantifier(operandStack, n, m) || ds.Peek() != '}') + return; + ds.Take(); + } + break; + + case '.': + PushOperand(operandStack, kAnyCharacterClass); + ImplicitConcatenation(atomCountStack, operatorStack); + break; + + case '[': + { + SizeType range; + if (!ParseRange(ds, &range)) + return; + SizeType s = NewState(kRegexInvalidState, kRegexInvalidState, kRangeCharacterClass); + GetState(s).rangeStart = range; + *operandStack.template Push() = Frag(s, s, s); + } + ImplicitConcatenation(atomCountStack, operatorStack); + break; + + case '\\': // Escape character + if (!CharacterEscape(ds, &codepoint)) + return; // Unsupported escape character + // fall through to default + RAPIDJSON_DELIBERATE_FALLTHROUGH; + + default: // Pattern character + PushOperand(operandStack, codepoint); + ImplicitConcatenation(atomCountStack, operatorStack); + } + } + + while (!operatorStack.Empty()) + if (!Eval(operandStack, *operatorStack.template Pop(1))) + return; + + // Link the operand to matching state. + if (operandStack.GetSize() == sizeof(Frag)) { + Frag* e = operandStack.template Pop(1); + Patch(e->out, NewState(kRegexInvalidState, kRegexInvalidState, 0)); + root_ = e->start; + +#if RAPIDJSON_REGEX_VERBOSE + printf("root: %d\n", root_); + for (SizeType i = 0; i < stateCount_ ; i++) { + State& s = GetState(i); + printf("[%2d] out: %2d out1: %2d c: '%c'\n", i, s.out, s.out1, (char)s.codepoint); + } + printf("\n"); +#endif + } + } + + SizeType NewState(SizeType out, SizeType out1, unsigned codepoint) { + State* s = states_.template Push(); + s->out = out; + s->out1 = out1; + s->codepoint = codepoint; + s->rangeStart = kRegexInvalidRange; + return stateCount_++; + } + + void PushOperand(Stack& operandStack, unsigned codepoint) { + SizeType s = NewState(kRegexInvalidState, kRegexInvalidState, codepoint); + *operandStack.template Push() = Frag(s, s, s); + } + + void ImplicitConcatenation(Stack& atomCountStack, Stack& operatorStack) { + if (*atomCountStack.template Top()) + *operatorStack.template Push() = kConcatenation; + (*atomCountStack.template Top())++; + } + + SizeType Append(SizeType l1, SizeType l2) { + SizeType old = l1; + while (GetState(l1).out != kRegexInvalidState) + l1 = GetState(l1).out; + GetState(l1).out = l2; + return old; + } + + void Patch(SizeType l, SizeType s) { + for (SizeType next; l != kRegexInvalidState; l = next) { + next = GetState(l).out; + GetState(l).out = s; + } + } + + bool Eval(Stack& operandStack, Operator op) { + switch (op) { + case kConcatenation: + RAPIDJSON_ASSERT(operandStack.GetSize() >= sizeof(Frag) * 2); + { + Frag e2 = *operandStack.template Pop(1); + Frag e1 = *operandStack.template Pop(1); + Patch(e1.out, e2.start); + *operandStack.template Push() = Frag(e1.start, e2.out, Min(e1.minIndex, e2.minIndex)); + } + return true; + + case kAlternation: + if (operandStack.GetSize() >= sizeof(Frag) * 2) { + Frag e2 = *operandStack.template Pop(1); + Frag e1 = *operandStack.template Pop(1); + SizeType s = NewState(e1.start, e2.start, 0); + *operandStack.template Push() = Frag(s, Append(e1.out, e2.out), Min(e1.minIndex, e2.minIndex)); + return true; + } + return false; + + case kZeroOrOne: + if (operandStack.GetSize() >= sizeof(Frag)) { + Frag e = *operandStack.template Pop(1); + SizeType s = NewState(kRegexInvalidState, e.start, 0); + *operandStack.template Push() = Frag(s, Append(e.out, s), e.minIndex); + return true; + } + return false; + + case kZeroOrMore: + if (operandStack.GetSize() >= sizeof(Frag)) { + Frag e = *operandStack.template Pop(1); + SizeType s = NewState(kRegexInvalidState, e.start, 0); + Patch(e.out, s); + *operandStack.template Push() = Frag(s, s, e.minIndex); + return true; + } + return false; + + case kOneOrMore: + if (operandStack.GetSize() >= sizeof(Frag)) { + Frag e = *operandStack.template Pop(1); + SizeType s = NewState(kRegexInvalidState, e.start, 0); + Patch(e.out, s); + *operandStack.template Push() = Frag(e.start, s, e.minIndex); + return true; + } + return false; + + default: + // syntax error (e.g. unclosed kLeftParenthesis) + return false; + } + } + + bool EvalQuantifier(Stack& operandStack, unsigned n, unsigned m) { + RAPIDJSON_ASSERT(n <= m); + RAPIDJSON_ASSERT(operandStack.GetSize() >= sizeof(Frag)); + + if (n == 0) { + if (m == 0) // a{0} not support + return false; + else if (m == kInfinityQuantifier) + Eval(operandStack, kZeroOrMore); // a{0,} -> a* + else { + Eval(operandStack, kZeroOrOne); // a{0,5} -> a? + for (unsigned i = 0; i < m - 1; i++) + CloneTopOperand(operandStack); // a{0,5} -> a? a? a? a? a? + for (unsigned i = 0; i < m - 1; i++) + Eval(operandStack, kConcatenation); // a{0,5} -> a?a?a?a?a? + } + return true; + } + + for (unsigned i = 0; i < n - 1; i++) // a{3} -> a a a + CloneTopOperand(operandStack); + + if (m == kInfinityQuantifier) + Eval(operandStack, kOneOrMore); // a{3,} -> a a a+ + else if (m > n) { + CloneTopOperand(operandStack); // a{3,5} -> a a a a + Eval(operandStack, kZeroOrOne); // a{3,5} -> a a a a? + for (unsigned i = n; i < m - 1; i++) + CloneTopOperand(operandStack); // a{3,5} -> a a a a? a? + for (unsigned i = n; i < m; i++) + Eval(operandStack, kConcatenation); // a{3,5} -> a a aa?a? + } + + for (unsigned i = 0; i < n - 1; i++) + Eval(operandStack, kConcatenation); // a{3} -> aaa, a{3,} -> aaa+, a{3.5} -> aaaa?a? + + return true; + } + + static SizeType Min(SizeType a, SizeType b) { return a < b ? a : b; } + + void CloneTopOperand(Stack& operandStack) { + const Frag src = *operandStack.template Top(); // Copy constructor to prevent invalidation + SizeType count = stateCount_ - src.minIndex; // Assumes top operand contains states in [src->minIndex, stateCount_) + State* s = states_.template Push(count); + memcpy(s, &GetState(src.minIndex), count * sizeof(State)); + for (SizeType j = 0; j < count; j++) { + if (s[j].out != kRegexInvalidState) + s[j].out += count; + if (s[j].out1 != kRegexInvalidState) + s[j].out1 += count; + } + *operandStack.template Push() = Frag(src.start + count, src.out + count, src.minIndex + count); + stateCount_ += count; + } + + template + bool ParseUnsigned(DecodedStream& ds, unsigned* u) { + unsigned r = 0; + if (ds.Peek() < '0' || ds.Peek() > '9') + return false; + while (ds.Peek() >= '0' && ds.Peek() <= '9') { + if (r >= 429496729 && ds.Peek() > '5') // 2^32 - 1 = 4294967295 + return false; // overflow + r = r * 10 + (ds.Take() - '0'); + } + *u = r; + return true; + } + + template + bool ParseRange(DecodedStream& ds, SizeType* range) { + bool isBegin = true; + bool negate = false; + int step = 0; + SizeType start = kRegexInvalidRange; + SizeType current = kRegexInvalidRange; + unsigned codepoint; + while ((codepoint = ds.Take()) != 0) { + if (isBegin) { + isBegin = false; + if (codepoint == '^') { + negate = true; + continue; + } + } + + switch (codepoint) { + case ']': + if (start == kRegexInvalidRange) + return false; // Error: nothing inside [] + if (step == 2) { // Add trailing '-' + SizeType r = NewRange('-'); + RAPIDJSON_ASSERT(current != kRegexInvalidRange); + GetRange(current).next = r; + } + if (negate) + GetRange(start).start |= kRangeNegationFlag; + *range = start; + return true; + + case '\\': + if (ds.Peek() == 'b') { + ds.Take(); + codepoint = 0x0008; // Escape backspace character + } + else if (!CharacterEscape(ds, &codepoint)) + return false; + // fall through to default + RAPIDJSON_DELIBERATE_FALLTHROUGH; + + default: + switch (step) { + case 1: + if (codepoint == '-') { + step++; + break; + } + // fall through to step 0 for other characters + RAPIDJSON_DELIBERATE_FALLTHROUGH; + + case 0: + { + SizeType r = NewRange(codepoint); + if (current != kRegexInvalidRange) + GetRange(current).next = r; + if (start == kRegexInvalidRange) + start = r; + current = r; + } + step = 1; + break; + + default: + RAPIDJSON_ASSERT(step == 2); + GetRange(current).end = codepoint; + step = 0; + } + } + } + return false; + } + + SizeType NewRange(unsigned codepoint) { + Range* r = ranges_.template Push(); + r->start = r->end = codepoint; + r->next = kRegexInvalidRange; + return rangeCount_++; + } + + template + bool CharacterEscape(DecodedStream& ds, unsigned* escapedCodepoint) { + unsigned codepoint; + switch (codepoint = ds.Take()) { + case '^': + case '$': + case '|': + case '(': + case ')': + case '?': + case '*': + case '+': + case '.': + case '[': + case ']': + case '{': + case '}': + case '\\': + *escapedCodepoint = codepoint; return true; + case 'f': *escapedCodepoint = 0x000C; return true; + case 'n': *escapedCodepoint = 0x000A; return true; + case 'r': *escapedCodepoint = 0x000D; return true; + case 't': *escapedCodepoint = 0x0009; return true; + case 'v': *escapedCodepoint = 0x000B; return true; + default: + return false; // Unsupported escape character + } + } + + Allocator* ownAllocator_; + Allocator* allocator_; + Stack states_; + Stack ranges_; + SizeType root_; + SizeType stateCount_; + SizeType rangeCount_; + + static const unsigned kInfinityQuantifier = ~0u; + + // For SearchWithAnchoring() + bool anchorBegin_; + bool anchorEnd_; +}; + +template +class GenericRegexSearch { +public: + typedef typename RegexType::EncodingType Encoding; + typedef typename Encoding::Ch Ch; + + GenericRegexSearch(const RegexType& regex, Allocator* allocator = 0) : + regex_(regex), allocator_(allocator), ownAllocator_(0), + state0_(allocator, 0), state1_(allocator, 0), stateSet_() + { + RAPIDJSON_ASSERT(regex_.IsValid()); + if (!allocator_) + ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator)(); + stateSet_ = static_cast(allocator_->Malloc(GetStateSetSize())); + state0_.template Reserve(regex_.stateCount_); + state1_.template Reserve(regex_.stateCount_); + } + + ~GenericRegexSearch() { + Allocator::Free(stateSet_); + RAPIDJSON_DELETE(ownAllocator_); + } + + template + bool Match(InputStream& is) { + return SearchWithAnchoring(is, true, true); + } + + bool Match(const Ch* s) { + GenericStringStream is(s); + return Match(is); + } + + template + bool Search(InputStream& is) { + return SearchWithAnchoring(is, regex_.anchorBegin_, regex_.anchorEnd_); + } + + bool Search(const Ch* s) { + GenericStringStream is(s); + return Search(is); + } + +private: + typedef typename RegexType::State State; + typedef typename RegexType::Range Range; + + template + bool SearchWithAnchoring(InputStream& is, bool anchorBegin, bool anchorEnd) { + DecodedStream ds(is); + + state0_.Clear(); + Stack *current = &state0_, *next = &state1_; + const size_t stateSetSize = GetStateSetSize(); + std::memset(stateSet_, 0, stateSetSize); + + bool matched = AddState(*current, regex_.root_); + unsigned codepoint; + while (!current->Empty() && (codepoint = ds.Take()) != 0) { + std::memset(stateSet_, 0, stateSetSize); + next->Clear(); + matched = false; + for (const SizeType* s = current->template Bottom(); s != current->template End(); ++s) { + const State& sr = regex_.GetState(*s); + if (sr.codepoint == codepoint || + sr.codepoint == RegexType::kAnyCharacterClass || + (sr.codepoint == RegexType::kRangeCharacterClass && MatchRange(sr.rangeStart, codepoint))) + { + matched = AddState(*next, sr.out) || matched; + if (!anchorEnd && matched) + return true; + } + if (!anchorBegin) + AddState(*next, regex_.root_); + } + internal::Swap(current, next); + } + + return matched; + } + + size_t GetStateSetSize() const { + return (regex_.stateCount_ + 31) / 32 * 4; + } + + // Return whether the added states is a match state + bool AddState(Stack& l, SizeType index) { + RAPIDJSON_ASSERT(index != kRegexInvalidState); + + const State& s = regex_.GetState(index); + if (s.out1 != kRegexInvalidState) { // Split + bool matched = AddState(l, s.out); + return AddState(l, s.out1) || matched; + } + else if (!(stateSet_[index >> 5] & (1u << (index & 31)))) { + stateSet_[index >> 5] |= (1u << (index & 31)); + *l.template PushUnsafe() = index; + } + return s.out == kRegexInvalidState; // by using PushUnsafe() above, we can ensure s is not validated due to reallocation. + } + + bool MatchRange(SizeType rangeIndex, unsigned codepoint) const { + bool yes = (regex_.GetRange(rangeIndex).start & RegexType::kRangeNegationFlag) == 0; + while (rangeIndex != kRegexInvalidRange) { + const Range& r = regex_.GetRange(rangeIndex); + if (codepoint >= (r.start & ~RegexType::kRangeNegationFlag) && codepoint <= r.end) + return yes; + rangeIndex = r.next; + } + return !yes; + } + + const RegexType& regex_; + Allocator* allocator_; + Allocator* ownAllocator_; + Stack state0_; + Stack state1_; + uint32_t* stateSet_; +}; + +typedef GenericRegex > Regex; +typedef GenericRegexSearch RegexSearch; + +} // namespace internal +RAPIDJSON_NAMESPACE_END + +#ifdef __GNUC__ +RAPIDJSON_DIAG_POP +#endif + +#if defined(__clang__) || defined(_MSC_VER) +RAPIDJSON_DIAG_POP +#endif + +#endif // RAPIDJSON_INTERNAL_REGEX_H_ diff --git a/include/rapidjson/internal/stack.h b/include/rapidjson/internal/stack.h new file mode 100644 index 0000000..73abd70 --- /dev/null +++ b/include/rapidjson/internal/stack.h @@ -0,0 +1,232 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// 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 RAPIDJSON_INTERNAL_STACK_H_ +#define RAPIDJSON_INTERNAL_STACK_H_ + +#include "../allocators.h" +#include "swap.h" +#include + +#if defined(__clang__) +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(c++98-compat) +#endif + +RAPIDJSON_NAMESPACE_BEGIN +namespace internal { + +/////////////////////////////////////////////////////////////////////////////// +// Stack + +//! A type-unsafe stack for storing different types of data. +/*! \tparam Allocator Allocator for allocating stack memory. +*/ +template +class Stack { +public: + // Optimization note: Do not allocate memory for stack_ in constructor. + // Do it lazily when first Push() -> Expand() -> Resize(). + Stack(Allocator* allocator, size_t stackCapacity) : allocator_(allocator), ownAllocator_(0), stack_(0), stackTop_(0), stackEnd_(0), initialCapacity_(stackCapacity) { + } + +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS + Stack(Stack&& rhs) + : allocator_(rhs.allocator_), + ownAllocator_(rhs.ownAllocator_), + stack_(rhs.stack_), + stackTop_(rhs.stackTop_), + stackEnd_(rhs.stackEnd_), + initialCapacity_(rhs.initialCapacity_) + { + rhs.allocator_ = 0; + rhs.ownAllocator_ = 0; + rhs.stack_ = 0; + rhs.stackTop_ = 0; + rhs.stackEnd_ = 0; + rhs.initialCapacity_ = 0; + } +#endif + + ~Stack() { + Destroy(); + } + +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS + Stack& operator=(Stack&& rhs) { + if (&rhs != this) + { + Destroy(); + + allocator_ = rhs.allocator_; + ownAllocator_ = rhs.ownAllocator_; + stack_ = rhs.stack_; + stackTop_ = rhs.stackTop_; + stackEnd_ = rhs.stackEnd_; + initialCapacity_ = rhs.initialCapacity_; + + rhs.allocator_ = 0; + rhs.ownAllocator_ = 0; + rhs.stack_ = 0; + rhs.stackTop_ = 0; + rhs.stackEnd_ = 0; + rhs.initialCapacity_ = 0; + } + return *this; + } +#endif + + void Swap(Stack& rhs) RAPIDJSON_NOEXCEPT { + internal::Swap(allocator_, rhs.allocator_); + internal::Swap(ownAllocator_, rhs.ownAllocator_); + internal::Swap(stack_, rhs.stack_); + internal::Swap(stackTop_, rhs.stackTop_); + internal::Swap(stackEnd_, rhs.stackEnd_); + internal::Swap(initialCapacity_, rhs.initialCapacity_); + } + + void Clear() { stackTop_ = stack_; } + + void ShrinkToFit() { + if (Empty()) { + // If the stack is empty, completely deallocate the memory. + Allocator::Free(stack_); // NOLINT (+clang-analyzer-unix.Malloc) + stack_ = 0; + stackTop_ = 0; + stackEnd_ = 0; + } + else + Resize(GetSize()); + } + + // Optimization note: try to minimize the size of this function for force inline. + // Expansion is run very infrequently, so it is moved to another (probably non-inline) function. + template + RAPIDJSON_FORCEINLINE void Reserve(size_t count = 1) { + // Expand the stack if needed + if (RAPIDJSON_UNLIKELY(static_cast(sizeof(T) * count) > (stackEnd_ - stackTop_))) + Expand(count); + } + + template + RAPIDJSON_FORCEINLINE T* Push(size_t count = 1) { + Reserve(count); + return PushUnsafe(count); + } + + template + RAPIDJSON_FORCEINLINE T* PushUnsafe(size_t count = 1) { + RAPIDJSON_ASSERT(stackTop_); + RAPIDJSON_ASSERT(static_cast(sizeof(T) * count) <= (stackEnd_ - stackTop_)); + T* ret = reinterpret_cast(stackTop_); + stackTop_ += sizeof(T) * count; + return ret; + } + + template + T* Pop(size_t count) { + RAPIDJSON_ASSERT(GetSize() >= count * sizeof(T)); + stackTop_ -= count * sizeof(T); + return reinterpret_cast(stackTop_); + } + + template + T* Top() { + RAPIDJSON_ASSERT(GetSize() >= sizeof(T)); + return reinterpret_cast(stackTop_ - sizeof(T)); + } + + template + const T* Top() const { + RAPIDJSON_ASSERT(GetSize() >= sizeof(T)); + return reinterpret_cast(stackTop_ - sizeof(T)); + } + + template + T* End() { return reinterpret_cast(stackTop_); } + + template + const T* End() const { return reinterpret_cast(stackTop_); } + + template + T* Bottom() { return reinterpret_cast(stack_); } + + template + const T* Bottom() const { return reinterpret_cast(stack_); } + + bool HasAllocator() const { + return allocator_ != 0; + } + + Allocator& GetAllocator() { + RAPIDJSON_ASSERT(allocator_); + return *allocator_; + } + + bool Empty() const { return stackTop_ == stack_; } + size_t GetSize() const { return static_cast(stackTop_ - stack_); } + size_t GetCapacity() const { return static_cast(stackEnd_ - stack_); } + +private: + template + void Expand(size_t count) { + // Only expand the capacity if the current stack exists. Otherwise just create a stack with initial capacity. + size_t newCapacity; + if (stack_ == 0) { + if (!allocator_) + ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator)(); + newCapacity = initialCapacity_; + } else { + newCapacity = GetCapacity(); + newCapacity += (newCapacity + 1) / 2; + } + size_t newSize = GetSize() + sizeof(T) * count; + if (newCapacity < newSize) + newCapacity = newSize; + + Resize(newCapacity); + } + + void Resize(size_t newCapacity) { + const size_t size = GetSize(); // Backup the current size + stack_ = static_cast(allocator_->Realloc(stack_, GetCapacity(), newCapacity)); + stackTop_ = stack_ + size; + stackEnd_ = stack_ + newCapacity; + } + + void Destroy() { + Allocator::Free(stack_); + RAPIDJSON_DELETE(ownAllocator_); // Only delete if it is owned by the stack + } + + // Prohibit copy constructor & assignment operator. + Stack(const Stack&); + Stack& operator=(const Stack&); + + Allocator* allocator_; + Allocator* ownAllocator_; + char *stack_; + char *stackTop_; + char *stackEnd_; + size_t initialCapacity_; +}; + +} // namespace internal +RAPIDJSON_NAMESPACE_END + +#if defined(__clang__) +RAPIDJSON_DIAG_POP +#endif + +#endif // RAPIDJSON_STACK_H_ diff --git a/include/rapidjson/internal/strfunc.h b/include/rapidjson/internal/strfunc.h new file mode 100644 index 0000000..b698a8f --- /dev/null +++ b/include/rapidjson/internal/strfunc.h @@ -0,0 +1,83 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// 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 RAPIDJSON_INTERNAL_STRFUNC_H_ +#define RAPIDJSON_INTERNAL_STRFUNC_H_ + +#include "../stream.h" +#include + +RAPIDJSON_NAMESPACE_BEGIN +namespace internal { + +//! Custom strlen() which works on different character types. +/*! \tparam Ch Character type (e.g. char, wchar_t, short) + \param s Null-terminated input string. + \return Number of characters in the string. + \note This has the same semantics as strlen(), the return value is not number of Unicode codepoints. +*/ +template +inline SizeType StrLen(const Ch* s) { + RAPIDJSON_ASSERT(s != 0); + const Ch* p = s; + while (*p) ++p; + return SizeType(p - s); +} + +template <> +inline SizeType StrLen(const char* s) { + return SizeType(std::strlen(s)); +} + +template <> +inline SizeType StrLen(const wchar_t* s) { + return SizeType(std::wcslen(s)); +} + +//! Custom strcmpn() which works on different character types. +/*! \tparam Ch Character type (e.g. char, wchar_t, short) + \param s1 Null-terminated input string. + \param s2 Null-terminated input string. + \return 0 if equal +*/ +template +inline int StrCmp(const Ch* s1, const Ch* s2) { + RAPIDJSON_ASSERT(s1 != 0); + RAPIDJSON_ASSERT(s2 != 0); + while(*s1 && (*s1 == *s2)) { s1++; s2++; } + return static_cast(*s1) < static_cast(*s2) ? -1 : static_cast(*s1) > static_cast(*s2); +} + +//! Returns number of code points in a encoded string. +template +bool CountStringCodePoint(const typename Encoding::Ch* s, SizeType length, SizeType* outCount) { + RAPIDJSON_ASSERT(s != 0); + RAPIDJSON_ASSERT(outCount != 0); + GenericStringStream is(s); + const typename Encoding::Ch* end = s + length; + SizeType count = 0; + while (is.src_ < end) { + unsigned codepoint; + if (!Encoding::Decode(is, &codepoint)) + return false; + count++; + } + *outCount = count; + return true; +} + +} // namespace internal +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_INTERNAL_STRFUNC_H_ diff --git a/include/rapidjson/internal/strtod.h b/include/rapidjson/internal/strtod.h new file mode 100644 index 0000000..55f0e38 --- /dev/null +++ b/include/rapidjson/internal/strtod.h @@ -0,0 +1,293 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// 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 RAPIDJSON_STRTOD_ +#define RAPIDJSON_STRTOD_ + +#include "ieee754.h" +#include "biginteger.h" +#include "diyfp.h" +#include "pow10.h" +#include +#include + +RAPIDJSON_NAMESPACE_BEGIN +namespace internal { + +inline double FastPath(double significand, int exp) { + if (exp < -308) + return 0.0; + else if (exp >= 0) + return significand * internal::Pow10(exp); + else + return significand / internal::Pow10(-exp); +} + +inline double StrtodNormalPrecision(double d, int p) { + if (p < -308) { + // Prevent expSum < -308, making Pow10(p) = 0 + d = FastPath(d, -308); + d = FastPath(d, p + 308); + } + else + d = FastPath(d, p); + return d; +} + +template +inline T Min3(T a, T b, T c) { + T m = a; + if (m > b) m = b; + if (m > c) m = c; + return m; +} + +inline int CheckWithinHalfULP(double b, const BigInteger& d, int dExp) { + const Double db(b); + const uint64_t bInt = db.IntegerSignificand(); + const int bExp = db.IntegerExponent(); + const int hExp = bExp - 1; + + int dS_Exp2 = 0, dS_Exp5 = 0, bS_Exp2 = 0, bS_Exp5 = 0, hS_Exp2 = 0, hS_Exp5 = 0; + + // Adjust for decimal exponent + if (dExp >= 0) { + dS_Exp2 += dExp; + dS_Exp5 += dExp; + } + else { + bS_Exp2 -= dExp; + bS_Exp5 -= dExp; + hS_Exp2 -= dExp; + hS_Exp5 -= dExp; + } + + // Adjust for binary exponent + if (bExp >= 0) + bS_Exp2 += bExp; + else { + dS_Exp2 -= bExp; + hS_Exp2 -= bExp; + } + + // Adjust for half ulp exponent + if (hExp >= 0) + hS_Exp2 += hExp; + else { + dS_Exp2 -= hExp; + bS_Exp2 -= hExp; + } + + // Remove common power of two factor from all three scaled values + int common_Exp2 = Min3(dS_Exp2, bS_Exp2, hS_Exp2); + dS_Exp2 -= common_Exp2; + bS_Exp2 -= common_Exp2; + hS_Exp2 -= common_Exp2; + + BigInteger dS = d; + dS.MultiplyPow5(static_cast(dS_Exp5)) <<= static_cast(dS_Exp2); + + BigInteger bS(bInt); + bS.MultiplyPow5(static_cast(bS_Exp5)) <<= static_cast(bS_Exp2); + + BigInteger hS(1); + hS.MultiplyPow5(static_cast(hS_Exp5)) <<= static_cast(hS_Exp2); + + BigInteger delta(0); + dS.Difference(bS, &delta); + + return delta.Compare(hS); +} + +inline bool StrtodFast(double d, int p, double* result) { + // Use fast path for string-to-double conversion if possible + // see http://www.exploringbinary.com/fast-path-decimal-to-floating-point-conversion/ + if (p > 22 && p < 22 + 16) { + // Fast Path Cases In Disguise + d *= internal::Pow10(p - 22); + p = 22; + } + + if (p >= -22 && p <= 22 && d <= 9007199254740991.0) { // 2^53 - 1 + *result = FastPath(d, p); + return true; + } + else + return false; +} + +// Compute an approximation and see if it is within 1/2 ULP +template +inline bool StrtodDiyFp(const Ch* decimals, int dLen, int dExp, double* result) { + uint64_t significand = 0; + int i = 0; // 2^64 - 1 = 18446744073709551615, 1844674407370955161 = 0x1999999999999999 + for (; i < dLen; i++) { + if (significand > RAPIDJSON_UINT64_C2(0x19999999, 0x99999999) || + (significand == RAPIDJSON_UINT64_C2(0x19999999, 0x99999999) && decimals[i] > Ch('5'))) + break; + significand = significand * 10u + static_cast(decimals[i] - Ch('0')); + } + + if (i < dLen && decimals[i] >= Ch('5')) // Rounding + significand++; + + int remaining = dLen - i; + const int kUlpShift = 3; + const int kUlp = 1 << kUlpShift; + int64_t error = (remaining == 0) ? 0 : kUlp / 2; + + DiyFp v(significand, 0); + v = v.Normalize(); + error <<= -v.e; + + dExp += remaining; + + int actualExp; + DiyFp cachedPower = GetCachedPower10(dExp, &actualExp); + if (actualExp != dExp) { + static const DiyFp kPow10[] = { + DiyFp(RAPIDJSON_UINT64_C2(0xa0000000, 0x00000000), -60), // 10^1 + DiyFp(RAPIDJSON_UINT64_C2(0xc8000000, 0x00000000), -57), // 10^2 + DiyFp(RAPIDJSON_UINT64_C2(0xfa000000, 0x00000000), -54), // 10^3 + DiyFp(RAPIDJSON_UINT64_C2(0x9c400000, 0x00000000), -50), // 10^4 + DiyFp(RAPIDJSON_UINT64_C2(0xc3500000, 0x00000000), -47), // 10^5 + DiyFp(RAPIDJSON_UINT64_C2(0xf4240000, 0x00000000), -44), // 10^6 + DiyFp(RAPIDJSON_UINT64_C2(0x98968000, 0x00000000), -40) // 10^7 + }; + int adjustment = dExp - actualExp; + RAPIDJSON_ASSERT(adjustment >= 1 && adjustment < 8); + v = v * kPow10[adjustment - 1]; + if (dLen + adjustment > 19) // has more digits than decimal digits in 64-bit + error += kUlp / 2; + } + + v = v * cachedPower; + + error += kUlp + (error == 0 ? 0 : 1); + + const int oldExp = v.e; + v = v.Normalize(); + error <<= oldExp - v.e; + + const int effectiveSignificandSize = Double::EffectiveSignificandSize(64 + v.e); + int precisionSize = 64 - effectiveSignificandSize; + if (precisionSize + kUlpShift >= 64) { + int scaleExp = (precisionSize + kUlpShift) - 63; + v.f >>= scaleExp; + v.e += scaleExp; + error = (error >> scaleExp) + 1 + kUlp; + precisionSize -= scaleExp; + } + + DiyFp rounded(v.f >> precisionSize, v.e + precisionSize); + const uint64_t precisionBits = (v.f & ((uint64_t(1) << precisionSize) - 1)) * kUlp; + const uint64_t halfWay = (uint64_t(1) << (precisionSize - 1)) * kUlp; + if (precisionBits >= halfWay + static_cast(error)) { + rounded.f++; + if (rounded.f & (DiyFp::kDpHiddenBit << 1)) { // rounding overflows mantissa (issue #340) + rounded.f >>= 1; + rounded.e++; + } + } + + *result = rounded.ToDouble(); + + return halfWay - static_cast(error) >= precisionBits || precisionBits >= halfWay + static_cast(error); +} + +template +inline double StrtodBigInteger(double approx, const Ch* decimals, int dLen, int dExp) { + RAPIDJSON_ASSERT(dLen >= 0); + const BigInteger dInt(decimals, static_cast(dLen)); + Double a(approx); + int cmp = CheckWithinHalfULP(a.Value(), dInt, dExp); + if (cmp < 0) + return a.Value(); // within half ULP + else if (cmp == 0) { + // Round towards even + if (a.Significand() & 1) + return a.NextPositiveDouble(); + else + return a.Value(); + } + else // adjustment + return a.NextPositiveDouble(); +} + +template +inline double StrtodFullPrecision(double d, int p, const Ch* decimals, size_t length, size_t decimalPosition, int exp) { + RAPIDJSON_ASSERT(d >= 0.0); + RAPIDJSON_ASSERT(length >= 1); + + double result = 0.0; + if (StrtodFast(d, p, &result)) + return result; + + RAPIDJSON_ASSERT(length <= INT_MAX); + int dLen = static_cast(length); + + RAPIDJSON_ASSERT(length >= decimalPosition); + RAPIDJSON_ASSERT(length - decimalPosition <= INT_MAX); + int dExpAdjust = static_cast(length - decimalPosition); + + RAPIDJSON_ASSERT(exp >= INT_MIN + dExpAdjust); + int dExp = exp - dExpAdjust; + + // Make sure length+dExp does not overflow + RAPIDJSON_ASSERT(dExp <= INT_MAX - dLen); + + // Trim leading zeros + while (dLen > 0 && *decimals == '0') { + dLen--; + decimals++; + } + + // Trim trailing zeros + while (dLen > 0 && decimals[dLen - 1] == '0') { + dLen--; + dExp++; + } + + if (dLen == 0) { // Buffer only contains zeros. + return 0.0; + } + + // Trim right-most digits + const int kMaxDecimalDigit = 767 + 1; + if (dLen > kMaxDecimalDigit) { + dExp += dLen - kMaxDecimalDigit; + dLen = kMaxDecimalDigit; + } + + // If too small, underflow to zero. + // Any x <= 10^-324 is interpreted as zero. + if (dLen + dExp <= -324) + return 0.0; + + // If too large, overflow to infinity. + // Any x >= 10^309 is interpreted as +infinity. + if (dLen + dExp > 309) + return std::numeric_limits::infinity(); + + if (StrtodDiyFp(decimals, dLen, dExp, &result)) + return result; + + // Use approximation from StrtodDiyFp and make adjustment with BigInteger comparison + return StrtodBigInteger(result, decimals, dLen, dExp); +} + +} // namespace internal +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_STRTOD_ diff --git a/include/rapidjson/internal/swap.h b/include/rapidjson/internal/swap.h new file mode 100644 index 0000000..2cf92f9 --- /dev/null +++ b/include/rapidjson/internal/swap.h @@ -0,0 +1,46 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// 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 RAPIDJSON_INTERNAL_SWAP_H_ +#define RAPIDJSON_INTERNAL_SWAP_H_ + +#include "../rapidjson.h" + +#if defined(__clang__) +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(c++98-compat) +#endif + +RAPIDJSON_NAMESPACE_BEGIN +namespace internal { + +//! Custom swap() to avoid dependency on C++ header +/*! \tparam T Type of the arguments to swap, should be instantiated with primitive C++ types only. + \note This has the same semantics as std::swap(). +*/ +template +inline void Swap(T& a, T& b) RAPIDJSON_NOEXCEPT { + T tmp = a; + a = b; + b = tmp; +} + +} // namespace internal +RAPIDJSON_NAMESPACE_END + +#if defined(__clang__) +RAPIDJSON_DIAG_POP +#endif + +#endif // RAPIDJSON_INTERNAL_SWAP_H_ diff --git a/include/rapidjson/istreamwrapper.h b/include/rapidjson/istreamwrapper.h new file mode 100644 index 0000000..01437ec --- /dev/null +++ b/include/rapidjson/istreamwrapper.h @@ -0,0 +1,128 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// 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 RAPIDJSON_ISTREAMWRAPPER_H_ +#define RAPIDJSON_ISTREAMWRAPPER_H_ + +#include "stream.h" +#include +#include + +#ifdef __clang__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(padded) +#elif defined(_MSC_VER) +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(4351) // new behavior: elements of array 'array' will be default initialized +#endif + +RAPIDJSON_NAMESPACE_BEGIN + +//! Wrapper of \c std::basic_istream into RapidJSON's Stream concept. +/*! + The classes can be wrapped including but not limited to: + + - \c std::istringstream + - \c std::stringstream + - \c std::wistringstream + - \c std::wstringstream + - \c std::ifstream + - \c std::fstream + - \c std::wifstream + - \c std::wfstream + + \tparam StreamType Class derived from \c std::basic_istream. +*/ + +template +class BasicIStreamWrapper { +public: + typedef typename StreamType::char_type Ch; + + //! Constructor. + /*! + \param stream stream opened for read. + */ + BasicIStreamWrapper(StreamType &stream) : stream_(stream), buffer_(peekBuffer_), bufferSize_(4), bufferLast_(0), current_(buffer_), readCount_(0), count_(0), eof_(false) { + Read(); + } + + //! Constructor. + /*! + \param stream stream opened for read. + \param buffer user-supplied buffer. + \param bufferSize size of buffer in bytes. Must >=4 bytes. + */ + BasicIStreamWrapper(StreamType &stream, char* buffer, size_t bufferSize) : stream_(stream), buffer_(buffer), bufferSize_(bufferSize), bufferLast_(0), current_(buffer_), readCount_(0), count_(0), eof_(false) { + RAPIDJSON_ASSERT(bufferSize >= 4); + Read(); + } + + Ch Peek() const { return *current_; } + Ch Take() { Ch c = *current_; Read(); return c; } + size_t Tell() const { return count_ + static_cast(current_ - buffer_); } + + // Not implemented + void Put(Ch) { RAPIDJSON_ASSERT(false); } + void Flush() { RAPIDJSON_ASSERT(false); } + Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; } + size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; } + + // For encoding detection only. + const Ch* Peek4() const { + return (current_ + 4 - !eof_ <= bufferLast_) ? current_ : 0; + } + +private: + BasicIStreamWrapper(); + BasicIStreamWrapper(const BasicIStreamWrapper&); + BasicIStreamWrapper& operator=(const BasicIStreamWrapper&); + + void Read() { + if (current_ < bufferLast_) + ++current_; + else if (!eof_) { + count_ += readCount_; + readCount_ = bufferSize_; + bufferLast_ = buffer_ + readCount_ - 1; + current_ = buffer_; + + if (!stream_.read(buffer_, static_cast(bufferSize_))) { + readCount_ = static_cast(stream_.gcount()); + *(bufferLast_ = buffer_ + readCount_) = '\0'; + eof_ = true; + } + } + } + + StreamType &stream_; + Ch peekBuffer_[4], *buffer_; + size_t bufferSize_; + Ch *bufferLast_; + Ch *current_; + size_t readCount_; + size_t count_; //!< Number of characters read + bool eof_; +}; + +typedef BasicIStreamWrapper IStreamWrapper; +typedef BasicIStreamWrapper WIStreamWrapper; + +#if defined(__clang__) || defined(_MSC_VER) +RAPIDJSON_DIAG_POP +#endif + +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_ISTREAMWRAPPER_H_ diff --git a/include/rapidjson/memorybuffer.h b/include/rapidjson/memorybuffer.h new file mode 100644 index 0000000..ffbc41e --- /dev/null +++ b/include/rapidjson/memorybuffer.h @@ -0,0 +1,70 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// 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 RAPIDJSON_MEMORYBUFFER_H_ +#define RAPIDJSON_MEMORYBUFFER_H_ + +#include "stream.h" +#include "internal/stack.h" + +RAPIDJSON_NAMESPACE_BEGIN + +//! Represents an in-memory output byte stream. +/*! + This class is mainly for being wrapped by EncodedOutputStream or AutoUTFOutputStream. + + It is similar to FileWriteBuffer but the destination is an in-memory buffer instead of a file. + + Differences between MemoryBuffer and StringBuffer: + 1. StringBuffer has Encoding but MemoryBuffer is only a byte buffer. + 2. StringBuffer::GetString() returns a null-terminated string. MemoryBuffer::GetBuffer() returns a buffer without terminator. + + \tparam Allocator type for allocating memory buffer. + \note implements Stream concept +*/ +template +struct GenericMemoryBuffer { + typedef char Ch; // byte + + GenericMemoryBuffer(Allocator* allocator = 0, size_t capacity = kDefaultCapacity) : stack_(allocator, capacity) {} + + void Put(Ch c) { *stack_.template Push() = c; } + void Flush() {} + + void Clear() { stack_.Clear(); } + void ShrinkToFit() { stack_.ShrinkToFit(); } + Ch* Push(size_t count) { return stack_.template Push(count); } + void Pop(size_t count) { stack_.template Pop(count); } + + const Ch* GetBuffer() const { + return stack_.template Bottom(); + } + + size_t GetSize() const { return stack_.GetSize(); } + + static const size_t kDefaultCapacity = 256; + mutable internal::Stack stack_; +}; + +typedef GenericMemoryBuffer<> MemoryBuffer; + +//! Implement specialized version of PutN() with memset() for better performance. +template<> +inline void PutN(MemoryBuffer& memoryBuffer, char c, size_t n) { + std::memset(memoryBuffer.stack_.Push(n), c, n * sizeof(c)); +} + +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_MEMORYBUFFER_H_ diff --git a/include/rapidjson/memorystream.h b/include/rapidjson/memorystream.h new file mode 100644 index 0000000..77af6c9 --- /dev/null +++ b/include/rapidjson/memorystream.h @@ -0,0 +1,71 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// 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 RAPIDJSON_MEMORYSTREAM_H_ +#define RAPIDJSON_MEMORYSTREAM_H_ + +#include "stream.h" + +#ifdef __clang__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(unreachable-code) +RAPIDJSON_DIAG_OFF(missing-noreturn) +#endif + +RAPIDJSON_NAMESPACE_BEGIN + +//! Represents an in-memory input byte stream. +/*! + This class is mainly for being wrapped by EncodedInputStream or AutoUTFInputStream. + + It is similar to FileReadBuffer but the source is an in-memory buffer instead of a file. + + Differences between MemoryStream and StringStream: + 1. StringStream has encoding but MemoryStream is a byte stream. + 2. MemoryStream needs size of the source buffer and the buffer don't need to be null terminated. StringStream assume null-terminated string as source. + 3. MemoryStream supports Peek4() for encoding detection. StringStream is specified with an encoding so it should not have Peek4(). + \note implements Stream concept +*/ +struct MemoryStream { + typedef char Ch; // byte + + MemoryStream(const Ch *src, size_t size) : src_(src), begin_(src), end_(src + size), size_(size) {} + + Ch Peek() const { return RAPIDJSON_UNLIKELY(src_ == end_) ? '\0' : *src_; } + Ch Take() { return RAPIDJSON_UNLIKELY(src_ == end_) ? '\0' : *src_++; } + size_t Tell() const { return static_cast(src_ - begin_); } + + Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; } + void Put(Ch) { RAPIDJSON_ASSERT(false); } + void Flush() { RAPIDJSON_ASSERT(false); } + size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; } + + // For encoding detection only. + const Ch* Peek4() const { + return Tell() + 4 <= size_ ? src_ : 0; + } + + const Ch* src_; //!< Current read position. + const Ch* begin_; //!< Original head of the string. + const Ch* end_; //!< End of stream. + size_t size_; //!< Size of the stream. +}; + +RAPIDJSON_NAMESPACE_END + +#ifdef __clang__ +RAPIDJSON_DIAG_POP +#endif + +#endif // RAPIDJSON_MEMORYBUFFER_H_ diff --git a/include/rapidjson/msinttypes/inttypes.h b/include/rapidjson/msinttypes/inttypes.h new file mode 100644 index 0000000..1811128 --- /dev/null +++ b/include/rapidjson/msinttypes/inttypes.h @@ -0,0 +1,316 @@ +// ISO C9x compliant inttypes.h for Microsoft Visual Studio +// Based on ISO/IEC 9899:TC2 Committee draft (May 6, 2005) WG14/N1124 +// +// Copyright (c) 2006-2013 Alexander Chemeris +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the product nor the names of its contributors may +// be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED +// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO +// EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; +// OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +/////////////////////////////////////////////////////////////////////////////// + +// The above software in this distribution may have been modified by +// THL A29 Limited ("Tencent Modifications"). +// All Tencent Modifications are Copyright (C) 2015 THL A29 Limited. + +#ifndef _MSC_VER // [ +#error "Use this header only with Microsoft Visual C++ compilers!" +#endif // _MSC_VER ] + +#ifndef _MSC_INTTYPES_H_ // [ +#define _MSC_INTTYPES_H_ + +#if _MSC_VER > 1000 +#pragma once +#endif + +#include "stdint.h" + +// miloyip: VC supports inttypes.h since VC2013 +#if _MSC_VER >= 1800 +#include +#else + +// 7.8 Format conversion of integer types + +typedef struct { + intmax_t quot; + intmax_t rem; +} imaxdiv_t; + +// 7.8.1 Macros for format specifiers + +#if !defined(__cplusplus) || defined(__STDC_FORMAT_MACROS) // [ See footnote 185 at page 198 + +// The fprintf macros for signed integers are: +#define PRId8 "d" +#define PRIi8 "i" +#define PRIdLEAST8 "d" +#define PRIiLEAST8 "i" +#define PRIdFAST8 "d" +#define PRIiFAST8 "i" + +#define PRId16 "hd" +#define PRIi16 "hi" +#define PRIdLEAST16 "hd" +#define PRIiLEAST16 "hi" +#define PRIdFAST16 "hd" +#define PRIiFAST16 "hi" + +#define PRId32 "I32d" +#define PRIi32 "I32i" +#define PRIdLEAST32 "I32d" +#define PRIiLEAST32 "I32i" +#define PRIdFAST32 "I32d" +#define PRIiFAST32 "I32i" + +#define PRId64 "I64d" +#define PRIi64 "I64i" +#define PRIdLEAST64 "I64d" +#define PRIiLEAST64 "I64i" +#define PRIdFAST64 "I64d" +#define PRIiFAST64 "I64i" + +#define PRIdMAX "I64d" +#define PRIiMAX "I64i" + +#define PRIdPTR "Id" +#define PRIiPTR "Ii" + +// The fprintf macros for unsigned integers are: +#define PRIo8 "o" +#define PRIu8 "u" +#define PRIx8 "x" +#define PRIX8 "X" +#define PRIoLEAST8 "o" +#define PRIuLEAST8 "u" +#define PRIxLEAST8 "x" +#define PRIXLEAST8 "X" +#define PRIoFAST8 "o" +#define PRIuFAST8 "u" +#define PRIxFAST8 "x" +#define PRIXFAST8 "X" + +#define PRIo16 "ho" +#define PRIu16 "hu" +#define PRIx16 "hx" +#define PRIX16 "hX" +#define PRIoLEAST16 "ho" +#define PRIuLEAST16 "hu" +#define PRIxLEAST16 "hx" +#define PRIXLEAST16 "hX" +#define PRIoFAST16 "ho" +#define PRIuFAST16 "hu" +#define PRIxFAST16 "hx" +#define PRIXFAST16 "hX" + +#define PRIo32 "I32o" +#define PRIu32 "I32u" +#define PRIx32 "I32x" +#define PRIX32 "I32X" +#define PRIoLEAST32 "I32o" +#define PRIuLEAST32 "I32u" +#define PRIxLEAST32 "I32x" +#define PRIXLEAST32 "I32X" +#define PRIoFAST32 "I32o" +#define PRIuFAST32 "I32u" +#define PRIxFAST32 "I32x" +#define PRIXFAST32 "I32X" + +#define PRIo64 "I64o" +#define PRIu64 "I64u" +#define PRIx64 "I64x" +#define PRIX64 "I64X" +#define PRIoLEAST64 "I64o" +#define PRIuLEAST64 "I64u" +#define PRIxLEAST64 "I64x" +#define PRIXLEAST64 "I64X" +#define PRIoFAST64 "I64o" +#define PRIuFAST64 "I64u" +#define PRIxFAST64 "I64x" +#define PRIXFAST64 "I64X" + +#define PRIoMAX "I64o" +#define PRIuMAX "I64u" +#define PRIxMAX "I64x" +#define PRIXMAX "I64X" + +#define PRIoPTR "Io" +#define PRIuPTR "Iu" +#define PRIxPTR "Ix" +#define PRIXPTR "IX" + +// The fscanf macros for signed integers are: +#define SCNd8 "d" +#define SCNi8 "i" +#define SCNdLEAST8 "d" +#define SCNiLEAST8 "i" +#define SCNdFAST8 "d" +#define SCNiFAST8 "i" + +#define SCNd16 "hd" +#define SCNi16 "hi" +#define SCNdLEAST16 "hd" +#define SCNiLEAST16 "hi" +#define SCNdFAST16 "hd" +#define SCNiFAST16 "hi" + +#define SCNd32 "ld" +#define SCNi32 "li" +#define SCNdLEAST32 "ld" +#define SCNiLEAST32 "li" +#define SCNdFAST32 "ld" +#define SCNiFAST32 "li" + +#define SCNd64 "I64d" +#define SCNi64 "I64i" +#define SCNdLEAST64 "I64d" +#define SCNiLEAST64 "I64i" +#define SCNdFAST64 "I64d" +#define SCNiFAST64 "I64i" + +#define SCNdMAX "I64d" +#define SCNiMAX "I64i" + +#ifdef _WIN64 // [ +# define SCNdPTR "I64d" +# define SCNiPTR "I64i" +#else // _WIN64 ][ +# define SCNdPTR "ld" +# define SCNiPTR "li" +#endif // _WIN64 ] + +// The fscanf macros for unsigned integers are: +#define SCNo8 "o" +#define SCNu8 "u" +#define SCNx8 "x" +#define SCNX8 "X" +#define SCNoLEAST8 "o" +#define SCNuLEAST8 "u" +#define SCNxLEAST8 "x" +#define SCNXLEAST8 "X" +#define SCNoFAST8 "o" +#define SCNuFAST8 "u" +#define SCNxFAST8 "x" +#define SCNXFAST8 "X" + +#define SCNo16 "ho" +#define SCNu16 "hu" +#define SCNx16 "hx" +#define SCNX16 "hX" +#define SCNoLEAST16 "ho" +#define SCNuLEAST16 "hu" +#define SCNxLEAST16 "hx" +#define SCNXLEAST16 "hX" +#define SCNoFAST16 "ho" +#define SCNuFAST16 "hu" +#define SCNxFAST16 "hx" +#define SCNXFAST16 "hX" + +#define SCNo32 "lo" +#define SCNu32 "lu" +#define SCNx32 "lx" +#define SCNX32 "lX" +#define SCNoLEAST32 "lo" +#define SCNuLEAST32 "lu" +#define SCNxLEAST32 "lx" +#define SCNXLEAST32 "lX" +#define SCNoFAST32 "lo" +#define SCNuFAST32 "lu" +#define SCNxFAST32 "lx" +#define SCNXFAST32 "lX" + +#define SCNo64 "I64o" +#define SCNu64 "I64u" +#define SCNx64 "I64x" +#define SCNX64 "I64X" +#define SCNoLEAST64 "I64o" +#define SCNuLEAST64 "I64u" +#define SCNxLEAST64 "I64x" +#define SCNXLEAST64 "I64X" +#define SCNoFAST64 "I64o" +#define SCNuFAST64 "I64u" +#define SCNxFAST64 "I64x" +#define SCNXFAST64 "I64X" + +#define SCNoMAX "I64o" +#define SCNuMAX "I64u" +#define SCNxMAX "I64x" +#define SCNXMAX "I64X" + +#ifdef _WIN64 // [ +# define SCNoPTR "I64o" +# define SCNuPTR "I64u" +# define SCNxPTR "I64x" +# define SCNXPTR "I64X" +#else // _WIN64 ][ +# define SCNoPTR "lo" +# define SCNuPTR "lu" +# define SCNxPTR "lx" +# define SCNXPTR "lX" +#endif // _WIN64 ] + +#endif // __STDC_FORMAT_MACROS ] + +// 7.8.2 Functions for greatest-width integer types + +// 7.8.2.1 The imaxabs function +#define imaxabs _abs64 + +// 7.8.2.2 The imaxdiv function + +// This is modified version of div() function from Microsoft's div.c found +// in %MSVC.NET%\crt\src\div.c +#ifdef STATIC_IMAXDIV // [ +static +#else // STATIC_IMAXDIV ][ +_inline +#endif // STATIC_IMAXDIV ] +imaxdiv_t __cdecl imaxdiv(intmax_t numer, intmax_t denom) +{ + imaxdiv_t result; + + result.quot = numer / denom; + result.rem = numer % denom; + + if (numer < 0 && result.rem > 0) { + // did division wrong; must fix up + ++result.quot; + result.rem -= denom; + } + + return result; +} + +// 7.8.2.3 The strtoimax and strtoumax functions +#define strtoimax _strtoi64 +#define strtoumax _strtoui64 + +// 7.8.2.4 The wcstoimax and wcstoumax functions +#define wcstoimax _wcstoi64 +#define wcstoumax _wcstoui64 + +#endif // _MSC_VER >= 1800 + +#endif // _MSC_INTTYPES_H_ ] diff --git a/include/rapidjson/msinttypes/stdint.h b/include/rapidjson/msinttypes/stdint.h new file mode 100644 index 0000000..3d4477b --- /dev/null +++ b/include/rapidjson/msinttypes/stdint.h @@ -0,0 +1,300 @@ +// ISO C9x compliant stdint.h for Microsoft Visual Studio +// Based on ISO/IEC 9899:TC2 Committee draft (May 6, 2005) WG14/N1124 +// +// Copyright (c) 2006-2013 Alexander Chemeris +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the product nor the names of its contributors may +// be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED +// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO +// EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; +// OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +/////////////////////////////////////////////////////////////////////////////// + +// The above software in this distribution may have been modified by +// THL A29 Limited ("Tencent Modifications"). +// All Tencent Modifications are Copyright (C) 2015 THL A29 Limited. + +#ifndef _MSC_VER // [ +#error "Use this header only with Microsoft Visual C++ compilers!" +#endif // _MSC_VER ] + +#ifndef _MSC_STDINT_H_ // [ +#define _MSC_STDINT_H_ + +#if _MSC_VER > 1000 +#pragma once +#endif + +// miloyip: Originally Visual Studio 2010 uses its own stdint.h. However it generates warning with INT64_C(), so change to use this file for vs2010. +#if _MSC_VER >= 1600 // [ +#include + +#if !defined(__cplusplus) || defined(__STDC_CONSTANT_MACROS) // [ See footnote 224 at page 260 + +#undef INT8_C +#undef INT16_C +#undef INT32_C +#undef INT64_C +#undef UINT8_C +#undef UINT16_C +#undef UINT32_C +#undef UINT64_C + +// 7.18.4.1 Macros for minimum-width integer constants + +#define INT8_C(val) val##i8 +#define INT16_C(val) val##i16 +#define INT32_C(val) val##i32 +#define INT64_C(val) val##i64 + +#define UINT8_C(val) val##ui8 +#define UINT16_C(val) val##ui16 +#define UINT32_C(val) val##ui32 +#define UINT64_C(val) val##ui64 + +// 7.18.4.2 Macros for greatest-width integer constants +// These #ifndef's are needed to prevent collisions with . +// Check out Issue 9 for the details. +#ifndef INTMAX_C // [ +# define INTMAX_C INT64_C +#endif // INTMAX_C ] +#ifndef UINTMAX_C // [ +# define UINTMAX_C UINT64_C +#endif // UINTMAX_C ] + +#endif // __STDC_CONSTANT_MACROS ] + +#else // ] _MSC_VER >= 1700 [ + +#include + +// For Visual Studio 6 in C++ mode and for many Visual Studio versions when +// compiling for ARM we have to wrap include with 'extern "C++" {}' +// or compiler would give many errors like this: +// error C2733: second C linkage of overloaded function 'wmemchr' not allowed +#if defined(__cplusplus) && !defined(_M_ARM) +extern "C" { +#endif +# include +#if defined(__cplusplus) && !defined(_M_ARM) +} +#endif + +// Define _W64 macros to mark types changing their size, like intptr_t. +#ifndef _W64 +# if !defined(__midl) && (defined(_X86_) || defined(_M_IX86)) && _MSC_VER >= 1300 +# define _W64 __w64 +# else +# define _W64 +# endif +#endif + + +// 7.18.1 Integer types + +// 7.18.1.1 Exact-width integer types + +// Visual Studio 6 and Embedded Visual C++ 4 doesn't +// realize that, e.g. char has the same size as __int8 +// so we give up on __intX for them. +#if (_MSC_VER < 1300) + typedef signed char int8_t; + typedef signed short int16_t; + typedef signed int int32_t; + typedef unsigned char uint8_t; + typedef unsigned short uint16_t; + typedef unsigned int uint32_t; +#else + typedef signed __int8 int8_t; + typedef signed __int16 int16_t; + typedef signed __int32 int32_t; + typedef unsigned __int8 uint8_t; + typedef unsigned __int16 uint16_t; + typedef unsigned __int32 uint32_t; +#endif +typedef signed __int64 int64_t; +typedef unsigned __int64 uint64_t; + + +// 7.18.1.2 Minimum-width integer types +typedef int8_t int_least8_t; +typedef int16_t int_least16_t; +typedef int32_t int_least32_t; +typedef int64_t int_least64_t; +typedef uint8_t uint_least8_t; +typedef uint16_t uint_least16_t; +typedef uint32_t uint_least32_t; +typedef uint64_t uint_least64_t; + +// 7.18.1.3 Fastest minimum-width integer types +typedef int8_t int_fast8_t; +typedef int16_t int_fast16_t; +typedef int32_t int_fast32_t; +typedef int64_t int_fast64_t; +typedef uint8_t uint_fast8_t; +typedef uint16_t uint_fast16_t; +typedef uint32_t uint_fast32_t; +typedef uint64_t uint_fast64_t; + +// 7.18.1.4 Integer types capable of holding object pointers +#ifdef _WIN64 // [ + typedef signed __int64 intptr_t; + typedef unsigned __int64 uintptr_t; +#else // _WIN64 ][ + typedef _W64 signed int intptr_t; + typedef _W64 unsigned int uintptr_t; +#endif // _WIN64 ] + +// 7.18.1.5 Greatest-width integer types +typedef int64_t intmax_t; +typedef uint64_t uintmax_t; + + +// 7.18.2 Limits of specified-width integer types + +#if !defined(__cplusplus) || defined(__STDC_LIMIT_MACROS) // [ See footnote 220 at page 257 and footnote 221 at page 259 + +// 7.18.2.1 Limits of exact-width integer types +#define INT8_MIN ((int8_t)_I8_MIN) +#define INT8_MAX _I8_MAX +#define INT16_MIN ((int16_t)_I16_MIN) +#define INT16_MAX _I16_MAX +#define INT32_MIN ((int32_t)_I32_MIN) +#define INT32_MAX _I32_MAX +#define INT64_MIN ((int64_t)_I64_MIN) +#define INT64_MAX _I64_MAX +#define UINT8_MAX _UI8_MAX +#define UINT16_MAX _UI16_MAX +#define UINT32_MAX _UI32_MAX +#define UINT64_MAX _UI64_MAX + +// 7.18.2.2 Limits of minimum-width integer types +#define INT_LEAST8_MIN INT8_MIN +#define INT_LEAST8_MAX INT8_MAX +#define INT_LEAST16_MIN INT16_MIN +#define INT_LEAST16_MAX INT16_MAX +#define INT_LEAST32_MIN INT32_MIN +#define INT_LEAST32_MAX INT32_MAX +#define INT_LEAST64_MIN INT64_MIN +#define INT_LEAST64_MAX INT64_MAX +#define UINT_LEAST8_MAX UINT8_MAX +#define UINT_LEAST16_MAX UINT16_MAX +#define UINT_LEAST32_MAX UINT32_MAX +#define UINT_LEAST64_MAX UINT64_MAX + +// 7.18.2.3 Limits of fastest minimum-width integer types +#define INT_FAST8_MIN INT8_MIN +#define INT_FAST8_MAX INT8_MAX +#define INT_FAST16_MIN INT16_MIN +#define INT_FAST16_MAX INT16_MAX +#define INT_FAST32_MIN INT32_MIN +#define INT_FAST32_MAX INT32_MAX +#define INT_FAST64_MIN INT64_MIN +#define INT_FAST64_MAX INT64_MAX +#define UINT_FAST8_MAX UINT8_MAX +#define UINT_FAST16_MAX UINT16_MAX +#define UINT_FAST32_MAX UINT32_MAX +#define UINT_FAST64_MAX UINT64_MAX + +// 7.18.2.4 Limits of integer types capable of holding object pointers +#ifdef _WIN64 // [ +# define INTPTR_MIN INT64_MIN +# define INTPTR_MAX INT64_MAX +# define UINTPTR_MAX UINT64_MAX +#else // _WIN64 ][ +# define INTPTR_MIN INT32_MIN +# define INTPTR_MAX INT32_MAX +# define UINTPTR_MAX UINT32_MAX +#endif // _WIN64 ] + +// 7.18.2.5 Limits of greatest-width integer types +#define INTMAX_MIN INT64_MIN +#define INTMAX_MAX INT64_MAX +#define UINTMAX_MAX UINT64_MAX + +// 7.18.3 Limits of other integer types + +#ifdef _WIN64 // [ +# define PTRDIFF_MIN _I64_MIN +# define PTRDIFF_MAX _I64_MAX +#else // _WIN64 ][ +# define PTRDIFF_MIN _I32_MIN +# define PTRDIFF_MAX _I32_MAX +#endif // _WIN64 ] + +#define SIG_ATOMIC_MIN INT_MIN +#define SIG_ATOMIC_MAX INT_MAX + +#ifndef SIZE_MAX // [ +# ifdef _WIN64 // [ +# define SIZE_MAX _UI64_MAX +# else // _WIN64 ][ +# define SIZE_MAX _UI32_MAX +# endif // _WIN64 ] +#endif // SIZE_MAX ] + +// WCHAR_MIN and WCHAR_MAX are also defined in +#ifndef WCHAR_MIN // [ +# define WCHAR_MIN 0 +#endif // WCHAR_MIN ] +#ifndef WCHAR_MAX // [ +# define WCHAR_MAX _UI16_MAX +#endif // WCHAR_MAX ] + +#define WINT_MIN 0 +#define WINT_MAX _UI16_MAX + +#endif // __STDC_LIMIT_MACROS ] + + +// 7.18.4 Limits of other integer types + +#if !defined(__cplusplus) || defined(__STDC_CONSTANT_MACROS) // [ See footnote 224 at page 260 + +// 7.18.4.1 Macros for minimum-width integer constants + +#define INT8_C(val) val##i8 +#define INT16_C(val) val##i16 +#define INT32_C(val) val##i32 +#define INT64_C(val) val##i64 + +#define UINT8_C(val) val##ui8 +#define UINT16_C(val) val##ui16 +#define UINT32_C(val) val##ui32 +#define UINT64_C(val) val##ui64 + +// 7.18.4.2 Macros for greatest-width integer constants +// These #ifndef's are needed to prevent collisions with . +// Check out Issue 9 for the details. +#ifndef INTMAX_C // [ +# define INTMAX_C INT64_C +#endif // INTMAX_C ] +#ifndef UINTMAX_C // [ +# define UINTMAX_C UINT64_C +#endif // UINTMAX_C ] + +#endif // __STDC_CONSTANT_MACROS ] + +#endif // _MSC_VER >= 1600 ] + +#endif // _MSC_STDINT_H_ ] diff --git a/include/rapidjson/ostreamwrapper.h b/include/rapidjson/ostreamwrapper.h new file mode 100644 index 0000000..11ed4d3 --- /dev/null +++ b/include/rapidjson/ostreamwrapper.h @@ -0,0 +1,81 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// 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 RAPIDJSON_OSTREAMWRAPPER_H_ +#define RAPIDJSON_OSTREAMWRAPPER_H_ + +#include "stream.h" +#include + +#ifdef __clang__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(padded) +#endif + +RAPIDJSON_NAMESPACE_BEGIN + +//! Wrapper of \c std::basic_ostream into RapidJSON's Stream concept. +/*! + The classes can be wrapped including but not limited to: + + - \c std::ostringstream + - \c std::stringstream + - \c std::wpstringstream + - \c std::wstringstream + - \c std::ifstream + - \c std::fstream + - \c std::wofstream + - \c std::wfstream + + \tparam StreamType Class derived from \c std::basic_ostream. +*/ + +template +class BasicOStreamWrapper { +public: + typedef typename StreamType::char_type Ch; + BasicOStreamWrapper(StreamType& stream) : stream_(stream) {} + + void Put(Ch c) { + stream_.put(c); + } + + void Flush() { + stream_.flush(); + } + + // Not implemented + char Peek() const { RAPIDJSON_ASSERT(false); return 0; } + char Take() { RAPIDJSON_ASSERT(false); return 0; } + size_t Tell() const { RAPIDJSON_ASSERT(false); return 0; } + char* PutBegin() { RAPIDJSON_ASSERT(false); return 0; } + size_t PutEnd(char*) { RAPIDJSON_ASSERT(false); return 0; } + +private: + BasicOStreamWrapper(const BasicOStreamWrapper&); + BasicOStreamWrapper& operator=(const BasicOStreamWrapper&); + + StreamType& stream_; +}; + +typedef BasicOStreamWrapper OStreamWrapper; +typedef BasicOStreamWrapper WOStreamWrapper; + +#ifdef __clang__ +RAPIDJSON_DIAG_POP +#endif + +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_OSTREAMWRAPPER_H_ diff --git a/include/rapidjson/pointer.h b/include/rapidjson/pointer.h new file mode 100644 index 0000000..05b1704 --- /dev/null +++ b/include/rapidjson/pointer.h @@ -0,0 +1,1470 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// 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 RAPIDJSON_POINTER_H_ +#define RAPIDJSON_POINTER_H_ + +#include "document.h" +#include "uri.h" +#include "internal/itoa.h" +#include "error/error.h" // PointerParseErrorCode + +#ifdef __clang__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(switch-enum) +#elif defined(_MSC_VER) +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(4512) // assignment operator could not be generated +#endif + +RAPIDJSON_NAMESPACE_BEGIN + +static const SizeType kPointerInvalidIndex = ~SizeType(0); //!< Represents an invalid index in GenericPointer::Token + +/////////////////////////////////////////////////////////////////////////////// +// GenericPointer + +//! Represents a JSON Pointer. Use Pointer for UTF8 encoding and default allocator. +/*! + This class implements RFC 6901 "JavaScript Object Notation (JSON) Pointer" + (https://tools.ietf.org/html/rfc6901). + + A JSON pointer is for identifying a specific value in a JSON document + (GenericDocument). It can simplify coding of DOM tree manipulation, because it + can access multiple-level depth of DOM tree with single API call. + + After it parses a string representation (e.g. "/foo/0" or URI fragment + representation (e.g. "#/foo/0") into its internal representation (tokens), + it can be used to resolve a specific value in multiple documents, or sub-tree + of documents. + + Contrary to GenericValue, Pointer can be copy constructed and copy assigned. + Apart from assignment, a Pointer cannot be modified after construction. + + Although Pointer is very convenient, please aware that constructing Pointer + involves parsing and dynamic memory allocation. A special constructor with user- + supplied tokens eliminates these. + + GenericPointer depends on GenericDocument and GenericValue. + + \tparam ValueType The value type of the DOM tree. E.g. GenericValue > + \tparam Allocator The allocator type for allocating memory for internal representation. + + \note GenericPointer uses same encoding of ValueType. + However, Allocator of GenericPointer is independent of Allocator of Value. +*/ +template +class GenericPointer { +public: + typedef typename ValueType::EncodingType EncodingType; //!< Encoding type from Value + typedef typename ValueType::Ch Ch; //!< Character type from Value + typedef GenericUri UriType; + + + //! A token is the basic units of internal representation. + /*! + A JSON pointer string representation "/foo/123" is parsed to two tokens: + "foo" and 123. 123 will be represented in both numeric form and string form. + They are resolved according to the actual value type (object or array). + + For token that are not numbers, or the numeric value is out of bound + (greater than limits of SizeType), they are only treated as string form + (i.e. the token's index will be equal to kPointerInvalidIndex). + + This struct is public so that user can create a Pointer without parsing and + allocation, using a special constructor. + */ + struct Token { + const Ch* name; //!< Name of the token. It has null character at the end but it can contain null character. + SizeType length; //!< Length of the name. + SizeType index; //!< A valid array index, if it is not equal to kPointerInvalidIndex. + }; + + //!@name Constructors and destructor. + //@{ + + //! Default constructor. + GenericPointer(Allocator* allocator = 0) : allocator_(allocator), ownAllocator_(), nameBuffer_(), tokens_(), tokenCount_(), parseErrorOffset_(), parseErrorCode_(kPointerParseErrorNone) {} + + //! Constructor that parses a string or URI fragment representation. + /*! + \param source A null-terminated, string or URI fragment representation of JSON pointer. + \param allocator User supplied allocator for this pointer. If no allocator is provided, it creates a self-owned one. + */ + explicit GenericPointer(const Ch* source, Allocator* allocator = 0) : allocator_(allocator), ownAllocator_(), nameBuffer_(), tokens_(), tokenCount_(), parseErrorOffset_(), parseErrorCode_(kPointerParseErrorNone) { + Parse(source, internal::StrLen(source)); + } + +#if RAPIDJSON_HAS_STDSTRING + //! Constructor that parses a string or URI fragment representation. + /*! + \param source A string or URI fragment representation of JSON pointer. + \param allocator User supplied allocator for this pointer. If no allocator is provided, it creates a self-owned one. + \note Requires the definition of the preprocessor symbol \ref RAPIDJSON_HAS_STDSTRING. + */ + explicit GenericPointer(const std::basic_string& source, Allocator* allocator = 0) : allocator_(allocator), ownAllocator_(), nameBuffer_(), tokens_(), tokenCount_(), parseErrorOffset_(), parseErrorCode_(kPointerParseErrorNone) { + Parse(source.c_str(), source.size()); + } +#endif + + //! Constructor that parses a string or URI fragment representation, with length of the source string. + /*! + \param source A string or URI fragment representation of JSON pointer. + \param length Length of source. + \param allocator User supplied allocator for this pointer. If no allocator is provided, it creates a self-owned one. + \note Slightly faster than the overload without length. + */ + GenericPointer(const Ch* source, size_t length, Allocator* allocator = 0) : allocator_(allocator), ownAllocator_(), nameBuffer_(), tokens_(), tokenCount_(), parseErrorOffset_(), parseErrorCode_(kPointerParseErrorNone) { + Parse(source, length); + } + + //! Constructor with user-supplied tokens. + /*! + This constructor let user supplies const array of tokens. + This prevents the parsing process and eliminates allocation. + This is preferred for memory constrained environments. + + \param tokens An constant array of tokens representing the JSON pointer. + \param tokenCount Number of tokens. + + \b Example + \code + #define NAME(s) { s, sizeof(s) / sizeof(s[0]) - 1, kPointerInvalidIndex } + #define INDEX(i) { #i, sizeof(#i) - 1, i } + + static const Pointer::Token kTokens[] = { NAME("foo"), INDEX(123) }; + static const Pointer p(kTokens, sizeof(kTokens) / sizeof(kTokens[0])); + // Equivalent to static const Pointer p("/foo/123"); + + #undef NAME + #undef INDEX + \endcode + */ + GenericPointer(const Token* tokens, size_t tokenCount) : allocator_(), ownAllocator_(), nameBuffer_(), tokens_(const_cast(tokens)), tokenCount_(tokenCount), parseErrorOffset_(), parseErrorCode_(kPointerParseErrorNone) {} + + //! Copy constructor. + GenericPointer(const GenericPointer& rhs) : allocator_(), ownAllocator_(), nameBuffer_(), tokens_(), tokenCount_(), parseErrorOffset_(), parseErrorCode_(kPointerParseErrorNone) { + *this = rhs; + } + + //! Copy constructor. + GenericPointer(const GenericPointer& rhs, Allocator* allocator) : allocator_(allocator), ownAllocator_(), nameBuffer_(), tokens_(), tokenCount_(), parseErrorOffset_(), parseErrorCode_(kPointerParseErrorNone) { + *this = rhs; + } + + //! Destructor. + ~GenericPointer() { + if (nameBuffer_) // If user-supplied tokens constructor is used, nameBuffer_ is nullptr and tokens_ are not deallocated. + Allocator::Free(tokens_); + RAPIDJSON_DELETE(ownAllocator_); + } + + //! Assignment operator. + GenericPointer& operator=(const GenericPointer& rhs) { + if (this != &rhs) { + // Do not delete ownAllcator + if (nameBuffer_) + Allocator::Free(tokens_); + + tokenCount_ = rhs.tokenCount_; + parseErrorOffset_ = rhs.parseErrorOffset_; + parseErrorCode_ = rhs.parseErrorCode_; + + if (rhs.nameBuffer_) + CopyFromRaw(rhs); // Normally parsed tokens. + else { + tokens_ = rhs.tokens_; // User supplied const tokens. + nameBuffer_ = 0; + } + } + return *this; + } + + //! Swap the content of this pointer with an other. + /*! + \param other The pointer to swap with. + \note Constant complexity. + */ + GenericPointer& Swap(GenericPointer& other) RAPIDJSON_NOEXCEPT { + internal::Swap(allocator_, other.allocator_); + internal::Swap(ownAllocator_, other.ownAllocator_); + internal::Swap(nameBuffer_, other.nameBuffer_); + internal::Swap(tokens_, other.tokens_); + internal::Swap(tokenCount_, other.tokenCount_); + internal::Swap(parseErrorOffset_, other.parseErrorOffset_); + internal::Swap(parseErrorCode_, other.parseErrorCode_); + return *this; + } + + //! free-standing swap function helper + /*! + Helper function to enable support for common swap implementation pattern based on \c std::swap: + \code + void swap(MyClass& a, MyClass& b) { + using std::swap; + swap(a.pointer, b.pointer); + // ... + } + \endcode + \see Swap() + */ + friend inline void swap(GenericPointer& a, GenericPointer& b) RAPIDJSON_NOEXCEPT { a.Swap(b); } + + //@} + + //!@name Append token + //@{ + + //! Append a token and return a new Pointer + /*! + \param token Token to be appended. + \param allocator Allocator for the newly return Pointer. + \return A new Pointer with appended token. + */ + GenericPointer Append(const Token& token, Allocator* allocator = 0) const { + GenericPointer r; + r.allocator_ = allocator; + Ch *p = r.CopyFromRaw(*this, 1, token.length + 1); + std::memcpy(p, token.name, (token.length + 1) * sizeof(Ch)); + r.tokens_[tokenCount_].name = p; + r.tokens_[tokenCount_].length = token.length; + r.tokens_[tokenCount_].index = token.index; + return r; + } + + //! Append a name token with length, and return a new Pointer + /*! + \param name Name to be appended. + \param length Length of name. + \param allocator Allocator for the newly return Pointer. + \return A new Pointer with appended token. + */ + GenericPointer Append(const Ch* name, SizeType length, Allocator* allocator = 0) const { + Token token = { name, length, kPointerInvalidIndex }; + return Append(token, allocator); + } + + //! Append a name token without length, and return a new Pointer + /*! + \param name Name (const Ch*) to be appended. + \param allocator Allocator for the newly return Pointer. + \return A new Pointer with appended token. + */ + template + RAPIDJSON_DISABLEIF_RETURN((internal::NotExpr::Type, Ch> >), (GenericPointer)) + Append(T* name, Allocator* allocator = 0) const { + return Append(name, internal::StrLen(name), allocator); + } + +#if RAPIDJSON_HAS_STDSTRING + //! Append a name token, and return a new Pointer + /*! + \param name Name to be appended. + \param allocator Allocator for the newly return Pointer. + \return A new Pointer with appended token. + */ + GenericPointer Append(const std::basic_string& name, Allocator* allocator = 0) const { + return Append(name.c_str(), static_cast(name.size()), allocator); + } +#endif + + //! Append a index token, and return a new Pointer + /*! + \param index Index to be appended. + \param allocator Allocator for the newly return Pointer. + \return A new Pointer with appended token. + */ + GenericPointer Append(SizeType index, Allocator* allocator = 0) const { + char buffer[21]; + char* end = sizeof(SizeType) == 4 ? internal::u32toa(index, buffer) : internal::u64toa(index, buffer); + SizeType length = static_cast(end - buffer); + buffer[length] = '\0'; + + if (sizeof(Ch) == 1) { + Token token = { reinterpret_cast(buffer), length, index }; + return Append(token, allocator); + } + else { + Ch name[21]; + for (size_t i = 0; i <= length; i++) + name[i] = static_cast(buffer[i]); + Token token = { name, length, index }; + return Append(token, allocator); + } + } + + //! Append a token by value, and return a new Pointer + /*! + \param token token to be appended. + \param allocator Allocator for the newly return Pointer. + \return A new Pointer with appended token. + */ + GenericPointer Append(const ValueType& token, Allocator* allocator = 0) const { + if (token.IsString()) + return Append(token.GetString(), token.GetStringLength(), allocator); + else { + RAPIDJSON_ASSERT(token.IsUint64()); + RAPIDJSON_ASSERT(token.GetUint64() <= SizeType(~0)); + return Append(static_cast(token.GetUint64()), allocator); + } + } + + //!@name Handling Parse Error + //@{ + + //! Check whether this is a valid pointer. + bool IsValid() const { return parseErrorCode_ == kPointerParseErrorNone; } + + //! Get the parsing error offset in code unit. + size_t GetParseErrorOffset() const { return parseErrorOffset_; } + + //! Get the parsing error code. + PointerParseErrorCode GetParseErrorCode() const { return parseErrorCode_; } + + //@} + + //! Get the allocator of this pointer. + Allocator& GetAllocator() { return *allocator_; } + + //!@name Tokens + //@{ + + //! Get the token array (const version only). + const Token* GetTokens() const { return tokens_; } + + //! Get the number of tokens. + size_t GetTokenCount() const { return tokenCount_; } + + //@} + + //!@name Equality/inequality operators + //@{ + + //! Equality operator. + /*! + \note When any pointers are invalid, always returns false. + */ + bool operator==(const GenericPointer& rhs) const { + if (!IsValid() || !rhs.IsValid() || tokenCount_ != rhs.tokenCount_) + return false; + + for (size_t i = 0; i < tokenCount_; i++) { + if (tokens_[i].index != rhs.tokens_[i].index || + tokens_[i].length != rhs.tokens_[i].length || + (tokens_[i].length != 0 && std::memcmp(tokens_[i].name, rhs.tokens_[i].name, sizeof(Ch)* tokens_[i].length) != 0)) + { + return false; + } + } + + return true; + } + + //! Inequality operator. + /*! + \note When any pointers are invalid, always returns true. + */ + bool operator!=(const GenericPointer& rhs) const { return !(*this == rhs); } + + //! Less than operator. + /*! + \note Invalid pointers are always greater than valid ones. + */ + bool operator<(const GenericPointer& rhs) const { + if (!IsValid()) + return false; + if (!rhs.IsValid()) + return true; + + if (tokenCount_ != rhs.tokenCount_) + return tokenCount_ < rhs.tokenCount_; + + for (size_t i = 0; i < tokenCount_; i++) { + if (tokens_[i].index != rhs.tokens_[i].index) + return tokens_[i].index < rhs.tokens_[i].index; + + if (tokens_[i].length != rhs.tokens_[i].length) + return tokens_[i].length < rhs.tokens_[i].length; + + if (int cmp = std::memcmp(tokens_[i].name, rhs.tokens_[i].name, sizeof(Ch) * tokens_[i].length)) + return cmp < 0; + } + + return false; + } + + //@} + + //!@name Stringify + //@{ + + //! Stringify the pointer into string representation. + /*! + \tparam OutputStream Type of output stream. + \param os The output stream. + */ + template + bool Stringify(OutputStream& os) const { + return Stringify(os); + } + + //! Stringify the pointer into URI fragment representation. + /*! + \tparam OutputStream Type of output stream. + \param os The output stream. + */ + template + bool StringifyUriFragment(OutputStream& os) const { + return Stringify(os); + } + + //@} + + //!@name Create value + //@{ + + //! Create a value in a subtree. + /*! + If the value is not exist, it creates all parent values and a JSON Null value. + So it always succeed and return the newly created or existing value. + + Remind that it may change types of parents according to tokens, so it + potentially removes previously stored values. For example, if a document + was an array, and "/foo" is used to create a value, then the document + will be changed to an object, and all existing array elements are lost. + + \param root Root value of a DOM subtree to be resolved. It can be any value other than document root. + \param allocator Allocator for creating the values if the specified value or its parents are not exist. + \param alreadyExist If non-null, it stores whether the resolved value is already exist. + \return The resolved newly created (a JSON Null value), or already exists value. + */ + ValueType& Create(ValueType& root, typename ValueType::AllocatorType& allocator, bool* alreadyExist = 0) const { + RAPIDJSON_ASSERT(IsValid()); + ValueType* v = &root; + bool exist = true; + for (const Token *t = tokens_; t != tokens_ + tokenCount_; ++t) { + if (v->IsArray() && t->name[0] == '-' && t->length == 1) { + v->PushBack(ValueType().Move(), allocator); + v = &((*v)[v->Size() - 1]); + exist = false; + } + else { + if (t->index == kPointerInvalidIndex) { // must be object name + if (!v->IsObject()) + v->SetObject(); // Change to Object + } + else { // object name or array index + if (!v->IsArray() && !v->IsObject()) + v->SetArray(); // Change to Array + } + + if (v->IsArray()) { + if (t->index >= v->Size()) { + v->Reserve(t->index + 1, allocator); + while (t->index >= v->Size()) + v->PushBack(ValueType().Move(), allocator); + exist = false; + } + v = &((*v)[t->index]); + } + else { + typename ValueType::MemberIterator m = v->FindMember(GenericValue(GenericStringRef(t->name, t->length))); + if (m == v->MemberEnd()) { + v->AddMember(ValueType(t->name, t->length, allocator).Move(), ValueType().Move(), allocator); + m = v->MemberEnd(); + v = &(--m)->value; // Assumes AddMember() appends at the end + exist = false; + } + else + v = &m->value; + } + } + } + + if (alreadyExist) + *alreadyExist = exist; + + return *v; + } + + //! Creates a value in a document. + /*! + \param document A document to be resolved. + \param alreadyExist If non-null, it stores whether the resolved value is already exist. + \return The resolved newly created, or already exists value. + */ + template + ValueType& Create(GenericDocument& document, bool* alreadyExist = 0) const { + return Create(document, document.GetAllocator(), alreadyExist); + } + + //@} + + //!@name Compute URI + //@{ + + //! Compute the in-scope URI for a subtree. + // For use with JSON pointers into JSON schema documents. + /*! + \param root Root value of a DOM sub-tree to be resolved. It can be any value other than document root. + \param rootUri Root URI + \param unresolvedTokenIndex If the pointer cannot resolve a token in the pointer, this parameter can obtain the index of unresolved token. + \param allocator Allocator for Uris + \return Uri if it can be resolved. Otherwise null. + + \note + There are only 3 situations when a URI cannot be resolved: + 1. A value in the path is not an array nor object. + 2. An object value does not contain the token. + 3. A token is out of range of an array value. + + Use unresolvedTokenIndex to retrieve the token index. + */ + UriType GetUri(ValueType& root, const UriType& rootUri, size_t* unresolvedTokenIndex = 0, Allocator* allocator = 0) const { + static const Ch kIdString[] = { 'i', 'd', '\0' }; + static const ValueType kIdValue(kIdString, 2); + UriType base = UriType(rootUri, allocator); + RAPIDJSON_ASSERT(IsValid()); + ValueType* v = &root; + for (const Token *t = tokens_; t != tokens_ + tokenCount_; ++t) { + switch (v->GetType()) { + case kObjectType: + { + // See if we have an id, and if so resolve with the current base + typename ValueType::MemberIterator m = v->FindMember(kIdValue); + if (m != v->MemberEnd() && (m->value).IsString()) { + UriType here = UriType(m->value, allocator).Resolve(base, allocator); + base = here; + } + m = v->FindMember(GenericValue(GenericStringRef(t->name, t->length))); + if (m == v->MemberEnd()) + break; + v = &m->value; + } + continue; + case kArrayType: + if (t->index == kPointerInvalidIndex || t->index >= v->Size()) + break; + v = &((*v)[t->index]); + continue; + default: + break; + } + + // Error: unresolved token + if (unresolvedTokenIndex) + *unresolvedTokenIndex = static_cast(t - tokens_); + return UriType(allocator); + } + return base; + } + + UriType GetUri(const ValueType& root, const UriType& rootUri, size_t* unresolvedTokenIndex = 0, Allocator* allocator = 0) const { + return GetUri(const_cast(root), rootUri, unresolvedTokenIndex, allocator); + } + + + //!@name Query value + //@{ + + //! Query a value in a subtree. + /*! + \param root Root value of a DOM sub-tree to be resolved. It can be any value other than document root. + \param unresolvedTokenIndex If the pointer cannot resolve a token in the pointer, this parameter can obtain the index of unresolved token. + \return Pointer to the value if it can be resolved. Otherwise null. + + \note + There are only 3 situations when a value cannot be resolved: + 1. A value in the path is not an array nor object. + 2. An object value does not contain the token. + 3. A token is out of range of an array value. + + Use unresolvedTokenIndex to retrieve the token index. + */ + ValueType* Get(ValueType& root, size_t* unresolvedTokenIndex = 0) const { + RAPIDJSON_ASSERT(IsValid()); + ValueType* v = &root; + for (const Token *t = tokens_; t != tokens_ + tokenCount_; ++t) { + switch (v->GetType()) { + case kObjectType: + { + typename ValueType::MemberIterator m = v->FindMember(GenericValue(GenericStringRef(t->name, t->length))); + if (m == v->MemberEnd()) + break; + v = &m->value; + } + continue; + case kArrayType: + if (t->index == kPointerInvalidIndex || t->index >= v->Size()) + break; + v = &((*v)[t->index]); + continue; + default: + break; + } + + // Error: unresolved token + if (unresolvedTokenIndex) + *unresolvedTokenIndex = static_cast(t - tokens_); + return 0; + } + return v; + } + + //! Query a const value in a const subtree. + /*! + \param root Root value of a DOM sub-tree to be resolved. It can be any value other than document root. + \return Pointer to the value if it can be resolved. Otherwise null. + */ + const ValueType* Get(const ValueType& root, size_t* unresolvedTokenIndex = 0) const { + return Get(const_cast(root), unresolvedTokenIndex); + } + + //@} + + //!@name Query a value with default + //@{ + + //! Query a value in a subtree with default value. + /*! + Similar to Get(), but if the specified value do not exists, it creates all parents and clone the default value. + So that this function always succeed. + + \param root Root value of a DOM sub-tree to be resolved. It can be any value other than document root. + \param defaultValue Default value to be cloned if the value was not exists. + \param allocator Allocator for creating the values if the specified value or its parents are not exist. + \see Create() + */ + ValueType& GetWithDefault(ValueType& root, const ValueType& defaultValue, typename ValueType::AllocatorType& allocator) const { + bool alreadyExist; + ValueType& v = Create(root, allocator, &alreadyExist); + return alreadyExist ? v : v.CopyFrom(defaultValue, allocator); + } + + //! Query a value in a subtree with default null-terminated string. + ValueType& GetWithDefault(ValueType& root, const Ch* defaultValue, typename ValueType::AllocatorType& allocator) const { + bool alreadyExist; + ValueType& v = Create(root, allocator, &alreadyExist); + return alreadyExist ? v : v.SetString(defaultValue, allocator); + } + +#if RAPIDJSON_HAS_STDSTRING + //! Query a value in a subtree with default std::basic_string. + ValueType& GetWithDefault(ValueType& root, const std::basic_string& defaultValue, typename ValueType::AllocatorType& allocator) const { + bool alreadyExist; + ValueType& v = Create(root, allocator, &alreadyExist); + return alreadyExist ? v : v.SetString(defaultValue, allocator); + } +#endif + + //! Query a value in a subtree with default primitive value. + /*! + \tparam T Either \ref Type, \c int, \c unsigned, \c int64_t, \c uint64_t, \c bool + */ + template + RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (ValueType&)) + GetWithDefault(ValueType& root, T defaultValue, typename ValueType::AllocatorType& allocator) const { + return GetWithDefault(root, ValueType(defaultValue).Move(), allocator); + } + + //! Query a value in a document with default value. + template + ValueType& GetWithDefault(GenericDocument& document, const ValueType& defaultValue) const { + return GetWithDefault(document, defaultValue, document.GetAllocator()); + } + + //! Query a value in a document with default null-terminated string. + template + ValueType& GetWithDefault(GenericDocument& document, const Ch* defaultValue) const { + return GetWithDefault(document, defaultValue, document.GetAllocator()); + } + +#if RAPIDJSON_HAS_STDSTRING + //! Query a value in a document with default std::basic_string. + template + ValueType& GetWithDefault(GenericDocument& document, const std::basic_string& defaultValue) const { + return GetWithDefault(document, defaultValue, document.GetAllocator()); + } +#endif + + //! Query a value in a document with default primitive value. + /*! + \tparam T Either \ref Type, \c int, \c unsigned, \c int64_t, \c uint64_t, \c bool + */ + template + RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (ValueType&)) + GetWithDefault(GenericDocument& document, T defaultValue) const { + return GetWithDefault(document, defaultValue, document.GetAllocator()); + } + + //@} + + //!@name Set a value + //@{ + + //! Set a value in a subtree, with move semantics. + /*! + It creates all parents if they are not exist or types are different to the tokens. + So this function always succeeds but potentially remove existing values. + + \param root Root value of a DOM sub-tree to be resolved. It can be any value other than document root. + \param value Value to be set. + \param allocator Allocator for creating the values if the specified value or its parents are not exist. + \see Create() + */ + ValueType& Set(ValueType& root, ValueType& value, typename ValueType::AllocatorType& allocator) const { + return Create(root, allocator) = value; + } + + //! Set a value in a subtree, with copy semantics. + ValueType& Set(ValueType& root, const ValueType& value, typename ValueType::AllocatorType& allocator) const { + return Create(root, allocator).CopyFrom(value, allocator); + } + + //! Set a null-terminated string in a subtree. + ValueType& Set(ValueType& root, const Ch* value, typename ValueType::AllocatorType& allocator) const { + return Create(root, allocator) = ValueType(value, allocator).Move(); + } + +#if RAPIDJSON_HAS_STDSTRING + //! Set a std::basic_string in a subtree. + ValueType& Set(ValueType& root, const std::basic_string& value, typename ValueType::AllocatorType& allocator) const { + return Create(root, allocator) = ValueType(value, allocator).Move(); + } +#endif + + //! Set a primitive value in a subtree. + /*! + \tparam T Either \ref Type, \c int, \c unsigned, \c int64_t, \c uint64_t, \c bool + */ + template + RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (ValueType&)) + Set(ValueType& root, T value, typename ValueType::AllocatorType& allocator) const { + return Create(root, allocator) = ValueType(value).Move(); + } + + //! Set a value in a document, with move semantics. + template + ValueType& Set(GenericDocument& document, ValueType& value) const { + return Create(document) = value; + } + + //! Set a value in a document, with copy semantics. + template + ValueType& Set(GenericDocument& document, const ValueType& value) const { + return Create(document).CopyFrom(value, document.GetAllocator()); + } + + //! Set a null-terminated string in a document. + template + ValueType& Set(GenericDocument& document, const Ch* value) const { + return Create(document) = ValueType(value, document.GetAllocator()).Move(); + } + +#if RAPIDJSON_HAS_STDSTRING + //! Sets a std::basic_string in a document. + template + ValueType& Set(GenericDocument& document, const std::basic_string& value) const { + return Create(document) = ValueType(value, document.GetAllocator()).Move(); + } +#endif + + //! Set a primitive value in a document. + /*! + \tparam T Either \ref Type, \c int, \c unsigned, \c int64_t, \c uint64_t, \c bool + */ + template + RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (ValueType&)) + Set(GenericDocument& document, T value) const { + return Create(document) = value; + } + + //@} + + //!@name Swap a value + //@{ + + //! Swap a value with a value in a subtree. + /*! + It creates all parents if they are not exist or types are different to the tokens. + So this function always succeeds but potentially remove existing values. + + \param root Root value of a DOM sub-tree to be resolved. It can be any value other than document root. + \param value Value to be swapped. + \param allocator Allocator for creating the values if the specified value or its parents are not exist. + \see Create() + */ + ValueType& Swap(ValueType& root, ValueType& value, typename ValueType::AllocatorType& allocator) const { + return Create(root, allocator).Swap(value); + } + + //! Swap a value with a value in a document. + template + ValueType& Swap(GenericDocument& document, ValueType& value) const { + return Create(document).Swap(value); + } + + //@} + + //! Erase a value in a subtree. + /*! + \param root Root value of a DOM sub-tree to be resolved. It can be any value other than document root. + \return Whether the resolved value is found and erased. + + \note Erasing with an empty pointer \c Pointer(""), i.e. the root, always fail and return false. + */ + bool Erase(ValueType& root) const { + RAPIDJSON_ASSERT(IsValid()); + if (tokenCount_ == 0) // Cannot erase the root + return false; + + ValueType* v = &root; + const Token* last = tokens_ + (tokenCount_ - 1); + for (const Token *t = tokens_; t != last; ++t) { + switch (v->GetType()) { + case kObjectType: + { + typename ValueType::MemberIterator m = v->FindMember(GenericValue(GenericStringRef(t->name, t->length))); + if (m == v->MemberEnd()) + return false; + v = &m->value; + } + break; + case kArrayType: + if (t->index == kPointerInvalidIndex || t->index >= v->Size()) + return false; + v = &((*v)[t->index]); + break; + default: + return false; + } + } + + switch (v->GetType()) { + case kObjectType: + return v->EraseMember(GenericStringRef(last->name, last->length)); + case kArrayType: + if (last->index == kPointerInvalidIndex || last->index >= v->Size()) + return false; + v->Erase(v->Begin() + last->index); + return true; + default: + return false; + } + } + +private: + //! Clone the content from rhs to this. + /*! + \param rhs Source pointer. + \param extraToken Extra tokens to be allocated. + \param extraNameBufferSize Extra name buffer size (in number of Ch) to be allocated. + \return Start of non-occupied name buffer, for storing extra names. + */ + Ch* CopyFromRaw(const GenericPointer& rhs, size_t extraToken = 0, size_t extraNameBufferSize = 0) { + if (!allocator_) // allocator is independently owned. + ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator)(); + + size_t nameBufferSize = rhs.tokenCount_; // null terminators for tokens + for (Token *t = rhs.tokens_; t != rhs.tokens_ + rhs.tokenCount_; ++t) + nameBufferSize += t->length; + + tokenCount_ = rhs.tokenCount_ + extraToken; + tokens_ = static_cast(allocator_->Malloc(tokenCount_ * sizeof(Token) + (nameBufferSize + extraNameBufferSize) * sizeof(Ch))); + nameBuffer_ = reinterpret_cast(tokens_ + tokenCount_); + if (rhs.tokenCount_ > 0) { + std::memcpy(tokens_, rhs.tokens_, rhs.tokenCount_ * sizeof(Token)); + } + if (nameBufferSize > 0) { + std::memcpy(nameBuffer_, rhs.nameBuffer_, nameBufferSize * sizeof(Ch)); + } + + // Adjust pointers to name buffer + std::ptrdiff_t diff = nameBuffer_ - rhs.nameBuffer_; + for (Token *t = tokens_; t != tokens_ + rhs.tokenCount_; ++t) + t->name += diff; + + return nameBuffer_ + nameBufferSize; + } + + //! Check whether a character should be percent-encoded. + /*! + According to RFC 3986 2.3 Unreserved Characters. + \param c The character (code unit) to be tested. + */ + bool NeedPercentEncode(Ch c) const { + return !((c >= '0' && c <= '9') || (c >= 'A' && c <='Z') || (c >= 'a' && c <= 'z') || c == '-' || c == '.' || c == '_' || c =='~'); + } + + //! Parse a JSON String or its URI fragment representation into tokens. +#ifndef __clang__ // -Wdocumentation + /*! + \param source Either a JSON Pointer string, or its URI fragment representation. Not need to be null terminated. + \param length Length of the source string. + \note Source cannot be JSON String Representation of JSON Pointer, e.g. In "/\u0000", \u0000 will not be unescaped. + */ +#endif + void Parse(const Ch* source, size_t length) { + RAPIDJSON_ASSERT(source != NULL); + RAPIDJSON_ASSERT(nameBuffer_ == 0); + RAPIDJSON_ASSERT(tokens_ == 0); + + // Create own allocator if user did not supply. + if (!allocator_) + ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator)(); + + // Count number of '/' as tokenCount + tokenCount_ = 0; + for (const Ch* s = source; s != source + length; s++) + if (*s == '/') + tokenCount_++; + + Token* token = tokens_ = static_cast(allocator_->Malloc(tokenCount_ * sizeof(Token) + length * sizeof(Ch))); + Ch* name = nameBuffer_ = reinterpret_cast(tokens_ + tokenCount_); + size_t i = 0; + + // Detect if it is a URI fragment + bool uriFragment = false; + if (source[i] == '#') { + uriFragment = true; + i++; + } + + if (i != length && source[i] != '/') { + parseErrorCode_ = kPointerParseErrorTokenMustBeginWithSolidus; + goto error; + } + + while (i < length) { + RAPIDJSON_ASSERT(source[i] == '/'); + i++; // consumes '/' + + token->name = name; + bool isNumber = true; + + while (i < length && source[i] != '/') { + Ch c = source[i]; + if (uriFragment) { + // Decoding percent-encoding for URI fragment + if (c == '%') { + PercentDecodeStream is(&source[i], source + length); + GenericInsituStringStream os(name); + Ch* begin = os.PutBegin(); + if (!Transcoder, EncodingType>().Validate(is, os) || !is.IsValid()) { + parseErrorCode_ = kPointerParseErrorInvalidPercentEncoding; + goto error; + } + size_t len = os.PutEnd(begin); + i += is.Tell() - 1; + if (len == 1) + c = *name; + else { + name += len; + isNumber = false; + i++; + continue; + } + } + else if (NeedPercentEncode(c)) { + parseErrorCode_ = kPointerParseErrorCharacterMustPercentEncode; + goto error; + } + } + + i++; + + // Escaping "~0" -> '~', "~1" -> '/' + if (c == '~') { + if (i < length) { + c = source[i]; + if (c == '0') c = '~'; + else if (c == '1') c = '/'; + else { + parseErrorCode_ = kPointerParseErrorInvalidEscape; + goto error; + } + i++; + } + else { + parseErrorCode_ = kPointerParseErrorInvalidEscape; + goto error; + } + } + + // First check for index: all of characters are digit + if (c < '0' || c > '9') + isNumber = false; + + *name++ = c; + } + token->length = static_cast(name - token->name); + if (token->length == 0) + isNumber = false; + *name++ = '\0'; // Null terminator + + // Second check for index: more than one digit cannot have leading zero + if (isNumber && token->length > 1 && token->name[0] == '0') + isNumber = false; + + // String to SizeType conversion + SizeType n = 0; + if (isNumber) { + for (size_t j = 0; j < token->length; j++) { + SizeType m = n * 10 + static_cast(token->name[j] - '0'); + if (m < n) { // overflow detection + isNumber = false; + break; + } + n = m; + } + } + + token->index = isNumber ? n : kPointerInvalidIndex; + token++; + } + + RAPIDJSON_ASSERT(name <= nameBuffer_ + length); // Should not overflow buffer + parseErrorCode_ = kPointerParseErrorNone; + return; + + error: + Allocator::Free(tokens_); + nameBuffer_ = 0; + tokens_ = 0; + tokenCount_ = 0; + parseErrorOffset_ = i; + return; + } + + //! Stringify to string or URI fragment representation. + /*! + \tparam uriFragment True for stringifying to URI fragment representation. False for string representation. + \tparam OutputStream type of output stream. + \param os The output stream. + */ + template + bool Stringify(OutputStream& os) const { + RAPIDJSON_ASSERT(IsValid()); + + if (uriFragment) + os.Put('#'); + + for (Token *t = tokens_; t != tokens_ + tokenCount_; ++t) { + os.Put('/'); + for (size_t j = 0; j < t->length; j++) { + Ch c = t->name[j]; + if (c == '~') { + os.Put('~'); + os.Put('0'); + } + else if (c == '/') { + os.Put('~'); + os.Put('1'); + } + else if (uriFragment && NeedPercentEncode(c)) { + // Transcode to UTF8 sequence + GenericStringStream source(&t->name[j]); + PercentEncodeStream target(os); + if (!Transcoder >().Validate(source, target)) + return false; + j += source.Tell() - 1; + } + else + os.Put(c); + } + } + return true; + } + + //! A helper stream for decoding a percent-encoded sequence into code unit. + /*! + This stream decodes %XY triplet into code unit (0-255). + If it encounters invalid characters, it sets output code unit as 0 and + mark invalid, and to be checked by IsValid(). + */ + class PercentDecodeStream { + public: + typedef typename ValueType::Ch Ch; + + //! Constructor + /*! + \param source Start of the stream + \param end Past-the-end of the stream. + */ + PercentDecodeStream(const Ch* source, const Ch* end) : src_(source), head_(source), end_(end), valid_(true) {} + + Ch Take() { + if (*src_ != '%' || src_ + 3 > end_) { // %XY triplet + valid_ = false; + return 0; + } + src_++; + Ch c = 0; + for (int j = 0; j < 2; j++) { + c = static_cast(c << 4); + Ch h = *src_; + if (h >= '0' && h <= '9') c = static_cast(c + h - '0'); + else if (h >= 'A' && h <= 'F') c = static_cast(c + h - 'A' + 10); + else if (h >= 'a' && h <= 'f') c = static_cast(c + h - 'a' + 10); + else { + valid_ = false; + return 0; + } + src_++; + } + return c; + } + + size_t Tell() const { return static_cast(src_ - head_); } + bool IsValid() const { return valid_; } + + private: + const Ch* src_; //!< Current read position. + const Ch* head_; //!< Original head of the string. + const Ch* end_; //!< Past-the-end position. + bool valid_; //!< Whether the parsing is valid. + }; + + //! A helper stream to encode character (UTF-8 code unit) into percent-encoded sequence. + template + class PercentEncodeStream { + public: + PercentEncodeStream(OutputStream& os) : os_(os) {} + void Put(char c) { // UTF-8 must be byte + unsigned char u = static_cast(c); + static const char hexDigits[16] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; + os_.Put('%'); + os_.Put(static_cast(hexDigits[u >> 4])); + os_.Put(static_cast(hexDigits[u & 15])); + } + private: + OutputStream& os_; + }; + + Allocator* allocator_; //!< The current allocator. It is either user-supplied or equal to ownAllocator_. + Allocator* ownAllocator_; //!< Allocator owned by this Pointer. + Ch* nameBuffer_; //!< A buffer containing all names in tokens. + Token* tokens_; //!< A list of tokens. + size_t tokenCount_; //!< Number of tokens in tokens_. + size_t parseErrorOffset_; //!< Offset in code unit when parsing fail. + PointerParseErrorCode parseErrorCode_; //!< Parsing error code. +}; + +//! GenericPointer for Value (UTF-8, default allocator). +typedef GenericPointer Pointer; + +//!@name Helper functions for GenericPointer +//@{ + +////////////////////////////////////////////////////////////////////////////// + +template +typename T::ValueType& CreateValueByPointer(T& root, const GenericPointer& pointer, typename T::AllocatorType& a) { + return pointer.Create(root, a); +} + +template +typename T::ValueType& CreateValueByPointer(T& root, const CharType(&source)[N], typename T::AllocatorType& a) { + return GenericPointer(source, N - 1).Create(root, a); +} + +// No allocator parameter + +template +typename DocumentType::ValueType& CreateValueByPointer(DocumentType& document, const GenericPointer& pointer) { + return pointer.Create(document); +} + +template +typename DocumentType::ValueType& CreateValueByPointer(DocumentType& document, const CharType(&source)[N]) { + return GenericPointer(source, N - 1).Create(document); +} + +////////////////////////////////////////////////////////////////////////////// + +template +typename T::ValueType* GetValueByPointer(T& root, const GenericPointer& pointer, size_t* unresolvedTokenIndex = 0) { + return pointer.Get(root, unresolvedTokenIndex); +} + +template +const typename T::ValueType* GetValueByPointer(const T& root, const GenericPointer& pointer, size_t* unresolvedTokenIndex = 0) { + return pointer.Get(root, unresolvedTokenIndex); +} + +template +typename T::ValueType* GetValueByPointer(T& root, const CharType (&source)[N], size_t* unresolvedTokenIndex = 0) { + return GenericPointer(source, N - 1).Get(root, unresolvedTokenIndex); +} + +template +const typename T::ValueType* GetValueByPointer(const T& root, const CharType(&source)[N], size_t* unresolvedTokenIndex = 0) { + return GenericPointer(source, N - 1).Get(root, unresolvedTokenIndex); +} + +////////////////////////////////////////////////////////////////////////////// + +template +typename T::ValueType& GetValueByPointerWithDefault(T& root, const GenericPointer& pointer, const typename T::ValueType& defaultValue, typename T::AllocatorType& a) { + return pointer.GetWithDefault(root, defaultValue, a); +} + +template +typename T::ValueType& GetValueByPointerWithDefault(T& root, const GenericPointer& pointer, const typename T::Ch* defaultValue, typename T::AllocatorType& a) { + return pointer.GetWithDefault(root, defaultValue, a); +} + +#if RAPIDJSON_HAS_STDSTRING +template +typename T::ValueType& GetValueByPointerWithDefault(T& root, const GenericPointer& pointer, const std::basic_string& defaultValue, typename T::AllocatorType& a) { + return pointer.GetWithDefault(root, defaultValue, a); +} +#endif + +template +RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (typename T::ValueType&)) +GetValueByPointerWithDefault(T& root, const GenericPointer& pointer, T2 defaultValue, typename T::AllocatorType& a) { + return pointer.GetWithDefault(root, defaultValue, a); +} + +template +typename T::ValueType& GetValueByPointerWithDefault(T& root, const CharType(&source)[N], const typename T::ValueType& defaultValue, typename T::AllocatorType& a) { + return GenericPointer(source, N - 1).GetWithDefault(root, defaultValue, a); +} + +template +typename T::ValueType& GetValueByPointerWithDefault(T& root, const CharType(&source)[N], const typename T::Ch* defaultValue, typename T::AllocatorType& a) { + return GenericPointer(source, N - 1).GetWithDefault(root, defaultValue, a); +} + +#if RAPIDJSON_HAS_STDSTRING +template +typename T::ValueType& GetValueByPointerWithDefault(T& root, const CharType(&source)[N], const std::basic_string& defaultValue, typename T::AllocatorType& a) { + return GenericPointer(source, N - 1).GetWithDefault(root, defaultValue, a); +} +#endif + +template +RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (typename T::ValueType&)) +GetValueByPointerWithDefault(T& root, const CharType(&source)[N], T2 defaultValue, typename T::AllocatorType& a) { + return GenericPointer(source, N - 1).GetWithDefault(root, defaultValue, a); +} + +// No allocator parameter + +template +typename DocumentType::ValueType& GetValueByPointerWithDefault(DocumentType& document, const GenericPointer& pointer, const typename DocumentType::ValueType& defaultValue) { + return pointer.GetWithDefault(document, defaultValue); +} + +template +typename DocumentType::ValueType& GetValueByPointerWithDefault(DocumentType& document, const GenericPointer& pointer, const typename DocumentType::Ch* defaultValue) { + return pointer.GetWithDefault(document, defaultValue); +} + +#if RAPIDJSON_HAS_STDSTRING +template +typename DocumentType::ValueType& GetValueByPointerWithDefault(DocumentType& document, const GenericPointer& pointer, const std::basic_string& defaultValue) { + return pointer.GetWithDefault(document, defaultValue); +} +#endif + +template +RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (typename DocumentType::ValueType&)) +GetValueByPointerWithDefault(DocumentType& document, const GenericPointer& pointer, T2 defaultValue) { + return pointer.GetWithDefault(document, defaultValue); +} + +template +typename DocumentType::ValueType& GetValueByPointerWithDefault(DocumentType& document, const CharType(&source)[N], const typename DocumentType::ValueType& defaultValue) { + return GenericPointer(source, N - 1).GetWithDefault(document, defaultValue); +} + +template +typename DocumentType::ValueType& GetValueByPointerWithDefault(DocumentType& document, const CharType(&source)[N], const typename DocumentType::Ch* defaultValue) { + return GenericPointer(source, N - 1).GetWithDefault(document, defaultValue); +} + +#if RAPIDJSON_HAS_STDSTRING +template +typename DocumentType::ValueType& GetValueByPointerWithDefault(DocumentType& document, const CharType(&source)[N], const std::basic_string& defaultValue) { + return GenericPointer(source, N - 1).GetWithDefault(document, defaultValue); +} +#endif + +template +RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (typename DocumentType::ValueType&)) +GetValueByPointerWithDefault(DocumentType& document, const CharType(&source)[N], T2 defaultValue) { + return GenericPointer(source, N - 1).GetWithDefault(document, defaultValue); +} + +////////////////////////////////////////////////////////////////////////////// + +template +typename T::ValueType& SetValueByPointer(T& root, const GenericPointer& pointer, typename T::ValueType& value, typename T::AllocatorType& a) { + return pointer.Set(root, value, a); +} + +template +typename T::ValueType& SetValueByPointer(T& root, const GenericPointer& pointer, const typename T::ValueType& value, typename T::AllocatorType& a) { + return pointer.Set(root, value, a); +} + +template +typename T::ValueType& SetValueByPointer(T& root, const GenericPointer& pointer, const typename T::Ch* value, typename T::AllocatorType& a) { + return pointer.Set(root, value, a); +} + +#if RAPIDJSON_HAS_STDSTRING +template +typename T::ValueType& SetValueByPointer(T& root, const GenericPointer& pointer, const std::basic_string& value, typename T::AllocatorType& a) { + return pointer.Set(root, value, a); +} +#endif + +template +RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (typename T::ValueType&)) +SetValueByPointer(T& root, const GenericPointer& pointer, T2 value, typename T::AllocatorType& a) { + return pointer.Set(root, value, a); +} + +template +typename T::ValueType& SetValueByPointer(T& root, const CharType(&source)[N], typename T::ValueType& value, typename T::AllocatorType& a) { + return GenericPointer(source, N - 1).Set(root, value, a); +} + +template +typename T::ValueType& SetValueByPointer(T& root, const CharType(&source)[N], const typename T::ValueType& value, typename T::AllocatorType& a) { + return GenericPointer(source, N - 1).Set(root, value, a); +} + +template +typename T::ValueType& SetValueByPointer(T& root, const CharType(&source)[N], const typename T::Ch* value, typename T::AllocatorType& a) { + return GenericPointer(source, N - 1).Set(root, value, a); +} + +#if RAPIDJSON_HAS_STDSTRING +template +typename T::ValueType& SetValueByPointer(T& root, const CharType(&source)[N], const std::basic_string& value, typename T::AllocatorType& a) { + return GenericPointer(source, N - 1).Set(root, value, a); +} +#endif + +template +RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (typename T::ValueType&)) +SetValueByPointer(T& root, const CharType(&source)[N], T2 value, typename T::AllocatorType& a) { + return GenericPointer(source, N - 1).Set(root, value, a); +} + +// No allocator parameter + +template +typename DocumentType::ValueType& SetValueByPointer(DocumentType& document, const GenericPointer& pointer, typename DocumentType::ValueType& value) { + return pointer.Set(document, value); +} + +template +typename DocumentType::ValueType& SetValueByPointer(DocumentType& document, const GenericPointer& pointer, const typename DocumentType::ValueType& value) { + return pointer.Set(document, value); +} + +template +typename DocumentType::ValueType& SetValueByPointer(DocumentType& document, const GenericPointer& pointer, const typename DocumentType::Ch* value) { + return pointer.Set(document, value); +} + +#if RAPIDJSON_HAS_STDSTRING +template +typename DocumentType::ValueType& SetValueByPointer(DocumentType& document, const GenericPointer& pointer, const std::basic_string& value) { + return pointer.Set(document, value); +} +#endif + +template +RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (typename DocumentType::ValueType&)) +SetValueByPointer(DocumentType& document, const GenericPointer& pointer, T2 value) { + return pointer.Set(document, value); +} + +template +typename DocumentType::ValueType& SetValueByPointer(DocumentType& document, const CharType(&source)[N], typename DocumentType::ValueType& value) { + return GenericPointer(source, N - 1).Set(document, value); +} + +template +typename DocumentType::ValueType& SetValueByPointer(DocumentType& document, const CharType(&source)[N], const typename DocumentType::ValueType& value) { + return GenericPointer(source, N - 1).Set(document, value); +} + +template +typename DocumentType::ValueType& SetValueByPointer(DocumentType& document, const CharType(&source)[N], const typename DocumentType::Ch* value) { + return GenericPointer(source, N - 1).Set(document, value); +} + +#if RAPIDJSON_HAS_STDSTRING +template +typename DocumentType::ValueType& SetValueByPointer(DocumentType& document, const CharType(&source)[N], const std::basic_string& value) { + return GenericPointer(source, N - 1).Set(document, value); +} +#endif + +template +RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (typename DocumentType::ValueType&)) +SetValueByPointer(DocumentType& document, const CharType(&source)[N], T2 value) { + return GenericPointer(source, N - 1).Set(document, value); +} + +////////////////////////////////////////////////////////////////////////////// + +template +typename T::ValueType& SwapValueByPointer(T& root, const GenericPointer& pointer, typename T::ValueType& value, typename T::AllocatorType& a) { + return pointer.Swap(root, value, a); +} + +template +typename T::ValueType& SwapValueByPointer(T& root, const CharType(&source)[N], typename T::ValueType& value, typename T::AllocatorType& a) { + return GenericPointer(source, N - 1).Swap(root, value, a); +} + +template +typename DocumentType::ValueType& SwapValueByPointer(DocumentType& document, const GenericPointer& pointer, typename DocumentType::ValueType& value) { + return pointer.Swap(document, value); +} + +template +typename DocumentType::ValueType& SwapValueByPointer(DocumentType& document, const CharType(&source)[N], typename DocumentType::ValueType& value) { + return GenericPointer(source, N - 1).Swap(document, value); +} + +////////////////////////////////////////////////////////////////////////////// + +template +bool EraseValueByPointer(T& root, const GenericPointer& pointer) { + return pointer.Erase(root); +} + +template +bool EraseValueByPointer(T& root, const CharType(&source)[N]) { + return GenericPointer(source, N - 1).Erase(root); +} + +//@} + +RAPIDJSON_NAMESPACE_END + +#if defined(__clang__) || defined(_MSC_VER) +RAPIDJSON_DIAG_POP +#endif + +#endif // RAPIDJSON_POINTER_H_ diff --git a/include/rapidjson/prettywriter.h b/include/rapidjson/prettywriter.h new file mode 100644 index 0000000..fe45df1 --- /dev/null +++ b/include/rapidjson/prettywriter.h @@ -0,0 +1,277 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// 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 RAPIDJSON_PRETTYWRITER_H_ +#define RAPIDJSON_PRETTYWRITER_H_ + +#include "writer.h" + +#ifdef __GNUC__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(effc++) +#endif + +#if defined(__clang__) +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(c++98-compat) +#endif + +RAPIDJSON_NAMESPACE_BEGIN + +//! Combination of PrettyWriter format flags. +/*! \see PrettyWriter::SetFormatOptions + */ +enum PrettyFormatOptions { + kFormatDefault = 0, //!< Default pretty formatting. + kFormatSingleLineArray = 1 //!< Format arrays on a single line. +}; + +//! Writer with indentation and spacing. +/*! + \tparam OutputStream Type of output os. + \tparam SourceEncoding Encoding of source string. + \tparam TargetEncoding Encoding of output stream. + \tparam StackAllocator Type of allocator for allocating memory of stack. +*/ +template, typename TargetEncoding = UTF8<>, typename StackAllocator = CrtAllocator, unsigned writeFlags = kWriteDefaultFlags> +class PrettyWriter : public Writer { +public: + typedef Writer Base; + typedef typename Base::Ch Ch; + + //! Constructor + /*! \param os Output stream. + \param allocator User supplied allocator. If it is null, it will create a private one. + \param levelDepth Initial capacity of stack. + */ + explicit PrettyWriter(OutputStream& os, StackAllocator* allocator = 0, size_t levelDepth = Base::kDefaultLevelDepth) : + Base(os, allocator, levelDepth), indentChar_(' '), indentCharCount_(4), formatOptions_(kFormatDefault) {} + + + explicit PrettyWriter(StackAllocator* allocator = 0, size_t levelDepth = Base::kDefaultLevelDepth) : + Base(allocator, levelDepth), indentChar_(' '), indentCharCount_(4), formatOptions_(kFormatDefault) {} + +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS + PrettyWriter(PrettyWriter&& rhs) : + Base(std::forward(rhs)), indentChar_(rhs.indentChar_), indentCharCount_(rhs.indentCharCount_), formatOptions_(rhs.formatOptions_) {} +#endif + + //! Set custom indentation. + /*! \param indentChar Character for indentation. Must be whitespace character (' ', '\\t', '\\n', '\\r'). + \param indentCharCount Number of indent characters for each indentation level. + \note The default indentation is 4 spaces. + */ + PrettyWriter& SetIndent(Ch indentChar, unsigned indentCharCount) { + RAPIDJSON_ASSERT(indentChar == ' ' || indentChar == '\t' || indentChar == '\n' || indentChar == '\r'); + indentChar_ = indentChar; + indentCharCount_ = indentCharCount; + return *this; + } + + //! Set pretty writer formatting options. + /*! \param options Formatting options. + */ + PrettyWriter& SetFormatOptions(PrettyFormatOptions options) { + formatOptions_ = options; + return *this; + } + + /*! @name Implementation of Handler + \see Handler + */ + //@{ + + bool Null() { PrettyPrefix(kNullType); return Base::EndValue(Base::WriteNull()); } + bool Bool(bool b) { PrettyPrefix(b ? kTrueType : kFalseType); return Base::EndValue(Base::WriteBool(b)); } + bool Int(int i) { PrettyPrefix(kNumberType); return Base::EndValue(Base::WriteInt(i)); } + bool Uint(unsigned u) { PrettyPrefix(kNumberType); return Base::EndValue(Base::WriteUint(u)); } + bool Int64(int64_t i64) { PrettyPrefix(kNumberType); return Base::EndValue(Base::WriteInt64(i64)); } + bool Uint64(uint64_t u64) { PrettyPrefix(kNumberType); return Base::EndValue(Base::WriteUint64(u64)); } + bool Double(double d) { PrettyPrefix(kNumberType); return Base::EndValue(Base::WriteDouble(d)); } + + bool RawNumber(const Ch* str, SizeType length, bool copy = false) { + RAPIDJSON_ASSERT(str != 0); + (void)copy; + PrettyPrefix(kNumberType); + return Base::EndValue(Base::WriteString(str, length)); + } + + bool String(const Ch* str, SizeType length, bool copy = false) { + RAPIDJSON_ASSERT(str != 0); + (void)copy; + PrettyPrefix(kStringType); + return Base::EndValue(Base::WriteString(str, length)); + } + +#if RAPIDJSON_HAS_STDSTRING + bool String(const std::basic_string& str) { + return String(str.data(), SizeType(str.size())); + } +#endif + + bool StartObject() { + PrettyPrefix(kObjectType); + new (Base::level_stack_.template Push()) typename Base::Level(false); + return Base::WriteStartObject(); + } + + bool Key(const Ch* str, SizeType length, bool copy = false) { return String(str, length, copy); } + +#if RAPIDJSON_HAS_STDSTRING + bool Key(const std::basic_string& str) { + return Key(str.data(), SizeType(str.size())); + } +#endif + + bool EndObject(SizeType memberCount = 0) { + (void)memberCount; + RAPIDJSON_ASSERT(Base::level_stack_.GetSize() >= sizeof(typename Base::Level)); // not inside an Object + RAPIDJSON_ASSERT(!Base::level_stack_.template Top()->inArray); // currently inside an Array, not Object + RAPIDJSON_ASSERT(0 == Base::level_stack_.template Top()->valueCount % 2); // Object has a Key without a Value + + bool empty = Base::level_stack_.template Pop(1)->valueCount == 0; + + if (!empty) { + Base::os_->Put('\n'); + WriteIndent(); + } + bool ret = Base::EndValue(Base::WriteEndObject()); + (void)ret; + RAPIDJSON_ASSERT(ret == true); + if (Base::level_stack_.Empty()) // end of json text + Base::Flush(); + return true; + } + + bool StartArray() { + PrettyPrefix(kArrayType); + new (Base::level_stack_.template Push()) typename Base::Level(true); + return Base::WriteStartArray(); + } + + bool EndArray(SizeType memberCount = 0) { + (void)memberCount; + RAPIDJSON_ASSERT(Base::level_stack_.GetSize() >= sizeof(typename Base::Level)); + RAPIDJSON_ASSERT(Base::level_stack_.template Top()->inArray); + bool empty = Base::level_stack_.template Pop(1)->valueCount == 0; + + if (!empty && !(formatOptions_ & kFormatSingleLineArray)) { + Base::os_->Put('\n'); + WriteIndent(); + } + bool ret = Base::EndValue(Base::WriteEndArray()); + (void)ret; + RAPIDJSON_ASSERT(ret == true); + if (Base::level_stack_.Empty()) // end of json text + Base::Flush(); + return true; + } + + //@} + + /*! @name Convenience extensions */ + //@{ + + //! Simpler but slower overload. + bool String(const Ch* str) { return String(str, internal::StrLen(str)); } + bool Key(const Ch* str) { return Key(str, internal::StrLen(str)); } + + //@} + + //! Write a raw JSON value. + /*! + For user to write a stringified JSON as a value. + + \param json A well-formed JSON value. It should not contain null character within [0, length - 1] range. + \param length Length of the json. + \param type Type of the root of json. + \note When using PrettyWriter::RawValue(), the result json may not be indented correctly. + */ + bool RawValue(const Ch* json, size_t length, Type type) { + RAPIDJSON_ASSERT(json != 0); + PrettyPrefix(type); + return Base::EndValue(Base::WriteRawValue(json, length)); + } + +protected: + void PrettyPrefix(Type type) { + (void)type; + if (Base::level_stack_.GetSize() != 0) { // this value is not at root + typename Base::Level* level = Base::level_stack_.template Top(); + + if (level->inArray) { + if (level->valueCount > 0) { + Base::os_->Put(','); // add comma if it is not the first element in array + if (formatOptions_ & kFormatSingleLineArray) + Base::os_->Put(' '); + } + + if (!(formatOptions_ & kFormatSingleLineArray)) { + Base::os_->Put('\n'); + WriteIndent(); + } + } + else { // in object + if (level->valueCount > 0) { + if (level->valueCount % 2 == 0) { + Base::os_->Put(','); + Base::os_->Put('\n'); + } + else { + Base::os_->Put(':'); + Base::os_->Put(' '); + } + } + else + Base::os_->Put('\n'); + + if (level->valueCount % 2 == 0) + WriteIndent(); + } + if (!level->inArray && level->valueCount % 2 == 0) + RAPIDJSON_ASSERT(type == kStringType); // if it's in object, then even number should be a name + level->valueCount++; + } + else { + RAPIDJSON_ASSERT(!Base::hasRoot_); // Should only has one and only one root. + Base::hasRoot_ = true; + } + } + + void WriteIndent() { + size_t count = (Base::level_stack_.GetSize() / sizeof(typename Base::Level)) * indentCharCount_; + PutN(*Base::os_, static_cast(indentChar_), count); + } + + Ch indentChar_; + unsigned indentCharCount_; + PrettyFormatOptions formatOptions_; + +private: + // Prohibit copy constructor & assignment operator. + PrettyWriter(const PrettyWriter&); + PrettyWriter& operator=(const PrettyWriter&); +}; + +RAPIDJSON_NAMESPACE_END + +#if defined(__clang__) +RAPIDJSON_DIAG_POP +#endif + +#ifdef __GNUC__ +RAPIDJSON_DIAG_POP +#endif + +#endif // RAPIDJSON_RAPIDJSON_H_ diff --git a/include/rapidjson/rapidjson.h b/include/rapidjson/rapidjson.h new file mode 100644 index 0000000..5ea6947 --- /dev/null +++ b/include/rapidjson/rapidjson.h @@ -0,0 +1,741 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// 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 RAPIDJSON_RAPIDJSON_H_ +#define RAPIDJSON_RAPIDJSON_H_ + +/*!\file rapidjson.h + \brief common definitions and configuration + + \see RAPIDJSON_CONFIG + */ + +/*! \defgroup RAPIDJSON_CONFIG RapidJSON configuration + \brief Configuration macros for library features + + Some RapidJSON features are configurable to adapt the library to a wide + variety of platforms, environments and usage scenarios. Most of the + features can be configured in terms of overridden or predefined + preprocessor macros at compile-time. + + Some additional customization is available in the \ref RAPIDJSON_ERRORS APIs. + + \note These macros should be given on the compiler command-line + (where applicable) to avoid inconsistent values when compiling + different translation units of a single application. + */ + +#include // malloc(), realloc(), free(), size_t +#include // memset(), memcpy(), memmove(), memcmp() + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_VERSION_STRING +// +// ALWAYS synchronize the following 3 macros with corresponding variables in /CMakeLists.txt. +// + +//!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN +// token stringification +#define RAPIDJSON_STRINGIFY(x) RAPIDJSON_DO_STRINGIFY(x) +#define RAPIDJSON_DO_STRINGIFY(x) #x + +// token concatenation +#define RAPIDJSON_JOIN(X, Y) RAPIDJSON_DO_JOIN(X, Y) +#define RAPIDJSON_DO_JOIN(X, Y) RAPIDJSON_DO_JOIN2(X, Y) +#define RAPIDJSON_DO_JOIN2(X, Y) X##Y +//!@endcond + +/*! \def RAPIDJSON_MAJOR_VERSION + \ingroup RAPIDJSON_CONFIG + \brief Major version of RapidJSON in integer. +*/ +/*! \def RAPIDJSON_MINOR_VERSION + \ingroup RAPIDJSON_CONFIG + \brief Minor version of RapidJSON in integer. +*/ +/*! \def RAPIDJSON_PATCH_VERSION + \ingroup RAPIDJSON_CONFIG + \brief Patch version of RapidJSON in integer. +*/ +/*! \def RAPIDJSON_VERSION_STRING + \ingroup RAPIDJSON_CONFIG + \brief Version of RapidJSON in ".." string format. +*/ +#define RAPIDJSON_MAJOR_VERSION 1 +#define RAPIDJSON_MINOR_VERSION 1 +#define RAPIDJSON_PATCH_VERSION 0 +#define RAPIDJSON_VERSION_STRING \ + RAPIDJSON_STRINGIFY(RAPIDJSON_MAJOR_VERSION.RAPIDJSON_MINOR_VERSION.RAPIDJSON_PATCH_VERSION) + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_NAMESPACE_(BEGIN|END) +/*! \def RAPIDJSON_NAMESPACE + \ingroup RAPIDJSON_CONFIG + \brief provide custom rapidjson namespace + + In order to avoid symbol clashes and/or "One Definition Rule" errors + between multiple inclusions of (different versions of) RapidJSON in + a single binary, users can customize the name of the main RapidJSON + namespace. + + In case of a single nesting level, defining \c RAPIDJSON_NAMESPACE + to a custom name (e.g. \c MyRapidJSON) is sufficient. If multiple + levels are needed, both \ref RAPIDJSON_NAMESPACE_BEGIN and \ref + RAPIDJSON_NAMESPACE_END need to be defined as well: + + \code + // in some .cpp file + #define RAPIDJSON_NAMESPACE my::rapidjson + #define RAPIDJSON_NAMESPACE_BEGIN namespace my { namespace rapidjson { + #define RAPIDJSON_NAMESPACE_END } } + #include "rapidjson/..." + \endcode + + \see rapidjson + */ +/*! \def RAPIDJSON_NAMESPACE_BEGIN + \ingroup RAPIDJSON_CONFIG + \brief provide custom rapidjson namespace (opening expression) + \see RAPIDJSON_NAMESPACE +*/ +/*! \def RAPIDJSON_NAMESPACE_END + \ingroup RAPIDJSON_CONFIG + \brief provide custom rapidjson namespace (closing expression) + \see RAPIDJSON_NAMESPACE +*/ +#ifndef RAPIDJSON_NAMESPACE +#define RAPIDJSON_NAMESPACE rapidjson +#endif +#ifndef RAPIDJSON_NAMESPACE_BEGIN +#define RAPIDJSON_NAMESPACE_BEGIN namespace RAPIDJSON_NAMESPACE { +#endif +#ifndef RAPIDJSON_NAMESPACE_END +#define RAPIDJSON_NAMESPACE_END } +#endif + +/////////////////////////////////////////////////////////////////////////////// +// __cplusplus macro + +//!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN + +#if defined(_MSC_VER) +#define RAPIDJSON_CPLUSPLUS _MSVC_LANG +#else +#define RAPIDJSON_CPLUSPLUS __cplusplus +#endif + +//!@endcond + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_HAS_STDSTRING + +#ifndef RAPIDJSON_HAS_STDSTRING +#ifdef RAPIDJSON_DOXYGEN_RUNNING +#define RAPIDJSON_HAS_STDSTRING 1 // force generation of documentation +#else +#define RAPIDJSON_HAS_STDSTRING 0 // no std::string support by default +#endif +/*! \def RAPIDJSON_HAS_STDSTRING + \ingroup RAPIDJSON_CONFIG + \brief Enable RapidJSON support for \c std::string + + By defining this preprocessor symbol to \c 1, several convenience functions for using + \ref rapidjson::GenericValue with \c std::string are enabled, especially + for construction and comparison. + + \hideinitializer +*/ +#endif // !defined(RAPIDJSON_HAS_STDSTRING) + +#if RAPIDJSON_HAS_STDSTRING +#include +#endif // RAPIDJSON_HAS_STDSTRING + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_USE_MEMBERSMAP + +/*! \def RAPIDJSON_USE_MEMBERSMAP + \ingroup RAPIDJSON_CONFIG + \brief Enable RapidJSON support for object members handling in a \c std::multimap + + By defining this preprocessor symbol to \c 1, \ref rapidjson::GenericValue object + members are stored in a \c std::multimap for faster lookup and deletion times, a + trade off with a slightly slower insertion time and a small object allocat(or)ed + memory overhead. + + \hideinitializer +*/ +#ifndef RAPIDJSON_USE_MEMBERSMAP +#define RAPIDJSON_USE_MEMBERSMAP 0 // not by default +#endif + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_NO_INT64DEFINE + +/*! \def RAPIDJSON_NO_INT64DEFINE + \ingroup RAPIDJSON_CONFIG + \brief Use external 64-bit integer types. + + RapidJSON requires the 64-bit integer types \c int64_t and \c uint64_t types + to be available at global scope. + + If users have their own definition, define RAPIDJSON_NO_INT64DEFINE to + prevent RapidJSON from defining its own types. +*/ +#ifndef RAPIDJSON_NO_INT64DEFINE +//!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN +#if defined(_MSC_VER) && (_MSC_VER < 1800) // Visual Studio 2013 +#include "msinttypes/stdint.h" +#include "msinttypes/inttypes.h" +#else +// Other compilers should have this. +#include +#include +#endif +//!@endcond +#ifdef RAPIDJSON_DOXYGEN_RUNNING +#define RAPIDJSON_NO_INT64DEFINE +#endif +#endif // RAPIDJSON_NO_INT64TYPEDEF + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_FORCEINLINE + +#ifndef RAPIDJSON_FORCEINLINE +//!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN +#if defined(_MSC_VER) && defined(NDEBUG) +#define RAPIDJSON_FORCEINLINE __forceinline +#elif defined(__GNUC__) && __GNUC__ >= 4 && defined(NDEBUG) +#define RAPIDJSON_FORCEINLINE __attribute__((always_inline)) +#else +#define RAPIDJSON_FORCEINLINE +#endif +//!@endcond +#endif // RAPIDJSON_FORCEINLINE + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_ENDIAN +#define RAPIDJSON_LITTLEENDIAN 0 //!< Little endian machine +#define RAPIDJSON_BIGENDIAN 1 //!< Big endian machine + +//! Endianness of the machine. +/*! + \def RAPIDJSON_ENDIAN + \ingroup RAPIDJSON_CONFIG + + GCC 4.6 provided macro for detecting endianness of the target machine. But other + compilers may not have this. User can define RAPIDJSON_ENDIAN to either + \ref RAPIDJSON_LITTLEENDIAN or \ref RAPIDJSON_BIGENDIAN. + + Default detection implemented with reference to + \li https://gcc.gnu.org/onlinedocs/gcc-4.6.0/cpp/Common-Predefined-Macros.html + \li http://www.boost.org/doc/libs/1_42_0/boost/detail/endian.hpp +*/ +#ifndef RAPIDJSON_ENDIAN +// Detect with GCC 4.6's macro +# ifdef __BYTE_ORDER__ +# if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ +# define RAPIDJSON_ENDIAN RAPIDJSON_LITTLEENDIAN +# elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ +# define RAPIDJSON_ENDIAN RAPIDJSON_BIGENDIAN +# else +# error Unknown machine endianness detected. User needs to define RAPIDJSON_ENDIAN. +# endif // __BYTE_ORDER__ +// Detect with GLIBC's endian.h +# elif defined(__GLIBC__) +# include +# if (__BYTE_ORDER == __LITTLE_ENDIAN) +# define RAPIDJSON_ENDIAN RAPIDJSON_LITTLEENDIAN +# elif (__BYTE_ORDER == __BIG_ENDIAN) +# define RAPIDJSON_ENDIAN RAPIDJSON_BIGENDIAN +# else +# error Unknown machine endianness detected. User needs to define RAPIDJSON_ENDIAN. +# endif // __GLIBC__ +// Detect with _LITTLE_ENDIAN and _BIG_ENDIAN macro +# elif defined(_LITTLE_ENDIAN) && !defined(_BIG_ENDIAN) +# define RAPIDJSON_ENDIAN RAPIDJSON_LITTLEENDIAN +# elif defined(_BIG_ENDIAN) && !defined(_LITTLE_ENDIAN) +# define RAPIDJSON_ENDIAN RAPIDJSON_BIGENDIAN +// Detect with architecture macros +# elif defined(__sparc) || defined(__sparc__) || defined(_POWER) || defined(__powerpc__) || defined(__ppc__) || defined(__hpux) || defined(__hppa) || defined(_MIPSEB) || defined(_POWER) || defined(__s390__) +# define RAPIDJSON_ENDIAN RAPIDJSON_BIGENDIAN +# elif defined(__i386__) || defined(__alpha__) || defined(__ia64) || defined(__ia64__) || defined(_M_IX86) || defined(_M_IA64) || defined(_M_ALPHA) || defined(__amd64) || defined(__amd64__) || defined(_M_AMD64) || defined(__x86_64) || defined(__x86_64__) || defined(_M_X64) || defined(__bfin__) +# define RAPIDJSON_ENDIAN RAPIDJSON_LITTLEENDIAN +# elif defined(_MSC_VER) && (defined(_M_ARM) || defined(_M_ARM64)) +# define RAPIDJSON_ENDIAN RAPIDJSON_LITTLEENDIAN +# elif defined(RAPIDJSON_DOXYGEN_RUNNING) +# define RAPIDJSON_ENDIAN +# else +# error Unknown machine endianness detected. User needs to define RAPIDJSON_ENDIAN. +# endif +#endif // RAPIDJSON_ENDIAN + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_64BIT + +//! Whether using 64-bit architecture +#ifndef RAPIDJSON_64BIT +#if defined(__LP64__) || (defined(__x86_64__) && defined(__ILP32__)) || defined(_WIN64) || defined(__EMSCRIPTEN__) +#define RAPIDJSON_64BIT 1 +#else +#define RAPIDJSON_64BIT 0 +#endif +#endif // RAPIDJSON_64BIT + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_ALIGN + +//! Data alignment of the machine. +/*! \ingroup RAPIDJSON_CONFIG + \param x pointer to align + + Some machines require strict data alignment. The default is 8 bytes. + User can customize by defining the RAPIDJSON_ALIGN function macro. +*/ +#ifndef RAPIDJSON_ALIGN +#define RAPIDJSON_ALIGN(x) (((x) + static_cast(7u)) & ~static_cast(7u)) +#endif + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_UINT64_C2 + +//! Construct a 64-bit literal by a pair of 32-bit integer. +/*! + 64-bit literal with or without ULL suffix is prone to compiler warnings. + UINT64_C() is C macro which cause compilation problems. + Use this macro to define 64-bit constants by a pair of 32-bit integer. +*/ +#ifndef RAPIDJSON_UINT64_C2 +#define RAPIDJSON_UINT64_C2(high32, low32) ((static_cast(high32) << 32) | static_cast(low32)) +#endif + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_48BITPOINTER_OPTIMIZATION + +//! Use only lower 48-bit address for some pointers. +/*! + \ingroup RAPIDJSON_CONFIG + + This optimization uses the fact that current X86-64 architecture only implement lower 48-bit virtual address. + The higher 16-bit can be used for storing other data. + \c GenericValue uses this optimization to reduce its size form 24 bytes to 16 bytes in 64-bit architecture. +*/ +#ifndef RAPIDJSON_48BITPOINTER_OPTIMIZATION +#if defined(__amd64__) || defined(__amd64) || defined(__x86_64__) || defined(__x86_64) || defined(_M_X64) || defined(_M_AMD64) +#define RAPIDJSON_48BITPOINTER_OPTIMIZATION 1 +#else +#define RAPIDJSON_48BITPOINTER_OPTIMIZATION 0 +#endif +#endif // RAPIDJSON_48BITPOINTER_OPTIMIZATION + +#if RAPIDJSON_48BITPOINTER_OPTIMIZATION == 1 +#if RAPIDJSON_64BIT != 1 +#error RAPIDJSON_48BITPOINTER_OPTIMIZATION can only be set to 1 when RAPIDJSON_64BIT=1 +#endif +#define RAPIDJSON_SETPOINTER(type, p, x) (p = reinterpret_cast((reinterpret_cast(p) & static_cast(RAPIDJSON_UINT64_C2(0xFFFF0000, 0x00000000))) | reinterpret_cast(reinterpret_cast(x)))) +#define RAPIDJSON_GETPOINTER(type, p) (reinterpret_cast(reinterpret_cast(p) & static_cast(RAPIDJSON_UINT64_C2(0x0000FFFF, 0xFFFFFFFF)))) +#else +#define RAPIDJSON_SETPOINTER(type, p, x) (p = (x)) +#define RAPIDJSON_GETPOINTER(type, p) (p) +#endif + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_SSE2/RAPIDJSON_SSE42/RAPIDJSON_NEON/RAPIDJSON_SIMD + +/*! \def RAPIDJSON_SIMD + \ingroup RAPIDJSON_CONFIG + \brief Enable SSE2/SSE4.2/Neon optimization. + + RapidJSON supports optimized implementations for some parsing operations + based on the SSE2, SSE4.2 or NEon SIMD extensions on modern Intel + or ARM compatible processors. + + To enable these optimizations, three different symbols can be defined; + \code + // Enable SSE2 optimization. + #define RAPIDJSON_SSE2 + + // Enable SSE4.2 optimization. + #define RAPIDJSON_SSE42 + \endcode + + // Enable ARM Neon optimization. + #define RAPIDJSON_NEON + \endcode + + \c RAPIDJSON_SSE42 takes precedence over SSE2, if both are defined. + + If any of these symbols is defined, RapidJSON defines the macro + \c RAPIDJSON_SIMD to indicate the availability of the optimized code. +*/ +#if defined(RAPIDJSON_SSE2) || defined(RAPIDJSON_SSE42) \ + || defined(RAPIDJSON_NEON) || defined(RAPIDJSON_DOXYGEN_RUNNING) +#define RAPIDJSON_SIMD +#endif + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_NO_SIZETYPEDEFINE + +#ifndef RAPIDJSON_NO_SIZETYPEDEFINE +/*! \def RAPIDJSON_NO_SIZETYPEDEFINE + \ingroup RAPIDJSON_CONFIG + \brief User-provided \c SizeType definition. + + In order to avoid using 32-bit size types for indexing strings and arrays, + define this preprocessor symbol and provide the type rapidjson::SizeType + before including RapidJSON: + \code + #define RAPIDJSON_NO_SIZETYPEDEFINE + namespace rapidjson { typedef ::std::size_t SizeType; } + #include "rapidjson/..." + \endcode + + \see rapidjson::SizeType +*/ +#ifdef RAPIDJSON_DOXYGEN_RUNNING +#define RAPIDJSON_NO_SIZETYPEDEFINE +#endif +RAPIDJSON_NAMESPACE_BEGIN +//! Size type (for string lengths, array sizes, etc.) +/*! RapidJSON uses 32-bit array/string indices even on 64-bit platforms, + instead of using \c size_t. Users may override the SizeType by defining + \ref RAPIDJSON_NO_SIZETYPEDEFINE. +*/ +typedef unsigned SizeType; +RAPIDJSON_NAMESPACE_END +#endif + +// always import std::size_t to rapidjson namespace +RAPIDJSON_NAMESPACE_BEGIN +using std::size_t; +RAPIDJSON_NAMESPACE_END + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_ASSERT + +//! Assertion. +/*! \ingroup RAPIDJSON_CONFIG + By default, rapidjson uses C \c assert() for internal assertions. + User can override it by defining RAPIDJSON_ASSERT(x) macro. + + \note Parsing errors are handled and can be customized by the + \ref RAPIDJSON_ERRORS APIs. +*/ +#ifndef RAPIDJSON_ASSERT +#include +#define RAPIDJSON_ASSERT(x) assert(x) +#endif // RAPIDJSON_ASSERT + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_STATIC_ASSERT + +// Prefer C++11 static_assert, if available +#ifndef RAPIDJSON_STATIC_ASSERT +#if RAPIDJSON_CPLUSPLUS >= 201103L || ( defined(_MSC_VER) && _MSC_VER >= 1800 ) +#define RAPIDJSON_STATIC_ASSERT(x) \ + static_assert(x, RAPIDJSON_STRINGIFY(x)) +#endif // C++11 +#endif // RAPIDJSON_STATIC_ASSERT + +// Adopt C++03 implementation from boost +#ifndef RAPIDJSON_STATIC_ASSERT +#ifndef __clang__ +//!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN +#endif +RAPIDJSON_NAMESPACE_BEGIN +template struct STATIC_ASSERTION_FAILURE; +template <> struct STATIC_ASSERTION_FAILURE { enum { value = 1 }; }; +template struct StaticAssertTest {}; +RAPIDJSON_NAMESPACE_END + +#if defined(__GNUC__) || defined(__clang__) +#define RAPIDJSON_STATIC_ASSERT_UNUSED_ATTRIBUTE __attribute__((unused)) +#else +#define RAPIDJSON_STATIC_ASSERT_UNUSED_ATTRIBUTE +#endif +#ifndef __clang__ +//!@endcond +#endif + +/*! \def RAPIDJSON_STATIC_ASSERT + \brief (Internal) macro to check for conditions at compile-time + \param x compile-time condition + \hideinitializer + */ +#define RAPIDJSON_STATIC_ASSERT(x) \ + typedef ::RAPIDJSON_NAMESPACE::StaticAssertTest< \ + sizeof(::RAPIDJSON_NAMESPACE::STATIC_ASSERTION_FAILURE)> \ + RAPIDJSON_JOIN(StaticAssertTypedef, __LINE__) RAPIDJSON_STATIC_ASSERT_UNUSED_ATTRIBUTE +#endif // RAPIDJSON_STATIC_ASSERT + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_LIKELY, RAPIDJSON_UNLIKELY + +//! Compiler branching hint for expression with high probability to be true. +/*! + \ingroup RAPIDJSON_CONFIG + \param x Boolean expression likely to be true. +*/ +#ifndef RAPIDJSON_LIKELY +#if defined(__GNUC__) || defined(__clang__) +#define RAPIDJSON_LIKELY(x) __builtin_expect(!!(x), 1) +#else +#define RAPIDJSON_LIKELY(x) (x) +#endif +#endif + +//! Compiler branching hint for expression with low probability to be true. +/*! + \ingroup RAPIDJSON_CONFIG + \param x Boolean expression unlikely to be true. +*/ +#ifndef RAPIDJSON_UNLIKELY +#if defined(__GNUC__) || defined(__clang__) +#define RAPIDJSON_UNLIKELY(x) __builtin_expect(!!(x), 0) +#else +#define RAPIDJSON_UNLIKELY(x) (x) +#endif +#endif + +/////////////////////////////////////////////////////////////////////////////// +// Helpers + +//!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN + +#define RAPIDJSON_MULTILINEMACRO_BEGIN do { +#define RAPIDJSON_MULTILINEMACRO_END \ +} while((void)0, 0) + +// adopted from Boost +#define RAPIDJSON_VERSION_CODE(x,y,z) \ + (((x)*100000) + ((y)*100) + (z)) + +#if defined(__has_builtin) +#define RAPIDJSON_HAS_BUILTIN(x) __has_builtin(x) +#else +#define RAPIDJSON_HAS_BUILTIN(x) 0 +#endif + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_DIAG_PUSH/POP, RAPIDJSON_DIAG_OFF + +#if defined(__GNUC__) +#define RAPIDJSON_GNUC \ + RAPIDJSON_VERSION_CODE(__GNUC__,__GNUC_MINOR__,__GNUC_PATCHLEVEL__) +#endif + +#if defined(__clang__) || (defined(RAPIDJSON_GNUC) && RAPIDJSON_GNUC >= RAPIDJSON_VERSION_CODE(4,2,0)) + +#define RAPIDJSON_PRAGMA(x) _Pragma(RAPIDJSON_STRINGIFY(x)) +#define RAPIDJSON_DIAG_PRAGMA(x) RAPIDJSON_PRAGMA(GCC diagnostic x) +#define RAPIDJSON_DIAG_OFF(x) \ + RAPIDJSON_DIAG_PRAGMA(ignored RAPIDJSON_STRINGIFY(RAPIDJSON_JOIN(-W,x))) + +// push/pop support in Clang and GCC>=4.6 +#if defined(__clang__) || (defined(RAPIDJSON_GNUC) && RAPIDJSON_GNUC >= RAPIDJSON_VERSION_CODE(4,6,0)) +#define RAPIDJSON_DIAG_PUSH RAPIDJSON_DIAG_PRAGMA(push) +#define RAPIDJSON_DIAG_POP RAPIDJSON_DIAG_PRAGMA(pop) +#else // GCC >= 4.2, < 4.6 +#define RAPIDJSON_DIAG_PUSH /* ignored */ +#define RAPIDJSON_DIAG_POP /* ignored */ +#endif + +#elif defined(_MSC_VER) + +// pragma (MSVC specific) +#define RAPIDJSON_PRAGMA(x) __pragma(x) +#define RAPIDJSON_DIAG_PRAGMA(x) RAPIDJSON_PRAGMA(warning(x)) + +#define RAPIDJSON_DIAG_OFF(x) RAPIDJSON_DIAG_PRAGMA(disable: x) +#define RAPIDJSON_DIAG_PUSH RAPIDJSON_DIAG_PRAGMA(push) +#define RAPIDJSON_DIAG_POP RAPIDJSON_DIAG_PRAGMA(pop) + +#else + +#define RAPIDJSON_DIAG_OFF(x) /* ignored */ +#define RAPIDJSON_DIAG_PUSH /* ignored */ +#define RAPIDJSON_DIAG_POP /* ignored */ + +#endif // RAPIDJSON_DIAG_* + +/////////////////////////////////////////////////////////////////////////////// +// C++11 features + +#ifndef RAPIDJSON_HAS_CXX11 +#define RAPIDJSON_HAS_CXX11 (RAPIDJSON_CPLUSPLUS >= 201103L) +#endif + +#ifndef RAPIDJSON_HAS_CXX11_RVALUE_REFS +#if RAPIDJSON_HAS_CXX11 +#define RAPIDJSON_HAS_CXX11_RVALUE_REFS 1 +#elif defined(__clang__) +#if __has_feature(cxx_rvalue_references) && \ + (defined(_MSC_VER) || defined(_LIBCPP_VERSION) || defined(__GLIBCXX__) && __GLIBCXX__ >= 20080306) +#define RAPIDJSON_HAS_CXX11_RVALUE_REFS 1 +#else +#define RAPIDJSON_HAS_CXX11_RVALUE_REFS 0 +#endif +#elif (defined(RAPIDJSON_GNUC) && (RAPIDJSON_GNUC >= RAPIDJSON_VERSION_CODE(4,3,0)) && defined(__GXX_EXPERIMENTAL_CXX0X__)) || \ + (defined(_MSC_VER) && _MSC_VER >= 1600) || \ + (defined(__SUNPRO_CC) && __SUNPRO_CC >= 0x5140 && defined(__GXX_EXPERIMENTAL_CXX0X__)) + +#define RAPIDJSON_HAS_CXX11_RVALUE_REFS 1 +#else +#define RAPIDJSON_HAS_CXX11_RVALUE_REFS 0 +#endif +#endif // RAPIDJSON_HAS_CXX11_RVALUE_REFS + +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS +#include // std::move +#endif + +#ifndef RAPIDJSON_HAS_CXX11_NOEXCEPT +#if RAPIDJSON_HAS_CXX11 +#define RAPIDJSON_HAS_CXX11_NOEXCEPT 1 +#elif defined(__clang__) +#define RAPIDJSON_HAS_CXX11_NOEXCEPT __has_feature(cxx_noexcept) +#elif (defined(RAPIDJSON_GNUC) && (RAPIDJSON_GNUC >= RAPIDJSON_VERSION_CODE(4,6,0)) && defined(__GXX_EXPERIMENTAL_CXX0X__)) || \ + (defined(_MSC_VER) && _MSC_VER >= 1900) || \ + (defined(__SUNPRO_CC) && __SUNPRO_CC >= 0x5140 && defined(__GXX_EXPERIMENTAL_CXX0X__)) +#define RAPIDJSON_HAS_CXX11_NOEXCEPT 1 +#else +#define RAPIDJSON_HAS_CXX11_NOEXCEPT 0 +#endif +#endif +#ifndef RAPIDJSON_NOEXCEPT +#if RAPIDJSON_HAS_CXX11_NOEXCEPT +#define RAPIDJSON_NOEXCEPT noexcept +#else +#define RAPIDJSON_NOEXCEPT throw() +#endif // RAPIDJSON_HAS_CXX11_NOEXCEPT +#endif + +// no automatic detection, yet +#ifndef RAPIDJSON_HAS_CXX11_TYPETRAITS +#if (defined(_MSC_VER) && _MSC_VER >= 1700) +#define RAPIDJSON_HAS_CXX11_TYPETRAITS 1 +#else +#define RAPIDJSON_HAS_CXX11_TYPETRAITS 0 +#endif +#endif + +#ifndef RAPIDJSON_HAS_CXX11_RANGE_FOR +#if defined(__clang__) +#define RAPIDJSON_HAS_CXX11_RANGE_FOR __has_feature(cxx_range_for) +#elif (defined(RAPIDJSON_GNUC) && (RAPIDJSON_GNUC >= RAPIDJSON_VERSION_CODE(4,6,0)) && defined(__GXX_EXPERIMENTAL_CXX0X__)) || \ + (defined(_MSC_VER) && _MSC_VER >= 1700) || \ + (defined(__SUNPRO_CC) && __SUNPRO_CC >= 0x5140 && defined(__GXX_EXPERIMENTAL_CXX0X__)) +#define RAPIDJSON_HAS_CXX11_RANGE_FOR 1 +#else +#define RAPIDJSON_HAS_CXX11_RANGE_FOR 0 +#endif +#endif // RAPIDJSON_HAS_CXX11_RANGE_FOR + +/////////////////////////////////////////////////////////////////////////////// +// C++17 features + +#ifndef RAPIDJSON_HAS_CXX17 +#define RAPIDJSON_HAS_CXX17 (RAPIDJSON_CPLUSPLUS >= 201703L) +#endif + +#if RAPIDJSON_HAS_CXX17 +# define RAPIDJSON_DELIBERATE_FALLTHROUGH [[fallthrough]] +#elif defined(__has_cpp_attribute) +# if __has_cpp_attribute(clang::fallthrough) +# define RAPIDJSON_DELIBERATE_FALLTHROUGH [[clang::fallthrough]] +# elif __has_cpp_attribute(fallthrough) +# define RAPIDJSON_DELIBERATE_FALLTHROUGH __attribute__((fallthrough)) +# else +# define RAPIDJSON_DELIBERATE_FALLTHROUGH +# endif +#else +# define RAPIDJSON_DELIBERATE_FALLTHROUGH +#endif + +//!@endcond + +//! Assertion (in non-throwing contexts). + /*! \ingroup RAPIDJSON_CONFIG + Some functions provide a \c noexcept guarantee, if the compiler supports it. + In these cases, the \ref RAPIDJSON_ASSERT macro cannot be overridden to + throw an exception. This macro adds a separate customization point for + such cases. + + Defaults to C \c assert() (as \ref RAPIDJSON_ASSERT), if \c noexcept is + supported, and to \ref RAPIDJSON_ASSERT otherwise. + */ + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_NOEXCEPT_ASSERT + +#ifndef RAPIDJSON_NOEXCEPT_ASSERT +#ifdef RAPIDJSON_ASSERT_THROWS +#include +#define RAPIDJSON_NOEXCEPT_ASSERT(x) assert(x) +#else +#define RAPIDJSON_NOEXCEPT_ASSERT(x) RAPIDJSON_ASSERT(x) +#endif // RAPIDJSON_ASSERT_THROWS +#endif // RAPIDJSON_NOEXCEPT_ASSERT + +/////////////////////////////////////////////////////////////////////////////// +// malloc/realloc/free + +#ifndef RAPIDJSON_MALLOC +///! customization point for global \c malloc +#define RAPIDJSON_MALLOC(size) std::malloc(size) +#endif +#ifndef RAPIDJSON_REALLOC +///! customization point for global \c realloc +#define RAPIDJSON_REALLOC(ptr, new_size) std::realloc(ptr, new_size) +#endif +#ifndef RAPIDJSON_FREE +///! customization point for global \c free +#define RAPIDJSON_FREE(ptr) std::free(ptr) +#endif + +/////////////////////////////////////////////////////////////////////////////// +// new/delete + +#ifndef RAPIDJSON_NEW +///! customization point for global \c new +#define RAPIDJSON_NEW(TypeName) new TypeName +#endif +#ifndef RAPIDJSON_DELETE +///! customization point for global \c delete +#define RAPIDJSON_DELETE(x) delete x +#endif + +/////////////////////////////////////////////////////////////////////////////// +// Type + +/*! \namespace rapidjson + \brief main RapidJSON namespace + \see RAPIDJSON_NAMESPACE +*/ +RAPIDJSON_NAMESPACE_BEGIN + +//! Type of JSON value +enum Type { + kNullType = 0, //!< null + kFalseType = 1, //!< false + kTrueType = 2, //!< true + kObjectType = 3, //!< object + kArrayType = 4, //!< array + kStringType = 5, //!< string + kNumberType = 6 //!< number +}; + +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_RAPIDJSON_H_ diff --git a/include/rapidjson/reader.h b/include/rapidjson/reader.h new file mode 100644 index 0000000..5554660 --- /dev/null +++ b/include/rapidjson/reader.h @@ -0,0 +1,2246 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// 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 RAPIDJSON_READER_H_ +#define RAPIDJSON_READER_H_ + +/*! \file reader.h */ + +#include "allocators.h" +#include "stream.h" +#include "encodedstream.h" +#include "internal/clzll.h" +#include "internal/meta.h" +#include "internal/stack.h" +#include "internal/strtod.h" +#include + +#if defined(RAPIDJSON_SIMD) && defined(_MSC_VER) +#include +#pragma intrinsic(_BitScanForward) +#endif +#ifdef RAPIDJSON_SSE42 +#include +#elif defined(RAPIDJSON_SSE2) +#include +#elif defined(RAPIDJSON_NEON) +#include +#endif + +#ifdef __clang__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(old-style-cast) +RAPIDJSON_DIAG_OFF(padded) +RAPIDJSON_DIAG_OFF(switch-enum) +#elif defined(_MSC_VER) +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(4127) // conditional expression is constant +RAPIDJSON_DIAG_OFF(4702) // unreachable code +#endif + +#ifdef __GNUC__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(effc++) +#endif + +//!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN +#define RAPIDJSON_NOTHING /* deliberately empty */ +#ifndef RAPIDJSON_PARSE_ERROR_EARLY_RETURN +#define RAPIDJSON_PARSE_ERROR_EARLY_RETURN(value) \ + RAPIDJSON_MULTILINEMACRO_BEGIN \ + if (RAPIDJSON_UNLIKELY(HasParseError())) { return value; } \ + RAPIDJSON_MULTILINEMACRO_END +#endif +#define RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID \ + RAPIDJSON_PARSE_ERROR_EARLY_RETURN(RAPIDJSON_NOTHING) +//!@endcond + +/*! \def RAPIDJSON_PARSE_ERROR_NORETURN + \ingroup RAPIDJSON_ERRORS + \brief Macro to indicate a parse error. + \param parseErrorCode \ref rapidjson::ParseErrorCode of the error + \param offset position of the error in JSON input (\c size_t) + + This macros can be used as a customization point for the internal + error handling mechanism of RapidJSON. + + A common usage model is to throw an exception instead of requiring the + caller to explicitly check the \ref rapidjson::GenericReader::Parse's + return value: + + \code + #define RAPIDJSON_PARSE_ERROR_NORETURN(parseErrorCode,offset) \ + throw ParseException(parseErrorCode, #parseErrorCode, offset) + + #include // std::runtime_error + #include "rapidjson/error/error.h" // rapidjson::ParseResult + + struct ParseException : std::runtime_error, rapidjson::ParseResult { + ParseException(rapidjson::ParseErrorCode code, const char* msg, size_t offset) + : std::runtime_error(msg), ParseResult(code, offset) {} + }; + + #include "rapidjson/reader.h" + \endcode + + \see RAPIDJSON_PARSE_ERROR, rapidjson::GenericReader::Parse + */ +#ifndef RAPIDJSON_PARSE_ERROR_NORETURN +#define RAPIDJSON_PARSE_ERROR_NORETURN(parseErrorCode, offset) \ + RAPIDJSON_MULTILINEMACRO_BEGIN \ + RAPIDJSON_ASSERT(!HasParseError()); /* Error can only be assigned once */ \ + SetParseError(parseErrorCode, offset); \ + RAPIDJSON_MULTILINEMACRO_END +#endif + +/*! \def RAPIDJSON_PARSE_ERROR + \ingroup RAPIDJSON_ERRORS + \brief (Internal) macro to indicate and handle a parse error. + \param parseErrorCode \ref rapidjson::ParseErrorCode of the error + \param offset position of the error in JSON input (\c size_t) + + Invokes RAPIDJSON_PARSE_ERROR_NORETURN and stops the parsing. + + \see RAPIDJSON_PARSE_ERROR_NORETURN + \hideinitializer + */ +#ifndef RAPIDJSON_PARSE_ERROR +#define RAPIDJSON_PARSE_ERROR(parseErrorCode, offset) \ + RAPIDJSON_MULTILINEMACRO_BEGIN \ + RAPIDJSON_PARSE_ERROR_NORETURN(parseErrorCode, offset); \ + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; \ + RAPIDJSON_MULTILINEMACRO_END +#endif + +#include "error/error.h" // ParseErrorCode, ParseResult + +RAPIDJSON_NAMESPACE_BEGIN + +/////////////////////////////////////////////////////////////////////////////// +// ParseFlag + +/*! \def RAPIDJSON_PARSE_DEFAULT_FLAGS + \ingroup RAPIDJSON_CONFIG + \brief User-defined kParseDefaultFlags definition. + + User can define this as any \c ParseFlag combinations. +*/ +#ifndef RAPIDJSON_PARSE_DEFAULT_FLAGS +#define RAPIDJSON_PARSE_DEFAULT_FLAGS kParseNoFlags +#endif + +//! Combination of parseFlags +/*! \see Reader::Parse, Document::Parse, Document::ParseInsitu, Document::ParseStream + */ +enum ParseFlag { + kParseNoFlags = 0, //!< No flags are set. + kParseInsituFlag = 1, //!< In-situ(destructive) parsing. + kParseValidateEncodingFlag = 2, //!< Validate encoding of JSON strings. + kParseIterativeFlag = 4, //!< Iterative(constant complexity in terms of function call stack size) parsing. + kParseStopWhenDoneFlag = 8, //!< After parsing a complete JSON root from stream, stop further processing the rest of stream. When this flag is used, parser will not generate kParseErrorDocumentRootNotSingular error. + kParseFullPrecisionFlag = 16, //!< Parse number in full precision (but slower). + kParseCommentsFlag = 32, //!< Allow one-line (//) and multi-line (/**/) comments. + kParseNumbersAsStringsFlag = 64, //!< Parse all numbers (ints/doubles) as strings. + kParseTrailingCommasFlag = 128, //!< Allow trailing commas at the end of objects and arrays. + kParseNanAndInfFlag = 256, //!< Allow parsing NaN, Inf, Infinity, -Inf and -Infinity as doubles. + kParseEscapedApostropheFlag = 512, //!< Allow escaped apostrophe in strings. + kParseDefaultFlags = RAPIDJSON_PARSE_DEFAULT_FLAGS //!< Default parse flags. Can be customized by defining RAPIDJSON_PARSE_DEFAULT_FLAGS +}; + +/////////////////////////////////////////////////////////////////////////////// +// Handler + +/*! \class rapidjson::Handler + \brief Concept for receiving events from GenericReader upon parsing. + The functions return true if no error occurs. If they return false, + the event publisher should terminate the process. +\code +concept Handler { + typename Ch; + + bool Null(); + bool Bool(bool b); + bool Int(int i); + bool Uint(unsigned i); + bool Int64(int64_t i); + bool Uint64(uint64_t i); + bool Double(double d); + /// enabled via kParseNumbersAsStringsFlag, string is not null-terminated (use length) + bool RawNumber(const Ch* str, SizeType length, bool copy); + bool String(const Ch* str, SizeType length, bool copy); + bool StartObject(); + bool Key(const Ch* str, SizeType length, bool copy); + bool EndObject(SizeType memberCount); + bool StartArray(); + bool EndArray(SizeType elementCount); +}; +\endcode +*/ +/////////////////////////////////////////////////////////////////////////////// +// BaseReaderHandler + +//! Default implementation of Handler. +/*! This can be used as base class of any reader handler. + \note implements Handler concept +*/ +template, typename Derived = void> +struct BaseReaderHandler { + typedef typename Encoding::Ch Ch; + + typedef typename internal::SelectIf, BaseReaderHandler, Derived>::Type Override; + + bool Default() { return true; } + bool Null() { return static_cast(*this).Default(); } + bool Bool(bool) { return static_cast(*this).Default(); } + bool Int(int) { return static_cast(*this).Default(); } + bool Uint(unsigned) { return static_cast(*this).Default(); } + bool Int64(int64_t) { return static_cast(*this).Default(); } + bool Uint64(uint64_t) { return static_cast(*this).Default(); } + bool Double(double) { return static_cast(*this).Default(); } + /// enabled via kParseNumbersAsStringsFlag, string is not null-terminated (use length) + bool RawNumber(const Ch* str, SizeType len, bool copy) { return static_cast(*this).String(str, len, copy); } + bool String(const Ch*, SizeType, bool) { return static_cast(*this).Default(); } + bool StartObject() { return static_cast(*this).Default(); } + bool Key(const Ch* str, SizeType len, bool copy) { return static_cast(*this).String(str, len, copy); } + bool EndObject(SizeType) { return static_cast(*this).Default(); } + bool StartArray() { return static_cast(*this).Default(); } + bool EndArray(SizeType) { return static_cast(*this).Default(); } +}; + +/////////////////////////////////////////////////////////////////////////////// +// StreamLocalCopy + +namespace internal { + +template::copyOptimization> +class StreamLocalCopy; + +//! Do copy optimization. +template +class StreamLocalCopy { +public: + StreamLocalCopy(Stream& original) : s(original), original_(original) {} + ~StreamLocalCopy() { original_ = s; } + + Stream s; + +private: + StreamLocalCopy& operator=(const StreamLocalCopy&) /* = delete */; + + Stream& original_; +}; + +//! Keep reference. +template +class StreamLocalCopy { +public: + StreamLocalCopy(Stream& original) : s(original) {} + + Stream& s; + +private: + StreamLocalCopy& operator=(const StreamLocalCopy&) /* = delete */; +}; + +} // namespace internal + +/////////////////////////////////////////////////////////////////////////////// +// SkipWhitespace + +//! Skip the JSON white spaces in a stream. +/*! \param is A input stream for skipping white spaces. + \note This function has SSE2/SSE4.2 specialization. +*/ +template +void SkipWhitespace(InputStream& is) { + internal::StreamLocalCopy copy(is); + InputStream& s(copy.s); + + typename InputStream::Ch c; + while ((c = s.Peek()) == ' ' || c == '\n' || c == '\r' || c == '\t') + s.Take(); +} + +inline const char* SkipWhitespace(const char* p, const char* end) { + while (p != end && (*p == ' ' || *p == '\n' || *p == '\r' || *p == '\t')) + ++p; + return p; +} + +#ifdef RAPIDJSON_SSE42 +//! Skip whitespace with SSE 4.2 pcmpistrm instruction, testing 16 8-byte characters at once. +inline const char *SkipWhitespace_SIMD(const char* p) { + // Fast return for single non-whitespace + if (*p == ' ' || *p == '\n' || *p == '\r' || *p == '\t') + ++p; + else + return p; + + // 16-byte align to the next boundary + const char* nextAligned = reinterpret_cast((reinterpret_cast(p) + 15) & static_cast(~15)); + while (p != nextAligned) + if (*p == ' ' || *p == '\n' || *p == '\r' || *p == '\t') + ++p; + else + return p; + + // The rest of string using SIMD + static const char whitespace[16] = " \n\r\t"; + const __m128i w = _mm_loadu_si128(reinterpret_cast(&whitespace[0])); + + for (;; p += 16) { + const __m128i s = _mm_load_si128(reinterpret_cast(p)); + const int r = _mm_cmpistri(w, s, _SIDD_UBYTE_OPS | _SIDD_CMP_EQUAL_ANY | _SIDD_LEAST_SIGNIFICANT | _SIDD_NEGATIVE_POLARITY); + if (r != 16) // some of characters is non-whitespace + return p + r; + } +} + +inline const char *SkipWhitespace_SIMD(const char* p, const char* end) { + // Fast return for single non-whitespace + if (p != end && (*p == ' ' || *p == '\n' || *p == '\r' || *p == '\t')) + ++p; + else + return p; + + // The middle of string using SIMD + static const char whitespace[16] = " \n\r\t"; + const __m128i w = _mm_loadu_si128(reinterpret_cast(&whitespace[0])); + + for (; p <= end - 16; p += 16) { + const __m128i s = _mm_loadu_si128(reinterpret_cast(p)); + const int r = _mm_cmpistri(w, s, _SIDD_UBYTE_OPS | _SIDD_CMP_EQUAL_ANY | _SIDD_LEAST_SIGNIFICANT | _SIDD_NEGATIVE_POLARITY); + if (r != 16) // some of characters is non-whitespace + return p + r; + } + + return SkipWhitespace(p, end); +} + +#elif defined(RAPIDJSON_SSE2) + +//! Skip whitespace with SSE2 instructions, testing 16 8-byte characters at once. +inline const char *SkipWhitespace_SIMD(const char* p) { + // Fast return for single non-whitespace + if (*p == ' ' || *p == '\n' || *p == '\r' || *p == '\t') + ++p; + else + return p; + + // 16-byte align to the next boundary + const char* nextAligned = reinterpret_cast((reinterpret_cast(p) + 15) & static_cast(~15)); + while (p != nextAligned) + if (*p == ' ' || *p == '\n' || *p == '\r' || *p == '\t') + ++p; + else + return p; + + // The rest of string + #define C16(c) { c, c, c, c, c, c, c, c, c, c, c, c, c, c, c, c } + static const char whitespaces[4][16] = { C16(' '), C16('\n'), C16('\r'), C16('\t') }; + #undef C16 + + const __m128i w0 = _mm_loadu_si128(reinterpret_cast(&whitespaces[0][0])); + const __m128i w1 = _mm_loadu_si128(reinterpret_cast(&whitespaces[1][0])); + const __m128i w2 = _mm_loadu_si128(reinterpret_cast(&whitespaces[2][0])); + const __m128i w3 = _mm_loadu_si128(reinterpret_cast(&whitespaces[3][0])); + + for (;; p += 16) { + const __m128i s = _mm_load_si128(reinterpret_cast(p)); + __m128i x = _mm_cmpeq_epi8(s, w0); + x = _mm_or_si128(x, _mm_cmpeq_epi8(s, w1)); + x = _mm_or_si128(x, _mm_cmpeq_epi8(s, w2)); + x = _mm_or_si128(x, _mm_cmpeq_epi8(s, w3)); + unsigned short r = static_cast(~_mm_movemask_epi8(x)); + if (r != 0) { // some of characters may be non-whitespace +#ifdef _MSC_VER // Find the index of first non-whitespace + unsigned long offset; + _BitScanForward(&offset, r); + return p + offset; +#else + return p + __builtin_ffs(r) - 1; +#endif + } + } +} + +inline const char *SkipWhitespace_SIMD(const char* p, const char* end) { + // Fast return for single non-whitespace + if (p != end && (*p == ' ' || *p == '\n' || *p == '\r' || *p == '\t')) + ++p; + else + return p; + + // The rest of string + #define C16(c) { c, c, c, c, c, c, c, c, c, c, c, c, c, c, c, c } + static const char whitespaces[4][16] = { C16(' '), C16('\n'), C16('\r'), C16('\t') }; + #undef C16 + + const __m128i w0 = _mm_loadu_si128(reinterpret_cast(&whitespaces[0][0])); + const __m128i w1 = _mm_loadu_si128(reinterpret_cast(&whitespaces[1][0])); + const __m128i w2 = _mm_loadu_si128(reinterpret_cast(&whitespaces[2][0])); + const __m128i w3 = _mm_loadu_si128(reinterpret_cast(&whitespaces[3][0])); + + for (; p <= end - 16; p += 16) { + const __m128i s = _mm_loadu_si128(reinterpret_cast(p)); + __m128i x = _mm_cmpeq_epi8(s, w0); + x = _mm_or_si128(x, _mm_cmpeq_epi8(s, w1)); + x = _mm_or_si128(x, _mm_cmpeq_epi8(s, w2)); + x = _mm_or_si128(x, _mm_cmpeq_epi8(s, w3)); + unsigned short r = static_cast(~_mm_movemask_epi8(x)); + if (r != 0) { // some of characters may be non-whitespace +#ifdef _MSC_VER // Find the index of first non-whitespace + unsigned long offset; + _BitScanForward(&offset, r); + return p + offset; +#else + return p + __builtin_ffs(r) - 1; +#endif + } + } + + return SkipWhitespace(p, end); +} + +#elif defined(RAPIDJSON_NEON) + +//! Skip whitespace with ARM Neon instructions, testing 16 8-byte characters at once. +inline const char *SkipWhitespace_SIMD(const char* p) { + // Fast return for single non-whitespace + if (*p == ' ' || *p == '\n' || *p == '\r' || *p == '\t') + ++p; + else + return p; + + // 16-byte align to the next boundary + const char* nextAligned = reinterpret_cast((reinterpret_cast(p) + 15) & static_cast(~15)); + while (p != nextAligned) + if (*p == ' ' || *p == '\n' || *p == '\r' || *p == '\t') + ++p; + else + return p; + + const uint8x16_t w0 = vmovq_n_u8(' '); + const uint8x16_t w1 = vmovq_n_u8('\n'); + const uint8x16_t w2 = vmovq_n_u8('\r'); + const uint8x16_t w3 = vmovq_n_u8('\t'); + + for (;; p += 16) { + const uint8x16_t s = vld1q_u8(reinterpret_cast(p)); + uint8x16_t x = vceqq_u8(s, w0); + x = vorrq_u8(x, vceqq_u8(s, w1)); + x = vorrq_u8(x, vceqq_u8(s, w2)); + x = vorrq_u8(x, vceqq_u8(s, w3)); + + x = vmvnq_u8(x); // Negate + x = vrev64q_u8(x); // Rev in 64 + uint64_t low = vgetq_lane_u64(vreinterpretq_u64_u8(x), 0); // extract + uint64_t high = vgetq_lane_u64(vreinterpretq_u64_u8(x), 1); // extract + + if (low == 0) { + if (high != 0) { + uint32_t lz = internal::clzll(high); + return p + 8 + (lz >> 3); + } + } else { + uint32_t lz = internal::clzll(low); + return p + (lz >> 3); + } + } +} + +inline const char *SkipWhitespace_SIMD(const char* p, const char* end) { + // Fast return for single non-whitespace + if (p != end && (*p == ' ' || *p == '\n' || *p == '\r' || *p == '\t')) + ++p; + else + return p; + + const uint8x16_t w0 = vmovq_n_u8(' '); + const uint8x16_t w1 = vmovq_n_u8('\n'); + const uint8x16_t w2 = vmovq_n_u8('\r'); + const uint8x16_t w3 = vmovq_n_u8('\t'); + + for (; p <= end - 16; p += 16) { + const uint8x16_t s = vld1q_u8(reinterpret_cast(p)); + uint8x16_t x = vceqq_u8(s, w0); + x = vorrq_u8(x, vceqq_u8(s, w1)); + x = vorrq_u8(x, vceqq_u8(s, w2)); + x = vorrq_u8(x, vceqq_u8(s, w3)); + + x = vmvnq_u8(x); // Negate + x = vrev64q_u8(x); // Rev in 64 + uint64_t low = vgetq_lane_u64(vreinterpretq_u64_u8(x), 0); // extract + uint64_t high = vgetq_lane_u64(vreinterpretq_u64_u8(x), 1); // extract + + if (low == 0) { + if (high != 0) { + uint32_t lz = internal::clzll(high); + return p + 8 + (lz >> 3); + } + } else { + uint32_t lz = internal::clzll(low); + return p + (lz >> 3); + } + } + + return SkipWhitespace(p, end); +} + +#endif // RAPIDJSON_NEON + +#ifdef RAPIDJSON_SIMD +//! Template function specialization for InsituStringStream +template<> inline void SkipWhitespace(InsituStringStream& is) { + is.src_ = const_cast(SkipWhitespace_SIMD(is.src_)); +} + +//! Template function specialization for StringStream +template<> inline void SkipWhitespace(StringStream& is) { + is.src_ = SkipWhitespace_SIMD(is.src_); +} + +template<> inline void SkipWhitespace(EncodedInputStream, MemoryStream>& is) { + is.is_.src_ = SkipWhitespace_SIMD(is.is_.src_, is.is_.end_); +} +#endif // RAPIDJSON_SIMD + +/////////////////////////////////////////////////////////////////////////////// +// GenericReader + +//! SAX-style JSON parser. Use \ref Reader for UTF8 encoding and default allocator. +/*! GenericReader parses JSON text from a stream, and send events synchronously to an + object implementing Handler concept. + + It needs to allocate a stack for storing a single decoded string during + non-destructive parsing. + + For in-situ parsing, the decoded string is directly written to the source + text string, no temporary buffer is required. + + A GenericReader object can be reused for parsing multiple JSON text. + + \tparam SourceEncoding Encoding of the input stream. + \tparam TargetEncoding Encoding of the parse output. + \tparam StackAllocator Allocator type for stack. +*/ +template +class GenericReader { +public: + typedef typename SourceEncoding::Ch Ch; //!< SourceEncoding character type + + //! Constructor. + /*! \param stackAllocator Optional allocator for allocating stack memory. (Only use for non-destructive parsing) + \param stackCapacity stack capacity in bytes for storing a single decoded string. (Only use for non-destructive parsing) + */ + GenericReader(StackAllocator* stackAllocator = 0, size_t stackCapacity = kDefaultStackCapacity) : + stack_(stackAllocator, stackCapacity), parseResult_(), state_(IterativeParsingStartState) {} + + //! Parse JSON text. + /*! \tparam parseFlags Combination of \ref ParseFlag. + \tparam InputStream Type of input stream, implementing Stream concept. + \tparam Handler Type of handler, implementing Handler concept. + \param is Input stream to be parsed. + \param handler The handler to receive events. + \return Whether the parsing is successful. + */ + template + ParseResult Parse(InputStream& is, Handler& handler) { + if (parseFlags & kParseIterativeFlag) + return IterativeParse(is, handler); + + parseResult_.Clear(); + + ClearStackOnExit scope(*this); + + SkipWhitespaceAndComments(is); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN(parseResult_); + + if (RAPIDJSON_UNLIKELY(is.Peek() == '\0')) { + RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorDocumentEmpty, is.Tell()); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN(parseResult_); + } + else { + ParseValue(is, handler); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN(parseResult_); + + if (!(parseFlags & kParseStopWhenDoneFlag)) { + SkipWhitespaceAndComments(is); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN(parseResult_); + + if (RAPIDJSON_UNLIKELY(is.Peek() != '\0')) { + RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorDocumentRootNotSingular, is.Tell()); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN(parseResult_); + } + } + } + + return parseResult_; + } + + //! Parse JSON text (with \ref kParseDefaultFlags) + /*! \tparam InputStream Type of input stream, implementing Stream concept + \tparam Handler Type of handler, implementing Handler concept. + \param is Input stream to be parsed. + \param handler The handler to receive events. + \return Whether the parsing is successful. + */ + template + ParseResult Parse(InputStream& is, Handler& handler) { + return Parse(is, handler); + } + + //! Initialize JSON text token-by-token parsing + /*! + */ + void IterativeParseInit() { + parseResult_.Clear(); + state_ = IterativeParsingStartState; + } + + //! Parse one token from JSON text + /*! \tparam InputStream Type of input stream, implementing Stream concept + \tparam Handler Type of handler, implementing Handler concept. + \param is Input stream to be parsed. + \param handler The handler to receive events. + \return Whether the parsing is successful. + */ + template + bool IterativeParseNext(InputStream& is, Handler& handler) { + while (RAPIDJSON_LIKELY(is.Peek() != '\0')) { + SkipWhitespaceAndComments(is); + + Token t = Tokenize(is.Peek()); + IterativeParsingState n = Predict(state_, t); + IterativeParsingState d = Transit(state_, t, n, is, handler); + + // If we've finished or hit an error... + if (RAPIDJSON_UNLIKELY(IsIterativeParsingCompleteState(d))) { + // Report errors. + if (d == IterativeParsingErrorState) { + HandleError(state_, is); + return false; + } + + // Transition to the finish state. + RAPIDJSON_ASSERT(d == IterativeParsingFinishState); + state_ = d; + + // If StopWhenDone is not set... + if (!(parseFlags & kParseStopWhenDoneFlag)) { + // ... and extra non-whitespace data is found... + SkipWhitespaceAndComments(is); + if (is.Peek() != '\0') { + // ... this is considered an error. + HandleError(state_, is); + return false; + } + } + + // Success! We are done! + return true; + } + + // Transition to the new state. + state_ = d; + + // If we parsed anything other than a delimiter, we invoked the handler, so we can return true now. + if (!IsIterativeParsingDelimiterState(n)) + return true; + } + + // We reached the end of file. + stack_.Clear(); + + if (state_ != IterativeParsingFinishState) { + HandleError(state_, is); + return false; + } + + return true; + } + + //! Check if token-by-token parsing JSON text is complete + /*! \return Whether the JSON has been fully decoded. + */ + RAPIDJSON_FORCEINLINE bool IterativeParseComplete() const { + return IsIterativeParsingCompleteState(state_); + } + + //! Whether a parse error has occurred in the last parsing. + bool HasParseError() const { return parseResult_.IsError(); } + + //! Get the \ref ParseErrorCode of last parsing. + ParseErrorCode GetParseErrorCode() const { return parseResult_.Code(); } + + //! Get the position of last parsing error in input, 0 otherwise. + size_t GetErrorOffset() const { return parseResult_.Offset(); } + +protected: + void SetParseError(ParseErrorCode code, size_t offset) { parseResult_.Set(code, offset); } + +private: + // Prohibit copy constructor & assignment operator. + GenericReader(const GenericReader&); + GenericReader& operator=(const GenericReader&); + + void ClearStack() { stack_.Clear(); } + + // clear stack on any exit from ParseStream, e.g. due to exception + struct ClearStackOnExit { + explicit ClearStackOnExit(GenericReader& r) : r_(r) {} + ~ClearStackOnExit() { r_.ClearStack(); } + private: + GenericReader& r_; + ClearStackOnExit(const ClearStackOnExit&); + ClearStackOnExit& operator=(const ClearStackOnExit&); + }; + + template + void SkipWhitespaceAndComments(InputStream& is) { + SkipWhitespace(is); + + if (parseFlags & kParseCommentsFlag) { + while (RAPIDJSON_UNLIKELY(Consume(is, '/'))) { + if (Consume(is, '*')) { + while (true) { + if (RAPIDJSON_UNLIKELY(is.Peek() == '\0')) + RAPIDJSON_PARSE_ERROR(kParseErrorUnspecificSyntaxError, is.Tell()); + else if (Consume(is, '*')) { + if (Consume(is, '/')) + break; + } + else + is.Take(); + } + } + else if (RAPIDJSON_LIKELY(Consume(is, '/'))) + while (is.Peek() != '\0' && is.Take() != '\n') {} + else + RAPIDJSON_PARSE_ERROR(kParseErrorUnspecificSyntaxError, is.Tell()); + + SkipWhitespace(is); + } + } + } + + // Parse object: { string : value, ... } + template + void ParseObject(InputStream& is, Handler& handler) { + RAPIDJSON_ASSERT(is.Peek() == '{'); + is.Take(); // Skip '{' + + if (RAPIDJSON_UNLIKELY(!handler.StartObject())) + RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); + + SkipWhitespaceAndComments(is); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; + + if (Consume(is, '}')) { + if (RAPIDJSON_UNLIKELY(!handler.EndObject(0))) // empty object + RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); + return; + } + + for (SizeType memberCount = 0;;) { + if (RAPIDJSON_UNLIKELY(is.Peek() != '"')) + RAPIDJSON_PARSE_ERROR(kParseErrorObjectMissName, is.Tell()); + + ParseString(is, handler, true); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; + + SkipWhitespaceAndComments(is); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; + + if (RAPIDJSON_UNLIKELY(!Consume(is, ':'))) + RAPIDJSON_PARSE_ERROR(kParseErrorObjectMissColon, is.Tell()); + + SkipWhitespaceAndComments(is); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; + + ParseValue(is, handler); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; + + SkipWhitespaceAndComments(is); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; + + ++memberCount; + + switch (is.Peek()) { + case ',': + is.Take(); + SkipWhitespaceAndComments(is); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; + break; + case '}': + is.Take(); + if (RAPIDJSON_UNLIKELY(!handler.EndObject(memberCount))) + RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); + return; + default: + RAPIDJSON_PARSE_ERROR(kParseErrorObjectMissCommaOrCurlyBracket, is.Tell()); break; // This useless break is only for making warning and coverage happy + } + + if (parseFlags & kParseTrailingCommasFlag) { + if (is.Peek() == '}') { + if (RAPIDJSON_UNLIKELY(!handler.EndObject(memberCount))) + RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); + is.Take(); + return; + } + } + } + } + + // Parse array: [ value, ... ] + template + void ParseArray(InputStream& is, Handler& handler) { + RAPIDJSON_ASSERT(is.Peek() == '['); + is.Take(); // Skip '[' + + if (RAPIDJSON_UNLIKELY(!handler.StartArray())) + RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); + + SkipWhitespaceAndComments(is); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; + + if (Consume(is, ']')) { + if (RAPIDJSON_UNLIKELY(!handler.EndArray(0))) // empty array + RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); + return; + } + + for (SizeType elementCount = 0;;) { + ParseValue(is, handler); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; + + ++elementCount; + SkipWhitespaceAndComments(is); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; + + if (Consume(is, ',')) { + SkipWhitespaceAndComments(is); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; + } + else if (Consume(is, ']')) { + if (RAPIDJSON_UNLIKELY(!handler.EndArray(elementCount))) + RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); + return; + } + else + RAPIDJSON_PARSE_ERROR(kParseErrorArrayMissCommaOrSquareBracket, is.Tell()); + + if (parseFlags & kParseTrailingCommasFlag) { + if (is.Peek() == ']') { + if (RAPIDJSON_UNLIKELY(!handler.EndArray(elementCount))) + RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); + is.Take(); + return; + } + } + } + } + + template + void ParseNull(InputStream& is, Handler& handler) { + RAPIDJSON_ASSERT(is.Peek() == 'n'); + is.Take(); + + if (RAPIDJSON_LIKELY(Consume(is, 'u') && Consume(is, 'l') && Consume(is, 'l'))) { + if (RAPIDJSON_UNLIKELY(!handler.Null())) + RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); + } + else + RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, is.Tell()); + } + + template + void ParseTrue(InputStream& is, Handler& handler) { + RAPIDJSON_ASSERT(is.Peek() == 't'); + is.Take(); + + if (RAPIDJSON_LIKELY(Consume(is, 'r') && Consume(is, 'u') && Consume(is, 'e'))) { + if (RAPIDJSON_UNLIKELY(!handler.Bool(true))) + RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); + } + else + RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, is.Tell()); + } + + template + void ParseFalse(InputStream& is, Handler& handler) { + RAPIDJSON_ASSERT(is.Peek() == 'f'); + is.Take(); + + if (RAPIDJSON_LIKELY(Consume(is, 'a') && Consume(is, 'l') && Consume(is, 's') && Consume(is, 'e'))) { + if (RAPIDJSON_UNLIKELY(!handler.Bool(false))) + RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); + } + else + RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, is.Tell()); + } + + template + RAPIDJSON_FORCEINLINE static bool Consume(InputStream& is, typename InputStream::Ch expect) { + if (RAPIDJSON_LIKELY(is.Peek() == expect)) { + is.Take(); + return true; + } + else + return false; + } + + // Helper function to parse four hexadecimal digits in \uXXXX in ParseString(). + template + unsigned ParseHex4(InputStream& is, size_t escapeOffset) { + unsigned codepoint = 0; + for (int i = 0; i < 4; i++) { + Ch c = is.Peek(); + codepoint <<= 4; + codepoint += static_cast(c); + if (c >= '0' && c <= '9') + codepoint -= '0'; + else if (c >= 'A' && c <= 'F') + codepoint -= 'A' - 10; + else if (c >= 'a' && c <= 'f') + codepoint -= 'a' - 10; + else { + RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorStringUnicodeEscapeInvalidHex, escapeOffset); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN(0); + } + is.Take(); + } + return codepoint; + } + + template + class StackStream { + public: + typedef CharType Ch; + + StackStream(internal::Stack& stack) : stack_(stack), length_(0) {} + RAPIDJSON_FORCEINLINE void Put(Ch c) { + *stack_.template Push() = c; + ++length_; + } + + RAPIDJSON_FORCEINLINE void* Push(SizeType count) { + length_ += count; + return stack_.template Push(count); + } + + size_t Length() const { return length_; } + + Ch* Pop() { + return stack_.template Pop(length_); + } + + private: + StackStream(const StackStream&); + StackStream& operator=(const StackStream&); + + internal::Stack& stack_; + SizeType length_; + }; + + // Parse string and generate String event. Different code paths for kParseInsituFlag. + template + void ParseString(InputStream& is, Handler& handler, bool isKey = false) { + internal::StreamLocalCopy copy(is); + InputStream& s(copy.s); + + RAPIDJSON_ASSERT(s.Peek() == '\"'); + s.Take(); // Skip '\"' + + bool success = false; + if (parseFlags & kParseInsituFlag) { + typename InputStream::Ch *head = s.PutBegin(); + ParseStringToStream(s, s); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; + size_t length = s.PutEnd(head) - 1; + RAPIDJSON_ASSERT(length <= 0xFFFFFFFF); + const typename TargetEncoding::Ch* const str = reinterpret_cast(head); + success = (isKey ? handler.Key(str, SizeType(length), false) : handler.String(str, SizeType(length), false)); + } + else { + StackStream stackStream(stack_); + ParseStringToStream(s, stackStream); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; + SizeType length = static_cast(stackStream.Length()) - 1; + const typename TargetEncoding::Ch* const str = stackStream.Pop(); + success = (isKey ? handler.Key(str, length, true) : handler.String(str, length, true)); + } + if (RAPIDJSON_UNLIKELY(!success)) + RAPIDJSON_PARSE_ERROR(kParseErrorTermination, s.Tell()); + } + + // Parse string to an output is + // This function handles the prefix/suffix double quotes, escaping, and optional encoding validation. + template + RAPIDJSON_FORCEINLINE void ParseStringToStream(InputStream& is, OutputStream& os) { +//!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN +#define Z16 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 + static const char escape[256] = { + Z16, Z16, 0, 0,'\"', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, '/', + Z16, Z16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,'\\', 0, 0, 0, + 0, 0,'\b', 0, 0, 0,'\f', 0, 0, 0, 0, 0, 0, 0,'\n', 0, + 0, 0,'\r', 0,'\t', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + Z16, Z16, Z16, Z16, Z16, Z16, Z16, Z16 + }; +#undef Z16 +//!@endcond + + for (;;) { + // Scan and copy string before "\\\"" or < 0x20. This is an optional optimzation. + if (!(parseFlags & kParseValidateEncodingFlag)) + ScanCopyUnescapedString(is, os); + + Ch c = is.Peek(); + if (RAPIDJSON_UNLIKELY(c == '\\')) { // Escape + size_t escapeOffset = is.Tell(); // For invalid escaping, report the initial '\\' as error offset + is.Take(); + Ch e = is.Peek(); + if ((sizeof(Ch) == 1 || unsigned(e) < 256) && RAPIDJSON_LIKELY(escape[static_cast(e)])) { + is.Take(); + os.Put(static_cast(escape[static_cast(e)])); + } + else if ((parseFlags & kParseEscapedApostropheFlag) && RAPIDJSON_LIKELY(e == '\'')) { // Allow escaped apostrophe + is.Take(); + os.Put('\''); + } + else if (RAPIDJSON_LIKELY(e == 'u')) { // Unicode + is.Take(); + unsigned codepoint = ParseHex4(is, escapeOffset); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; + if (RAPIDJSON_UNLIKELY(codepoint >= 0xD800 && codepoint <= 0xDFFF)) { + // high surrogate, check if followed by valid low surrogate + if (RAPIDJSON_LIKELY(codepoint <= 0xDBFF)) { + // Handle UTF-16 surrogate pair + if (RAPIDJSON_UNLIKELY(!Consume(is, '\\') || !Consume(is, 'u'))) + RAPIDJSON_PARSE_ERROR(kParseErrorStringUnicodeSurrogateInvalid, escapeOffset); + unsigned codepoint2 = ParseHex4(is, escapeOffset); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; + if (RAPIDJSON_UNLIKELY(codepoint2 < 0xDC00 || codepoint2 > 0xDFFF)) + RAPIDJSON_PARSE_ERROR(kParseErrorStringUnicodeSurrogateInvalid, escapeOffset); + codepoint = (((codepoint - 0xD800) << 10) | (codepoint2 - 0xDC00)) + 0x10000; + } + // single low surrogate + else + { + RAPIDJSON_PARSE_ERROR(kParseErrorStringUnicodeSurrogateInvalid, escapeOffset); + } + } + TEncoding::Encode(os, codepoint); + } + else + RAPIDJSON_PARSE_ERROR(kParseErrorStringEscapeInvalid, escapeOffset); + } + else if (RAPIDJSON_UNLIKELY(c == '"')) { // Closing double quote + is.Take(); + os.Put('\0'); // null-terminate the string + return; + } + else if (RAPIDJSON_UNLIKELY(static_cast(c) < 0x20)) { // RFC 4627: unescaped = %x20-21 / %x23-5B / %x5D-10FFFF + if (c == '\0') + RAPIDJSON_PARSE_ERROR(kParseErrorStringMissQuotationMark, is.Tell()); + else + RAPIDJSON_PARSE_ERROR(kParseErrorStringInvalidEncoding, is.Tell()); + } + else { + size_t offset = is.Tell(); + if (RAPIDJSON_UNLIKELY((parseFlags & kParseValidateEncodingFlag ? + !Transcoder::Validate(is, os) : + !Transcoder::Transcode(is, os)))) + RAPIDJSON_PARSE_ERROR(kParseErrorStringInvalidEncoding, offset); + } + } + } + + template + static RAPIDJSON_FORCEINLINE void ScanCopyUnescapedString(InputStream&, OutputStream&) { + // Do nothing for generic version + } + +#if defined(RAPIDJSON_SSE2) || defined(RAPIDJSON_SSE42) + // StringStream -> StackStream + static RAPIDJSON_FORCEINLINE void ScanCopyUnescapedString(StringStream& is, StackStream& os) { + const char* p = is.src_; + + // Scan one by one until alignment (unaligned load may cross page boundary and cause crash) + const char* nextAligned = reinterpret_cast((reinterpret_cast(p) + 15) & static_cast(~15)); + while (p != nextAligned) + if (RAPIDJSON_UNLIKELY(*p == '\"') || RAPIDJSON_UNLIKELY(*p == '\\') || RAPIDJSON_UNLIKELY(static_cast(*p) < 0x20)) { + is.src_ = p; + return; + } + else + os.Put(*p++); + + // The rest of string using SIMD + static const char dquote[16] = { '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"' }; + static const char bslash[16] = { '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\' }; + static const char space[16] = { 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F }; + const __m128i dq = _mm_loadu_si128(reinterpret_cast(&dquote[0])); + const __m128i bs = _mm_loadu_si128(reinterpret_cast(&bslash[0])); + const __m128i sp = _mm_loadu_si128(reinterpret_cast(&space[0])); + + for (;; p += 16) { + const __m128i s = _mm_load_si128(reinterpret_cast(p)); + const __m128i t1 = _mm_cmpeq_epi8(s, dq); + const __m128i t2 = _mm_cmpeq_epi8(s, bs); + const __m128i t3 = _mm_cmpeq_epi8(_mm_max_epu8(s, sp), sp); // s < 0x20 <=> max(s, 0x1F) == 0x1F + const __m128i x = _mm_or_si128(_mm_or_si128(t1, t2), t3); + unsigned short r = static_cast(_mm_movemask_epi8(x)); + if (RAPIDJSON_UNLIKELY(r != 0)) { // some of characters is escaped + SizeType length; + #ifdef _MSC_VER // Find the index of first escaped + unsigned long offset; + _BitScanForward(&offset, r); + length = offset; + #else + length = static_cast(__builtin_ffs(r) - 1); + #endif + if (length != 0) { + char* q = reinterpret_cast(os.Push(length)); + for (size_t i = 0; i < length; i++) + q[i] = p[i]; + + p += length; + } + break; + } + _mm_storeu_si128(reinterpret_cast<__m128i *>(os.Push(16)), s); + } + + is.src_ = p; + } + + // InsituStringStream -> InsituStringStream + static RAPIDJSON_FORCEINLINE void ScanCopyUnescapedString(InsituStringStream& is, InsituStringStream& os) { + RAPIDJSON_ASSERT(&is == &os); + (void)os; + + if (is.src_ == is.dst_) { + SkipUnescapedString(is); + return; + } + + char* p = is.src_; + char *q = is.dst_; + + // Scan one by one until alignment (unaligned load may cross page boundary and cause crash) + const char* nextAligned = reinterpret_cast((reinterpret_cast(p) + 15) & static_cast(~15)); + while (p != nextAligned) + if (RAPIDJSON_UNLIKELY(*p == '\"') || RAPIDJSON_UNLIKELY(*p == '\\') || RAPIDJSON_UNLIKELY(static_cast(*p) < 0x20)) { + is.src_ = p; + is.dst_ = q; + return; + } + else + *q++ = *p++; + + // The rest of string using SIMD + static const char dquote[16] = { '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"' }; + static const char bslash[16] = { '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\' }; + static const char space[16] = { 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F }; + const __m128i dq = _mm_loadu_si128(reinterpret_cast(&dquote[0])); + const __m128i bs = _mm_loadu_si128(reinterpret_cast(&bslash[0])); + const __m128i sp = _mm_loadu_si128(reinterpret_cast(&space[0])); + + for (;; p += 16, q += 16) { + const __m128i s = _mm_load_si128(reinterpret_cast(p)); + const __m128i t1 = _mm_cmpeq_epi8(s, dq); + const __m128i t2 = _mm_cmpeq_epi8(s, bs); + const __m128i t3 = _mm_cmpeq_epi8(_mm_max_epu8(s, sp), sp); // s < 0x20 <=> max(s, 0x1F) == 0x1F + const __m128i x = _mm_or_si128(_mm_or_si128(t1, t2), t3); + unsigned short r = static_cast(_mm_movemask_epi8(x)); + if (RAPIDJSON_UNLIKELY(r != 0)) { // some of characters is escaped + size_t length; +#ifdef _MSC_VER // Find the index of first escaped + unsigned long offset; + _BitScanForward(&offset, r); + length = offset; +#else + length = static_cast(__builtin_ffs(r) - 1); +#endif + for (const char* pend = p + length; p != pend; ) + *q++ = *p++; + break; + } + _mm_storeu_si128(reinterpret_cast<__m128i *>(q), s); + } + + is.src_ = p; + is.dst_ = q; + } + + // When read/write pointers are the same for insitu stream, just skip unescaped characters + static RAPIDJSON_FORCEINLINE void SkipUnescapedString(InsituStringStream& is) { + RAPIDJSON_ASSERT(is.src_ == is.dst_); + char* p = is.src_; + + // Scan one by one until alignment (unaligned load may cross page boundary and cause crash) + const char* nextAligned = reinterpret_cast((reinterpret_cast(p) + 15) & static_cast(~15)); + for (; p != nextAligned; p++) + if (RAPIDJSON_UNLIKELY(*p == '\"') || RAPIDJSON_UNLIKELY(*p == '\\') || RAPIDJSON_UNLIKELY(static_cast(*p) < 0x20)) { + is.src_ = is.dst_ = p; + return; + } + + // The rest of string using SIMD + static const char dquote[16] = { '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"' }; + static const char bslash[16] = { '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\' }; + static const char space[16] = { 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F }; + const __m128i dq = _mm_loadu_si128(reinterpret_cast(&dquote[0])); + const __m128i bs = _mm_loadu_si128(reinterpret_cast(&bslash[0])); + const __m128i sp = _mm_loadu_si128(reinterpret_cast(&space[0])); + + for (;; p += 16) { + const __m128i s = _mm_load_si128(reinterpret_cast(p)); + const __m128i t1 = _mm_cmpeq_epi8(s, dq); + const __m128i t2 = _mm_cmpeq_epi8(s, bs); + const __m128i t3 = _mm_cmpeq_epi8(_mm_max_epu8(s, sp), sp); // s < 0x20 <=> max(s, 0x1F) == 0x1F + const __m128i x = _mm_or_si128(_mm_or_si128(t1, t2), t3); + unsigned short r = static_cast(_mm_movemask_epi8(x)); + if (RAPIDJSON_UNLIKELY(r != 0)) { // some of characters is escaped + size_t length; +#ifdef _MSC_VER // Find the index of first escaped + unsigned long offset; + _BitScanForward(&offset, r); + length = offset; +#else + length = static_cast(__builtin_ffs(r) - 1); +#endif + p += length; + break; + } + } + + is.src_ = is.dst_ = p; + } +#elif defined(RAPIDJSON_NEON) + // StringStream -> StackStream + static RAPIDJSON_FORCEINLINE void ScanCopyUnescapedString(StringStream& is, StackStream& os) { + const char* p = is.src_; + + // Scan one by one until alignment (unaligned load may cross page boundary and cause crash) + const char* nextAligned = reinterpret_cast((reinterpret_cast(p) + 15) & static_cast(~15)); + while (p != nextAligned) + if (RAPIDJSON_UNLIKELY(*p == '\"') || RAPIDJSON_UNLIKELY(*p == '\\') || RAPIDJSON_UNLIKELY(static_cast(*p) < 0x20)) { + is.src_ = p; + return; + } + else + os.Put(*p++); + + // The rest of string using SIMD + const uint8x16_t s0 = vmovq_n_u8('"'); + const uint8x16_t s1 = vmovq_n_u8('\\'); + const uint8x16_t s2 = vmovq_n_u8('\b'); + const uint8x16_t s3 = vmovq_n_u8(32); + + for (;; p += 16) { + const uint8x16_t s = vld1q_u8(reinterpret_cast(p)); + uint8x16_t x = vceqq_u8(s, s0); + x = vorrq_u8(x, vceqq_u8(s, s1)); + x = vorrq_u8(x, vceqq_u8(s, s2)); + x = vorrq_u8(x, vcltq_u8(s, s3)); + + x = vrev64q_u8(x); // Rev in 64 + uint64_t low = vgetq_lane_u64(vreinterpretq_u64_u8(x), 0); // extract + uint64_t high = vgetq_lane_u64(vreinterpretq_u64_u8(x), 1); // extract + + SizeType length = 0; + bool escaped = false; + if (low == 0) { + if (high != 0) { + uint32_t lz = internal::clzll(high); + length = 8 + (lz >> 3); + escaped = true; + } + } else { + uint32_t lz = internal::clzll(low); + length = lz >> 3; + escaped = true; + } + if (RAPIDJSON_UNLIKELY(escaped)) { // some of characters is escaped + if (length != 0) { + char* q = reinterpret_cast(os.Push(length)); + for (size_t i = 0; i < length; i++) + q[i] = p[i]; + + p += length; + } + break; + } + vst1q_u8(reinterpret_cast(os.Push(16)), s); + } + + is.src_ = p; + } + + // InsituStringStream -> InsituStringStream + static RAPIDJSON_FORCEINLINE void ScanCopyUnescapedString(InsituStringStream& is, InsituStringStream& os) { + RAPIDJSON_ASSERT(&is == &os); + (void)os; + + if (is.src_ == is.dst_) { + SkipUnescapedString(is); + return; + } + + char* p = is.src_; + char *q = is.dst_; + + // Scan one by one until alignment (unaligned load may cross page boundary and cause crash) + const char* nextAligned = reinterpret_cast((reinterpret_cast(p) + 15) & static_cast(~15)); + while (p != nextAligned) + if (RAPIDJSON_UNLIKELY(*p == '\"') || RAPIDJSON_UNLIKELY(*p == '\\') || RAPIDJSON_UNLIKELY(static_cast(*p) < 0x20)) { + is.src_ = p; + is.dst_ = q; + return; + } + else + *q++ = *p++; + + // The rest of string using SIMD + const uint8x16_t s0 = vmovq_n_u8('"'); + const uint8x16_t s1 = vmovq_n_u8('\\'); + const uint8x16_t s2 = vmovq_n_u8('\b'); + const uint8x16_t s3 = vmovq_n_u8(32); + + for (;; p += 16, q += 16) { + const uint8x16_t s = vld1q_u8(reinterpret_cast(p)); + uint8x16_t x = vceqq_u8(s, s0); + x = vorrq_u8(x, vceqq_u8(s, s1)); + x = vorrq_u8(x, vceqq_u8(s, s2)); + x = vorrq_u8(x, vcltq_u8(s, s3)); + + x = vrev64q_u8(x); // Rev in 64 + uint64_t low = vgetq_lane_u64(vreinterpretq_u64_u8(x), 0); // extract + uint64_t high = vgetq_lane_u64(vreinterpretq_u64_u8(x), 1); // extract + + SizeType length = 0; + bool escaped = false; + if (low == 0) { + if (high != 0) { + uint32_t lz = internal::clzll(high); + length = 8 + (lz >> 3); + escaped = true; + } + } else { + uint32_t lz = internal::clzll(low); + length = lz >> 3; + escaped = true; + } + if (RAPIDJSON_UNLIKELY(escaped)) { // some of characters is escaped + for (const char* pend = p + length; p != pend; ) { + *q++ = *p++; + } + break; + } + vst1q_u8(reinterpret_cast(q), s); + } + + is.src_ = p; + is.dst_ = q; + } + + // When read/write pointers are the same for insitu stream, just skip unescaped characters + static RAPIDJSON_FORCEINLINE void SkipUnescapedString(InsituStringStream& is) { + RAPIDJSON_ASSERT(is.src_ == is.dst_); + char* p = is.src_; + + // Scan one by one until alignment (unaligned load may cross page boundary and cause crash) + const char* nextAligned = reinterpret_cast((reinterpret_cast(p) + 15) & static_cast(~15)); + for (; p != nextAligned; p++) + if (RAPIDJSON_UNLIKELY(*p == '\"') || RAPIDJSON_UNLIKELY(*p == '\\') || RAPIDJSON_UNLIKELY(static_cast(*p) < 0x20)) { + is.src_ = is.dst_ = p; + return; + } + + // The rest of string using SIMD + const uint8x16_t s0 = vmovq_n_u8('"'); + const uint8x16_t s1 = vmovq_n_u8('\\'); + const uint8x16_t s2 = vmovq_n_u8('\b'); + const uint8x16_t s3 = vmovq_n_u8(32); + + for (;; p += 16) { + const uint8x16_t s = vld1q_u8(reinterpret_cast(p)); + uint8x16_t x = vceqq_u8(s, s0); + x = vorrq_u8(x, vceqq_u8(s, s1)); + x = vorrq_u8(x, vceqq_u8(s, s2)); + x = vorrq_u8(x, vcltq_u8(s, s3)); + + x = vrev64q_u8(x); // Rev in 64 + uint64_t low = vgetq_lane_u64(vreinterpretq_u64_u8(x), 0); // extract + uint64_t high = vgetq_lane_u64(vreinterpretq_u64_u8(x), 1); // extract + + if (low == 0) { + if (high != 0) { + uint32_t lz = internal::clzll(high); + p += 8 + (lz >> 3); + break; + } + } else { + uint32_t lz = internal::clzll(low); + p += lz >> 3; + break; + } + } + + is.src_ = is.dst_ = p; + } +#endif // RAPIDJSON_NEON + + template + class NumberStream; + + template + class NumberStream { + public: + typedef typename InputStream::Ch Ch; + + NumberStream(GenericReader& reader, InputStream& s) : is(s) { (void)reader; } + + RAPIDJSON_FORCEINLINE Ch Peek() const { return is.Peek(); } + RAPIDJSON_FORCEINLINE Ch TakePush() { return is.Take(); } + RAPIDJSON_FORCEINLINE Ch Take() { return is.Take(); } + RAPIDJSON_FORCEINLINE void Push(char) {} + + size_t Tell() { return is.Tell(); } + size_t Length() { return 0; } + const StackCharacter* Pop() { return 0; } + + protected: + NumberStream& operator=(const NumberStream&); + + InputStream& is; + }; + + template + class NumberStream : public NumberStream { + typedef NumberStream Base; + public: + NumberStream(GenericReader& reader, InputStream& s) : Base(reader, s), stackStream(reader.stack_) {} + + RAPIDJSON_FORCEINLINE Ch TakePush() { + stackStream.Put(static_cast(Base::is.Peek())); + return Base::is.Take(); + } + + RAPIDJSON_FORCEINLINE void Push(StackCharacter c) { + stackStream.Put(c); + } + + size_t Length() { return stackStream.Length(); } + + const StackCharacter* Pop() { + stackStream.Put('\0'); + return stackStream.Pop(); + } + + private: + StackStream stackStream; + }; + + template + class NumberStream : public NumberStream { + typedef NumberStream Base; + public: + NumberStream(GenericReader& reader, InputStream& s) : Base(reader, s) {} + + RAPIDJSON_FORCEINLINE Ch Take() { return Base::TakePush(); } + }; + + template + void ParseNumber(InputStream& is, Handler& handler) { + typedef typename internal::SelectIf, typename TargetEncoding::Ch, char>::Type NumberCharacter; + + internal::StreamLocalCopy copy(is); + NumberStream s(*this, copy.s); + + size_t startOffset = s.Tell(); + double d = 0.0; + bool useNanOrInf = false; + + // Parse minus + bool minus = Consume(s, '-'); + + // Parse int: zero / ( digit1-9 *DIGIT ) + unsigned i = 0; + uint64_t i64 = 0; + bool use64bit = false; + int significandDigit = 0; + if (RAPIDJSON_UNLIKELY(s.Peek() == '0')) { + i = 0; + s.TakePush(); + } + else if (RAPIDJSON_LIKELY(s.Peek() >= '1' && s.Peek() <= '9')) { + i = static_cast(s.TakePush() - '0'); + + if (minus) + while (RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) { + if (RAPIDJSON_UNLIKELY(i >= 214748364)) { // 2^31 = 2147483648 + if (RAPIDJSON_LIKELY(i != 214748364 || s.Peek() > '8')) { + i64 = i; + use64bit = true; + break; + } + } + i = i * 10 + static_cast(s.TakePush() - '0'); + significandDigit++; + } + else + while (RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) { + if (RAPIDJSON_UNLIKELY(i >= 429496729)) { // 2^32 - 1 = 4294967295 + if (RAPIDJSON_LIKELY(i != 429496729 || s.Peek() > '5')) { + i64 = i; + use64bit = true; + break; + } + } + i = i * 10 + static_cast(s.TakePush() - '0'); + significandDigit++; + } + } + // Parse NaN or Infinity here + else if ((parseFlags & kParseNanAndInfFlag) && RAPIDJSON_LIKELY((s.Peek() == 'I' || s.Peek() == 'N'))) { + if (Consume(s, 'N')) { + if (Consume(s, 'a') && Consume(s, 'N')) { + d = std::numeric_limits::quiet_NaN(); + useNanOrInf = true; + } + } + else if (RAPIDJSON_LIKELY(Consume(s, 'I'))) { + if (Consume(s, 'n') && Consume(s, 'f')) { + d = (minus ? -std::numeric_limits::infinity() : std::numeric_limits::infinity()); + useNanOrInf = true; + + if (RAPIDJSON_UNLIKELY(s.Peek() == 'i' && !(Consume(s, 'i') && Consume(s, 'n') + && Consume(s, 'i') && Consume(s, 't') && Consume(s, 'y')))) { + RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, s.Tell()); + } + } + } + + if (RAPIDJSON_UNLIKELY(!useNanOrInf)) { + RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, s.Tell()); + } + } + else + RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, s.Tell()); + + // Parse 64bit int + bool useDouble = false; + if (use64bit) { + if (minus) + while (RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) { + if (RAPIDJSON_UNLIKELY(i64 >= RAPIDJSON_UINT64_C2(0x0CCCCCCC, 0xCCCCCCCC))) // 2^63 = 9223372036854775808 + if (RAPIDJSON_LIKELY(i64 != RAPIDJSON_UINT64_C2(0x0CCCCCCC, 0xCCCCCCCC) || s.Peek() > '8')) { + d = static_cast(i64); + useDouble = true; + break; + } + i64 = i64 * 10 + static_cast(s.TakePush() - '0'); + significandDigit++; + } + else + while (RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) { + if (RAPIDJSON_UNLIKELY(i64 >= RAPIDJSON_UINT64_C2(0x19999999, 0x99999999))) // 2^64 - 1 = 18446744073709551615 + if (RAPIDJSON_LIKELY(i64 != RAPIDJSON_UINT64_C2(0x19999999, 0x99999999) || s.Peek() > '5')) { + d = static_cast(i64); + useDouble = true; + break; + } + i64 = i64 * 10 + static_cast(s.TakePush() - '0'); + significandDigit++; + } + } + + // Force double for big integer + if (useDouble) { + while (RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) { + d = d * 10 + (s.TakePush() - '0'); + } + } + + // Parse frac = decimal-point 1*DIGIT + int expFrac = 0; + size_t decimalPosition; + if (Consume(s, '.')) { + decimalPosition = s.Length(); + + if (RAPIDJSON_UNLIKELY(!(s.Peek() >= '0' && s.Peek() <= '9'))) + RAPIDJSON_PARSE_ERROR(kParseErrorNumberMissFraction, s.Tell()); + + if (!useDouble) { +#if RAPIDJSON_64BIT + // Use i64 to store significand in 64-bit architecture + if (!use64bit) + i64 = i; + + while (RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) { + if (i64 > RAPIDJSON_UINT64_C2(0x1FFFFF, 0xFFFFFFFF)) // 2^53 - 1 for fast path + break; + else { + i64 = i64 * 10 + static_cast(s.TakePush() - '0'); + --expFrac; + if (i64 != 0) + significandDigit++; + } + } + + d = static_cast(i64); +#else + // Use double to store significand in 32-bit architecture + d = static_cast(use64bit ? i64 : i); +#endif + useDouble = true; + } + + while (RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) { + if (significandDigit < 17) { + d = d * 10.0 + (s.TakePush() - '0'); + --expFrac; + if (RAPIDJSON_LIKELY(d > 0.0)) + significandDigit++; + } + else + s.TakePush(); + } + } + else + decimalPosition = s.Length(); // decimal position at the end of integer. + + // Parse exp = e [ minus / plus ] 1*DIGIT + int exp = 0; + if (Consume(s, 'e') || Consume(s, 'E')) { + if (!useDouble) { + d = static_cast(use64bit ? i64 : i); + useDouble = true; + } + + bool expMinus = false; + if (Consume(s, '+')) + ; + else if (Consume(s, '-')) + expMinus = true; + + if (RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) { + exp = static_cast(s.Take() - '0'); + if (expMinus) { + // (exp + expFrac) must not underflow int => we're detecting when -exp gets + // dangerously close to INT_MIN (a pessimistic next digit 9 would push it into + // underflow territory): + // + // -(exp * 10 + 9) + expFrac >= INT_MIN + // <=> exp <= (expFrac - INT_MIN - 9) / 10 + RAPIDJSON_ASSERT(expFrac <= 0); + int maxExp = (expFrac + 2147483639) / 10; + + while (RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) { + exp = exp * 10 + static_cast(s.Take() - '0'); + if (RAPIDJSON_UNLIKELY(exp > maxExp)) { + while (RAPIDJSON_UNLIKELY(s.Peek() >= '0' && s.Peek() <= '9')) // Consume the rest of exponent + s.Take(); + } + } + } + else { // positive exp + int maxExp = 308 - expFrac; + while (RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) { + exp = exp * 10 + static_cast(s.Take() - '0'); + if (RAPIDJSON_UNLIKELY(exp > maxExp)) + RAPIDJSON_PARSE_ERROR(kParseErrorNumberTooBig, startOffset); + } + } + } + else + RAPIDJSON_PARSE_ERROR(kParseErrorNumberMissExponent, s.Tell()); + + if (expMinus) + exp = -exp; + } + + // Finish parsing, call event according to the type of number. + bool cont = true; + + if (parseFlags & kParseNumbersAsStringsFlag) { + if (parseFlags & kParseInsituFlag) { + s.Pop(); // Pop stack no matter if it will be used or not. + typename InputStream::Ch* head = is.PutBegin(); + const size_t length = s.Tell() - startOffset; + RAPIDJSON_ASSERT(length <= 0xFFFFFFFF); + // unable to insert the \0 character here, it will erase the comma after this number + const typename TargetEncoding::Ch* const str = reinterpret_cast(head); + cont = handler.RawNumber(str, SizeType(length), false); + } + else { + SizeType numCharsToCopy = static_cast(s.Length()); + GenericStringStream > srcStream(s.Pop()); + StackStream dstStream(stack_); + while (numCharsToCopy--) { + Transcoder, TargetEncoding>::Transcode(srcStream, dstStream); + } + dstStream.Put('\0'); + const typename TargetEncoding::Ch* str = dstStream.Pop(); + const SizeType length = static_cast(dstStream.Length()) - 1; + cont = handler.RawNumber(str, SizeType(length), true); + } + } + else { + size_t length = s.Length(); + const NumberCharacter* decimal = s.Pop(); // Pop stack no matter if it will be used or not. + + if (useDouble) { + int p = exp + expFrac; + if (parseFlags & kParseFullPrecisionFlag) + d = internal::StrtodFullPrecision(d, p, decimal, length, decimalPosition, exp); + else + d = internal::StrtodNormalPrecision(d, p); + + // Use > max, instead of == inf, to fix bogus warning -Wfloat-equal + if (d > (std::numeric_limits::max)()) { + // Overflow + // TODO: internal::StrtodX should report overflow (or underflow) + RAPIDJSON_PARSE_ERROR(kParseErrorNumberTooBig, startOffset); + } + + cont = handler.Double(minus ? -d : d); + } + else if (useNanOrInf) { + cont = handler.Double(d); + } + else { + if (use64bit) { + if (minus) + cont = handler.Int64(static_cast(~i64 + 1)); + else + cont = handler.Uint64(i64); + } + else { + if (minus) + cont = handler.Int(static_cast(~i + 1)); + else + cont = handler.Uint(i); + } + } + } + if (RAPIDJSON_UNLIKELY(!cont)) + RAPIDJSON_PARSE_ERROR(kParseErrorTermination, startOffset); + } + + // Parse any JSON value + template + void ParseValue(InputStream& is, Handler& handler) { + switch (is.Peek()) { + case 'n': ParseNull (is, handler); break; + case 't': ParseTrue (is, handler); break; + case 'f': ParseFalse (is, handler); break; + case '"': ParseString(is, handler); break; + case '{': ParseObject(is, handler); break; + case '[': ParseArray (is, handler); break; + default : + ParseNumber(is, handler); + break; + + } + } + + // Iterative Parsing + + // States + enum IterativeParsingState { + IterativeParsingFinishState = 0, // sink states at top + IterativeParsingErrorState, // sink states at top + IterativeParsingStartState, + + // Object states + IterativeParsingObjectInitialState, + IterativeParsingMemberKeyState, + IterativeParsingMemberValueState, + IterativeParsingObjectFinishState, + + // Array states + IterativeParsingArrayInitialState, + IterativeParsingElementState, + IterativeParsingArrayFinishState, + + // Single value state + IterativeParsingValueState, + + // Delimiter states (at bottom) + IterativeParsingElementDelimiterState, + IterativeParsingMemberDelimiterState, + IterativeParsingKeyValueDelimiterState, + + cIterativeParsingStateCount + }; + + // Tokens + enum Token { + LeftBracketToken = 0, + RightBracketToken, + + LeftCurlyBracketToken, + RightCurlyBracketToken, + + CommaToken, + ColonToken, + + StringToken, + FalseToken, + TrueToken, + NullToken, + NumberToken, + + kTokenCount + }; + + RAPIDJSON_FORCEINLINE Token Tokenize(Ch c) const { + +//!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN +#define N NumberToken +#define N16 N,N,N,N,N,N,N,N,N,N,N,N,N,N,N,N + // Maps from ASCII to Token + static const unsigned char tokenMap[256] = { + N16, // 00~0F + N16, // 10~1F + N, N, StringToken, N, N, N, N, N, N, N, N, N, CommaToken, N, N, N, // 20~2F + N, N, N, N, N, N, N, N, N, N, ColonToken, N, N, N, N, N, // 30~3F + N16, // 40~4F + N, N, N, N, N, N, N, N, N, N, N, LeftBracketToken, N, RightBracketToken, N, N, // 50~5F + N, N, N, N, N, N, FalseToken, N, N, N, N, N, N, N, NullToken, N, // 60~6F + N, N, N, N, TrueToken, N, N, N, N, N, N, LeftCurlyBracketToken, N, RightCurlyBracketToken, N, N, // 70~7F + N16, N16, N16, N16, N16, N16, N16, N16 // 80~FF + }; +#undef N +#undef N16 +//!@endcond + + if (sizeof(Ch) == 1 || static_cast(c) < 256) + return static_cast(tokenMap[static_cast(c)]); + else + return NumberToken; + } + + RAPIDJSON_FORCEINLINE IterativeParsingState Predict(IterativeParsingState state, Token token) const { + // current state x one lookahead token -> new state + static const char G[cIterativeParsingStateCount][kTokenCount] = { + // Finish(sink state) + { + IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, + IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, + IterativeParsingErrorState + }, + // Error(sink state) + { + IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, + IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, + IterativeParsingErrorState + }, + // Start + { + IterativeParsingArrayInitialState, // Left bracket + IterativeParsingErrorState, // Right bracket + IterativeParsingObjectInitialState, // Left curly bracket + IterativeParsingErrorState, // Right curly bracket + IterativeParsingErrorState, // Comma + IterativeParsingErrorState, // Colon + IterativeParsingValueState, // String + IterativeParsingValueState, // False + IterativeParsingValueState, // True + IterativeParsingValueState, // Null + IterativeParsingValueState // Number + }, + // ObjectInitial + { + IterativeParsingErrorState, // Left bracket + IterativeParsingErrorState, // Right bracket + IterativeParsingErrorState, // Left curly bracket + IterativeParsingObjectFinishState, // Right curly bracket + IterativeParsingErrorState, // Comma + IterativeParsingErrorState, // Colon + IterativeParsingMemberKeyState, // String + IterativeParsingErrorState, // False + IterativeParsingErrorState, // True + IterativeParsingErrorState, // Null + IterativeParsingErrorState // Number + }, + // MemberKey + { + IterativeParsingErrorState, // Left bracket + IterativeParsingErrorState, // Right bracket + IterativeParsingErrorState, // Left curly bracket + IterativeParsingErrorState, // Right curly bracket + IterativeParsingErrorState, // Comma + IterativeParsingKeyValueDelimiterState, // Colon + IterativeParsingErrorState, // String + IterativeParsingErrorState, // False + IterativeParsingErrorState, // True + IterativeParsingErrorState, // Null + IterativeParsingErrorState // Number + }, + // MemberValue + { + IterativeParsingErrorState, // Left bracket + IterativeParsingErrorState, // Right bracket + IterativeParsingErrorState, // Left curly bracket + IterativeParsingObjectFinishState, // Right curly bracket + IterativeParsingMemberDelimiterState, // Comma + IterativeParsingErrorState, // Colon + IterativeParsingErrorState, // String + IterativeParsingErrorState, // False + IterativeParsingErrorState, // True + IterativeParsingErrorState, // Null + IterativeParsingErrorState // Number + }, + // ObjectFinish(sink state) + { + IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, + IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, + IterativeParsingErrorState + }, + // ArrayInitial + { + IterativeParsingArrayInitialState, // Left bracket(push Element state) + IterativeParsingArrayFinishState, // Right bracket + IterativeParsingObjectInitialState, // Left curly bracket(push Element state) + IterativeParsingErrorState, // Right curly bracket + IterativeParsingErrorState, // Comma + IterativeParsingErrorState, // Colon + IterativeParsingElementState, // String + IterativeParsingElementState, // False + IterativeParsingElementState, // True + IterativeParsingElementState, // Null + IterativeParsingElementState // Number + }, + // Element + { + IterativeParsingErrorState, // Left bracket + IterativeParsingArrayFinishState, // Right bracket + IterativeParsingErrorState, // Left curly bracket + IterativeParsingErrorState, // Right curly bracket + IterativeParsingElementDelimiterState, // Comma + IterativeParsingErrorState, // Colon + IterativeParsingErrorState, // String + IterativeParsingErrorState, // False + IterativeParsingErrorState, // True + IterativeParsingErrorState, // Null + IterativeParsingErrorState // Number + }, + // ArrayFinish(sink state) + { + IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, + IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, + IterativeParsingErrorState + }, + // Single Value (sink state) + { + IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, + IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, + IterativeParsingErrorState + }, + // ElementDelimiter + { + IterativeParsingArrayInitialState, // Left bracket(push Element state) + IterativeParsingArrayFinishState, // Right bracket + IterativeParsingObjectInitialState, // Left curly bracket(push Element state) + IterativeParsingErrorState, // Right curly bracket + IterativeParsingErrorState, // Comma + IterativeParsingErrorState, // Colon + IterativeParsingElementState, // String + IterativeParsingElementState, // False + IterativeParsingElementState, // True + IterativeParsingElementState, // Null + IterativeParsingElementState // Number + }, + // MemberDelimiter + { + IterativeParsingErrorState, // Left bracket + IterativeParsingErrorState, // Right bracket + IterativeParsingErrorState, // Left curly bracket + IterativeParsingObjectFinishState, // Right curly bracket + IterativeParsingErrorState, // Comma + IterativeParsingErrorState, // Colon + IterativeParsingMemberKeyState, // String + IterativeParsingErrorState, // False + IterativeParsingErrorState, // True + IterativeParsingErrorState, // Null + IterativeParsingErrorState // Number + }, + // KeyValueDelimiter + { + IterativeParsingArrayInitialState, // Left bracket(push MemberValue state) + IterativeParsingErrorState, // Right bracket + IterativeParsingObjectInitialState, // Left curly bracket(push MemberValue state) + IterativeParsingErrorState, // Right curly bracket + IterativeParsingErrorState, // Comma + IterativeParsingErrorState, // Colon + IterativeParsingMemberValueState, // String + IterativeParsingMemberValueState, // False + IterativeParsingMemberValueState, // True + IterativeParsingMemberValueState, // Null + IterativeParsingMemberValueState // Number + }, + }; // End of G + + return static_cast(G[state][token]); + } + + // Make an advance in the token stream and state based on the candidate destination state which was returned by Transit(). + // May return a new state on state pop. + template + RAPIDJSON_FORCEINLINE IterativeParsingState Transit(IterativeParsingState src, Token token, IterativeParsingState dst, InputStream& is, Handler& handler) { + (void)token; + + switch (dst) { + case IterativeParsingErrorState: + return dst; + + case IterativeParsingObjectInitialState: + case IterativeParsingArrayInitialState: + { + // Push the state(Element or MemeberValue) if we are nested in another array or value of member. + // In this way we can get the correct state on ObjectFinish or ArrayFinish by frame pop. + IterativeParsingState n = src; + if (src == IterativeParsingArrayInitialState || src == IterativeParsingElementDelimiterState) + n = IterativeParsingElementState; + else if (src == IterativeParsingKeyValueDelimiterState) + n = IterativeParsingMemberValueState; + // Push current state. + *stack_.template Push(1) = n; + // Initialize and push the member/element count. + *stack_.template Push(1) = 0; + // Call handler + bool hr = (dst == IterativeParsingObjectInitialState) ? handler.StartObject() : handler.StartArray(); + // On handler short circuits the parsing. + if (!hr) { + RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorTermination, is.Tell()); + return IterativeParsingErrorState; + } + else { + is.Take(); + return dst; + } + } + + case IterativeParsingMemberKeyState: + ParseString(is, handler, true); + if (HasParseError()) + return IterativeParsingErrorState; + else + return dst; + + case IterativeParsingKeyValueDelimiterState: + RAPIDJSON_ASSERT(token == ColonToken); + is.Take(); + return dst; + + case IterativeParsingMemberValueState: + // Must be non-compound value. Or it would be ObjectInitial or ArrayInitial state. + ParseValue(is, handler); + if (HasParseError()) { + return IterativeParsingErrorState; + } + return dst; + + case IterativeParsingElementState: + // Must be non-compound value. Or it would be ObjectInitial or ArrayInitial state. + ParseValue(is, handler); + if (HasParseError()) { + return IterativeParsingErrorState; + } + return dst; + + case IterativeParsingMemberDelimiterState: + case IterativeParsingElementDelimiterState: + is.Take(); + // Update member/element count. + *stack_.template Top() = *stack_.template Top() + 1; + return dst; + + case IterativeParsingObjectFinishState: + { + // Transit from delimiter is only allowed when trailing commas are enabled + if (!(parseFlags & kParseTrailingCommasFlag) && src == IterativeParsingMemberDelimiterState) { + RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorObjectMissName, is.Tell()); + return IterativeParsingErrorState; + } + // Get member count. + SizeType c = *stack_.template Pop(1); + // If the object is not empty, count the last member. + if (src == IterativeParsingMemberValueState) + ++c; + // Restore the state. + IterativeParsingState n = static_cast(*stack_.template Pop(1)); + // Transit to Finish state if this is the topmost scope. + if (n == IterativeParsingStartState) + n = IterativeParsingFinishState; + // Call handler + bool hr = handler.EndObject(c); + // On handler short circuits the parsing. + if (!hr) { + RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorTermination, is.Tell()); + return IterativeParsingErrorState; + } + else { + is.Take(); + return n; + } + } + + case IterativeParsingArrayFinishState: + { + // Transit from delimiter is only allowed when trailing commas are enabled + if (!(parseFlags & kParseTrailingCommasFlag) && src == IterativeParsingElementDelimiterState) { + RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorValueInvalid, is.Tell()); + return IterativeParsingErrorState; + } + // Get element count. + SizeType c = *stack_.template Pop(1); + // If the array is not empty, count the last element. + if (src == IterativeParsingElementState) + ++c; + // Restore the state. + IterativeParsingState n = static_cast(*stack_.template Pop(1)); + // Transit to Finish state if this is the topmost scope. + if (n == IterativeParsingStartState) + n = IterativeParsingFinishState; + // Call handler + bool hr = handler.EndArray(c); + // On handler short circuits the parsing. + if (!hr) { + RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorTermination, is.Tell()); + return IterativeParsingErrorState; + } + else { + is.Take(); + return n; + } + } + + default: + // This branch is for IterativeParsingValueState actually. + // Use `default:` rather than + // `case IterativeParsingValueState:` is for code coverage. + + // The IterativeParsingStartState is not enumerated in this switch-case. + // It is impossible for that case. And it can be caught by following assertion. + + // The IterativeParsingFinishState is not enumerated in this switch-case either. + // It is a "derivative" state which cannot triggered from Predict() directly. + // Therefore it cannot happen here. And it can be caught by following assertion. + RAPIDJSON_ASSERT(dst == IterativeParsingValueState); + + // Must be non-compound value. Or it would be ObjectInitial or ArrayInitial state. + ParseValue(is, handler); + if (HasParseError()) { + return IterativeParsingErrorState; + } + return IterativeParsingFinishState; + } + } + + template + void HandleError(IterativeParsingState src, InputStream& is) { + if (HasParseError()) { + // Error flag has been set. + return; + } + + switch (src) { + case IterativeParsingStartState: RAPIDJSON_PARSE_ERROR(kParseErrorDocumentEmpty, is.Tell()); return; + case IterativeParsingFinishState: RAPIDJSON_PARSE_ERROR(kParseErrorDocumentRootNotSingular, is.Tell()); return; + case IterativeParsingObjectInitialState: + case IterativeParsingMemberDelimiterState: RAPIDJSON_PARSE_ERROR(kParseErrorObjectMissName, is.Tell()); return; + case IterativeParsingMemberKeyState: RAPIDJSON_PARSE_ERROR(kParseErrorObjectMissColon, is.Tell()); return; + case IterativeParsingMemberValueState: RAPIDJSON_PARSE_ERROR(kParseErrorObjectMissCommaOrCurlyBracket, is.Tell()); return; + case IterativeParsingKeyValueDelimiterState: + case IterativeParsingArrayInitialState: + case IterativeParsingElementDelimiterState: RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, is.Tell()); return; + default: RAPIDJSON_ASSERT(src == IterativeParsingElementState); RAPIDJSON_PARSE_ERROR(kParseErrorArrayMissCommaOrSquareBracket, is.Tell()); return; + } + } + + RAPIDJSON_FORCEINLINE bool IsIterativeParsingDelimiterState(IterativeParsingState s) const { + return s >= IterativeParsingElementDelimiterState; + } + + RAPIDJSON_FORCEINLINE bool IsIterativeParsingCompleteState(IterativeParsingState s) const { + return s <= IterativeParsingErrorState; + } + + template + ParseResult IterativeParse(InputStream& is, Handler& handler) { + parseResult_.Clear(); + ClearStackOnExit scope(*this); + IterativeParsingState state = IterativeParsingStartState; + + SkipWhitespaceAndComments(is); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN(parseResult_); + while (is.Peek() != '\0') { + Token t = Tokenize(is.Peek()); + IterativeParsingState n = Predict(state, t); + IterativeParsingState d = Transit(state, t, n, is, handler); + + if (d == IterativeParsingErrorState) { + HandleError(state, is); + break; + } + + state = d; + + // Do not further consume streams if a root JSON has been parsed. + if ((parseFlags & kParseStopWhenDoneFlag) && state == IterativeParsingFinishState) + break; + + SkipWhitespaceAndComments(is); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN(parseResult_); + } + + // Handle the end of file. + if (state != IterativeParsingFinishState) + HandleError(state, is); + + return parseResult_; + } + + static const size_t kDefaultStackCapacity = 256; //!< Default stack capacity in bytes for storing a single decoded string. + internal::Stack stack_; //!< A stack for storing decoded string temporarily during non-destructive parsing. + ParseResult parseResult_; + IterativeParsingState state_; +}; // class GenericReader + +//! Reader with UTF8 encoding and default allocator. +typedef GenericReader, UTF8<> > Reader; + +RAPIDJSON_NAMESPACE_END + +#if defined(__clang__) || defined(_MSC_VER) +RAPIDJSON_DIAG_POP +#endif + + +#ifdef __GNUC__ +RAPIDJSON_DIAG_POP +#endif + +#endif // RAPIDJSON_READER_H_ diff --git a/include/rapidjson/schema.h b/include/rapidjson/schema.h new file mode 100644 index 0000000..439133f --- /dev/null +++ b/include/rapidjson/schema.h @@ -0,0 +1,3262 @@ +// Tencent is pleased to support the open source community by making RapidJSON available-> +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip-> All rights reserved-> +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License-> You may obtain a copy of the License at +// +// http://opensource->org/licenses/MIT +// +// 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 RAPIDJSON_SCHEMA_H_ +#define RAPIDJSON_SCHEMA_H_ + +#include "document.h" +#include "pointer.h" +#include "stringbuffer.h" +#include "error/en.h" +#include "uri.h" +#include // abs, floor + +#if !defined(RAPIDJSON_SCHEMA_USE_INTERNALREGEX) +#define RAPIDJSON_SCHEMA_USE_INTERNALREGEX 1 +#else +#define RAPIDJSON_SCHEMA_USE_INTERNALREGEX 0 +#endif + +#if !RAPIDJSON_SCHEMA_USE_INTERNALREGEX && defined(RAPIDJSON_SCHEMA_USE_STDREGEX) && (__cplusplus >=201103L || (defined(_MSC_VER) && _MSC_VER >= 1800)) +#define RAPIDJSON_SCHEMA_USE_STDREGEX 1 +#else +#define RAPIDJSON_SCHEMA_USE_STDREGEX 0 +#endif + +#if RAPIDJSON_SCHEMA_USE_INTERNALREGEX +#include "internal/regex.h" +#elif RAPIDJSON_SCHEMA_USE_STDREGEX +#include +#endif + +#if RAPIDJSON_SCHEMA_USE_INTERNALREGEX || RAPIDJSON_SCHEMA_USE_STDREGEX +#define RAPIDJSON_SCHEMA_HAS_REGEX 1 +#else +#define RAPIDJSON_SCHEMA_HAS_REGEX 0 +#endif + +#ifndef RAPIDJSON_SCHEMA_VERBOSE +#define RAPIDJSON_SCHEMA_VERBOSE 0 +#endif + +RAPIDJSON_DIAG_PUSH + +#if defined(__GNUC__) +RAPIDJSON_DIAG_OFF(effc++) +#endif + +#ifdef __clang__ +RAPIDJSON_DIAG_OFF(weak-vtables) +RAPIDJSON_DIAG_OFF(exit-time-destructors) +RAPIDJSON_DIAG_OFF(c++98-compat-pedantic) +RAPIDJSON_DIAG_OFF(variadic-macros) +#elif defined(_MSC_VER) +RAPIDJSON_DIAG_OFF(4512) // assignment operator could not be generated +#endif + +RAPIDJSON_NAMESPACE_BEGIN + +/////////////////////////////////////////////////////////////////////////////// +// Verbose Utilities + +#if RAPIDJSON_SCHEMA_VERBOSE + +namespace internal { + +inline void PrintInvalidKeywordData(const char* keyword) { + printf(" Fail keyword: '%s'\n", keyword); +} + +inline void PrintInvalidKeywordData(const wchar_t* keyword) { + wprintf(L" Fail keyword: '%ls'\n", keyword); +} + +inline void PrintInvalidDocumentData(const char* document) { + printf(" Fail document: '%s'\n", document); +} + +inline void PrintInvalidDocumentData(const wchar_t* document) { + wprintf(L" Fail document: '%ls'\n", document); +} + +inline void PrintValidatorPointersData(const char* s, const char* d, unsigned depth) { + printf(" Sch: %*s'%s'\n Doc: %*s'%s'\n", depth * 4, " ", s, depth * 4, " ", d); +} + +inline void PrintValidatorPointersData(const wchar_t* s, const wchar_t* d, unsigned depth) { + wprintf(L" Sch: %*ls'%ls'\n Doc: %*ls'%ls'\n", depth * 4, L" ", s, depth * 4, L" ", d); +} + +inline void PrintSchemaIdsData(const char* base, const char* local, const char* resolved) { + printf(" Resolving id: Base: '%s', Local: '%s', Resolved: '%s'\n", base, local, resolved); +} + +inline void PrintSchemaIdsData(const wchar_t* base, const wchar_t* local, const wchar_t* resolved) { + wprintf(L" Resolving id: Base: '%ls', Local: '%ls', Resolved: '%ls'\n", base, local, resolved); +} + +inline void PrintMethodData(const char* method) { + printf("%s\n", method); +} + +inline void PrintMethodData(const char* method, bool b) { + printf("%s, Data: '%s'\n", method, b ? "true" : "false"); +} + +inline void PrintMethodData(const char* method, int64_t i) { + printf("%s, Data: '%" PRId64 "'\n", method, i); +} + +inline void PrintMethodData(const char* method, uint64_t u) { + printf("%s, Data: '%" PRIu64 "'\n", method, u); +} + +inline void PrintMethodData(const char* method, double d) { + printf("%s, Data: '%lf'\n", method, d); +} + +inline void PrintMethodData(const char* method, const char* s) { + printf("%s, Data: '%s'\n", method, s); +} + +inline void PrintMethodData(const char* method, const wchar_t* s) { + wprintf(L"%hs, Data: '%ls'\n", method, s); +} + +inline void PrintMethodData(const char* method, const char* s1, const char* s2) { + printf("%s, Data: '%s', '%s'\n", method, s1, s2); +} + +inline void PrintMethodData(const char* method, const wchar_t* s1, const wchar_t* s2) { + wprintf(L"%hs, Data: '%ls', '%ls'\n", method, s1, s2); +} + +} // namespace internal + +#endif // RAPIDJSON_SCHEMA_VERBOSE + +#ifndef RAPIDJSON_SCHEMA_PRINT +#if RAPIDJSON_SCHEMA_VERBOSE +#define RAPIDJSON_SCHEMA_PRINT(name, ...) internal::Print##name##Data(__VA_ARGS__) +#else +#define RAPIDJSON_SCHEMA_PRINT(name, ...) +#endif +#endif + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_INVALID_KEYWORD_RETURN + +#define RAPIDJSON_INVALID_KEYWORD_RETURN(code)\ +RAPIDJSON_MULTILINEMACRO_BEGIN\ + context.invalidCode = code;\ + context.invalidKeyword = SchemaType::GetValidateErrorKeyword(code).GetString();\ + RAPIDJSON_SCHEMA_PRINT(InvalidKeyword, context.invalidKeyword);\ + return false;\ +RAPIDJSON_MULTILINEMACRO_END + +/////////////////////////////////////////////////////////////////////////////// +// ValidateFlag + +/*! \def RAPIDJSON_VALIDATE_DEFAULT_FLAGS + \ingroup RAPIDJSON_CONFIG + \brief User-defined kValidateDefaultFlags definition. + + User can define this as any \c ValidateFlag combinations. +*/ +#ifndef RAPIDJSON_VALIDATE_DEFAULT_FLAGS +#define RAPIDJSON_VALIDATE_DEFAULT_FLAGS kValidateNoFlags +#endif + +//! Combination of validate flags +/*! \see + */ +enum ValidateFlag { + kValidateNoFlags = 0, //!< No flags are set. + kValidateContinueOnErrorFlag = 1, //!< Don't stop after first validation error. + kValidateReadFlag = 2, //!< Validation is for a read semantic. + kValidateWriteFlag = 4, //!< Validation is for a write semantic. + kValidateDefaultFlags = RAPIDJSON_VALIDATE_DEFAULT_FLAGS //!< Default validate flags. Can be customized by defining RAPIDJSON_VALIDATE_DEFAULT_FLAGS +}; + +/////////////////////////////////////////////////////////////////////////////// +// Specification +enum SchemaDraft { + kDraftUnknown = -1, + kDraftNone = 0, + kDraft03 = 3, + kDraftMin = 4, //!< Current minimum supported draft + kDraft04 = 4, + kDraft05 = 5, + kDraftMax = 5, //!< Current maximum supported draft + kDraft06 = 6, + kDraft07 = 7, + kDraft2019_09 = 8, + kDraft2020_12 = 9 +}; + +enum OpenApiVersion { + kVersionUnknown = -1, + kVersionNone = 0, + kVersionMin = 2, //!< Current minimum supported version + kVersion20 = 2, + kVersion30 = 3, + kVersionMax = 3, //!< Current maximum supported version + kVersion31 = 4, +}; + +struct Specification { + Specification(SchemaDraft d) : draft(d), oapi(kVersionNone) {} + Specification(OpenApiVersion o) : oapi(o) { + if (oapi == kVersion20) draft = kDraft04; + else if (oapi == kVersion30) draft = kDraft05; + else if (oapi == kVersion31) draft = kDraft2020_12; + else draft = kDraft04; + } + ~Specification() {} + bool IsSupported() const { + return ((draft >= kDraftMin && draft <= kDraftMax) && ((oapi == kVersionNone) || (oapi >= kVersionMin && oapi <= kVersionMax))); + } + SchemaDraft draft; + OpenApiVersion oapi; +}; + +/////////////////////////////////////////////////////////////////////////////// +// Forward declarations + +template +class GenericSchemaDocument; + +namespace internal { + +template +class Schema; + +/////////////////////////////////////////////////////////////////////////////// +// ISchemaValidator + +class ISchemaValidator { +public: + virtual ~ISchemaValidator() {} + virtual bool IsValid() const = 0; + virtual void SetValidateFlags(unsigned flags) = 0; + virtual unsigned GetValidateFlags() const = 0; +}; + +/////////////////////////////////////////////////////////////////////////////// +// ISchemaStateFactory + +template +class ISchemaStateFactory { +public: + virtual ~ISchemaStateFactory() {} + virtual ISchemaValidator* CreateSchemaValidator(const SchemaType&, const bool inheritContinueOnErrors) = 0; + virtual void DestroySchemaValidator(ISchemaValidator* validator) = 0; + virtual void* CreateHasher() = 0; + virtual uint64_t GetHashCode(void* hasher) = 0; + virtual void DestroryHasher(void* hasher) = 0; + virtual void* MallocState(size_t size) = 0; + virtual void FreeState(void* p) = 0; +}; + +/////////////////////////////////////////////////////////////////////////////// +// IValidationErrorHandler + +template +class IValidationErrorHandler { +public: + typedef typename SchemaType::Ch Ch; + typedef typename SchemaType::SValue SValue; + + virtual ~IValidationErrorHandler() {} + + virtual void NotMultipleOf(int64_t actual, const SValue& expected) = 0; + virtual void NotMultipleOf(uint64_t actual, const SValue& expected) = 0; + virtual void NotMultipleOf(double actual, const SValue& expected) = 0; + virtual void AboveMaximum(int64_t actual, const SValue& expected, bool exclusive) = 0; + virtual void AboveMaximum(uint64_t actual, const SValue& expected, bool exclusive) = 0; + virtual void AboveMaximum(double actual, const SValue& expected, bool exclusive) = 0; + virtual void BelowMinimum(int64_t actual, const SValue& expected, bool exclusive) = 0; + virtual void BelowMinimum(uint64_t actual, const SValue& expected, bool exclusive) = 0; + virtual void BelowMinimum(double actual, const SValue& expected, bool exclusive) = 0; + + virtual void TooLong(const Ch* str, SizeType length, SizeType expected) = 0; + virtual void TooShort(const Ch* str, SizeType length, SizeType expected) = 0; + virtual void DoesNotMatch(const Ch* str, SizeType length) = 0; + + virtual void DisallowedItem(SizeType index) = 0; + virtual void TooFewItems(SizeType actualCount, SizeType expectedCount) = 0; + virtual void TooManyItems(SizeType actualCount, SizeType expectedCount) = 0; + virtual void DuplicateItems(SizeType index1, SizeType index2) = 0; + + virtual void TooManyProperties(SizeType actualCount, SizeType expectedCount) = 0; + virtual void TooFewProperties(SizeType actualCount, SizeType expectedCount) = 0; + virtual void StartMissingProperties() = 0; + virtual void AddMissingProperty(const SValue& name) = 0; + virtual bool EndMissingProperties() = 0; + virtual void PropertyViolations(ISchemaValidator** subvalidators, SizeType count) = 0; + virtual void DisallowedProperty(const Ch* name, SizeType length) = 0; + + virtual void StartDependencyErrors() = 0; + virtual void StartMissingDependentProperties() = 0; + virtual void AddMissingDependentProperty(const SValue& targetName) = 0; + virtual void EndMissingDependentProperties(const SValue& sourceName) = 0; + virtual void AddDependencySchemaError(const SValue& souceName, ISchemaValidator* subvalidator) = 0; + virtual bool EndDependencyErrors() = 0; + + virtual void DisallowedValue(const ValidateErrorCode code) = 0; + virtual void StartDisallowedType() = 0; + virtual void AddExpectedType(const typename SchemaType::ValueType& expectedType) = 0; + virtual void EndDisallowedType(const typename SchemaType::ValueType& actualType) = 0; + virtual void NotAllOf(ISchemaValidator** subvalidators, SizeType count) = 0; + virtual void NoneOf(ISchemaValidator** subvalidators, SizeType count) = 0; + virtual void NotOneOf(ISchemaValidator** subvalidators, SizeType count) = 0; + virtual void MultipleOneOf(SizeType index1, SizeType index2) = 0; + virtual void Disallowed() = 0; + virtual void DisallowedWhenWriting() = 0; + virtual void DisallowedWhenReading() = 0; +}; + + +/////////////////////////////////////////////////////////////////////////////// +// Hasher + +// For comparison of compound value +template +class Hasher { +public: + typedef typename Encoding::Ch Ch; + + Hasher(Allocator* allocator = 0, size_t stackCapacity = kDefaultSize) : stack_(allocator, stackCapacity) {} + + bool Null() { return WriteType(kNullType); } + bool Bool(bool b) { return WriteType(b ? kTrueType : kFalseType); } + bool Int(int i) { Number n; n.u.i = i; n.d = static_cast(i); return WriteNumber(n); } + bool Uint(unsigned u) { Number n; n.u.u = u; n.d = static_cast(u); return WriteNumber(n); } + bool Int64(int64_t i) { Number n; n.u.i = i; n.d = static_cast(i); return WriteNumber(n); } + bool Uint64(uint64_t u) { Number n; n.u.u = u; n.d = static_cast(u); return WriteNumber(n); } + bool Double(double d) { + Number n; + if (d < 0) n.u.i = static_cast(d); + else n.u.u = static_cast(d); + n.d = d; + return WriteNumber(n); + } + + bool RawNumber(const Ch* str, SizeType len, bool) { + WriteBuffer(kNumberType, str, len * sizeof(Ch)); + return true; + } + + bool String(const Ch* str, SizeType len, bool) { + WriteBuffer(kStringType, str, len * sizeof(Ch)); + return true; + } + + bool StartObject() { return true; } + bool Key(const Ch* str, SizeType len, bool copy) { return String(str, len, copy); } + bool EndObject(SizeType memberCount) { + uint64_t h = Hash(0, kObjectType); + uint64_t* kv = stack_.template Pop(memberCount * 2); + for (SizeType i = 0; i < memberCount; i++) + h ^= Hash(kv[i * 2], kv[i * 2 + 1]); // Use xor to achieve member order insensitive + *stack_.template Push() = h; + return true; + } + + bool StartArray() { return true; } + bool EndArray(SizeType elementCount) { + uint64_t h = Hash(0, kArrayType); + uint64_t* e = stack_.template Pop(elementCount); + for (SizeType i = 0; i < elementCount; i++) + h = Hash(h, e[i]); // Use hash to achieve element order sensitive + *stack_.template Push() = h; + return true; + } + + bool IsValid() const { return stack_.GetSize() == sizeof(uint64_t); } + + uint64_t GetHashCode() const { + RAPIDJSON_ASSERT(IsValid()); + return *stack_.template Top(); + } + +private: + static const size_t kDefaultSize = 256; + struct Number { + union U { + uint64_t u; + int64_t i; + }u; + double d; + }; + + bool WriteType(Type type) { return WriteBuffer(type, 0, 0); } + + bool WriteNumber(const Number& n) { return WriteBuffer(kNumberType, &n, sizeof(n)); } + + bool WriteBuffer(Type type, const void* data, size_t len) { + // FNV-1a from http://isthe.com/chongo/tech/comp/fnv/ + uint64_t h = Hash(RAPIDJSON_UINT64_C2(0x84222325, 0xcbf29ce4), type); + const unsigned char* d = static_cast(data); + for (size_t i = 0; i < len; i++) + h = Hash(h, d[i]); + *stack_.template Push() = h; + return true; + } + + static uint64_t Hash(uint64_t h, uint64_t d) { + static const uint64_t kPrime = RAPIDJSON_UINT64_C2(0x00000100, 0x000001b3); + h ^= d; + h *= kPrime; + return h; + } + + Stack stack_; +}; + +/////////////////////////////////////////////////////////////////////////////// +// SchemaValidationContext + +template +struct SchemaValidationContext { + typedef Schema SchemaType; + typedef ISchemaStateFactory SchemaValidatorFactoryType; + typedef IValidationErrorHandler ErrorHandlerType; + typedef typename SchemaType::ValueType ValueType; + typedef typename ValueType::Ch Ch; + + enum PatternValidatorType { + kPatternValidatorOnly, + kPatternValidatorWithProperty, + kPatternValidatorWithAdditionalProperty + }; + + SchemaValidationContext(SchemaValidatorFactoryType& f, ErrorHandlerType& eh, const SchemaType* s, unsigned fl = 0) : + factory(f), + error_handler(eh), + schema(s), + flags(fl), + valueSchema(), + invalidKeyword(), + invalidCode(), + hasher(), + arrayElementHashCodes(), + validators(), + validatorCount(), + patternPropertiesValidators(), + patternPropertiesValidatorCount(), + patternPropertiesSchemas(), + patternPropertiesSchemaCount(), + valuePatternValidatorType(kPatternValidatorOnly), + propertyExist(), + inArray(false), + valueUniqueness(false), + arrayUniqueness(false) + { + } + + ~SchemaValidationContext() { + if (hasher) + factory.DestroryHasher(hasher); + if (validators) { + for (SizeType i = 0; i < validatorCount; i++) { + if (validators[i]) { + factory.DestroySchemaValidator(validators[i]); + } + } + factory.FreeState(validators); + } + if (patternPropertiesValidators) { + for (SizeType i = 0; i < patternPropertiesValidatorCount; i++) { + if (patternPropertiesValidators[i]) { + factory.DestroySchemaValidator(patternPropertiesValidators[i]); + } + } + factory.FreeState(patternPropertiesValidators); + } + if (patternPropertiesSchemas) + factory.FreeState(patternPropertiesSchemas); + if (propertyExist) + factory.FreeState(propertyExist); + } + + SchemaValidatorFactoryType& factory; + ErrorHandlerType& error_handler; + const SchemaType* schema; + unsigned flags; + const SchemaType* valueSchema; + const Ch* invalidKeyword; + ValidateErrorCode invalidCode; + void* hasher; // Only validator access + void* arrayElementHashCodes; // Only validator access this + ISchemaValidator** validators; + SizeType validatorCount; + ISchemaValidator** patternPropertiesValidators; + SizeType patternPropertiesValidatorCount; + const SchemaType** patternPropertiesSchemas; + SizeType patternPropertiesSchemaCount; + PatternValidatorType valuePatternValidatorType; + PatternValidatorType objectPatternValidatorType; + SizeType arrayElementIndex; + bool* propertyExist; + bool inArray; + bool valueUniqueness; + bool arrayUniqueness; +}; + +/////////////////////////////////////////////////////////////////////////////// +// Schema + +template +class Schema { +public: + typedef typename SchemaDocumentType::ValueType ValueType; + typedef typename SchemaDocumentType::AllocatorType AllocatorType; + typedef typename SchemaDocumentType::PointerType PointerType; + typedef typename ValueType::EncodingType EncodingType; + typedef typename EncodingType::Ch Ch; + typedef SchemaValidationContext Context; + typedef Schema SchemaType; + typedef GenericValue SValue; + typedef IValidationErrorHandler ErrorHandler; + typedef GenericUri UriType; + friend class GenericSchemaDocument; + + Schema(SchemaDocumentType* schemaDocument, const PointerType& p, const ValueType& value, const ValueType& document, AllocatorType* allocator, const UriType& id = UriType()) : + allocator_(allocator), + uri_(schemaDocument->GetURI(), *allocator), + id_(id, allocator), + spec_(schemaDocument->GetSpecification()), + pointer_(p, allocator), + typeless_(schemaDocument->GetTypeless()), + enum_(), + enumCount_(), + not_(), + type_((1 << kTotalSchemaType) - 1), // typeless + validatorCount_(), + notValidatorIndex_(), + properties_(), + additionalPropertiesSchema_(), + patternProperties_(), + patternPropertyCount_(), + propertyCount_(), + minProperties_(), + maxProperties_(SizeType(~0)), + additionalProperties_(true), + hasDependencies_(), + hasRequired_(), + hasSchemaDependencies_(), + additionalItemsSchema_(), + itemsList_(), + itemsTuple_(), + itemsTupleCount_(), + minItems_(), + maxItems_(SizeType(~0)), + additionalItems_(true), + uniqueItems_(false), + pattern_(), + minLength_(0), + maxLength_(~SizeType(0)), + exclusiveMinimum_(false), + exclusiveMaximum_(false), + defaultValueLength_(0), + readOnly_(false), + writeOnly_(false), + nullable_(false) + { + GenericStringBuffer sb; + p.StringifyUriFragment(sb); + RAPIDJSON_SCHEMA_PRINT(Method, "Schema::Schema", sb.GetString(), id.GetString()); + + typedef typename ValueType::ConstValueIterator ConstValueIterator; + typedef typename ValueType::ConstMemberIterator ConstMemberIterator; + + // PR #1393 + // Early add this Schema and its $ref(s) in schemaDocument's map to avoid infinite + // recursion (with recursive schemas), since schemaDocument->getSchema() is always + // checked before creating a new one. Don't cache typeless_, though. + if (this != typeless_) { + typedef typename SchemaDocumentType::SchemaEntry SchemaEntry; + SchemaEntry *entry = schemaDocument->schemaMap_.template Push(); + new (entry) SchemaEntry(pointer_, this, true, allocator_); + schemaDocument->AddSchemaRefs(this); + } + + if (!value.IsObject()) + return; + + // If we have an id property, resolve it with the in-scope id + // Not supported for open api 2.0 or 3.0 + if (spec_.oapi != kVersion20 && spec_.oapi != kVersion30) + if (const ValueType* v = GetMember(value, GetIdString())) { + if (v->IsString()) { + UriType local(*v, allocator); + id_ = local.Resolve(id_, allocator); + RAPIDJSON_SCHEMA_PRINT(SchemaIds, id.GetString(), v->GetString(), id_.GetString()); + } + } + + if (const ValueType* v = GetMember(value, GetTypeString())) { + type_ = 0; + if (v->IsString()) + AddType(*v); + else if (v->IsArray()) + for (ConstValueIterator itr = v->Begin(); itr != v->End(); ++itr) + AddType(*itr); + } + + if (const ValueType* v = GetMember(value, GetEnumString())) { + if (v->IsArray() && v->Size() > 0) { + enum_ = static_cast(allocator_->Malloc(sizeof(uint64_t) * v->Size())); + for (ConstValueIterator itr = v->Begin(); itr != v->End(); ++itr) { + typedef Hasher > EnumHasherType; + char buffer[256u + 24]; + MemoryPoolAllocator hasherAllocator(buffer, sizeof(buffer)); + EnumHasherType h(&hasherAllocator, 256); + itr->Accept(h); + enum_[enumCount_++] = h.GetHashCode(); + } + } + } + + if (schemaDocument) + AssignIfExist(allOf_, *schemaDocument, p, value, GetAllOfString(), document); + + // AnyOf, OneOf, Not not supported for open api 2.0 + if (schemaDocument && spec_.oapi != kVersion20) { + AssignIfExist(anyOf_, *schemaDocument, p, value, GetAnyOfString(), document); + AssignIfExist(oneOf_, *schemaDocument, p, value, GetOneOfString(), document); + + if (const ValueType* v = GetMember(value, GetNotString())) { + schemaDocument->CreateSchema(¬_, p.Append(GetNotString(), allocator_), *v, document, id_); + notValidatorIndex_ = validatorCount_; + validatorCount_++; + } + } + + // Object + + const ValueType* properties = GetMember(value, GetPropertiesString()); + const ValueType* required = GetMember(value, GetRequiredString()); + const ValueType* dependencies = GetMember(value, GetDependenciesString()); + { + // Gather properties from properties/required/dependencies + SValue allProperties(kArrayType); + + if (properties && properties->IsObject()) + for (ConstMemberIterator itr = properties->MemberBegin(); itr != properties->MemberEnd(); ++itr) + AddUniqueElement(allProperties, itr->name); + + if (required && required->IsArray()) + for (ConstValueIterator itr = required->Begin(); itr != required->End(); ++itr) + if (itr->IsString()) + AddUniqueElement(allProperties, *itr); + + // Dependencies not supported for open api 2.0 and 3.0 + if (spec_.oapi != kVersion20 && spec_.oapi != kVersion30) + if (dependencies && dependencies->IsObject()) + for (ConstMemberIterator itr = dependencies->MemberBegin(); itr != dependencies->MemberEnd(); ++itr) { + AddUniqueElement(allProperties, itr->name); + if (itr->value.IsArray()) + for (ConstValueIterator i = itr->value.Begin(); i != itr->value.End(); ++i) + if (i->IsString()) + AddUniqueElement(allProperties, *i); + } + + if (allProperties.Size() > 0) { + propertyCount_ = allProperties.Size(); + properties_ = static_cast(allocator_->Malloc(sizeof(Property) * propertyCount_)); + for (SizeType i = 0; i < propertyCount_; i++) { + new (&properties_[i]) Property(); + properties_[i].name = allProperties[i]; + properties_[i].schema = typeless_; + } + } + } + + if (properties && properties->IsObject()) { + PointerType q = p.Append(GetPropertiesString(), allocator_); + for (ConstMemberIterator itr = properties->MemberBegin(); itr != properties->MemberEnd(); ++itr) { + SizeType index; + if (FindPropertyIndex(itr->name, &index)) + schemaDocument->CreateSchema(&properties_[index].schema, q.Append(itr->name, allocator_), itr->value, document, id_); + } + } + + // PatternProperties not supported for open api 2.0 and 3.0 + if (spec_.oapi != kVersion20 && spec_.oapi != kVersion30) + if (const ValueType* v = GetMember(value, GetPatternPropertiesString())) { + PointerType q = p.Append(GetPatternPropertiesString(), allocator_); + patternProperties_ = static_cast(allocator_->Malloc(sizeof(PatternProperty) * v->MemberCount())); + patternPropertyCount_ = 0; + + for (ConstMemberIterator itr = v->MemberBegin(); itr != v->MemberEnd(); ++itr) { + new (&patternProperties_[patternPropertyCount_]) PatternProperty(); + PointerType r = q.Append(itr->name, allocator_); + patternProperties_[patternPropertyCount_].pattern = CreatePattern(itr->name, schemaDocument, r); + schemaDocument->CreateSchema(&patternProperties_[patternPropertyCount_].schema, r, itr->value, document, id_); + patternPropertyCount_++; + } + } + + if (required && required->IsArray()) + for (ConstValueIterator itr = required->Begin(); itr != required->End(); ++itr) + if (itr->IsString()) { + SizeType index; + if (FindPropertyIndex(*itr, &index)) { + properties_[index].required = true; + hasRequired_ = true; + } + } + + // Dependencies not supported for open api 2.0 and 3.0 + if (spec_.oapi != kVersion20 && spec_.oapi != kVersion30) + if (dependencies && dependencies->IsObject()) { + PointerType q = p.Append(GetDependenciesString(), allocator_); + hasDependencies_ = true; + for (ConstMemberIterator itr = dependencies->MemberBegin(); itr != dependencies->MemberEnd(); ++itr) { + SizeType sourceIndex; + if (FindPropertyIndex(itr->name, &sourceIndex)) { + if (itr->value.IsArray()) { + properties_[sourceIndex].dependencies = static_cast(allocator_->Malloc(sizeof(bool) * propertyCount_)); + std::memset(properties_[sourceIndex].dependencies, 0, sizeof(bool)* propertyCount_); + for (ConstValueIterator targetItr = itr->value.Begin(); targetItr != itr->value.End(); ++targetItr) { + SizeType targetIndex; + if (FindPropertyIndex(*targetItr, &targetIndex)) + properties_[sourceIndex].dependencies[targetIndex] = true; + } + } + else if (itr->value.IsObject()) { + hasSchemaDependencies_ = true; + schemaDocument->CreateSchema(&properties_[sourceIndex].dependenciesSchema, q.Append(itr->name, allocator_), itr->value, document, id_); + properties_[sourceIndex].dependenciesValidatorIndex = validatorCount_; + validatorCount_++; + } + } + } + } + + if (const ValueType* v = GetMember(value, GetAdditionalPropertiesString())) { + if (v->IsBool()) + additionalProperties_ = v->GetBool(); + else if (v->IsObject()) + schemaDocument->CreateSchema(&additionalPropertiesSchema_, p.Append(GetAdditionalPropertiesString(), allocator_), *v, document, id_); + } + + AssignIfExist(minProperties_, value, GetMinPropertiesString()); + AssignIfExist(maxProperties_, value, GetMaxPropertiesString()); + + // Array + if (const ValueType* v = GetMember(value, GetItemsString())) { + PointerType q = p.Append(GetItemsString(), allocator_); + if (v->IsObject()) // List validation + schemaDocument->CreateSchema(&itemsList_, q, *v, document, id_); + else if (v->IsArray()) { // Tuple validation + itemsTuple_ = static_cast(allocator_->Malloc(sizeof(const Schema*) * v->Size())); + SizeType index = 0; + for (ConstValueIterator itr = v->Begin(); itr != v->End(); ++itr, index++) + schemaDocument->CreateSchema(&itemsTuple_[itemsTupleCount_++], q.Append(index, allocator_), *itr, document, id_); + } + } + + AssignIfExist(minItems_, value, GetMinItemsString()); + AssignIfExist(maxItems_, value, GetMaxItemsString()); + + // AdditionalItems not supported for openapi 2.0 and 3.0 + if (spec_.oapi != kVersion20 && spec_.oapi != kVersion30) + if (const ValueType* v = GetMember(value, GetAdditionalItemsString())) { + if (v->IsBool()) + additionalItems_ = v->GetBool(); + else if (v->IsObject()) + schemaDocument->CreateSchema(&additionalItemsSchema_, p.Append(GetAdditionalItemsString(), allocator_), *v, document, id_); + } + + AssignIfExist(uniqueItems_, value, GetUniqueItemsString()); + + // String + AssignIfExist(minLength_, value, GetMinLengthString()); + AssignIfExist(maxLength_, value, GetMaxLengthString()); + + if (const ValueType* v = GetMember(value, GetPatternString())) + pattern_ = CreatePattern(*v, schemaDocument, p.Append(GetPatternString(), allocator_)); + + // Number + if (const ValueType* v = GetMember(value, GetMinimumString())) + if (v->IsNumber()) + minimum_.CopyFrom(*v, *allocator_); + + if (const ValueType* v = GetMember(value, GetMaximumString())) + if (v->IsNumber()) + maximum_.CopyFrom(*v, *allocator_); + + AssignIfExist(exclusiveMinimum_, value, GetExclusiveMinimumString()); + AssignIfExist(exclusiveMaximum_, value, GetExclusiveMaximumString()); + + if (const ValueType* v = GetMember(value, GetMultipleOfString())) + if (v->IsNumber() && v->GetDouble() > 0.0) + multipleOf_.CopyFrom(*v, *allocator_); + + // Default + if (const ValueType* v = GetMember(value, GetDefaultValueString())) + if (v->IsString()) + defaultValueLength_ = v->GetStringLength(); + + // ReadOnly - open api only (until draft 7 supported) + // WriteOnly - open api 3 only (until draft 7 supported) + // Both can't be true + if (spec_.oapi != kVersionNone) + AssignIfExist(readOnly_, value, GetReadOnlyString()); + if (spec_.oapi >= kVersion30) + AssignIfExist(writeOnly_, value, GetWriteOnlyString()); + if (readOnly_ && writeOnly_) + schemaDocument->SchemaError(kSchemaErrorReadOnlyAndWriteOnly, p); + + // Nullable - open api 3 only + // If true add 'null' as allowable type + if (spec_.oapi >= kVersion30) { + AssignIfExist(nullable_, value, GetNullableString()); + if (nullable_) + AddType(GetNullString()); + } + } + + ~Schema() { + AllocatorType::Free(enum_); + if (properties_) { + for (SizeType i = 0; i < propertyCount_; i++) + properties_[i].~Property(); + AllocatorType::Free(properties_); + } + if (patternProperties_) { + for (SizeType i = 0; i < patternPropertyCount_; i++) + patternProperties_[i].~PatternProperty(); + AllocatorType::Free(patternProperties_); + } + AllocatorType::Free(itemsTuple_); +#if RAPIDJSON_SCHEMA_HAS_REGEX + if (pattern_) { + pattern_->~RegexType(); + AllocatorType::Free(pattern_); + } +#endif + } + + const SValue& GetURI() const { + return uri_; + } + + const UriType& GetId() const { + return id_; + } + + const Specification& GetSpecification() const { + return spec_; + } + + const PointerType& GetPointer() const { + return pointer_; + } + + bool BeginValue(Context& context) const { + RAPIDJSON_SCHEMA_PRINT(Method, "Schema::BeginValue"); + if (context.inArray) { + if (uniqueItems_) + context.valueUniqueness = true; + + if (itemsList_) + context.valueSchema = itemsList_; + else if (itemsTuple_) { + if (context.arrayElementIndex < itemsTupleCount_) + context.valueSchema = itemsTuple_[context.arrayElementIndex]; + else if (additionalItemsSchema_) + context.valueSchema = additionalItemsSchema_; + else if (additionalItems_) + context.valueSchema = typeless_; + else { + context.error_handler.DisallowedItem(context.arrayElementIndex); + // Must set valueSchema for when kValidateContinueOnErrorFlag is set, else reports spurious type error + context.valueSchema = typeless_; + // Must bump arrayElementIndex for when kValidateContinueOnErrorFlag is set + context.arrayElementIndex++; + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorAdditionalItems); + } + } + else + context.valueSchema = typeless_; + + context.arrayElementIndex++; + } + return true; + } + + RAPIDJSON_FORCEINLINE bool EndValue(Context& context) const { + RAPIDJSON_SCHEMA_PRINT(Method, "Schema::EndValue"); + // Only check pattern properties if we have validators + if (context.patternPropertiesValidatorCount > 0) { + bool otherValid = false; + SizeType count = context.patternPropertiesValidatorCount; + if (context.objectPatternValidatorType != Context::kPatternValidatorOnly) + otherValid = context.patternPropertiesValidators[--count]->IsValid(); + + bool patternValid = true; + for (SizeType i = 0; i < count; i++) + if (!context.patternPropertiesValidators[i]->IsValid()) { + patternValid = false; + break; + } + + if (context.objectPatternValidatorType == Context::kPatternValidatorOnly) { + if (!patternValid) { + context.error_handler.PropertyViolations(context.patternPropertiesValidators, count); + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorPatternProperties); + } + } + else if (context.objectPatternValidatorType == Context::kPatternValidatorWithProperty) { + if (!patternValid || !otherValid) { + context.error_handler.PropertyViolations(context.patternPropertiesValidators, count + 1); + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorPatternProperties); + } + } + else if (!patternValid && !otherValid) { // kPatternValidatorWithAdditionalProperty) + context.error_handler.PropertyViolations(context.patternPropertiesValidators, count + 1); + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorPatternProperties); + } + } + + // For enums only check if we have a hasher + if (enum_ && context.hasher) { + const uint64_t h = context.factory.GetHashCode(context.hasher); + for (SizeType i = 0; i < enumCount_; i++) + if (enum_[i] == h) + goto foundEnum; + context.error_handler.DisallowedValue(kValidateErrorEnum); + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorEnum); + foundEnum:; + } + + // Only check allOf etc if we have validators + if (context.validatorCount > 0) { + if (allOf_.schemas) + for (SizeType i = allOf_.begin; i < allOf_.begin + allOf_.count; i++) + if (!context.validators[i]->IsValid()) { + context.error_handler.NotAllOf(&context.validators[allOf_.begin], allOf_.count); + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorAllOf); + } + + if (anyOf_.schemas) { + for (SizeType i = anyOf_.begin; i < anyOf_.begin + anyOf_.count; i++) + if (context.validators[i]->IsValid()) + goto foundAny; + context.error_handler.NoneOf(&context.validators[anyOf_.begin], anyOf_.count); + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorAnyOf); + foundAny:; + } + + if (oneOf_.schemas) { + bool oneValid = false; + SizeType firstMatch = 0; + for (SizeType i = oneOf_.begin; i < oneOf_.begin + oneOf_.count; i++) + if (context.validators[i]->IsValid()) { + if (oneValid) { + context.error_handler.MultipleOneOf(firstMatch, i - oneOf_.begin); + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorOneOfMatch); + } else { + oneValid = true; + firstMatch = i - oneOf_.begin; + } + } + if (!oneValid) { + context.error_handler.NotOneOf(&context.validators[oneOf_.begin], oneOf_.count); + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorOneOf); + } + } + + if (not_ && context.validators[notValidatorIndex_]->IsValid()) { + context.error_handler.Disallowed(); + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorNot); + } + } + + return true; + } + + bool Null(Context& context) const { + RAPIDJSON_SCHEMA_PRINT(Method, "Schema::Null"); + if (!(type_ & (1 << kNullSchemaType))) { + DisallowedType(context, GetNullString()); + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorType); + } + return CreateParallelValidator(context); + } + + bool Bool(Context& context, bool b) const { + RAPIDJSON_SCHEMA_PRINT(Method, "Schema::Bool", b); + if (!CheckBool(context, b)) + return false; + return CreateParallelValidator(context); + } + + bool Int(Context& context, int i) const { + RAPIDJSON_SCHEMA_PRINT(Method, "Schema::Int", (int64_t)i); + if (!CheckInt(context, i)) + return false; + return CreateParallelValidator(context); + } + + bool Uint(Context& context, unsigned u) const { + RAPIDJSON_SCHEMA_PRINT(Method, "Schema::Uint", (uint64_t)u); + if (!CheckUint(context, u)) + return false; + return CreateParallelValidator(context); + } + + bool Int64(Context& context, int64_t i) const { + RAPIDJSON_SCHEMA_PRINT(Method, "Schema::Int64", i); + if (!CheckInt(context, i)) + return false; + return CreateParallelValidator(context); + } + + bool Uint64(Context& context, uint64_t u) const { + RAPIDJSON_SCHEMA_PRINT(Method, "Schema::Uint64", u); + if (!CheckUint(context, u)) + return false; + return CreateParallelValidator(context); + } + + bool Double(Context& context, double d) const { + RAPIDJSON_SCHEMA_PRINT(Method, "Schema::Double", d); + if (!(type_ & (1 << kNumberSchemaType))) { + DisallowedType(context, GetNumberString()); + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorType); + } + + if (!minimum_.IsNull() && !CheckDoubleMinimum(context, d)) + return false; + + if (!maximum_.IsNull() && !CheckDoubleMaximum(context, d)) + return false; + + if (!multipleOf_.IsNull() && !CheckDoubleMultipleOf(context, d)) + return false; + + return CreateParallelValidator(context); + } + + bool String(Context& context, const Ch* str, SizeType length, bool) const { + RAPIDJSON_SCHEMA_PRINT(Method, "Schema::String", str); + if (!(type_ & (1 << kStringSchemaType))) { + DisallowedType(context, GetStringString()); + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorType); + } + + if (minLength_ != 0 || maxLength_ != SizeType(~0)) { + SizeType count; + if (internal::CountStringCodePoint(str, length, &count)) { + if (count < minLength_) { + context.error_handler.TooShort(str, length, minLength_); + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorMinLength); + } + if (count > maxLength_) { + context.error_handler.TooLong(str, length, maxLength_); + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorMaxLength); + } + } + } + + if (pattern_ && !IsPatternMatch(pattern_, str, length)) { + context.error_handler.DoesNotMatch(str, length); + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorPattern); + } + + return CreateParallelValidator(context); + } + + bool StartObject(Context& context) const { + RAPIDJSON_SCHEMA_PRINT(Method, "Schema::StartObject"); + if (!(type_ & (1 << kObjectSchemaType))) { + DisallowedType(context, GetObjectString()); + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorType); + } + + if (hasDependencies_ || hasRequired_) { + context.propertyExist = static_cast(context.factory.MallocState(sizeof(bool) * propertyCount_)); + std::memset(context.propertyExist, 0, sizeof(bool) * propertyCount_); + } + + if (patternProperties_) { // pre-allocate schema array + SizeType count = patternPropertyCount_ + 1; // extra for valuePatternValidatorType + context.patternPropertiesSchemas = static_cast(context.factory.MallocState(sizeof(const SchemaType*) * count)); + context.patternPropertiesSchemaCount = 0; + std::memset(context.patternPropertiesSchemas, 0, sizeof(SchemaType*) * count); + } + + return CreateParallelValidator(context); + } + + bool Key(Context& context, const Ch* str, SizeType len, bool) const { + RAPIDJSON_SCHEMA_PRINT(Method, "Schema::Key", str); + + if (patternProperties_) { + context.patternPropertiesSchemaCount = 0; + for (SizeType i = 0; i < patternPropertyCount_; i++) + if (patternProperties_[i].pattern && IsPatternMatch(patternProperties_[i].pattern, str, len)) { + context.patternPropertiesSchemas[context.patternPropertiesSchemaCount++] = patternProperties_[i].schema; + context.valueSchema = typeless_; + } + } + + SizeType index = 0; + if (FindPropertyIndex(ValueType(str, len).Move(), &index)) { + if (context.patternPropertiesSchemaCount > 0) { + context.patternPropertiesSchemas[context.patternPropertiesSchemaCount++] = properties_[index].schema; + context.valueSchema = typeless_; + context.valuePatternValidatorType = Context::kPatternValidatorWithProperty; + } + else + context.valueSchema = properties_[index].schema; + + if (context.propertyExist) + context.propertyExist[index] = true; + + return true; + } + + if (additionalPropertiesSchema_) { + if (context.patternPropertiesSchemaCount > 0) { + context.patternPropertiesSchemas[context.patternPropertiesSchemaCount++] = additionalPropertiesSchema_; + context.valueSchema = typeless_; + context.valuePatternValidatorType = Context::kPatternValidatorWithAdditionalProperty; + } + else + context.valueSchema = additionalPropertiesSchema_; + return true; + } + else if (additionalProperties_) { + context.valueSchema = typeless_; + return true; + } + + if (context.patternPropertiesSchemaCount == 0) { // patternProperties are not additional properties + // Must set valueSchema for when kValidateContinueOnErrorFlag is set, else reports spurious type error + context.valueSchema = typeless_; + context.error_handler.DisallowedProperty(str, len); + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorAdditionalProperties); + } + + return true; + } + + bool EndObject(Context& context, SizeType memberCount) const { + RAPIDJSON_SCHEMA_PRINT(Method, "Schema::EndObject"); + if (hasRequired_) { + context.error_handler.StartMissingProperties(); + for (SizeType index = 0; index < propertyCount_; index++) + if (properties_[index].required && !context.propertyExist[index]) + if (properties_[index].schema->defaultValueLength_ == 0 ) + context.error_handler.AddMissingProperty(properties_[index].name); + if (context.error_handler.EndMissingProperties()) + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorRequired); + } + + if (memberCount < minProperties_) { + context.error_handler.TooFewProperties(memberCount, minProperties_); + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorMinProperties); + } + + if (memberCount > maxProperties_) { + context.error_handler.TooManyProperties(memberCount, maxProperties_); + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorMaxProperties); + } + + if (hasDependencies_) { + context.error_handler.StartDependencyErrors(); + for (SizeType sourceIndex = 0; sourceIndex < propertyCount_; sourceIndex++) { + const Property& source = properties_[sourceIndex]; + if (context.propertyExist[sourceIndex]) { + if (source.dependencies) { + context.error_handler.StartMissingDependentProperties(); + for (SizeType targetIndex = 0; targetIndex < propertyCount_; targetIndex++) + if (source.dependencies[targetIndex] && !context.propertyExist[targetIndex]) + context.error_handler.AddMissingDependentProperty(properties_[targetIndex].name); + context.error_handler.EndMissingDependentProperties(source.name); + } + else if (source.dependenciesSchema) { + ISchemaValidator* dependenciesValidator = context.validators[source.dependenciesValidatorIndex]; + if (!dependenciesValidator->IsValid()) + context.error_handler.AddDependencySchemaError(source.name, dependenciesValidator); + } + } + } + if (context.error_handler.EndDependencyErrors()) + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorDependencies); + } + + return true; + } + + bool StartArray(Context& context) const { + RAPIDJSON_SCHEMA_PRINT(Method, "Schema::StartArray"); + context.arrayElementIndex = 0; + context.inArray = true; // Ensure we note that we are in an array + + if (!(type_ & (1 << kArraySchemaType))) { + DisallowedType(context, GetArrayString()); + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorType); + } + + return CreateParallelValidator(context); + } + + bool EndArray(Context& context, SizeType elementCount) const { + RAPIDJSON_SCHEMA_PRINT(Method, "Schema::EndArray"); + context.inArray = false; + + if (elementCount < minItems_) { + context.error_handler.TooFewItems(elementCount, minItems_); + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorMinItems); + } + + if (elementCount > maxItems_) { + context.error_handler.TooManyItems(elementCount, maxItems_); + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorMaxItems); + } + + return true; + } + + static const ValueType& GetValidateErrorKeyword(ValidateErrorCode validateErrorCode) { + switch (validateErrorCode) { + case kValidateErrorMultipleOf: return GetMultipleOfString(); + case kValidateErrorMaximum: return GetMaximumString(); + case kValidateErrorExclusiveMaximum: return GetMaximumString(); // Same + case kValidateErrorMinimum: return GetMinimumString(); + case kValidateErrorExclusiveMinimum: return GetMinimumString(); // Same + + case kValidateErrorMaxLength: return GetMaxLengthString(); + case kValidateErrorMinLength: return GetMinLengthString(); + case kValidateErrorPattern: return GetPatternString(); + + case kValidateErrorMaxItems: return GetMaxItemsString(); + case kValidateErrorMinItems: return GetMinItemsString(); + case kValidateErrorUniqueItems: return GetUniqueItemsString(); + case kValidateErrorAdditionalItems: return GetAdditionalItemsString(); + + case kValidateErrorMaxProperties: return GetMaxPropertiesString(); + case kValidateErrorMinProperties: return GetMinPropertiesString(); + case kValidateErrorRequired: return GetRequiredString(); + case kValidateErrorAdditionalProperties: return GetAdditionalPropertiesString(); + case kValidateErrorPatternProperties: return GetPatternPropertiesString(); + case kValidateErrorDependencies: return GetDependenciesString(); + + case kValidateErrorEnum: return GetEnumString(); + case kValidateErrorType: return GetTypeString(); + + case kValidateErrorOneOf: return GetOneOfString(); + case kValidateErrorOneOfMatch: return GetOneOfString(); // Same + case kValidateErrorAllOf: return GetAllOfString(); + case kValidateErrorAnyOf: return GetAnyOfString(); + case kValidateErrorNot: return GetNotString(); + + case kValidateErrorReadOnly: return GetReadOnlyString(); + case kValidateErrorWriteOnly: return GetWriteOnlyString(); + + default: return GetNullString(); + } + } + + + // Generate functions for string literal according to Ch +#define RAPIDJSON_STRING_(name, ...) \ + static const ValueType& Get##name##String() {\ + static const Ch s[] = { __VA_ARGS__, '\0' };\ + static const ValueType v(s, static_cast(sizeof(s) / sizeof(Ch) - 1));\ + return v;\ + } + + RAPIDJSON_STRING_(Null, 'n', 'u', 'l', 'l') + RAPIDJSON_STRING_(Boolean, 'b', 'o', 'o', 'l', 'e', 'a', 'n') + RAPIDJSON_STRING_(Object, 'o', 'b', 'j', 'e', 'c', 't') + RAPIDJSON_STRING_(Array, 'a', 'r', 'r', 'a', 'y') + RAPIDJSON_STRING_(String, 's', 't', 'r', 'i', 'n', 'g') + RAPIDJSON_STRING_(Number, 'n', 'u', 'm', 'b', 'e', 'r') + RAPIDJSON_STRING_(Integer, 'i', 'n', 't', 'e', 'g', 'e', 'r') + RAPIDJSON_STRING_(Type, 't', 'y', 'p', 'e') + RAPIDJSON_STRING_(Enum, 'e', 'n', 'u', 'm') + RAPIDJSON_STRING_(AllOf, 'a', 'l', 'l', 'O', 'f') + RAPIDJSON_STRING_(AnyOf, 'a', 'n', 'y', 'O', 'f') + RAPIDJSON_STRING_(OneOf, 'o', 'n', 'e', 'O', 'f') + RAPIDJSON_STRING_(Not, 'n', 'o', 't') + RAPIDJSON_STRING_(Properties, 'p', 'r', 'o', 'p', 'e', 'r', 't', 'i', 'e', 's') + RAPIDJSON_STRING_(Required, 'r', 'e', 'q', 'u', 'i', 'r', 'e', 'd') + RAPIDJSON_STRING_(Dependencies, 'd', 'e', 'p', 'e', 'n', 'd', 'e', 'n', 'c', 'i', 'e', 's') + RAPIDJSON_STRING_(PatternProperties, 'p', 'a', 't', 't', 'e', 'r', 'n', 'P', 'r', 'o', 'p', 'e', 'r', 't', 'i', 'e', 's') + RAPIDJSON_STRING_(AdditionalProperties, 'a', 'd', 'd', 'i', 't', 'i', 'o', 'n', 'a', 'l', 'P', 'r', 'o', 'p', 'e', 'r', 't', 'i', 'e', 's') + RAPIDJSON_STRING_(MinProperties, 'm', 'i', 'n', 'P', 'r', 'o', 'p', 'e', 'r', 't', 'i', 'e', 's') + RAPIDJSON_STRING_(MaxProperties, 'm', 'a', 'x', 'P', 'r', 'o', 'p', 'e', 'r', 't', 'i', 'e', 's') + RAPIDJSON_STRING_(Items, 'i', 't', 'e', 'm', 's') + RAPIDJSON_STRING_(MinItems, 'm', 'i', 'n', 'I', 't', 'e', 'm', 's') + RAPIDJSON_STRING_(MaxItems, 'm', 'a', 'x', 'I', 't', 'e', 'm', 's') + RAPIDJSON_STRING_(AdditionalItems, 'a', 'd', 'd', 'i', 't', 'i', 'o', 'n', 'a', 'l', 'I', 't', 'e', 'm', 's') + RAPIDJSON_STRING_(UniqueItems, 'u', 'n', 'i', 'q', 'u', 'e', 'I', 't', 'e', 'm', 's') + RAPIDJSON_STRING_(MinLength, 'm', 'i', 'n', 'L', 'e', 'n', 'g', 't', 'h') + RAPIDJSON_STRING_(MaxLength, 'm', 'a', 'x', 'L', 'e', 'n', 'g', 't', 'h') + RAPIDJSON_STRING_(Pattern, 'p', 'a', 't', 't', 'e', 'r', 'n') + RAPIDJSON_STRING_(Minimum, 'm', 'i', 'n', 'i', 'm', 'u', 'm') + RAPIDJSON_STRING_(Maximum, 'm', 'a', 'x', 'i', 'm', 'u', 'm') + RAPIDJSON_STRING_(ExclusiveMinimum, 'e', 'x', 'c', 'l', 'u', 's', 'i', 'v', 'e', 'M', 'i', 'n', 'i', 'm', 'u', 'm') + RAPIDJSON_STRING_(ExclusiveMaximum, 'e', 'x', 'c', 'l', 'u', 's', 'i', 'v', 'e', 'M', 'a', 'x', 'i', 'm', 'u', 'm') + RAPIDJSON_STRING_(MultipleOf, 'm', 'u', 'l', 't', 'i', 'p', 'l', 'e', 'O', 'f') + RAPIDJSON_STRING_(DefaultValue, 'd', 'e', 'f', 'a', 'u', 'l', 't') + RAPIDJSON_STRING_(Schema, '$', 's', 'c', 'h', 'e', 'm', 'a') + RAPIDJSON_STRING_(Ref, '$', 'r', 'e', 'f') + RAPIDJSON_STRING_(Id, 'i', 'd') + RAPIDJSON_STRING_(Swagger, 's', 'w', 'a', 'g', 'g', 'e', 'r') + RAPIDJSON_STRING_(OpenApi, 'o', 'p', 'e', 'n', 'a', 'p', 'i') + RAPIDJSON_STRING_(ReadOnly, 'r', 'e', 'a', 'd', 'O', 'n', 'l', 'y') + RAPIDJSON_STRING_(WriteOnly, 'w', 'r', 'i', 't', 'e', 'O', 'n', 'l', 'y') + RAPIDJSON_STRING_(Nullable, 'n', 'u', 'l', 'l', 'a', 'b', 'l', 'e') + +#undef RAPIDJSON_STRING_ + +private: + enum SchemaValueType { + kNullSchemaType, + kBooleanSchemaType, + kObjectSchemaType, + kArraySchemaType, + kStringSchemaType, + kNumberSchemaType, + kIntegerSchemaType, + kTotalSchemaType + }; + +#if RAPIDJSON_SCHEMA_USE_INTERNALREGEX + typedef internal::GenericRegex RegexType; +#elif RAPIDJSON_SCHEMA_USE_STDREGEX + typedef std::basic_regex RegexType; +#else + typedef char RegexType; +#endif + + struct SchemaArray { + SchemaArray() : schemas(), count() {} + ~SchemaArray() { AllocatorType::Free(schemas); } + const SchemaType** schemas; + SizeType begin; // begin index of context.validators + SizeType count; + }; + + template + void AddUniqueElement(V1& a, const V2& v) { + for (typename V1::ConstValueIterator itr = a.Begin(); itr != a.End(); ++itr) + if (*itr == v) + return; + V1 c(v, *allocator_); + a.PushBack(c, *allocator_); + } + + static const ValueType* GetMember(const ValueType& value, const ValueType& name) { + typename ValueType::ConstMemberIterator itr = value.FindMember(name); + return itr != value.MemberEnd() ? &(itr->value) : 0; + } + + static void AssignIfExist(bool& out, const ValueType& value, const ValueType& name) { + if (const ValueType* v = GetMember(value, name)) + if (v->IsBool()) + out = v->GetBool(); + } + + static void AssignIfExist(SizeType& out, const ValueType& value, const ValueType& name) { + if (const ValueType* v = GetMember(value, name)) + if (v->IsUint64() && v->GetUint64() <= SizeType(~0)) + out = static_cast(v->GetUint64()); + } + + void AssignIfExist(SchemaArray& out, SchemaDocumentType& schemaDocument, const PointerType& p, const ValueType& value, const ValueType& name, const ValueType& document) { + if (const ValueType* v = GetMember(value, name)) { + if (v->IsArray() && v->Size() > 0) { + PointerType q = p.Append(name, allocator_); + out.count = v->Size(); + out.schemas = static_cast(allocator_->Malloc(out.count * sizeof(const Schema*))); + memset(out.schemas, 0, sizeof(Schema*)* out.count); + for (SizeType i = 0; i < out.count; i++) + schemaDocument.CreateSchema(&out.schemas[i], q.Append(i, allocator_), (*v)[i], document, id_); + out.begin = validatorCount_; + validatorCount_ += out.count; + } + } + } + +#if RAPIDJSON_SCHEMA_USE_INTERNALREGEX + template + RegexType* CreatePattern(const ValueType& value, SchemaDocumentType* sd, const PointerType& p) { + if (value.IsString()) { + RegexType* r = new (allocator_->Malloc(sizeof(RegexType))) RegexType(value.GetString(), allocator_); + if (!r->IsValid()) { + sd->SchemaErrorValue(kSchemaErrorRegexInvalid, p, value.GetString(), value.GetStringLength()); + r->~RegexType(); + AllocatorType::Free(r); + r = 0; + } + return r; + } + return 0; + } + + static bool IsPatternMatch(const RegexType* pattern, const Ch *str, SizeType) { + GenericRegexSearch rs(*pattern); + return rs.Search(str); + } +#elif RAPIDJSON_SCHEMA_USE_STDREGEX + template + RegexType* CreatePattern(const ValueType& value, SchemaDocumentType* sd, const PointerType& p) { + if (value.IsString()) { + RegexType *r = static_cast(allocator_->Malloc(sizeof(RegexType))); + try { + return new (r) RegexType(value.GetString(), std::size_t(value.GetStringLength()), std::regex_constants::ECMAScript); + } + catch (const std::regex_error& e) { + sd->SchemaErrorValue(kSchemaErrorRegexInvalid, p, value.GetString(), value.GetStringLength()); + AllocatorType::Free(r); + } + } + return 0; + } + + static bool IsPatternMatch(const RegexType* pattern, const Ch *str, SizeType length) { + std::match_results r; + return std::regex_search(str, str + length, r, *pattern); + } +#else + template + RegexType* CreatePattern(const ValueType&) { + return 0; + } + + static bool IsPatternMatch(const RegexType*, const Ch *, SizeType) { return true; } +#endif // RAPIDJSON_SCHEMA_USE_STDREGEX + + void AddType(const ValueType& type) { + if (type == GetNullString() ) type_ |= 1 << kNullSchemaType; + else if (type == GetBooleanString()) type_ |= 1 << kBooleanSchemaType; + else if (type == GetObjectString() ) type_ |= 1 << kObjectSchemaType; + else if (type == GetArrayString() ) type_ |= 1 << kArraySchemaType; + else if (type == GetStringString() ) type_ |= 1 << kStringSchemaType; + else if (type == GetIntegerString()) type_ |= 1 << kIntegerSchemaType; + else if (type == GetNumberString() ) type_ |= (1 << kNumberSchemaType) | (1 << kIntegerSchemaType); + } + + // Creates parallel validators for allOf, anyOf, oneOf, not and schema dependencies, if required. + // Also creates a hasher for enums and array uniqueness, if required. + // Also a useful place to add type-independent error checks. + bool CreateParallelValidator(Context& context) const { + if (enum_ || context.arrayUniqueness) + context.hasher = context.factory.CreateHasher(); + + if (validatorCount_) { + RAPIDJSON_ASSERT(context.validators == 0); + context.validators = static_cast(context.factory.MallocState(sizeof(ISchemaValidator*) * validatorCount_)); + std::memset(context.validators, 0, sizeof(ISchemaValidator*) * validatorCount_); + context.validatorCount = validatorCount_; + + // Always return after first failure for these sub-validators + if (allOf_.schemas) + CreateSchemaValidators(context, allOf_, false); + + if (anyOf_.schemas) + CreateSchemaValidators(context, anyOf_, false); + + if (oneOf_.schemas) + CreateSchemaValidators(context, oneOf_, false); + + if (not_) + context.validators[notValidatorIndex_] = context.factory.CreateSchemaValidator(*not_, false); + + if (hasSchemaDependencies_) { + for (SizeType i = 0; i < propertyCount_; i++) + if (properties_[i].dependenciesSchema) + context.validators[properties_[i].dependenciesValidatorIndex] = context.factory.CreateSchemaValidator(*properties_[i].dependenciesSchema, false); + } + } + + // Add any other type-independent checks here + if (readOnly_ && (context.flags & kValidateWriteFlag)) { + context.error_handler.DisallowedWhenWriting(); + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorReadOnly); + } + if (writeOnly_ && (context.flags & kValidateReadFlag)) { + context.error_handler.DisallowedWhenReading(); + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorWriteOnly); + } + + return true; + } + + void CreateSchemaValidators(Context& context, const SchemaArray& schemas, const bool inheritContinueOnErrors) const { + for (SizeType i = 0; i < schemas.count; i++) + context.validators[schemas.begin + i] = context.factory.CreateSchemaValidator(*schemas.schemas[i], inheritContinueOnErrors); + } + + // O(n) + bool FindPropertyIndex(const ValueType& name, SizeType* outIndex) const { + SizeType len = name.GetStringLength(); + const Ch* str = name.GetString(); + for (SizeType index = 0; index < propertyCount_; index++) + if (properties_[index].name.GetStringLength() == len && + (std::memcmp(properties_[index].name.GetString(), str, sizeof(Ch) * len) == 0)) + { + *outIndex = index; + return true; + } + return false; + } + + bool CheckBool(Context& context, bool) const { + if (!(type_ & (1 << kBooleanSchemaType))) { + DisallowedType(context, GetBooleanString()); + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorType); + } + return true; + } + + bool CheckInt(Context& context, int64_t i) const { + if (!(type_ & ((1 << kIntegerSchemaType) | (1 << kNumberSchemaType)))) { + DisallowedType(context, GetIntegerString()); + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorType); + } + + if (!minimum_.IsNull()) { + if (minimum_.IsInt64()) { + if (exclusiveMinimum_ ? i <= minimum_.GetInt64() : i < minimum_.GetInt64()) { + context.error_handler.BelowMinimum(i, minimum_, exclusiveMinimum_); + RAPIDJSON_INVALID_KEYWORD_RETURN(exclusiveMinimum_ ? kValidateErrorExclusiveMinimum : kValidateErrorMinimum); + } + } + else if (minimum_.IsUint64()) { + context.error_handler.BelowMinimum(i, minimum_, exclusiveMinimum_); + RAPIDJSON_INVALID_KEYWORD_RETURN(exclusiveMinimum_ ? kValidateErrorExclusiveMinimum : kValidateErrorMinimum); // i <= max(int64_t) < minimum.GetUint64() + } + else if (!CheckDoubleMinimum(context, static_cast(i))) + return false; + } + + if (!maximum_.IsNull()) { + if (maximum_.IsInt64()) { + if (exclusiveMaximum_ ? i >= maximum_.GetInt64() : i > maximum_.GetInt64()) { + context.error_handler.AboveMaximum(i, maximum_, exclusiveMaximum_); + RAPIDJSON_INVALID_KEYWORD_RETURN(exclusiveMaximum_ ? kValidateErrorExclusiveMaximum : kValidateErrorMaximum); + } + } + else if (maximum_.IsUint64()) { } + /* do nothing */ // i <= max(int64_t) < maximum_.GetUint64() + else if (!CheckDoubleMaximum(context, static_cast(i))) + return false; + } + + if (!multipleOf_.IsNull()) { + if (multipleOf_.IsUint64()) { + if (static_cast(i >= 0 ? i : -i) % multipleOf_.GetUint64() != 0) { + context.error_handler.NotMultipleOf(i, multipleOf_); + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorMultipleOf); + } + } + else if (!CheckDoubleMultipleOf(context, static_cast(i))) + return false; + } + + return true; + } + + bool CheckUint(Context& context, uint64_t i) const { + if (!(type_ & ((1 << kIntegerSchemaType) | (1 << kNumberSchemaType)))) { + DisallowedType(context, GetIntegerString()); + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorType); + } + + if (!minimum_.IsNull()) { + if (minimum_.IsUint64()) { + if (exclusiveMinimum_ ? i <= minimum_.GetUint64() : i < minimum_.GetUint64()) { + context.error_handler.BelowMinimum(i, minimum_, exclusiveMinimum_); + RAPIDJSON_INVALID_KEYWORD_RETURN(exclusiveMinimum_ ? kValidateErrorExclusiveMinimum : kValidateErrorMinimum); + } + } + else if (minimum_.IsInt64()) + /* do nothing */; // i >= 0 > minimum.Getint64() + else if (!CheckDoubleMinimum(context, static_cast(i))) + return false; + } + + if (!maximum_.IsNull()) { + if (maximum_.IsUint64()) { + if (exclusiveMaximum_ ? i >= maximum_.GetUint64() : i > maximum_.GetUint64()) { + context.error_handler.AboveMaximum(i, maximum_, exclusiveMaximum_); + RAPIDJSON_INVALID_KEYWORD_RETURN(exclusiveMaximum_ ? kValidateErrorExclusiveMaximum : kValidateErrorMaximum); + } + } + else if (maximum_.IsInt64()) { + context.error_handler.AboveMaximum(i, maximum_, exclusiveMaximum_); + RAPIDJSON_INVALID_KEYWORD_RETURN(exclusiveMaximum_ ? kValidateErrorExclusiveMaximum : kValidateErrorMaximum); // i >= 0 > maximum_ + } + else if (!CheckDoubleMaximum(context, static_cast(i))) + return false; + } + + if (!multipleOf_.IsNull()) { + if (multipleOf_.IsUint64()) { + if (i % multipleOf_.GetUint64() != 0) { + context.error_handler.NotMultipleOf(i, multipleOf_); + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorMultipleOf); + } + } + else if (!CheckDoubleMultipleOf(context, static_cast(i))) + return false; + } + + return true; + } + + bool CheckDoubleMinimum(Context& context, double d) const { + if (exclusiveMinimum_ ? d <= minimum_.GetDouble() : d < minimum_.GetDouble()) { + context.error_handler.BelowMinimum(d, minimum_, exclusiveMinimum_); + RAPIDJSON_INVALID_KEYWORD_RETURN(exclusiveMinimum_ ? kValidateErrorExclusiveMinimum : kValidateErrorMinimum); + } + return true; + } + + bool CheckDoubleMaximum(Context& context, double d) const { + if (exclusiveMaximum_ ? d >= maximum_.GetDouble() : d > maximum_.GetDouble()) { + context.error_handler.AboveMaximum(d, maximum_, exclusiveMaximum_); + RAPIDJSON_INVALID_KEYWORD_RETURN(exclusiveMaximum_ ? kValidateErrorExclusiveMaximum : kValidateErrorMaximum); + } + return true; + } + + bool CheckDoubleMultipleOf(Context& context, double d) const { + double a = std::abs(d), b = std::abs(multipleOf_.GetDouble()); + double q = std::floor(a / b); + double r = a - q * b; + if (r > 0.0) { + context.error_handler.NotMultipleOf(d, multipleOf_); + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorMultipleOf); + } + return true; + } + + void DisallowedType(Context& context, const ValueType& actualType) const { + ErrorHandler& eh = context.error_handler; + eh.StartDisallowedType(); + + if (type_ & (1 << kNullSchemaType)) eh.AddExpectedType(GetNullString()); + if (type_ & (1 << kBooleanSchemaType)) eh.AddExpectedType(GetBooleanString()); + if (type_ & (1 << kObjectSchemaType)) eh.AddExpectedType(GetObjectString()); + if (type_ & (1 << kArraySchemaType)) eh.AddExpectedType(GetArrayString()); + if (type_ & (1 << kStringSchemaType)) eh.AddExpectedType(GetStringString()); + + if (type_ & (1 << kNumberSchemaType)) eh.AddExpectedType(GetNumberString()); + else if (type_ & (1 << kIntegerSchemaType)) eh.AddExpectedType(GetIntegerString()); + + eh.EndDisallowedType(actualType); + } + + struct Property { + Property() : schema(), dependenciesSchema(), dependenciesValidatorIndex(), dependencies(), required(false) {} + ~Property() { AllocatorType::Free(dependencies); } + SValue name; + const SchemaType* schema; + const SchemaType* dependenciesSchema; + SizeType dependenciesValidatorIndex; + bool* dependencies; + bool required; + }; + + struct PatternProperty { + PatternProperty() : schema(), pattern() {} + ~PatternProperty() { + if (pattern) { + pattern->~RegexType(); + AllocatorType::Free(pattern); + } + } + const SchemaType* schema; + RegexType* pattern; + }; + + AllocatorType* allocator_; + SValue uri_; + UriType id_; + Specification spec_; + PointerType pointer_; + const SchemaType* typeless_; + uint64_t* enum_; + SizeType enumCount_; + SchemaArray allOf_; + SchemaArray anyOf_; + SchemaArray oneOf_; + const SchemaType* not_; + unsigned type_; // bitmask of kSchemaType + SizeType validatorCount_; + SizeType notValidatorIndex_; + + Property* properties_; + const SchemaType* additionalPropertiesSchema_; + PatternProperty* patternProperties_; + SizeType patternPropertyCount_; + SizeType propertyCount_; + SizeType minProperties_; + SizeType maxProperties_; + bool additionalProperties_; + bool hasDependencies_; + bool hasRequired_; + bool hasSchemaDependencies_; + + const SchemaType* additionalItemsSchema_; + const SchemaType* itemsList_; + const SchemaType** itemsTuple_; + SizeType itemsTupleCount_; + SizeType minItems_; + SizeType maxItems_; + bool additionalItems_; + bool uniqueItems_; + + RegexType* pattern_; + SizeType minLength_; + SizeType maxLength_; + + SValue minimum_; + SValue maximum_; + SValue multipleOf_; + bool exclusiveMinimum_; + bool exclusiveMaximum_; + + SizeType defaultValueLength_; + + bool readOnly_; + bool writeOnly_; + bool nullable_; +}; + +template +struct TokenHelper { + RAPIDJSON_FORCEINLINE static void AppendIndexToken(Stack& documentStack, SizeType index) { + *documentStack.template Push() = '/'; + char buffer[21]; + size_t length = static_cast((sizeof(SizeType) == 4 ? u32toa(index, buffer) : u64toa(index, buffer)) - buffer); + for (size_t i = 0; i < length; i++) + *documentStack.template Push() = static_cast(buffer[i]); + } +}; + +// Partial specialized version for char to prevent buffer copying. +template +struct TokenHelper { + RAPIDJSON_FORCEINLINE static void AppendIndexToken(Stack& documentStack, SizeType index) { + if (sizeof(SizeType) == 4) { + char *buffer = documentStack.template Push(1 + 10); // '/' + uint + *buffer++ = '/'; + const char* end = internal::u32toa(index, buffer); + documentStack.template Pop(static_cast(10 - (end - buffer))); + } + else { + char *buffer = documentStack.template Push(1 + 20); // '/' + uint64 + *buffer++ = '/'; + const char* end = internal::u64toa(index, buffer); + documentStack.template Pop(static_cast(20 - (end - buffer))); + } + } +}; + +} // namespace internal + +/////////////////////////////////////////////////////////////////////////////// +// IGenericRemoteSchemaDocumentProvider + +template +class IGenericRemoteSchemaDocumentProvider { +public: + typedef typename SchemaDocumentType::Ch Ch; + typedef typename SchemaDocumentType::ValueType ValueType; + typedef typename SchemaDocumentType::AllocatorType AllocatorType; + + virtual ~IGenericRemoteSchemaDocumentProvider() {} + virtual const SchemaDocumentType* GetRemoteDocument(const Ch* uri, SizeType length) = 0; + virtual const SchemaDocumentType* GetRemoteDocument(const GenericUri uri, Specification& spec) { + // Default implementation just calls through for compatibility + // Following line suppresses unused parameter warning + (void)spec; + // printf("GetRemoteDocument: %d %d\n", spec.draft, spec.oapi); + return GetRemoteDocument(uri.GetBaseString(), uri.GetBaseStringLength()); + } +}; + +/////////////////////////////////////////////////////////////////////////////// +// GenericSchemaDocument + +//! JSON schema document. +/*! + A JSON schema document is a compiled version of a JSON schema. + It is basically a tree of internal::Schema. + + \note This is an immutable class (i.e. its instance cannot be modified after construction). + \tparam ValueT Type of JSON value (e.g. \c Value ), which also determine the encoding. + \tparam Allocator Allocator type for allocating memory of this document. +*/ +template +class GenericSchemaDocument { +public: + typedef ValueT ValueType; + typedef IGenericRemoteSchemaDocumentProvider IRemoteSchemaDocumentProviderType; + typedef Allocator AllocatorType; + typedef typename ValueType::EncodingType EncodingType; + typedef typename EncodingType::Ch Ch; + typedef internal::Schema SchemaType; + typedef GenericPointer PointerType; + typedef GenericValue GValue; + typedef GenericUri UriType; + typedef GenericStringRef StringRefType; + friend class internal::Schema; + template + friend class GenericSchemaValidator; + + //! Constructor. + /*! + Compile a JSON document into schema document. + + \param document A JSON document as source. + \param uri The base URI of this schema document for purposes of violation reporting. + \param uriLength Length of \c name, in code points. + \param remoteProvider An optional remote schema document provider for resolving remote reference. Can be null. + \param allocator An optional allocator instance for allocating memory. Can be null. + \param pointer An optional JSON pointer to the start of the schema document + \param spec Optional schema draft or OpenAPI version. Used if no specification in document. Defaults to draft-04. + */ + explicit GenericSchemaDocument(const ValueType& document, const Ch* uri = 0, SizeType uriLength = 0, + IRemoteSchemaDocumentProviderType* remoteProvider = 0, Allocator* allocator = 0, + const PointerType& pointer = PointerType(), // PR #1393 + const Specification& spec = Specification(kDraft04)) : + remoteProvider_(remoteProvider), + allocator_(allocator), + ownAllocator_(), + root_(), + typeless_(), + schemaMap_(allocator, kInitialSchemaMapSize), + schemaRef_(allocator, kInitialSchemaRefSize), + spec_(spec), + error_(kObjectType), + currentError_() + { + RAPIDJSON_SCHEMA_PRINT(Method, "GenericSchemaDocument::GenericSchemaDocument"); + if (!allocator_) + ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator)(); + + Ch noUri[1] = {0}; + uri_.SetString(uri ? uri : noUri, uriLength, *allocator_); + docId_ = UriType(uri_, allocator_); + + typeless_ = static_cast(allocator_->Malloc(sizeof(SchemaType))); + new (typeless_) SchemaType(this, PointerType(), ValueType(kObjectType).Move(), ValueType(kObjectType).Move(), allocator_, docId_); + + // Establish the schema draft or open api version. + // We only ever look for '$schema' or 'swagger' or 'openapi' at the root of the document. + SetSchemaSpecification(document); + + // Generate root schema, it will call CreateSchema() to create sub-schemas, + // And call HandleRefSchema() if there are $ref. + // PR #1393 use input pointer if supplied + root_ = typeless_; + if (pointer.GetTokenCount() == 0) { + CreateSchemaRecursive(&root_, pointer, document, document, docId_); + } + else if (const ValueType* v = pointer.Get(document)) { + CreateSchema(&root_, pointer, *v, document, docId_); + } + else { + GenericStringBuffer sb; + pointer.StringifyUriFragment(sb); + SchemaErrorValue(kSchemaErrorStartUnknown, PointerType(), sb.GetString(), static_cast(sb.GetSize() / sizeof(Ch))); + } + + RAPIDJSON_ASSERT(root_ != 0); + + schemaRef_.ShrinkToFit(); // Deallocate all memory for ref + } + +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS + //! Move constructor in C++11 + GenericSchemaDocument(GenericSchemaDocument&& rhs) RAPIDJSON_NOEXCEPT : + remoteProvider_(rhs.remoteProvider_), + allocator_(rhs.allocator_), + ownAllocator_(rhs.ownAllocator_), + root_(rhs.root_), + typeless_(rhs.typeless_), + schemaMap_(std::move(rhs.schemaMap_)), + schemaRef_(std::move(rhs.schemaRef_)), + uri_(std::move(rhs.uri_)), + docId_(std::move(rhs.docId_)), + spec_(rhs.spec_), + error_(std::move(rhs.error_)), + currentError_(std::move(rhs.currentError_)) + { + rhs.remoteProvider_ = 0; + rhs.allocator_ = 0; + rhs.ownAllocator_ = 0; + rhs.typeless_ = 0; + } +#endif + + //! Destructor + ~GenericSchemaDocument() { + while (!schemaMap_.Empty()) + schemaMap_.template Pop(1)->~SchemaEntry(); + + if (typeless_) { + typeless_->~SchemaType(); + Allocator::Free(typeless_); + } + + // these may contain some allocator data so clear before deleting ownAllocator_ + uri_.SetNull(); + error_.SetNull(); + currentError_.SetNull(); + + RAPIDJSON_DELETE(ownAllocator_); + } + + const GValue& GetURI() const { return uri_; } + + const Specification& GetSpecification() const { return spec_; } + bool IsSupportedSpecification() const { return spec_.IsSupported(); } + + //! Static method to get the specification of any schema document + // Returns kDraftNone if document is silent + static const Specification GetSpecification(const ValueType& document) { + SchemaDraft draft = GetSchemaDraft(document); + if (draft != kDraftNone) + return Specification(draft); + else { + OpenApiVersion oapi = GetOpenApiVersion(document); + if (oapi != kVersionNone) + return Specification(oapi); + } + return Specification(kDraftNone); + } + + //! Get the root schema. + const SchemaType& GetRoot() const { return *root_; } + + //! Gets the error object. + GValue& GetError() { return error_; } + const GValue& GetError() const { return error_; } + + static const StringRefType& GetSchemaErrorKeyword(SchemaErrorCode schemaErrorCode) { + switch (schemaErrorCode) { + case kSchemaErrorStartUnknown: return GetStartUnknownString(); + case kSchemaErrorRefPlainName: return GetRefPlainNameString(); + case kSchemaErrorRefInvalid: return GetRefInvalidString(); + case kSchemaErrorRefPointerInvalid: return GetRefPointerInvalidString(); + case kSchemaErrorRefUnknown: return GetRefUnknownString(); + case kSchemaErrorRefCyclical: return GetRefCyclicalString(); + case kSchemaErrorRefNoRemoteProvider: return GetRefNoRemoteProviderString(); + case kSchemaErrorRefNoRemoteSchema: return GetRefNoRemoteSchemaString(); + case kSchemaErrorRegexInvalid: return GetRegexInvalidString(); + case kSchemaErrorSpecUnknown: return GetSpecUnknownString(); + case kSchemaErrorSpecUnsupported: return GetSpecUnsupportedString(); + case kSchemaErrorSpecIllegal: return GetSpecIllegalString(); + case kSchemaErrorReadOnlyAndWriteOnly: return GetReadOnlyAndWriteOnlyString(); + default: return GetNullString(); + } + } + + //! Default error method + void SchemaError(const SchemaErrorCode code, const PointerType& location) { + currentError_ = GValue(kObjectType); + AddCurrentError(code, location); + } + + //! Method for error with single string value insert + void SchemaErrorValue(const SchemaErrorCode code, const PointerType& location, const Ch* value, SizeType length) { + currentError_ = GValue(kObjectType); + currentError_.AddMember(GetValueString(), GValue(value, length, *allocator_).Move(), *allocator_); + AddCurrentError(code, location); + } + + //! Method for error with invalid pointer + void SchemaErrorPointer(const SchemaErrorCode code, const PointerType& location, const Ch* value, SizeType length, const PointerType& pointer) { + currentError_ = GValue(kObjectType); + currentError_.AddMember(GetValueString(), GValue(value, length, *allocator_).Move(), *allocator_); + currentError_.AddMember(GetOffsetString(), static_cast(pointer.GetParseErrorOffset() / sizeof(Ch)), *allocator_); + AddCurrentError(code, location); + } + + private: + //! Prohibit copying + GenericSchemaDocument(const GenericSchemaDocument&); + //! Prohibit assignment + GenericSchemaDocument& operator=(const GenericSchemaDocument&); + + typedef const PointerType* SchemaRefPtr; // PR #1393 + + struct SchemaEntry { + SchemaEntry(const PointerType& p, SchemaType* s, bool o, Allocator* allocator) : pointer(p, allocator), schema(s), owned(o) {} + ~SchemaEntry() { + if (owned) { + schema->~SchemaType(); + Allocator::Free(schema); + } + } + PointerType pointer; + SchemaType* schema; + bool owned; + }; + + void AddErrorInstanceLocation(GValue& result, const PointerType& location) { + GenericStringBuffer sb; + location.StringifyUriFragment(sb); + GValue instanceRef(sb.GetString(), static_cast(sb.GetSize() / sizeof(Ch)), *allocator_); + result.AddMember(GetInstanceRefString(), instanceRef, *allocator_); + } + + void AddError(GValue& keyword, GValue& error) { + typename GValue::MemberIterator member = error_.FindMember(keyword); + if (member == error_.MemberEnd()) + error_.AddMember(keyword, error, *allocator_); + else { + if (member->value.IsObject()) { + GValue errors(kArrayType); + errors.PushBack(member->value, *allocator_); + member->value = errors; + } + member->value.PushBack(error, *allocator_); + } + } + + void AddCurrentError(const SchemaErrorCode code, const PointerType& location) { + RAPIDJSON_SCHEMA_PRINT(InvalidKeyword, GetSchemaErrorKeyword(code)); + currentError_.AddMember(GetErrorCodeString(), code, *allocator_); + AddErrorInstanceLocation(currentError_, location); + AddError(GValue(GetSchemaErrorKeyword(code)).Move(), currentError_); + } + +#define RAPIDJSON_STRING_(name, ...) \ + static const StringRefType& Get##name##String() {\ + static const Ch s[] = { __VA_ARGS__, '\0' };\ + static const StringRefType v(s, static_cast(sizeof(s) / sizeof(Ch) - 1)); \ + return v;\ + } + + RAPIDJSON_STRING_(InstanceRef, 'i', 'n', 's', 't', 'a', 'n', 'c', 'e', 'R', 'e', 'f') + RAPIDJSON_STRING_(ErrorCode, 'e', 'r', 'r', 'o', 'r', 'C', 'o', 'd', 'e') + RAPIDJSON_STRING_(Value, 'v', 'a', 'l', 'u', 'e') + RAPIDJSON_STRING_(Offset, 'o', 'f', 'f', 's', 'e', 't') + + RAPIDJSON_STRING_(Null, 'n', 'u', 'l', 'l') + RAPIDJSON_STRING_(SpecUnknown, 'S', 'p', 'e', 'c', 'U', 'n', 'k', 'n', 'o', 'w', 'n') + RAPIDJSON_STRING_(SpecUnsupported, 'S', 'p', 'e', 'c', 'U', 'n', 's', 'u', 'p', 'p', 'o', 'r', 't', 'e', 'd') + RAPIDJSON_STRING_(SpecIllegal, 'S', 'p', 'e', 'c', 'I', 'l', 'l', 'e', 'g', 'a', 'l') + RAPIDJSON_STRING_(StartUnknown, 'S', 't', 'a', 'r', 't', 'U', 'n', 'k', 'n', 'o', 'w', 'n') + RAPIDJSON_STRING_(RefPlainName, 'R', 'e', 'f', 'P', 'l', 'a', 'i', 'n', 'N', 'a', 'm', 'e') + RAPIDJSON_STRING_(RefInvalid, 'R', 'e', 'f', 'I', 'n', 'v', 'a', 'l', 'i', 'd') + RAPIDJSON_STRING_(RefPointerInvalid, 'R', 'e', 'f', 'P', 'o', 'i', 'n', 't', 'e', 'r', 'I', 'n', 'v', 'a', 'l', 'i', 'd') + RAPIDJSON_STRING_(RefUnknown, 'R', 'e', 'f', 'U', 'n', 'k', 'n', 'o', 'w', 'n') + RAPIDJSON_STRING_(RefCyclical, 'R', 'e', 'f', 'C', 'y', 'c', 'l', 'i', 'c', 'a', 'l') + RAPIDJSON_STRING_(RefNoRemoteProvider, 'R', 'e', 'f', 'N', 'o', 'R', 'e', 'm', 'o', 't', 'e', 'P', 'r', 'o', 'v', 'i', 'd', 'e', 'r') + RAPIDJSON_STRING_(RefNoRemoteSchema, 'R', 'e', 'f', 'N', 'o', 'R', 'e', 'm', 'o', 't', 'e', 'S', 'c', 'h', 'e', 'm', 'a') + RAPIDJSON_STRING_(ReadOnlyAndWriteOnly, 'R', 'e', 'a', 'd', 'O', 'n', 'l', 'y', 'A', 'n', 'd', 'W', 'r', 'i', 't', 'e', 'O', 'n', 'l', 'y') + RAPIDJSON_STRING_(RegexInvalid, 'R', 'e', 'g', 'e', 'x', 'I', 'n', 'v', 'a', 'l', 'i', 'd') + +#undef RAPIDJSON_STRING_ + + // Static method to get schema draft of any schema document + static SchemaDraft GetSchemaDraft(const ValueType& document) { + static const Ch kDraft03String[] = { 'h', 't', 't', 'p', ':', '/', '/', 'j', 's', 'o', 'n', '-', 's', 'c', 'h', 'e', 'm', 'a', '.', 'o', 'r', 'g', '/', 'd', 'r', 'a', 'f', 't', '-', '0', '3', '/', 's', 'c', 'h', 'e', 'm', 'a', '#', '\0' }; + static const Ch kDraft04String[] = { 'h', 't', 't', 'p', ':', '/', '/', 'j', 's', 'o', 'n', '-', 's', 'c', 'h', 'e', 'm', 'a', '.', 'o', 'r', 'g', '/', 'd', 'r', 'a', 'f', 't', '-', '0', '4', '/', 's', 'c', 'h', 'e', 'm', 'a', '#', '\0' }; + static const Ch kDraft05String[] = { 'h', 't', 't', 'p', ':', '/', '/', 'j', 's', 'o', 'n', '-', 's', 'c', 'h', 'e', 'm', 'a', '.', 'o', 'r', 'g', '/', 'd', 'r', 'a', 'f', 't', '-', '0', '5', '/', 's', 'c', 'h', 'e', 'm', 'a', '#', '\0' }; + static const Ch kDraft06String[] = { 'h', 't', 't', 'p', ':', '/', '/', 'j', 's', 'o', 'n', '-', 's', 'c', 'h', 'e', 'm', 'a', '.', 'o', 'r', 'g', '/', 'd', 'r', 'a', 'f', 't', '-', '0', '6', '/', 's', 'c', 'h', 'e', 'm', 'a', '#', '\0' }; + static const Ch kDraft07String[] = { 'h', 't', 't', 'p', ':', '/', '/', 'j', 's', 'o', 'n', '-', 's', 'c', 'h', 'e', 'm', 'a', '.', 'o', 'r', 'g', '/', 'd', 'r', 'a', 'f', 't', '-', '0', '7', '/', 's', 'c', 'h', 'e', 'm', 'a', '#', '\0' }; + static const Ch kDraft2019_09String[] = { 'h', 't', 't', 'p', 's', ':', '/', '/', 'j', 's', 'o', 'n', '-', 's', 'c', 'h', 'e', 'm', 'a', '.', 'o', 'r', 'g', '/', 'd', 'r', 'a', 'f', 't', '/', '2', '0', '1', '9', '-', '0', '9', '/', 's', 'c', 'h', 'e', 'm', 'a', '\0' }; + static const Ch kDraft2020_12String[] = { 'h', 't', 't', 'p', 's', ':', '/', '/', 'j', 's', 'o', 'n', '-', 's', 'c', 'h', 'e', 'm', 'a', '.', 'o', 'r', 'g', '/', 'd', 'r', 'a', 'f', 't', '/', '2', '0', '2', '0', '-', '1', '2', '/', 's', 'c', 'h', 'e', 'm', 'a', '\0' }; + + if (!document.IsObject()) { + return kDraftNone; + } + + // Get the schema draft from the $schema keyword at the supplied location + typename ValueType::ConstMemberIterator itr = document.FindMember(SchemaType::GetSchemaString()); + if (itr != document.MemberEnd()) { + if (!itr->value.IsString()) return kDraftUnknown; + const UriType draftUri(itr->value); + // Check base uri for match + if (draftUri.Match(UriType(kDraft04String), false)) return kDraft04; + if (draftUri.Match(UriType(kDraft05String), false)) return kDraft05; + if (draftUri.Match(UriType(kDraft06String), false)) return kDraft06; + if (draftUri.Match(UriType(kDraft07String), false)) return kDraft07; + if (draftUri.Match(UriType(kDraft03String), false)) return kDraft03; + if (draftUri.Match(UriType(kDraft2019_09String), false)) return kDraft2019_09; + if (draftUri.Match(UriType(kDraft2020_12String), false)) return kDraft2020_12; + return kDraftUnknown; + } + // $schema not found + return kDraftNone; + } + + + // Get open api version of any schema document + static OpenApiVersion GetOpenApiVersion(const ValueType& document) { + static const Ch kVersion20String[] = { '2', '.', '0', '\0' }; + static const Ch kVersion30String[] = { '3', '.', '0', '.', '\0' }; // ignore patch level + static const Ch kVersion31String[] = { '3', '.', '1', '.', '\0' }; // ignore patch level + static SizeType len = internal::StrLen(kVersion30String); + + if (!document.IsObject()) { + return kVersionNone; + } + + // Get the open api version from the swagger / openapi keyword at the supplied location + typename ValueType::ConstMemberIterator itr = document.FindMember(SchemaType::GetSwaggerString()); + if (itr == document.MemberEnd()) itr = document.FindMember(SchemaType::GetOpenApiString()); + if (itr != document.MemberEnd()) { + if (!itr->value.IsString()) return kVersionUnknown; + const ValueType kVersion20Value(kVersion20String); + if (kVersion20Value == itr->value) return kVersion20; // must match 2.0 exactly + const ValueType kVersion30Value(kVersion30String); + if (itr->value.GetStringLength() > len && kVersion30Value == ValueType(itr->value.GetString(), len)) return kVersion30; // must match 3.0.x + const ValueType kVersion31Value(kVersion31String); + if (itr->value.GetStringLength() > len && kVersion31Value == ValueType(itr->value.GetString(), len)) return kVersion31; // must match 3.1.x + return kVersionUnknown; + } + // swagger or openapi not found + return kVersionNone; + } + + // Get the draft of the schema or the open api version (which implies the draft). + // Report an error if schema draft or open api version not supported or not recognized, or both in document, and carry on. + void SetSchemaSpecification(const ValueType& document) { + // Look for '$schema', 'swagger' or 'openapi' keyword at document root + SchemaDraft docDraft = GetSchemaDraft(document); + OpenApiVersion docOapi = GetOpenApiVersion(document); + // Error if both in document + if (docDraft != kDraftNone && docOapi != kVersionNone) + SchemaError(kSchemaErrorSpecIllegal, PointerType()); + // Use document draft or open api version if present or use spec from constructor + if (docDraft != kDraftNone) + spec_ = Specification(docDraft); + else if (docOapi != kVersionNone) + spec_ = Specification(docOapi); + // Error if draft or version unknown + if (spec_.draft == kDraftUnknown || spec_.oapi == kVersionUnknown) + SchemaError(kSchemaErrorSpecUnknown, PointerType()); + else if (!spec_.IsSupported()) + SchemaError(kSchemaErrorSpecUnsupported, PointerType()); + } + + // Changed by PR #1393 + void CreateSchemaRecursive(const SchemaType** schema, const PointerType& pointer, const ValueType& v, const ValueType& document, const UriType& id) { + if (v.GetType() == kObjectType) { + UriType newid = UriType(CreateSchema(schema, pointer, v, document, id), allocator_); + + for (typename ValueType::ConstMemberIterator itr = v.MemberBegin(); itr != v.MemberEnd(); ++itr) + CreateSchemaRecursive(0, pointer.Append(itr->name, allocator_), itr->value, document, newid); + } + else if (v.GetType() == kArrayType) + for (SizeType i = 0; i < v.Size(); i++) + CreateSchemaRecursive(0, pointer.Append(i, allocator_), v[i], document, id); + } + + // Changed by PR #1393 + const UriType& CreateSchema(const SchemaType** schema, const PointerType& pointer, const ValueType& v, const ValueType& document, const UriType& id) { + RAPIDJSON_ASSERT(pointer.IsValid()); + GenericStringBuffer sb; + pointer.StringifyUriFragment(sb); + RAPIDJSON_SCHEMA_PRINT(Method, "GenericSchemaDocument::CreateSchema", sb.GetString(), id.GetString()); + if (v.IsObject()) { + if (const SchemaType* sc = GetSchema(pointer)) { + if (schema) + *schema = sc; + AddSchemaRefs(const_cast(sc)); + } + else if (!HandleRefSchema(pointer, schema, v, document, id)) { + // The new schema constructor adds itself and its $ref(s) to schemaMap_ + SchemaType* s = new (allocator_->Malloc(sizeof(SchemaType))) SchemaType(this, pointer, v, document, allocator_, id); + if (schema) + *schema = s; + return s->GetId(); + } + } + else { + if (schema) + *schema = typeless_; + AddSchemaRefs(typeless_); + } + return id; + } + + // Changed by PR #1393 + // TODO should this return a UriType& ? + bool HandleRefSchema(const PointerType& source, const SchemaType** schema, const ValueType& v, const ValueType& document, const UriType& id) { + typename ValueType::ConstMemberIterator itr = v.FindMember(SchemaType::GetRefString()); + if (itr == v.MemberEnd()) + return false; + + GenericStringBuffer sb; + source.StringifyUriFragment(sb); + RAPIDJSON_SCHEMA_PRINT(Method, "GenericSchemaDocument::HandleRefSchema", sb.GetString(), id.GetString()); + // Resolve the source pointer to the $ref'ed schema (finally) + new (schemaRef_.template Push()) SchemaRefPtr(&source); + + if (itr->value.IsString()) { + SizeType len = itr->value.GetStringLength(); + if (len == 0) + SchemaError(kSchemaErrorRefInvalid, source); + else { + // First resolve $ref against the in-scope id + UriType scopeId = UriType(id, allocator_); + UriType ref = UriType(itr->value, allocator_).Resolve(scopeId, allocator_); + RAPIDJSON_SCHEMA_PRINT(SchemaIds, id.GetString(), itr->value.GetString(), ref.GetString()); + // See if the resolved $ref minus the fragment matches a resolved id in this document + // Search from the root. Returns the subschema in the document and its absolute JSON pointer. + PointerType basePointer = PointerType(); + const ValueType *base = FindId(document, ref, basePointer, docId_, false); + if (!base) { + // Remote reference - call the remote document provider + if (!remoteProvider_) + SchemaError(kSchemaErrorRefNoRemoteProvider, source); + else { + if (const GenericSchemaDocument* remoteDocument = remoteProvider_->GetRemoteDocument(ref, spec_)) { + const Ch* s = ref.GetFragString(); + len = ref.GetFragStringLength(); + if (len <= 1 || s[1] == '/') { + // JSON pointer fragment, absolute in the remote schema + const PointerType pointer(s, len, allocator_); + if (!pointer.IsValid()) + SchemaErrorPointer(kSchemaErrorRefPointerInvalid, source, s, len, pointer); + else { + // Get the subschema + if (const SchemaType *sc = remoteDocument->GetSchema(pointer)) { + if (schema) + *schema = sc; + AddSchemaRefs(const_cast(sc)); + return true; + } else + SchemaErrorValue(kSchemaErrorRefUnknown, source, ref.GetString(), ref.GetStringLength()); + } + } else + // Plain name fragment, not allowed in remote schema + SchemaErrorValue(kSchemaErrorRefPlainName, source, s, len); + } else + SchemaErrorValue(kSchemaErrorRefNoRemoteSchema, source, ref.GetString(), ref.GetStringLength()); + } + } + else { // Local reference + const Ch* s = ref.GetFragString(); + len = ref.GetFragStringLength(); + if (len <= 1 || s[1] == '/') { + // JSON pointer fragment, relative to the resolved URI + const PointerType relPointer(s, len, allocator_); + if (!relPointer.IsValid()) + SchemaErrorPointer(kSchemaErrorRefPointerInvalid, source, s, len, relPointer); + else { + // Get the subschema + if (const ValueType *pv = relPointer.Get(*base)) { + // Now get the absolute JSON pointer by adding relative to base + PointerType pointer(basePointer, allocator_); + for (SizeType i = 0; i < relPointer.GetTokenCount(); i++) + pointer = pointer.Append(relPointer.GetTokens()[i], allocator_); + if (IsCyclicRef(pointer)) + SchemaErrorValue(kSchemaErrorRefCyclical, source, ref.GetString(), ref.GetStringLength()); + else { + // Call CreateSchema recursively, but first compute the in-scope id for the $ref target as we have jumped there + // TODO: cache pointer <-> id mapping + size_t unresolvedTokenIndex; + scopeId = pointer.GetUri(document, docId_, &unresolvedTokenIndex, allocator_); + CreateSchema(schema, pointer, *pv, document, scopeId); + return true; + } + } else + SchemaErrorValue(kSchemaErrorRefUnknown, source, ref.GetString(), ref.GetStringLength()); + } + } else { + // Plain name fragment, relative to the resolved URI + // Not supported in open api 2.0 and 3.0 + PointerType pointer(allocator_); + if (spec_.oapi == kVersion20 || spec_.oapi == kVersion30) + SchemaErrorValue(kSchemaErrorRefPlainName, source, s, len); + // See if the fragment matches an id in this document. + // Search from the base we just established. Returns the subschema in the document and its absolute JSON pointer. + else if (const ValueType *pv = FindId(*base, ref, pointer, UriType(ref.GetBaseString(), ref.GetBaseStringLength(), allocator_), true, basePointer)) { + if (IsCyclicRef(pointer)) + SchemaErrorValue(kSchemaErrorRefCyclical, source, ref.GetString(), ref.GetStringLength()); + else { + // Call CreateSchema recursively, but first compute the in-scope id for the $ref target as we have jumped there + // TODO: cache pointer <-> id mapping + size_t unresolvedTokenIndex; + scopeId = pointer.GetUri(document, docId_, &unresolvedTokenIndex, allocator_); + CreateSchema(schema, pointer, *pv, document, scopeId); + return true; + } + } else + SchemaErrorValue(kSchemaErrorRefUnknown, source, ref.GetString(), ref.GetStringLength()); + } + } + } + } + + // Invalid/Unknown $ref + if (schema) + *schema = typeless_; + AddSchemaRefs(typeless_); + return true; + } + + //! Find the first subschema with a resolved 'id' that matches the specified URI. + // If full specified use all URI else ignore fragment. + // If found, return a pointer to the subschema and its JSON pointer. + // TODO cache pointer <-> id mapping + ValueType* FindId(const ValueType& doc, const UriType& finduri, PointerType& resptr, const UriType& baseuri, bool full, const PointerType& here = PointerType()) const { + SizeType i = 0; + ValueType* resval = 0; + UriType tempuri = UriType(finduri, allocator_); + UriType localuri = UriType(baseuri, allocator_); + if (doc.GetType() == kObjectType) { + // Establish the base URI of this object + typename ValueType::ConstMemberIterator m = doc.FindMember(SchemaType::GetIdString()); + if (m != doc.MemberEnd() && m->value.GetType() == kStringType) { + localuri = UriType(m->value, allocator_).Resolve(baseuri, allocator_); + } + // See if it matches + if (localuri.Match(finduri, full)) { + RAPIDJSON_SCHEMA_PRINT(Method, "GenericSchemaDocument::FindId (match)", full ? localuri.GetString() : localuri.GetBaseString()); + resval = const_cast(&doc); + resptr = here; + return resval; + } + // No match, continue looking + for (m = doc.MemberBegin(); m != doc.MemberEnd(); ++m) { + if (m->value.GetType() == kObjectType || m->value.GetType() == kArrayType) { + resval = FindId(m->value, finduri, resptr, localuri, full, here.Append(m->name.GetString(), m->name.GetStringLength(), allocator_)); + } + if (resval) break; + } + } else if (doc.GetType() == kArrayType) { + // Continue looking + for (typename ValueType::ConstValueIterator v = doc.Begin(); v != doc.End(); ++v) { + if (v->GetType() == kObjectType || v->GetType() == kArrayType) { + resval = FindId(*v, finduri, resptr, localuri, full, here.Append(i, allocator_)); + } + if (resval) break; + i++; + } + } + return resval; + } + + // Added by PR #1393 + void AddSchemaRefs(SchemaType* schema) { + RAPIDJSON_SCHEMA_PRINT(Method, "GenericSchemaDocument::AddSchemaRefs"); + while (!schemaRef_.Empty()) { + SchemaRefPtr *ref = schemaRef_.template Pop(1); + SchemaEntry *entry = schemaMap_.template Push(); + new (entry) SchemaEntry(**ref, schema, false, allocator_); + } + } + + // Added by PR #1393 + bool IsCyclicRef(const PointerType& pointer) const { + for (const SchemaRefPtr* ref = schemaRef_.template Bottom(); ref != schemaRef_.template End(); ++ref) + if (pointer == **ref) + return true; + return false; + } + + const SchemaType* GetSchema(const PointerType& pointer) const { + for (const SchemaEntry* target = schemaMap_.template Bottom(); target != schemaMap_.template End(); ++target) + if (pointer == target->pointer) + return target->schema; + return 0; + } + + PointerType GetPointer(const SchemaType* schema) const { + for (const SchemaEntry* target = schemaMap_.template Bottom(); target != schemaMap_.template End(); ++target) + if (schema == target->schema) + return target->pointer; + return PointerType(); + } + + const SchemaType* GetTypeless() const { return typeless_; } + + static const size_t kInitialSchemaMapSize = 64; + static const size_t kInitialSchemaRefSize = 64; + + IRemoteSchemaDocumentProviderType* remoteProvider_; + Allocator *allocator_; + Allocator *ownAllocator_; + const SchemaType* root_; //!< Root schema. + SchemaType* typeless_; + internal::Stack schemaMap_; // Stores created Pointer -> Schemas + internal::Stack schemaRef_; // Stores Pointer(s) from $ref(s) until resolved + GValue uri_; // Schema document URI + UriType docId_; + Specification spec_; + GValue error_; + GValue currentError_; +}; + +//! GenericSchemaDocument using Value type. +typedef GenericSchemaDocument SchemaDocument; +//! IGenericRemoteSchemaDocumentProvider using SchemaDocument. +typedef IGenericRemoteSchemaDocumentProvider IRemoteSchemaDocumentProvider; + +/////////////////////////////////////////////////////////////////////////////// +// GenericSchemaValidator + +//! JSON Schema Validator. +/*! + A SAX style JSON schema validator. + It uses a \c GenericSchemaDocument to validate SAX events. + It delegates the incoming SAX events to an output handler. + The default output handler does nothing. + It can be reused multiple times by calling \c Reset(). + + \tparam SchemaDocumentType Type of schema document. + \tparam OutputHandler Type of output handler. Default handler does nothing. + \tparam StateAllocator Allocator for storing the internal validation states. +*/ +template < + typename SchemaDocumentType, + typename OutputHandler = BaseReaderHandler, + typename StateAllocator = CrtAllocator> +class GenericSchemaValidator : + public internal::ISchemaStateFactory, + public internal::ISchemaValidator, + public internal::IValidationErrorHandler { +public: + typedef typename SchemaDocumentType::SchemaType SchemaType; + typedef typename SchemaDocumentType::PointerType PointerType; + typedef typename SchemaType::EncodingType EncodingType; + typedef typename SchemaType::SValue SValue; + typedef typename EncodingType::Ch Ch; + typedef GenericStringRef StringRefType; + typedef GenericValue ValueType; + + //! Constructor without output handler. + /*! + \param schemaDocument The schema document to conform to. + \param allocator Optional allocator for storing internal validation states. + \param schemaStackCapacity Optional initial capacity of schema path stack. + \param documentStackCapacity Optional initial capacity of document path stack. + */ + GenericSchemaValidator( + const SchemaDocumentType& schemaDocument, + StateAllocator* allocator = 0, + size_t schemaStackCapacity = kDefaultSchemaStackCapacity, + size_t documentStackCapacity = kDefaultDocumentStackCapacity) + : + schemaDocument_(&schemaDocument), + root_(schemaDocument.GetRoot()), + stateAllocator_(allocator), + ownStateAllocator_(0), + schemaStack_(allocator, schemaStackCapacity), + documentStack_(allocator, documentStackCapacity), + outputHandler_(0), + error_(kObjectType), + currentError_(), + missingDependents_(), + valid_(true), + flags_(kValidateDefaultFlags), + depth_(0) + { + RAPIDJSON_SCHEMA_PRINT(Method, "GenericSchemaValidator::GenericSchemaValidator"); + } + + //! Constructor with output handler. + /*! + \param schemaDocument The schema document to conform to. + \param allocator Optional allocator for storing internal validation states. + \param schemaStackCapacity Optional initial capacity of schema path stack. + \param documentStackCapacity Optional initial capacity of document path stack. + */ + GenericSchemaValidator( + const SchemaDocumentType& schemaDocument, + OutputHandler& outputHandler, + StateAllocator* allocator = 0, + size_t schemaStackCapacity = kDefaultSchemaStackCapacity, + size_t documentStackCapacity = kDefaultDocumentStackCapacity) + : + schemaDocument_(&schemaDocument), + root_(schemaDocument.GetRoot()), + stateAllocator_(allocator), + ownStateAllocator_(0), + schemaStack_(allocator, schemaStackCapacity), + documentStack_(allocator, documentStackCapacity), + outputHandler_(&outputHandler), + error_(kObjectType), + currentError_(), + missingDependents_(), + valid_(true), + flags_(kValidateDefaultFlags), + depth_(0) + { + RAPIDJSON_SCHEMA_PRINT(Method, "GenericSchemaValidator::GenericSchemaValidator (output handler)"); + } + + //! Destructor. + ~GenericSchemaValidator() { + Reset(); + RAPIDJSON_DELETE(ownStateAllocator_); + } + + //! Reset the internal states. + void Reset() { + while (!schemaStack_.Empty()) + PopSchema(); + documentStack_.Clear(); + ResetError(); + } + + //! Reset the error state. + void ResetError() { + error_.SetObject(); + currentError_.SetNull(); + missingDependents_.SetNull(); + valid_ = true; + } + + //! Implementation of ISchemaValidator + void SetValidateFlags(unsigned flags) { + flags_ = flags; + } + virtual unsigned GetValidateFlags() const { + return flags_; + } + + virtual bool IsValid() const { + if (!valid_) return false; + if (GetContinueOnErrors() && !error_.ObjectEmpty()) return false; + return true; + } + //! End of Implementation of ISchemaValidator + + //! Gets the error object. + ValueType& GetError() { return error_; } + const ValueType& GetError() const { return error_; } + + //! Gets the JSON pointer pointed to the invalid schema. + // If reporting all errors, the stack will be empty. + PointerType GetInvalidSchemaPointer() const { + return schemaStack_.Empty() ? PointerType() : CurrentSchema().GetPointer(); + } + + //! Gets the keyword of invalid schema. + // If reporting all errors, the stack will be empty, so return "errors". + const Ch* GetInvalidSchemaKeyword() const { + if (!schemaStack_.Empty()) return CurrentContext().invalidKeyword; + if (GetContinueOnErrors() && !error_.ObjectEmpty()) return (const Ch*)GetErrorsString(); + return 0; + } + + //! Gets the error code of invalid schema. + // If reporting all errors, the stack will be empty, so return kValidateErrors. + ValidateErrorCode GetInvalidSchemaCode() const { + if (!schemaStack_.Empty()) return CurrentContext().invalidCode; + if (GetContinueOnErrors() && !error_.ObjectEmpty()) return kValidateErrors; + return kValidateErrorNone; + } + + //! Gets the JSON pointer pointed to the invalid value. + // If reporting all errors, the stack will be empty. + PointerType GetInvalidDocumentPointer() const { + if (documentStack_.Empty()) { + return PointerType(); + } + else { + return PointerType(documentStack_.template Bottom(), documentStack_.GetSize() / sizeof(Ch)); + } + } + + void NotMultipleOf(int64_t actual, const SValue& expected) { + AddNumberError(kValidateErrorMultipleOf, ValueType(actual).Move(), expected); + } + void NotMultipleOf(uint64_t actual, const SValue& expected) { + AddNumberError(kValidateErrorMultipleOf, ValueType(actual).Move(), expected); + } + void NotMultipleOf(double actual, const SValue& expected) { + AddNumberError(kValidateErrorMultipleOf, ValueType(actual).Move(), expected); + } + void AboveMaximum(int64_t actual, const SValue& expected, bool exclusive) { + AddNumberError(exclusive ? kValidateErrorExclusiveMaximum : kValidateErrorMaximum, ValueType(actual).Move(), expected, + exclusive ? &SchemaType::GetExclusiveMaximumString : 0); + } + void AboveMaximum(uint64_t actual, const SValue& expected, bool exclusive) { + AddNumberError(exclusive ? kValidateErrorExclusiveMaximum : kValidateErrorMaximum, ValueType(actual).Move(), expected, + exclusive ? &SchemaType::GetExclusiveMaximumString : 0); + } + void AboveMaximum(double actual, const SValue& expected, bool exclusive) { + AddNumberError(exclusive ? kValidateErrorExclusiveMaximum : kValidateErrorMaximum, ValueType(actual).Move(), expected, + exclusive ? &SchemaType::GetExclusiveMaximumString : 0); + } + void BelowMinimum(int64_t actual, const SValue& expected, bool exclusive) { + AddNumberError(exclusive ? kValidateErrorExclusiveMinimum : kValidateErrorMinimum, ValueType(actual).Move(), expected, + exclusive ? &SchemaType::GetExclusiveMinimumString : 0); + } + void BelowMinimum(uint64_t actual, const SValue& expected, bool exclusive) { + AddNumberError(exclusive ? kValidateErrorExclusiveMinimum : kValidateErrorMinimum, ValueType(actual).Move(), expected, + exclusive ? &SchemaType::GetExclusiveMinimumString : 0); + } + void BelowMinimum(double actual, const SValue& expected, bool exclusive) { + AddNumberError(exclusive ? kValidateErrorExclusiveMinimum : kValidateErrorMinimum, ValueType(actual).Move(), expected, + exclusive ? &SchemaType::GetExclusiveMinimumString : 0); + } + + void TooLong(const Ch* str, SizeType length, SizeType expected) { + AddNumberError(kValidateErrorMaxLength, + ValueType(str, length, GetStateAllocator()).Move(), SValue(expected).Move()); + } + void TooShort(const Ch* str, SizeType length, SizeType expected) { + AddNumberError(kValidateErrorMinLength, + ValueType(str, length, GetStateAllocator()).Move(), SValue(expected).Move()); + } + void DoesNotMatch(const Ch* str, SizeType length) { + currentError_.SetObject(); + currentError_.AddMember(GetActualString(), ValueType(str, length, GetStateAllocator()).Move(), GetStateAllocator()); + AddCurrentError(kValidateErrorPattern); + } + + void DisallowedItem(SizeType index) { + currentError_.SetObject(); + currentError_.AddMember(GetDisallowedString(), ValueType(index).Move(), GetStateAllocator()); + AddCurrentError(kValidateErrorAdditionalItems, true); + } + void TooFewItems(SizeType actualCount, SizeType expectedCount) { + AddNumberError(kValidateErrorMinItems, + ValueType(actualCount).Move(), SValue(expectedCount).Move()); + } + void TooManyItems(SizeType actualCount, SizeType expectedCount) { + AddNumberError(kValidateErrorMaxItems, + ValueType(actualCount).Move(), SValue(expectedCount).Move()); + } + void DuplicateItems(SizeType index1, SizeType index2) { + ValueType duplicates(kArrayType); + duplicates.PushBack(index1, GetStateAllocator()); + duplicates.PushBack(index2, GetStateAllocator()); + currentError_.SetObject(); + currentError_.AddMember(GetDuplicatesString(), duplicates, GetStateAllocator()); + AddCurrentError(kValidateErrorUniqueItems, true); + } + + void TooManyProperties(SizeType actualCount, SizeType expectedCount) { + AddNumberError(kValidateErrorMaxProperties, + ValueType(actualCount).Move(), SValue(expectedCount).Move()); + } + void TooFewProperties(SizeType actualCount, SizeType expectedCount) { + AddNumberError(kValidateErrorMinProperties, + ValueType(actualCount).Move(), SValue(expectedCount).Move()); + } + void StartMissingProperties() { + currentError_.SetArray(); + } + void AddMissingProperty(const SValue& name) { + currentError_.PushBack(ValueType(name, GetStateAllocator()).Move(), GetStateAllocator()); + } + bool EndMissingProperties() { + if (currentError_.Empty()) + return false; + ValueType error(kObjectType); + error.AddMember(GetMissingString(), currentError_, GetStateAllocator()); + currentError_ = error; + AddCurrentError(kValidateErrorRequired); + return true; + } + void PropertyViolations(ISchemaValidator** subvalidators, SizeType count) { + for (SizeType i = 0; i < count; ++i) + MergeError(static_cast(subvalidators[i])->GetError()); + } + void DisallowedProperty(const Ch* name, SizeType length) { + currentError_.SetObject(); + currentError_.AddMember(GetDisallowedString(), ValueType(name, length, GetStateAllocator()).Move(), GetStateAllocator()); + AddCurrentError(kValidateErrorAdditionalProperties, true); + } + + void StartDependencyErrors() { + currentError_.SetObject(); + } + void StartMissingDependentProperties() { + missingDependents_.SetArray(); + } + void AddMissingDependentProperty(const SValue& targetName) { + missingDependents_.PushBack(ValueType(targetName, GetStateAllocator()).Move(), GetStateAllocator()); + } + void EndMissingDependentProperties(const SValue& sourceName) { + if (!missingDependents_.Empty()) { + // Create equivalent 'required' error + ValueType error(kObjectType); + ValidateErrorCode code = kValidateErrorRequired; + error.AddMember(GetMissingString(), missingDependents_.Move(), GetStateAllocator()); + AddErrorCode(error, code); + AddErrorInstanceLocation(error, false); + // When appending to a pointer ensure its allocator is used + PointerType schemaRef = GetInvalidSchemaPointer().Append(SchemaType::GetValidateErrorKeyword(kValidateErrorDependencies), &GetInvalidSchemaPointer().GetAllocator()); + AddErrorSchemaLocation(error, schemaRef.Append(sourceName.GetString(), sourceName.GetStringLength(), &GetInvalidSchemaPointer().GetAllocator())); + ValueType wrapper(kObjectType); + wrapper.AddMember(ValueType(SchemaType::GetValidateErrorKeyword(code), GetStateAllocator()).Move(), error, GetStateAllocator()); + currentError_.AddMember(ValueType(sourceName, GetStateAllocator()).Move(), wrapper, GetStateAllocator()); + } + } + void AddDependencySchemaError(const SValue& sourceName, ISchemaValidator* subvalidator) { + currentError_.AddMember(ValueType(sourceName, GetStateAllocator()).Move(), + static_cast(subvalidator)->GetError(), GetStateAllocator()); + } + bool EndDependencyErrors() { + if (currentError_.ObjectEmpty()) + return false; + ValueType error(kObjectType); + error.AddMember(GetErrorsString(), currentError_, GetStateAllocator()); + currentError_ = error; + AddCurrentError(kValidateErrorDependencies); + return true; + } + + void DisallowedValue(const ValidateErrorCode code = kValidateErrorEnum) { + currentError_.SetObject(); + AddCurrentError(code); + } + void StartDisallowedType() { + currentError_.SetArray(); + } + void AddExpectedType(const typename SchemaType::ValueType& expectedType) { + currentError_.PushBack(ValueType(expectedType, GetStateAllocator()).Move(), GetStateAllocator()); + } + void EndDisallowedType(const typename SchemaType::ValueType& actualType) { + ValueType error(kObjectType); + error.AddMember(GetExpectedString(), currentError_, GetStateAllocator()); + error.AddMember(GetActualString(), ValueType(actualType, GetStateAllocator()).Move(), GetStateAllocator()); + currentError_ = error; + AddCurrentError(kValidateErrorType); + } + void NotAllOf(ISchemaValidator** subvalidators, SizeType count) { + // Treat allOf like oneOf and anyOf to match https://rapidjson.org/md_doc_schema.html#allOf-anyOf-oneOf + AddErrorArray(kValidateErrorAllOf, subvalidators, count); + //for (SizeType i = 0; i < count; ++i) { + // MergeError(static_cast(subvalidators[i])->GetError()); + //} + } + void NoneOf(ISchemaValidator** subvalidators, SizeType count) { + AddErrorArray(kValidateErrorAnyOf, subvalidators, count); + } + void NotOneOf(ISchemaValidator** subvalidators, SizeType count) { + AddErrorArray(kValidateErrorOneOf, subvalidators, count); + } + void MultipleOneOf(SizeType index1, SizeType index2) { + ValueType matches(kArrayType); + matches.PushBack(index1, GetStateAllocator()); + matches.PushBack(index2, GetStateAllocator()); + currentError_.SetObject(); + currentError_.AddMember(GetMatchesString(), matches, GetStateAllocator()); + AddCurrentError(kValidateErrorOneOfMatch); + } + void Disallowed() { + currentError_.SetObject(); + AddCurrentError(kValidateErrorNot); + } + void DisallowedWhenWriting() { + currentError_.SetObject(); + AddCurrentError(kValidateErrorReadOnly); + } + void DisallowedWhenReading() { + currentError_.SetObject(); + AddCurrentError(kValidateErrorWriteOnly); + } + +#define RAPIDJSON_STRING_(name, ...) \ + static const StringRefType& Get##name##String() {\ + static const Ch s[] = { __VA_ARGS__, '\0' };\ + static const StringRefType v(s, static_cast(sizeof(s) / sizeof(Ch) - 1)); \ + return v;\ + } + + RAPIDJSON_STRING_(InstanceRef, 'i', 'n', 's', 't', 'a', 'n', 'c', 'e', 'R', 'e', 'f') + RAPIDJSON_STRING_(SchemaRef, 's', 'c', 'h', 'e', 'm', 'a', 'R', 'e', 'f') + RAPIDJSON_STRING_(Expected, 'e', 'x', 'p', 'e', 'c', 't', 'e', 'd') + RAPIDJSON_STRING_(Actual, 'a', 'c', 't', 'u', 'a', 'l') + RAPIDJSON_STRING_(Disallowed, 'd', 'i', 's', 'a', 'l', 'l', 'o', 'w', 'e', 'd') + RAPIDJSON_STRING_(Missing, 'm', 'i', 's', 's', 'i', 'n', 'g') + RAPIDJSON_STRING_(Errors, 'e', 'r', 'r', 'o', 'r', 's') + RAPIDJSON_STRING_(ErrorCode, 'e', 'r', 'r', 'o', 'r', 'C', 'o', 'd', 'e') + RAPIDJSON_STRING_(ErrorMessage, 'e', 'r', 'r', 'o', 'r', 'M', 'e', 's', 's', 'a', 'g', 'e') + RAPIDJSON_STRING_(Duplicates, 'd', 'u', 'p', 'l', 'i', 'c', 'a', 't', 'e', 's') + RAPIDJSON_STRING_(Matches, 'm', 'a', 't', 'c', 'h', 'e', 's') + +#undef RAPIDJSON_STRING_ + +#define RAPIDJSON_SCHEMA_HANDLE_BEGIN_(method, arg1)\ + if (!valid_) return false; \ + if ((!BeginValue() && !GetContinueOnErrors()) || (!CurrentSchema().method arg1 && !GetContinueOnErrors())) {\ + *documentStack_.template Push() = '\0';\ + documentStack_.template Pop(1);\ + RAPIDJSON_SCHEMA_PRINT(InvalidDocument, documentStack_.template Bottom());\ + valid_ = false;\ + return valid_;\ + } + +#define RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(method, arg2)\ + for (Context* context = schemaStack_.template Bottom(); context != schemaStack_.template End(); context++) {\ + if (context->hasher)\ + static_cast(context->hasher)->method arg2;\ + if (context->validators)\ + for (SizeType i_ = 0; i_ < context->validatorCount; i_++)\ + static_cast(context->validators[i_])->method arg2;\ + if (context->patternPropertiesValidators)\ + for (SizeType i_ = 0; i_ < context->patternPropertiesValidatorCount; i_++)\ + static_cast(context->patternPropertiesValidators[i_])->method arg2;\ + } + +#define RAPIDJSON_SCHEMA_HANDLE_END_(method, arg2)\ + valid_ = (EndValue() || GetContinueOnErrors()) && (!outputHandler_ || outputHandler_->method arg2);\ + return valid_; + +#define RAPIDJSON_SCHEMA_HANDLE_VALUE_(method, arg1, arg2) \ + RAPIDJSON_SCHEMA_HANDLE_BEGIN_ (method, arg1);\ + RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(method, arg2);\ + RAPIDJSON_SCHEMA_HANDLE_END_ (method, arg2) + + bool Null() { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Null, (CurrentContext()), ( )); } + bool Bool(bool b) { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Bool, (CurrentContext(), b), (b)); } + bool Int(int i) { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Int, (CurrentContext(), i), (i)); } + bool Uint(unsigned u) { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Uint, (CurrentContext(), u), (u)); } + bool Int64(int64_t i) { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Int64, (CurrentContext(), i), (i)); } + bool Uint64(uint64_t u) { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Uint64, (CurrentContext(), u), (u)); } + bool Double(double d) { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Double, (CurrentContext(), d), (d)); } + bool RawNumber(const Ch* str, SizeType length, bool copy) + { RAPIDJSON_SCHEMA_HANDLE_VALUE_(String, (CurrentContext(), str, length, copy), (str, length, copy)); } + bool String(const Ch* str, SizeType length, bool copy) + { RAPIDJSON_SCHEMA_HANDLE_VALUE_(String, (CurrentContext(), str, length, copy), (str, length, copy)); } + + bool StartObject() { + RAPIDJSON_SCHEMA_PRINT(Method, "GenericSchemaValidator::StartObject"); + RAPIDJSON_SCHEMA_HANDLE_BEGIN_(StartObject, (CurrentContext())); + RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(StartObject, ()); + valid_ = !outputHandler_ || outputHandler_->StartObject(); + return valid_; + } + + bool Key(const Ch* str, SizeType len, bool copy) { + RAPIDJSON_SCHEMA_PRINT(Method, "GenericSchemaValidator::Key", str); + if (!valid_) return false; + AppendToken(str, len); + if (!CurrentSchema().Key(CurrentContext(), str, len, copy) && !GetContinueOnErrors()) { + valid_ = false; + return valid_; + } + RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(Key, (str, len, copy)); + valid_ = !outputHandler_ || outputHandler_->Key(str, len, copy); + return valid_; + } + + bool EndObject(SizeType memberCount) { + RAPIDJSON_SCHEMA_PRINT(Method, "GenericSchemaValidator::EndObject"); + if (!valid_) return false; + RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(EndObject, (memberCount)); + if (!CurrentSchema().EndObject(CurrentContext(), memberCount) && !GetContinueOnErrors()) { + valid_ = false; + return valid_; + } + RAPIDJSON_SCHEMA_HANDLE_END_(EndObject, (memberCount)); + } + + bool StartArray() { + RAPIDJSON_SCHEMA_PRINT(Method, "GenericSchemaValidator::StartArray"); + RAPIDJSON_SCHEMA_HANDLE_BEGIN_(StartArray, (CurrentContext())); + RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(StartArray, ()); + valid_ = !outputHandler_ || outputHandler_->StartArray(); + return valid_; + } + + bool EndArray(SizeType elementCount) { + RAPIDJSON_SCHEMA_PRINT(Method, "GenericSchemaValidator::EndArray"); + if (!valid_) return false; + RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(EndArray, (elementCount)); + if (!CurrentSchema().EndArray(CurrentContext(), elementCount) && !GetContinueOnErrors()) { + valid_ = false; + return valid_; + } + RAPIDJSON_SCHEMA_HANDLE_END_(EndArray, (elementCount)); + } + +#undef RAPIDJSON_SCHEMA_HANDLE_BEGIN_ +#undef RAPIDJSON_SCHEMA_HANDLE_PARALLEL_ +#undef RAPIDJSON_SCHEMA_HANDLE_VALUE_ + + // Implementation of ISchemaStateFactory + virtual ISchemaValidator* CreateSchemaValidator(const SchemaType& root, const bool inheritContinueOnErrors) { + *documentStack_.template Push() = '\0'; + documentStack_.template Pop(1); + ISchemaValidator* sv = new (GetStateAllocator().Malloc(sizeof(GenericSchemaValidator))) GenericSchemaValidator(*schemaDocument_, root, documentStack_.template Bottom(), documentStack_.GetSize(), + depth_ + 1, + &GetStateAllocator()); + sv->SetValidateFlags(inheritContinueOnErrors ? GetValidateFlags() : GetValidateFlags() & ~(unsigned)kValidateContinueOnErrorFlag); + return sv; + } + + virtual void DestroySchemaValidator(ISchemaValidator* validator) { + GenericSchemaValidator* v = static_cast(validator); + v->~GenericSchemaValidator(); + StateAllocator::Free(v); + } + + virtual void* CreateHasher() { + return new (GetStateAllocator().Malloc(sizeof(HasherType))) HasherType(&GetStateAllocator()); + } + + virtual uint64_t GetHashCode(void* hasher) { + return static_cast(hasher)->GetHashCode(); + } + + virtual void DestroryHasher(void* hasher) { + HasherType* h = static_cast(hasher); + h->~HasherType(); + StateAllocator::Free(h); + } + + virtual void* MallocState(size_t size) { + return GetStateAllocator().Malloc(size); + } + + virtual void FreeState(void* p) { + StateAllocator::Free(p); + } + // End of implementation of ISchemaStateFactory + +private: + typedef typename SchemaType::Context Context; + typedef GenericValue, StateAllocator> HashCodeArray; + typedef internal::Hasher HasherType; + + GenericSchemaValidator( + const SchemaDocumentType& schemaDocument, + const SchemaType& root, + const char* basePath, size_t basePathSize, + unsigned depth, + StateAllocator* allocator = 0, + size_t schemaStackCapacity = kDefaultSchemaStackCapacity, + size_t documentStackCapacity = kDefaultDocumentStackCapacity) + : + schemaDocument_(&schemaDocument), + root_(root), + stateAllocator_(allocator), + ownStateAllocator_(0), + schemaStack_(allocator, schemaStackCapacity), + documentStack_(allocator, documentStackCapacity), + outputHandler_(0), + error_(kObjectType), + currentError_(), + missingDependents_(), + valid_(true), + flags_(kValidateDefaultFlags), + depth_(depth) + { + RAPIDJSON_SCHEMA_PRINT(Method, "GenericSchemaValidator::GenericSchemaValidator (internal)", basePath && basePathSize ? basePath : ""); + if (basePath && basePathSize) + memcpy(documentStack_.template Push(basePathSize), basePath, basePathSize); + } + + StateAllocator& GetStateAllocator() { + if (!stateAllocator_) + stateAllocator_ = ownStateAllocator_ = RAPIDJSON_NEW(StateAllocator)(); + return *stateAllocator_; + } + + bool GetContinueOnErrors() const { + return flags_ & kValidateContinueOnErrorFlag; + } + + bool BeginValue() { + RAPIDJSON_SCHEMA_PRINT(Method, "GenericSchemaValidator::BeginValue"); + if (schemaStack_.Empty()) + PushSchema(root_); + else { + if (CurrentContext().inArray) + internal::TokenHelper, Ch>::AppendIndexToken(documentStack_, CurrentContext().arrayElementIndex); + + if (!CurrentSchema().BeginValue(CurrentContext()) && !GetContinueOnErrors()) + return false; + + SizeType count = CurrentContext().patternPropertiesSchemaCount; + const SchemaType** sa = CurrentContext().patternPropertiesSchemas; + typename Context::PatternValidatorType patternValidatorType = CurrentContext().valuePatternValidatorType; + bool valueUniqueness = CurrentContext().valueUniqueness; + RAPIDJSON_ASSERT(CurrentContext().valueSchema); + PushSchema(*CurrentContext().valueSchema); + + if (count > 0) { + CurrentContext().objectPatternValidatorType = patternValidatorType; + ISchemaValidator**& va = CurrentContext().patternPropertiesValidators; + SizeType& validatorCount = CurrentContext().patternPropertiesValidatorCount; + va = static_cast(MallocState(sizeof(ISchemaValidator*) * count)); + std::memset(va, 0, sizeof(ISchemaValidator*) * count); + for (SizeType i = 0; i < count; i++) + va[validatorCount++] = CreateSchemaValidator(*sa[i], true); // Inherit continueOnError + } + + CurrentContext().arrayUniqueness = valueUniqueness; + } + return true; + } + + bool EndValue() { + RAPIDJSON_SCHEMA_PRINT(Method, "GenericSchemaValidator::EndValue"); + if (!CurrentSchema().EndValue(CurrentContext()) && !GetContinueOnErrors()) + return false; + + GenericStringBuffer sb; + schemaDocument_->GetPointer(&CurrentSchema()).StringifyUriFragment(sb); + *documentStack_.template Push() = '\0'; + documentStack_.template Pop(1); + RAPIDJSON_SCHEMA_PRINT(ValidatorPointers, sb.GetString(), documentStack_.template Bottom(), depth_); + void* hasher = CurrentContext().hasher; + uint64_t h = hasher && CurrentContext().arrayUniqueness ? static_cast(hasher)->GetHashCode() : 0; + + PopSchema(); + + if (!schemaStack_.Empty()) { + Context& context = CurrentContext(); + // Only check uniqueness if there is a hasher + if (hasher && context.valueUniqueness) { + HashCodeArray* a = static_cast(context.arrayElementHashCodes); + if (!a) + CurrentContext().arrayElementHashCodes = a = new (GetStateAllocator().Malloc(sizeof(HashCodeArray))) HashCodeArray(kArrayType); + for (typename HashCodeArray::ConstValueIterator itr = a->Begin(); itr != a->End(); ++itr) + if (itr->GetUint64() == h) { + DuplicateItems(static_cast(itr - a->Begin()), a->Size()); + // Cleanup before returning if continuing + if (GetContinueOnErrors()) { + a->PushBack(h, GetStateAllocator()); + while (!documentStack_.Empty() && *documentStack_.template Pop(1) != '/'); + } + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorUniqueItems); + } + a->PushBack(h, GetStateAllocator()); + } + } + + // Remove the last token of document pointer + while (!documentStack_.Empty() && *documentStack_.template Pop(1) != '/') + ; + + return true; + } + + void AppendToken(const Ch* str, SizeType len) { + documentStack_.template Reserve(1 + len * 2); // worst case all characters are escaped as two characters + *documentStack_.template PushUnsafe() = '/'; + for (SizeType i = 0; i < len; i++) { + if (str[i] == '~') { + *documentStack_.template PushUnsafe() = '~'; + *documentStack_.template PushUnsafe() = '0'; + } + else if (str[i] == '/') { + *documentStack_.template PushUnsafe() = '~'; + *documentStack_.template PushUnsafe() = '1'; + } + else + *documentStack_.template PushUnsafe() = str[i]; + } + } + + RAPIDJSON_FORCEINLINE void PushSchema(const SchemaType& schema) { new (schemaStack_.template Push()) Context(*this, *this, &schema, flags_); } + + RAPIDJSON_FORCEINLINE void PopSchema() { + Context* c = schemaStack_.template Pop(1); + if (HashCodeArray* a = static_cast(c->arrayElementHashCodes)) { + a->~HashCodeArray(); + StateAllocator::Free(a); + } + c->~Context(); + } + + void AddErrorInstanceLocation(ValueType& result, bool parent) { + GenericStringBuffer sb; + PointerType instancePointer = GetInvalidDocumentPointer(); + ((parent && instancePointer.GetTokenCount() > 0) + ? PointerType(instancePointer.GetTokens(), instancePointer.GetTokenCount() - 1) + : instancePointer).StringifyUriFragment(sb); + ValueType instanceRef(sb.GetString(), static_cast(sb.GetSize() / sizeof(Ch)), + GetStateAllocator()); + result.AddMember(GetInstanceRefString(), instanceRef, GetStateAllocator()); + } + + void AddErrorSchemaLocation(ValueType& result, PointerType schema = PointerType()) { + GenericStringBuffer sb; + SizeType len = CurrentSchema().GetURI().GetStringLength(); + if (len) memcpy(sb.Push(len), CurrentSchema().GetURI().GetString(), len * sizeof(Ch)); + if (schema.GetTokenCount()) schema.StringifyUriFragment(sb); + else GetInvalidSchemaPointer().StringifyUriFragment(sb); + ValueType schemaRef(sb.GetString(), static_cast(sb.GetSize() / sizeof(Ch)), + GetStateAllocator()); + result.AddMember(GetSchemaRefString(), schemaRef, GetStateAllocator()); + } + + void AddErrorCode(ValueType& result, const ValidateErrorCode code) { + result.AddMember(GetErrorCodeString(), code, GetStateAllocator()); + } + + void AddError(ValueType& keyword, ValueType& error) { + typename ValueType::MemberIterator member = error_.FindMember(keyword); + if (member == error_.MemberEnd()) + error_.AddMember(keyword, error, GetStateAllocator()); + else { + if (member->value.IsObject()) { + ValueType errors(kArrayType); + errors.PushBack(member->value, GetStateAllocator()); + member->value = errors; + } + member->value.PushBack(error, GetStateAllocator()); + } + } + + void AddCurrentError(const ValidateErrorCode code, bool parent = false) { + AddErrorCode(currentError_, code); + AddErrorInstanceLocation(currentError_, parent); + AddErrorSchemaLocation(currentError_); + AddError(ValueType(SchemaType::GetValidateErrorKeyword(code), GetStateAllocator(), false).Move(), currentError_); + } + + void MergeError(ValueType& other) { + for (typename ValueType::MemberIterator it = other.MemberBegin(), end = other.MemberEnd(); it != end; ++it) { + AddError(it->name, it->value); + } + } + + void AddNumberError(const ValidateErrorCode code, ValueType& actual, const SValue& expected, + const typename SchemaType::ValueType& (*exclusive)() = 0) { + currentError_.SetObject(); + currentError_.AddMember(GetActualString(), actual, GetStateAllocator()); + currentError_.AddMember(GetExpectedString(), ValueType(expected, GetStateAllocator()).Move(), GetStateAllocator()); + if (exclusive) + currentError_.AddMember(ValueType(exclusive(), GetStateAllocator()).Move(), true, GetStateAllocator()); + AddCurrentError(code); + } + + void AddErrorArray(const ValidateErrorCode code, + ISchemaValidator** subvalidators, SizeType count) { + ValueType errors(kArrayType); + for (SizeType i = 0; i < count; ++i) + errors.PushBack(static_cast(subvalidators[i])->GetError(), GetStateAllocator()); + currentError_.SetObject(); + currentError_.AddMember(GetErrorsString(), errors, GetStateAllocator()); + AddCurrentError(code); + } + + const SchemaType& CurrentSchema() const { return *schemaStack_.template Top()->schema; } + Context& CurrentContext() { return *schemaStack_.template Top(); } + const Context& CurrentContext() const { return *schemaStack_.template Top(); } + + static const size_t kDefaultSchemaStackCapacity = 1024; + static const size_t kDefaultDocumentStackCapacity = 256; + const SchemaDocumentType* schemaDocument_; + const SchemaType& root_; + StateAllocator* stateAllocator_; + StateAllocator* ownStateAllocator_; + internal::Stack schemaStack_; //!< stack to store the current path of schema (BaseSchemaType *) + internal::Stack documentStack_; //!< stack to store the current path of validating document (Ch) + OutputHandler* outputHandler_; + ValueType error_; + ValueType currentError_; + ValueType missingDependents_; + bool valid_; + unsigned flags_; + unsigned depth_; +}; + +typedef GenericSchemaValidator SchemaValidator; + +/////////////////////////////////////////////////////////////////////////////// +// SchemaValidatingReader + +//! A helper class for parsing with validation. +/*! + This helper class is a functor, designed as a parameter of \ref GenericDocument::Populate(). + + \tparam parseFlags Combination of \ref ParseFlag. + \tparam InputStream Type of input stream, implementing Stream concept. + \tparam SourceEncoding Encoding of the input stream. + \tparam SchemaDocumentType Type of schema document. + \tparam StackAllocator Allocator type for stack. +*/ +template < + unsigned parseFlags, + typename InputStream, + typename SourceEncoding, + typename SchemaDocumentType = SchemaDocument, + typename StackAllocator = CrtAllocator> +class SchemaValidatingReader { +public: + typedef typename SchemaDocumentType::PointerType PointerType; + typedef typename InputStream::Ch Ch; + typedef GenericValue ValueType; + + //! Constructor + /*! + \param is Input stream. + \param sd Schema document. + */ + SchemaValidatingReader(InputStream& is, const SchemaDocumentType& sd) : is_(is), sd_(sd), invalidSchemaKeyword_(), invalidSchemaCode_(kValidateErrorNone), error_(kObjectType), isValid_(true) {} + + template + bool operator()(Handler& handler) { + GenericReader reader; + GenericSchemaValidator validator(sd_, handler); + parseResult_ = reader.template Parse(is_, validator); + + isValid_ = validator.IsValid(); + if (isValid_) { + invalidSchemaPointer_ = PointerType(); + invalidSchemaKeyword_ = 0; + invalidDocumentPointer_ = PointerType(); + error_.SetObject(); + } + else { + invalidSchemaPointer_ = validator.GetInvalidSchemaPointer(); + invalidSchemaKeyword_ = validator.GetInvalidSchemaKeyword(); + invalidSchemaCode_ = validator.GetInvalidSchemaCode(); + invalidDocumentPointer_ = validator.GetInvalidDocumentPointer(); + error_.CopyFrom(validator.GetError(), allocator_); + } + + return parseResult_; + } + + const ParseResult& GetParseResult() const { return parseResult_; } + bool IsValid() const { return isValid_; } + const PointerType& GetInvalidSchemaPointer() const { return invalidSchemaPointer_; } + const Ch* GetInvalidSchemaKeyword() const { return invalidSchemaKeyword_; } + const PointerType& GetInvalidDocumentPointer() const { return invalidDocumentPointer_; } + const ValueType& GetError() const { return error_; } + ValidateErrorCode GetInvalidSchemaCode() const { return invalidSchemaCode_; } + +private: + InputStream& is_; + const SchemaDocumentType& sd_; + + ParseResult parseResult_; + PointerType invalidSchemaPointer_; + const Ch* invalidSchemaKeyword_; + PointerType invalidDocumentPointer_; + ValidateErrorCode invalidSchemaCode_; + StackAllocator allocator_; + ValueType error_; + bool isValid_; +}; + +RAPIDJSON_NAMESPACE_END +RAPIDJSON_DIAG_POP + +#endif // RAPIDJSON_SCHEMA_H_ diff --git a/include/rapidjson/stream.h b/include/rapidjson/stream.h new file mode 100644 index 0000000..1fd7091 --- /dev/null +++ b/include/rapidjson/stream.h @@ -0,0 +1,223 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// 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 "rapidjson.h" + +#ifndef RAPIDJSON_STREAM_H_ +#define RAPIDJSON_STREAM_H_ + +#include "encodings.h" + +RAPIDJSON_NAMESPACE_BEGIN + +/////////////////////////////////////////////////////////////////////////////// +// Stream + +/*! \class rapidjson::Stream + \brief Concept for reading and writing characters. + + For read-only stream, no need to implement PutBegin(), Put(), Flush() and PutEnd(). + + For write-only stream, only need to implement Put() and Flush(). + +\code +concept Stream { + typename Ch; //!< Character type of the stream. + + //! Read the current character from stream without moving the read cursor. + Ch Peek() const; + + //! Read the current character from stream and moving the read cursor to next character. + Ch Take(); + + //! Get the current read cursor. + //! \return Number of characters read from start. + size_t Tell(); + + //! Begin writing operation at the current read pointer. + //! \return The begin writer pointer. + Ch* PutBegin(); + + //! Write a character. + void Put(Ch c); + + //! Flush the buffer. + void Flush(); + + //! End the writing operation. + //! \param begin The begin write pointer returned by PutBegin(). + //! \return Number of characters written. + size_t PutEnd(Ch* begin); +} +\endcode +*/ + +//! Provides additional information for stream. +/*! + By using traits pattern, this type provides a default configuration for stream. + For custom stream, this type can be specialized for other configuration. + See TEST(Reader, CustomStringStream) in readertest.cpp for example. +*/ +template +struct StreamTraits { + //! Whether to make local copy of stream for optimization during parsing. + /*! + By default, for safety, streams do not use local copy optimization. + Stream that can be copied fast should specialize this, like StreamTraits. + */ + enum { copyOptimization = 0 }; +}; + +//! Reserve n characters for writing to a stream. +template +inline void PutReserve(Stream& stream, size_t count) { + (void)stream; + (void)count; +} + +//! Write character to a stream, presuming buffer is reserved. +template +inline void PutUnsafe(Stream& stream, typename Stream::Ch c) { + stream.Put(c); +} + +//! Put N copies of a character to a stream. +template +inline void PutN(Stream& stream, Ch c, size_t n) { + PutReserve(stream, n); + for (size_t i = 0; i < n; i++) + PutUnsafe(stream, c); +} + +/////////////////////////////////////////////////////////////////////////////// +// GenericStreamWrapper + +//! A Stream Wrapper +/*! \tThis string stream is a wrapper for any stream by just forwarding any + \treceived message to the origin stream. + \note implements Stream concept +*/ + +#if defined(_MSC_VER) && _MSC_VER <= 1800 +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(4702) // unreachable code +RAPIDJSON_DIAG_OFF(4512) // assignment operator could not be generated +#endif + +template > +class GenericStreamWrapper { +public: + typedef typename Encoding::Ch Ch; + GenericStreamWrapper(InputStream& is): is_(is) {} + + Ch Peek() const { return is_.Peek(); } + Ch Take() { return is_.Take(); } + size_t Tell() { return is_.Tell(); } + Ch* PutBegin() { return is_.PutBegin(); } + void Put(Ch ch) { is_.Put(ch); } + void Flush() { is_.Flush(); } + size_t PutEnd(Ch* ch) { return is_.PutEnd(ch); } + + // wrapper for MemoryStream + const Ch* Peek4() const { return is_.Peek4(); } + + // wrapper for AutoUTFInputStream + UTFType GetType() const { return is_.GetType(); } + bool HasBOM() const { return is_.HasBOM(); } + +protected: + InputStream& is_; +}; + +#if defined(_MSC_VER) && _MSC_VER <= 1800 +RAPIDJSON_DIAG_POP +#endif + +/////////////////////////////////////////////////////////////////////////////// +// StringStream + +//! Read-only string stream. +/*! \note implements Stream concept +*/ +template +struct GenericStringStream { + typedef typename Encoding::Ch Ch; + + GenericStringStream(const Ch *src) : src_(src), head_(src) {} + + Ch Peek() const { return *src_; } + Ch Take() { return *src_++; } + size_t Tell() const { return static_cast(src_ - head_); } + + Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; } + void Put(Ch) { RAPIDJSON_ASSERT(false); } + void Flush() { RAPIDJSON_ASSERT(false); } + size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; } + + const Ch* src_; //!< Current read position. + const Ch* head_; //!< Original head of the string. +}; + +template +struct StreamTraits > { + enum { copyOptimization = 1 }; +}; + +//! String stream with UTF8 encoding. +typedef GenericStringStream > StringStream; + +/////////////////////////////////////////////////////////////////////////////// +// InsituStringStream + +//! A read-write string stream. +/*! This string stream is particularly designed for in-situ parsing. + \note implements Stream concept +*/ +template +struct GenericInsituStringStream { + typedef typename Encoding::Ch Ch; + + GenericInsituStringStream(Ch *src) : src_(src), dst_(0), head_(src) {} + + // Read + Ch Peek() { return *src_; } + Ch Take() { return *src_++; } + size_t Tell() { return static_cast(src_ - head_); } + + // Write + void Put(Ch c) { RAPIDJSON_ASSERT(dst_ != 0); *dst_++ = c; } + + Ch* PutBegin() { return dst_ = src_; } + size_t PutEnd(Ch* begin) { return static_cast(dst_ - begin); } + void Flush() {} + + Ch* Push(size_t count) { Ch* begin = dst_; dst_ += count; return begin; } + void Pop(size_t count) { dst_ -= count; } + + Ch* src_; + Ch* dst_; + Ch* head_; +}; + +template +struct StreamTraits > { + enum { copyOptimization = 1 }; +}; + +//! Insitu string stream with UTF8 encoding. +typedef GenericInsituStringStream > InsituStringStream; + +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_STREAM_H_ diff --git a/include/rapidjson/stringbuffer.h b/include/rapidjson/stringbuffer.h new file mode 100644 index 0000000..82ad3ca --- /dev/null +++ b/include/rapidjson/stringbuffer.h @@ -0,0 +1,121 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// 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 RAPIDJSON_STRINGBUFFER_H_ +#define RAPIDJSON_STRINGBUFFER_H_ + +#include "stream.h" +#include "internal/stack.h" + +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS +#include // std::move +#endif + +#include "internal/stack.h" + +#if defined(__clang__) +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(c++98-compat) +#endif + +RAPIDJSON_NAMESPACE_BEGIN + +//! Represents an in-memory output stream. +/*! + \tparam Encoding Encoding of the stream. + \tparam Allocator type for allocating memory buffer. + \note implements Stream concept +*/ +template +class GenericStringBuffer { +public: + typedef typename Encoding::Ch Ch; + + GenericStringBuffer(Allocator* allocator = 0, size_t capacity = kDefaultCapacity) : stack_(allocator, capacity) {} + +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS + GenericStringBuffer(GenericStringBuffer&& rhs) : stack_(std::move(rhs.stack_)) {} + GenericStringBuffer& operator=(GenericStringBuffer&& rhs) { + if (&rhs != this) + stack_ = std::move(rhs.stack_); + return *this; + } +#endif + + void Put(Ch c) { *stack_.template Push() = c; } + void PutUnsafe(Ch c) { *stack_.template PushUnsafe() = c; } + void Flush() {} + + void Clear() { stack_.Clear(); } + void ShrinkToFit() { + // Push and pop a null terminator. This is safe. + *stack_.template Push() = '\0'; + stack_.ShrinkToFit(); + stack_.template Pop(1); + } + + void Reserve(size_t count) { stack_.template Reserve(count); } + Ch* Push(size_t count) { return stack_.template Push(count); } + Ch* PushUnsafe(size_t count) { return stack_.template PushUnsafe(count); } + void Pop(size_t count) { stack_.template Pop(count); } + + const Ch* GetString() const { + // Push and pop a null terminator. This is safe. + *stack_.template Push() = '\0'; + stack_.template Pop(1); + + return stack_.template Bottom(); + } + + //! Get the size of string in bytes in the string buffer. + size_t GetSize() const { return stack_.GetSize(); } + + //! Get the length of string in Ch in the string buffer. + size_t GetLength() const { return stack_.GetSize() / sizeof(Ch); } + + static const size_t kDefaultCapacity = 256; + mutable internal::Stack stack_; + +private: + // Prohibit copy constructor & assignment operator. + GenericStringBuffer(const GenericStringBuffer&); + GenericStringBuffer& operator=(const GenericStringBuffer&); +}; + +//! String buffer with UTF8 encoding +typedef GenericStringBuffer > StringBuffer; + +template +inline void PutReserve(GenericStringBuffer& stream, size_t count) { + stream.Reserve(count); +} + +template +inline void PutUnsafe(GenericStringBuffer& stream, typename Encoding::Ch c) { + stream.PutUnsafe(c); +} + +//! Implement specialized version of PutN() with memset() for better performance. +template<> +inline void PutN(GenericStringBuffer >& stream, char c, size_t n) { + std::memset(stream.stack_.Push(n), c, n * sizeof(c)); +} + +RAPIDJSON_NAMESPACE_END + +#if defined(__clang__) +RAPIDJSON_DIAG_POP +#endif + +#endif // RAPIDJSON_STRINGBUFFER_H_ diff --git a/include/rapidjson/uri.h b/include/rapidjson/uri.h new file mode 100644 index 0000000..f93e508 --- /dev/null +++ b/include/rapidjson/uri.h @@ -0,0 +1,481 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// (C) Copyright IBM Corporation 2021 +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// 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 RAPIDJSON_URI_H_ +#define RAPIDJSON_URI_H_ + +#include "internal/strfunc.h" + +#if defined(__clang__) +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(c++98-compat) +#elif defined(_MSC_VER) +RAPIDJSON_DIAG_OFF(4512) // assignment operator could not be generated +#endif + +RAPIDJSON_NAMESPACE_BEGIN + +/////////////////////////////////////////////////////////////////////////////// +// GenericUri + +template +class GenericUri { +public: + typedef typename ValueType::Ch Ch; +#if RAPIDJSON_HAS_STDSTRING + typedef std::basic_string String; +#endif + + //! Constructors + GenericUri(Allocator* allocator = 0) : uri_(), base_(), scheme_(), auth_(), path_(), query_(), frag_(), allocator_(allocator), ownAllocator_() { + } + + GenericUri(const Ch* uri, SizeType len, Allocator* allocator = 0) : uri_(), base_(), scheme_(), auth_(), path_(), query_(), frag_(), allocator_(allocator), ownAllocator_() { + Parse(uri, len); + } + + GenericUri(const Ch* uri, Allocator* allocator = 0) : uri_(), base_(), scheme_(), auth_(), path_(), query_(), frag_(), allocator_(allocator), ownAllocator_() { + Parse(uri, internal::StrLen(uri)); + } + + // Use with specializations of GenericValue + template GenericUri(const T& uri, Allocator* allocator = 0) : uri_(), base_(), scheme_(), auth_(), path_(), query_(), frag_(), allocator_(allocator), ownAllocator_() { + const Ch* u = uri.template Get(); // TypeHelper from document.h + Parse(u, internal::StrLen(u)); + } + +#if RAPIDJSON_HAS_STDSTRING + GenericUri(const String& uri, Allocator* allocator = 0) : uri_(), base_(), scheme_(), auth_(), path_(), query_(), frag_(), allocator_(allocator), ownAllocator_() { + Parse(uri.c_str(), internal::StrLen(uri.c_str())); + } +#endif + + //! Copy constructor + GenericUri(const GenericUri& rhs) : uri_(), base_(), scheme_(), auth_(), path_(), query_(), frag_(), allocator_(), ownAllocator_() { + *this = rhs; + } + + //! Copy constructor + GenericUri(const GenericUri& rhs, Allocator* allocator) : uri_(), base_(), scheme_(), auth_(), path_(), query_(), frag_(), allocator_(allocator), ownAllocator_() { + *this = rhs; + } + + //! Destructor. + ~GenericUri() { + Free(); + RAPIDJSON_DELETE(ownAllocator_); + } + + //! Assignment operator + GenericUri& operator=(const GenericUri& rhs) { + if (this != &rhs) { + // Do not delete ownAllocator + Free(); + Allocate(rhs.GetStringLength()); + auth_ = CopyPart(scheme_, rhs.scheme_, rhs.GetSchemeStringLength()); + path_ = CopyPart(auth_, rhs.auth_, rhs.GetAuthStringLength()); + query_ = CopyPart(path_, rhs.path_, rhs.GetPathStringLength()); + frag_ = CopyPart(query_, rhs.query_, rhs.GetQueryStringLength()); + base_ = CopyPart(frag_, rhs.frag_, rhs.GetFragStringLength()); + uri_ = CopyPart(base_, rhs.base_, rhs.GetBaseStringLength()); + CopyPart(uri_, rhs.uri_, rhs.GetStringLength()); + } + return *this; + } + + //! Getters + // Use with specializations of GenericValue + template void Get(T& uri, Allocator& allocator) { + uri.template Set(this->GetString(), allocator); // TypeHelper from document.h + } + + const Ch* GetString() const { return uri_; } + SizeType GetStringLength() const { return uri_ == 0 ? 0 : internal::StrLen(uri_); } + const Ch* GetBaseString() const { return base_; } + SizeType GetBaseStringLength() const { return base_ == 0 ? 0 : internal::StrLen(base_); } + const Ch* GetSchemeString() const { return scheme_; } + SizeType GetSchemeStringLength() const { return scheme_ == 0 ? 0 : internal::StrLen(scheme_); } + const Ch* GetAuthString() const { return auth_; } + SizeType GetAuthStringLength() const { return auth_ == 0 ? 0 : internal::StrLen(auth_); } + const Ch* GetPathString() const { return path_; } + SizeType GetPathStringLength() const { return path_ == 0 ? 0 : internal::StrLen(path_); } + const Ch* GetQueryString() const { return query_; } + SizeType GetQueryStringLength() const { return query_ == 0 ? 0 : internal::StrLen(query_); } + const Ch* GetFragString() const { return frag_; } + SizeType GetFragStringLength() const { return frag_ == 0 ? 0 : internal::StrLen(frag_); } + +#if RAPIDJSON_HAS_STDSTRING + static String Get(const GenericUri& uri) { return String(uri.GetString(), uri.GetStringLength()); } + static String GetBase(const GenericUri& uri) { return String(uri.GetBaseString(), uri.GetBaseStringLength()); } + static String GetScheme(const GenericUri& uri) { return String(uri.GetSchemeString(), uri.GetSchemeStringLength()); } + static String GetAuth(const GenericUri& uri) { return String(uri.GetAuthString(), uri.GetAuthStringLength()); } + static String GetPath(const GenericUri& uri) { return String(uri.GetPathString(), uri.GetPathStringLength()); } + static String GetQuery(const GenericUri& uri) { return String(uri.GetQueryString(), uri.GetQueryStringLength()); } + static String GetFrag(const GenericUri& uri) { return String(uri.GetFragString(), uri.GetFragStringLength()); } +#endif + + //! Equality operators + bool operator==(const GenericUri& rhs) const { + return Match(rhs, true); + } + + bool operator!=(const GenericUri& rhs) const { + return !Match(rhs, true); + } + + bool Match(const GenericUri& uri, bool full = true) const { + Ch* s1; + Ch* s2; + if (full) { + s1 = uri_; + s2 = uri.uri_; + } else { + s1 = base_; + s2 = uri.base_; + } + if (s1 == s2) return true; + if (s1 == 0 || s2 == 0) return false; + return internal::StrCmp(s1, s2) == 0; + } + + //! Resolve this URI against another (base) URI in accordance with URI resolution rules. + // See https://tools.ietf.org/html/rfc3986 + // Use for resolving an id or $ref with an in-scope id. + // Returns a new GenericUri for the resolved URI. + GenericUri Resolve(const GenericUri& baseuri, Allocator* allocator = 0) { + GenericUri resuri; + resuri.allocator_ = allocator; + // Ensure enough space for combining paths + resuri.Allocate(GetStringLength() + baseuri.GetStringLength() + 1); // + 1 for joining slash + + if (!(GetSchemeStringLength() == 0)) { + // Use all of this URI + resuri.auth_ = CopyPart(resuri.scheme_, scheme_, GetSchemeStringLength()); + resuri.path_ = CopyPart(resuri.auth_, auth_, GetAuthStringLength()); + resuri.query_ = CopyPart(resuri.path_, path_, GetPathStringLength()); + resuri.frag_ = CopyPart(resuri.query_, query_, GetQueryStringLength()); + resuri.RemoveDotSegments(); + } else { + // Use the base scheme + resuri.auth_ = CopyPart(resuri.scheme_, baseuri.scheme_, baseuri.GetSchemeStringLength()); + if (!(GetAuthStringLength() == 0)) { + // Use this auth, path, query + resuri.path_ = CopyPart(resuri.auth_, auth_, GetAuthStringLength()); + resuri.query_ = CopyPart(resuri.path_, path_, GetPathStringLength()); + resuri.frag_ = CopyPart(resuri.query_, query_, GetQueryStringLength()); + resuri.RemoveDotSegments(); + } else { + // Use the base auth + resuri.path_ = CopyPart(resuri.auth_, baseuri.auth_, baseuri.GetAuthStringLength()); + if (GetPathStringLength() == 0) { + // Use the base path + resuri.query_ = CopyPart(resuri.path_, baseuri.path_, baseuri.GetPathStringLength()); + if (GetQueryStringLength() == 0) { + // Use the base query + resuri.frag_ = CopyPart(resuri.query_, baseuri.query_, baseuri.GetQueryStringLength()); + } else { + // Use this query + resuri.frag_ = CopyPart(resuri.query_, query_, GetQueryStringLength()); + } + } else { + if (path_[0] == '/') { + // Absolute path - use all of this path + resuri.query_ = CopyPart(resuri.path_, path_, GetPathStringLength()); + resuri.RemoveDotSegments(); + } else { + // Relative path - append this path to base path after base path's last slash + size_t pos = 0; + if (!(baseuri.GetAuthStringLength() == 0) && baseuri.GetPathStringLength() == 0) { + resuri.path_[pos] = '/'; + pos++; + } + size_t lastslashpos = baseuri.GetPathStringLength(); + while (lastslashpos > 0) { + if (baseuri.path_[lastslashpos - 1] == '/') break; + lastslashpos--; + } + std::memcpy(&resuri.path_[pos], baseuri.path_, lastslashpos * sizeof(Ch)); + pos += lastslashpos; + resuri.query_ = CopyPart(&resuri.path_[pos], path_, GetPathStringLength()); + resuri.RemoveDotSegments(); + } + // Use this query + resuri.frag_ = CopyPart(resuri.query_, query_, GetQueryStringLength()); + } + } + } + // Always use this frag + resuri.base_ = CopyPart(resuri.frag_, frag_, GetFragStringLength()); + + // Re-constitute base_ and uri_ + resuri.SetBase(); + resuri.uri_ = resuri.base_ + resuri.GetBaseStringLength() + 1; + resuri.SetUri(); + return resuri; + } + + //! Get the allocator of this GenericUri. + Allocator& GetAllocator() { return *allocator_; } + +private: + // Allocate memory for a URI + // Returns total amount allocated + std::size_t Allocate(std::size_t len) { + // Create own allocator if user did not supply. + if (!allocator_) + ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator)(); + + // Allocate one block containing each part of the URI (5) plus base plus full URI, all null terminated. + // Order: scheme, auth, path, query, frag, base, uri + // Note need to set, increment, assign in 3 stages to avoid compiler warning bug. + size_t total = (3 * len + 7) * sizeof(Ch); + scheme_ = static_cast(allocator_->Malloc(total)); + *scheme_ = '\0'; + auth_ = scheme_; + auth_++; + *auth_ = '\0'; + path_ = auth_; + path_++; + *path_ = '\0'; + query_ = path_; + query_++; + *query_ = '\0'; + frag_ = query_; + frag_++; + *frag_ = '\0'; + base_ = frag_; + base_++; + *base_ = '\0'; + uri_ = base_; + uri_++; + *uri_ = '\0'; + return total; + } + + // Free memory for a URI + void Free() { + if (scheme_) { + Allocator::Free(scheme_); + scheme_ = 0; + } + } + + // Parse a URI into constituent scheme, authority, path, query, & fragment parts + // Supports URIs that match regex ^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))? as per + // https://tools.ietf.org/html/rfc3986 + void Parse(const Ch* uri, std::size_t len) { + std::size_t start = 0, pos1 = 0, pos2 = 0; + Allocate(len); + + // Look for scheme ([^:/?#]+):)? + if (start < len) { + while (pos1 < len) { + if (uri[pos1] == ':') break; + pos1++; + } + if (pos1 != len) { + while (pos2 < len) { + if (uri[pos2] == '/') break; + if (uri[pos2] == '?') break; + if (uri[pos2] == '#') break; + pos2++; + } + if (pos1 < pos2) { + pos1++; + std::memcpy(scheme_, &uri[start], pos1 * sizeof(Ch)); + scheme_[pos1] = '\0'; + start = pos1; + } + } + } + // Look for auth (//([^/?#]*))? + // Note need to set, increment, assign in 3 stages to avoid compiler warning bug. + auth_ = scheme_ + GetSchemeStringLength(); + auth_++; + *auth_ = '\0'; + if (start < len - 1 && uri[start] == '/' && uri[start + 1] == '/') { + pos2 = start + 2; + while (pos2 < len) { + if (uri[pos2] == '/') break; + if (uri[pos2] == '?') break; + if (uri[pos2] == '#') break; + pos2++; + } + std::memcpy(auth_, &uri[start], (pos2 - start) * sizeof(Ch)); + auth_[pos2 - start] = '\0'; + start = pos2; + } + // Look for path ([^?#]*) + // Note need to set, increment, assign in 3 stages to avoid compiler warning bug. + path_ = auth_ + GetAuthStringLength(); + path_++; + *path_ = '\0'; + if (start < len) { + pos2 = start; + while (pos2 < len) { + if (uri[pos2] == '?') break; + if (uri[pos2] == '#') break; + pos2++; + } + if (start != pos2) { + std::memcpy(path_, &uri[start], (pos2 - start) * sizeof(Ch)); + path_[pos2 - start] = '\0'; + if (path_[0] == '/') + RemoveDotSegments(); // absolute path - normalize + start = pos2; + } + } + // Look for query (\?([^#]*))? + // Note need to set, increment, assign in 3 stages to avoid compiler warning bug. + query_ = path_ + GetPathStringLength(); + query_++; + *query_ = '\0'; + if (start < len && uri[start] == '?') { + pos2 = start + 1; + while (pos2 < len) { + if (uri[pos2] == '#') break; + pos2++; + } + if (start != pos2) { + std::memcpy(query_, &uri[start], (pos2 - start) * sizeof(Ch)); + query_[pos2 - start] = '\0'; + start = pos2; + } + } + // Look for fragment (#(.*))? + // Note need to set, increment, assign in 3 stages to avoid compiler warning bug. + frag_ = query_ + GetQueryStringLength(); + frag_++; + *frag_ = '\0'; + if (start < len && uri[start] == '#') { + std::memcpy(frag_, &uri[start], (len - start) * sizeof(Ch)); + frag_[len - start] = '\0'; + } + + // Re-constitute base_ and uri_ + base_ = frag_ + GetFragStringLength() + 1; + SetBase(); + uri_ = base_ + GetBaseStringLength() + 1; + SetUri(); + } + + // Reconstitute base + void SetBase() { + Ch* next = base_; + std::memcpy(next, scheme_, GetSchemeStringLength() * sizeof(Ch)); + next+= GetSchemeStringLength(); + std::memcpy(next, auth_, GetAuthStringLength() * sizeof(Ch)); + next+= GetAuthStringLength(); + std::memcpy(next, path_, GetPathStringLength() * sizeof(Ch)); + next+= GetPathStringLength(); + std::memcpy(next, query_, GetQueryStringLength() * sizeof(Ch)); + next+= GetQueryStringLength(); + *next = '\0'; + } + + // Reconstitute uri + void SetUri() { + Ch* next = uri_; + std::memcpy(next, base_, GetBaseStringLength() * sizeof(Ch)); + next+= GetBaseStringLength(); + std::memcpy(next, frag_, GetFragStringLength() * sizeof(Ch)); + next+= GetFragStringLength(); + *next = '\0'; + } + + // Copy a part from one GenericUri to another + // Return the pointer to the next part to be copied to + Ch* CopyPart(Ch* to, Ch* from, std::size_t len) { + RAPIDJSON_ASSERT(to != 0); + RAPIDJSON_ASSERT(from != 0); + std::memcpy(to, from, len * sizeof(Ch)); + to[len] = '\0'; + Ch* next = to + len + 1; + return next; + } + + // Remove . and .. segments from the path_ member. + // https://tools.ietf.org/html/rfc3986 + // This is done in place as we are only removing segments. + void RemoveDotSegments() { + std::size_t pathlen = GetPathStringLength(); + std::size_t pathpos = 0; // Position in path_ + std::size_t newpos = 0; // Position in new path_ + + // Loop through each segment in original path_ + while (pathpos < pathlen) { + // Get next segment, bounded by '/' or end + size_t slashpos = 0; + while ((pathpos + slashpos) < pathlen) { + if (path_[pathpos + slashpos] == '/') break; + slashpos++; + } + // Check for .. and . segments + if (slashpos == 2 && path_[pathpos] == '.' && path_[pathpos + 1] == '.') { + // Backup a .. segment in the new path_ + // We expect to find a previously added slash at the end or nothing + RAPIDJSON_ASSERT(newpos == 0 || path_[newpos - 1] == '/'); + size_t lastslashpos = newpos; + // Make sure we don't go beyond the start segment + if (lastslashpos > 1) { + // Find the next to last slash and back up to it + lastslashpos--; + while (lastslashpos > 0) { + if (path_[lastslashpos - 1] == '/') break; + lastslashpos--; + } + // Set the new path_ position + newpos = lastslashpos; + } + } else if (slashpos == 1 && path_[pathpos] == '.') { + // Discard . segment, leaves new path_ unchanged + } else { + // Move any other kind of segment to the new path_ + RAPIDJSON_ASSERT(newpos <= pathpos); + std::memmove(&path_[newpos], &path_[pathpos], slashpos * sizeof(Ch)); + newpos += slashpos; + // Add slash if not at end + if ((pathpos + slashpos) < pathlen) { + path_[newpos] = '/'; + newpos++; + } + } + // Move to next segment + pathpos += slashpos + 1; + } + path_[newpos] = '\0'; + } + + Ch* uri_; // Everything + Ch* base_; // Everything except fragment + Ch* scheme_; // Includes the : + Ch* auth_; // Includes the // + Ch* path_; // Absolute if starts with / + Ch* query_; // Includes the ? + Ch* frag_; // Includes the # + + Allocator* allocator_; //!< The current allocator. It is either user-supplied or equal to ownAllocator_. + Allocator* ownAllocator_; //!< Allocator owned by this Uri. +}; + +//! GenericUri for Value (UTF-8, default allocator). +typedef GenericUri Uri; + +RAPIDJSON_NAMESPACE_END + +#if defined(__clang__) +RAPIDJSON_DIAG_POP +#endif + +#endif // RAPIDJSON_URI_H_ diff --git a/include/rapidjson/writer.h b/include/rapidjson/writer.h new file mode 100644 index 0000000..8b38921 --- /dev/null +++ b/include/rapidjson/writer.h @@ -0,0 +1,710 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// 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 RAPIDJSON_WRITER_H_ +#define RAPIDJSON_WRITER_H_ + +#include "stream.h" +#include "internal/clzll.h" +#include "internal/meta.h" +#include "internal/stack.h" +#include "internal/strfunc.h" +#include "internal/dtoa.h" +#include "internal/itoa.h" +#include "stringbuffer.h" +#include // placement new + +#if defined(RAPIDJSON_SIMD) && defined(_MSC_VER) +#include +#pragma intrinsic(_BitScanForward) +#endif +#ifdef RAPIDJSON_SSE42 +#include +#elif defined(RAPIDJSON_SSE2) +#include +#elif defined(RAPIDJSON_NEON) +#include +#endif + +#ifdef __clang__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(padded) +RAPIDJSON_DIAG_OFF(unreachable-code) +RAPIDJSON_DIAG_OFF(c++98-compat) +#elif defined(_MSC_VER) +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(4127) // conditional expression is constant +#endif + +RAPIDJSON_NAMESPACE_BEGIN + +/////////////////////////////////////////////////////////////////////////////// +// WriteFlag + +/*! \def RAPIDJSON_WRITE_DEFAULT_FLAGS + \ingroup RAPIDJSON_CONFIG + \brief User-defined kWriteDefaultFlags definition. + + User can define this as any \c WriteFlag combinations. +*/ +#ifndef RAPIDJSON_WRITE_DEFAULT_FLAGS +#define RAPIDJSON_WRITE_DEFAULT_FLAGS kWriteNoFlags +#endif + +//! Combination of writeFlags +enum WriteFlag { + kWriteNoFlags = 0, //!< No flags are set. + kWriteValidateEncodingFlag = 1, //!< Validate encoding of JSON strings. + kWriteNanAndInfFlag = 2, //!< Allow writing of Infinity, -Infinity and NaN. + kWriteDefaultFlags = RAPIDJSON_WRITE_DEFAULT_FLAGS //!< Default write flags. Can be customized by defining RAPIDJSON_WRITE_DEFAULT_FLAGS +}; + +//! JSON writer +/*! Writer implements the concept Handler. + It generates JSON text by events to an output os. + + User may programmatically calls the functions of a writer to generate JSON text. + + On the other side, a writer can also be passed to objects that generates events, + + for example Reader::Parse() and Document::Accept(). + + \tparam OutputStream Type of output stream. + \tparam SourceEncoding Encoding of source string. + \tparam TargetEncoding Encoding of output stream. + \tparam StackAllocator Type of allocator for allocating memory of stack. + \note implements Handler concept +*/ +template, typename TargetEncoding = UTF8<>, typename StackAllocator = CrtAllocator, unsigned writeFlags = kWriteDefaultFlags> +class Writer { +public: + typedef typename SourceEncoding::Ch Ch; + + static const int kDefaultMaxDecimalPlaces = 324; + + //! Constructor + /*! \param os Output stream. + \param stackAllocator User supplied allocator. If it is null, it will create a private one. + \param levelDepth Initial capacity of stack. + */ + explicit + Writer(OutputStream& os, StackAllocator* stackAllocator = 0, size_t levelDepth = kDefaultLevelDepth) : + os_(&os), level_stack_(stackAllocator, levelDepth * sizeof(Level)), maxDecimalPlaces_(kDefaultMaxDecimalPlaces), hasRoot_(false) {} + + explicit + Writer(StackAllocator* allocator = 0, size_t levelDepth = kDefaultLevelDepth) : + os_(0), level_stack_(allocator, levelDepth * sizeof(Level)), maxDecimalPlaces_(kDefaultMaxDecimalPlaces), hasRoot_(false) {} + +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS + Writer(Writer&& rhs) : + os_(rhs.os_), level_stack_(std::move(rhs.level_stack_)), maxDecimalPlaces_(rhs.maxDecimalPlaces_), hasRoot_(rhs.hasRoot_) { + rhs.os_ = 0; + } +#endif + + //! Reset the writer with a new stream. + /*! + This function reset the writer with a new stream and default settings, + in order to make a Writer object reusable for output multiple JSONs. + + \param os New output stream. + \code + Writer writer(os1); + writer.StartObject(); + // ... + writer.EndObject(); + + writer.Reset(os2); + writer.StartObject(); + // ... + writer.EndObject(); + \endcode + */ + void Reset(OutputStream& os) { + os_ = &os; + hasRoot_ = false; + level_stack_.Clear(); + } + + //! Checks whether the output is a complete JSON. + /*! + A complete JSON has a complete root object or array. + */ + bool IsComplete() const { + return hasRoot_ && level_stack_.Empty(); + } + + int GetMaxDecimalPlaces() const { + return maxDecimalPlaces_; + } + + //! Sets the maximum number of decimal places for double output. + /*! + This setting truncates the output with specified number of decimal places. + + For example, + + \code + writer.SetMaxDecimalPlaces(3); + writer.StartArray(); + writer.Double(0.12345); // "0.123" + writer.Double(0.0001); // "0.0" + writer.Double(1.234567890123456e30); // "1.234567890123456e30" (do not truncate significand for positive exponent) + writer.Double(1.23e-4); // "0.0" (do truncate significand for negative exponent) + writer.EndArray(); + \endcode + + The default setting does not truncate any decimal places. You can restore to this setting by calling + \code + writer.SetMaxDecimalPlaces(Writer::kDefaultMaxDecimalPlaces); + \endcode + */ + void SetMaxDecimalPlaces(int maxDecimalPlaces) { + maxDecimalPlaces_ = maxDecimalPlaces; + } + + /*!@name Implementation of Handler + \see Handler + */ + //@{ + + bool Null() { Prefix(kNullType); return EndValue(WriteNull()); } + bool Bool(bool b) { Prefix(b ? kTrueType : kFalseType); return EndValue(WriteBool(b)); } + bool Int(int i) { Prefix(kNumberType); return EndValue(WriteInt(i)); } + bool Uint(unsigned u) { Prefix(kNumberType); return EndValue(WriteUint(u)); } + bool Int64(int64_t i64) { Prefix(kNumberType); return EndValue(WriteInt64(i64)); } + bool Uint64(uint64_t u64) { Prefix(kNumberType); return EndValue(WriteUint64(u64)); } + + //! Writes the given \c double value to the stream + /*! + \param d The value to be written. + \return Whether it is succeed. + */ + bool Double(double d) { Prefix(kNumberType); return EndValue(WriteDouble(d)); } + + bool RawNumber(const Ch* str, SizeType length, bool copy = false) { + RAPIDJSON_ASSERT(str != 0); + (void)copy; + Prefix(kNumberType); + return EndValue(WriteString(str, length)); + } + + bool String(const Ch* str, SizeType length, bool copy = false) { + RAPIDJSON_ASSERT(str != 0); + (void)copy; + Prefix(kStringType); + return EndValue(WriteString(str, length)); + } + +#if RAPIDJSON_HAS_STDSTRING + bool String(const std::basic_string& str) { + return String(str.data(), SizeType(str.size())); + } +#endif + + bool StartObject() { + Prefix(kObjectType); + new (level_stack_.template Push()) Level(false); + return WriteStartObject(); + } + + bool Key(const Ch* str, SizeType length, bool copy = false) { return String(str, length, copy); } + +#if RAPIDJSON_HAS_STDSTRING + bool Key(const std::basic_string& str) + { + return Key(str.data(), SizeType(str.size())); + } +#endif + + bool EndObject(SizeType memberCount = 0) { + (void)memberCount; + RAPIDJSON_ASSERT(level_stack_.GetSize() >= sizeof(Level)); // not inside an Object + RAPIDJSON_ASSERT(!level_stack_.template Top()->inArray); // currently inside an Array, not Object + RAPIDJSON_ASSERT(0 == level_stack_.template Top()->valueCount % 2); // Object has a Key without a Value + level_stack_.template Pop(1); + return EndValue(WriteEndObject()); + } + + bool StartArray() { + Prefix(kArrayType); + new (level_stack_.template Push()) Level(true); + return WriteStartArray(); + } + + bool EndArray(SizeType elementCount = 0) { + (void)elementCount; + RAPIDJSON_ASSERT(level_stack_.GetSize() >= sizeof(Level)); + RAPIDJSON_ASSERT(level_stack_.template Top()->inArray); + level_stack_.template Pop(1); + return EndValue(WriteEndArray()); + } + //@} + + /*! @name Convenience extensions */ + //@{ + + //! Simpler but slower overload. + bool String(const Ch* const& str) { return String(str, internal::StrLen(str)); } + bool Key(const Ch* const& str) { return Key(str, internal::StrLen(str)); } + + //@} + + //! Write a raw JSON value. + /*! + For user to write a stringified JSON as a value. + + \param json A well-formed JSON value. It should not contain null character within [0, length - 1] range. + \param length Length of the json. + \param type Type of the root of json. + */ + bool RawValue(const Ch* json, size_t length, Type type) { + RAPIDJSON_ASSERT(json != 0); + Prefix(type); + return EndValue(WriteRawValue(json, length)); + } + + //! Flush the output stream. + /*! + Allows the user to flush the output stream immediately. + */ + void Flush() { + os_->Flush(); + } + + static const size_t kDefaultLevelDepth = 32; + +protected: + //! Information for each nested level + struct Level { + Level(bool inArray_) : valueCount(0), inArray(inArray_) {} + size_t valueCount; //!< number of values in this level + bool inArray; //!< true if in array, otherwise in object + }; + + bool WriteNull() { + PutReserve(*os_, 4); + PutUnsafe(*os_, 'n'); PutUnsafe(*os_, 'u'); PutUnsafe(*os_, 'l'); PutUnsafe(*os_, 'l'); return true; + } + + bool WriteBool(bool b) { + if (b) { + PutReserve(*os_, 4); + PutUnsafe(*os_, 't'); PutUnsafe(*os_, 'r'); PutUnsafe(*os_, 'u'); PutUnsafe(*os_, 'e'); + } + else { + PutReserve(*os_, 5); + PutUnsafe(*os_, 'f'); PutUnsafe(*os_, 'a'); PutUnsafe(*os_, 'l'); PutUnsafe(*os_, 's'); PutUnsafe(*os_, 'e'); + } + return true; + } + + bool WriteInt(int i) { + char buffer[11]; + const char* end = internal::i32toa(i, buffer); + PutReserve(*os_, static_cast(end - buffer)); + for (const char* p = buffer; p != end; ++p) + PutUnsafe(*os_, static_cast(*p)); + return true; + } + + bool WriteUint(unsigned u) { + char buffer[10]; + const char* end = internal::u32toa(u, buffer); + PutReserve(*os_, static_cast(end - buffer)); + for (const char* p = buffer; p != end; ++p) + PutUnsafe(*os_, static_cast(*p)); + return true; + } + + bool WriteInt64(int64_t i64) { + char buffer[21]; + const char* end = internal::i64toa(i64, buffer); + PutReserve(*os_, static_cast(end - buffer)); + for (const char* p = buffer; p != end; ++p) + PutUnsafe(*os_, static_cast(*p)); + return true; + } + + bool WriteUint64(uint64_t u64) { + char buffer[20]; + char* end = internal::u64toa(u64, buffer); + PutReserve(*os_, static_cast(end - buffer)); + for (char* p = buffer; p != end; ++p) + PutUnsafe(*os_, static_cast(*p)); + return true; + } + + bool WriteDouble(double d) { + if (internal::Double(d).IsNanOrInf()) { + if (!(writeFlags & kWriteNanAndInfFlag)) + return false; + if (internal::Double(d).IsNan()) { + PutReserve(*os_, 3); + PutUnsafe(*os_, 'N'); PutUnsafe(*os_, 'a'); PutUnsafe(*os_, 'N'); + return true; + } + if (internal::Double(d).Sign()) { + PutReserve(*os_, 9); + PutUnsafe(*os_, '-'); + } + else + PutReserve(*os_, 8); + PutUnsafe(*os_, 'I'); PutUnsafe(*os_, 'n'); PutUnsafe(*os_, 'f'); + PutUnsafe(*os_, 'i'); PutUnsafe(*os_, 'n'); PutUnsafe(*os_, 'i'); PutUnsafe(*os_, 't'); PutUnsafe(*os_, 'y'); + return true; + } + + char buffer[25]; + char* end = internal::dtoa(d, buffer, maxDecimalPlaces_); + PutReserve(*os_, static_cast(end - buffer)); + for (char* p = buffer; p != end; ++p) + PutUnsafe(*os_, static_cast(*p)); + return true; + } + + bool WriteString(const Ch* str, SizeType length) { + static const typename OutputStream::Ch hexDigits[16] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; + static const char escape[256] = { +#define Z16 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 + //0 1 2 3 4 5 6 7 8 9 A B C D E F + 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'b', 't', 'n', 'u', 'f', 'r', 'u', 'u', // 00 + 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', // 10 + 0, 0, '"', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 20 + Z16, Z16, // 30~4F + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,'\\', 0, 0, 0, // 50 + Z16, Z16, Z16, Z16, Z16, Z16, Z16, Z16, Z16, Z16 // 60~FF +#undef Z16 + }; + + if (TargetEncoding::supportUnicode) + PutReserve(*os_, 2 + length * 6); // "\uxxxx..." + else + PutReserve(*os_, 2 + length * 12); // "\uxxxx\uyyyy..." + + PutUnsafe(*os_, '\"'); + GenericStringStream is(str); + while (ScanWriteUnescapedString(is, length)) { + const Ch c = is.Peek(); + if (!TargetEncoding::supportUnicode && static_cast(c) >= 0x80) { + // Unicode escaping + unsigned codepoint; + if (RAPIDJSON_UNLIKELY(!SourceEncoding::Decode(is, &codepoint))) + return false; + PutUnsafe(*os_, '\\'); + PutUnsafe(*os_, 'u'); + if (codepoint <= 0xD7FF || (codepoint >= 0xE000 && codepoint <= 0xFFFF)) { + PutUnsafe(*os_, hexDigits[(codepoint >> 12) & 15]); + PutUnsafe(*os_, hexDigits[(codepoint >> 8) & 15]); + PutUnsafe(*os_, hexDigits[(codepoint >> 4) & 15]); + PutUnsafe(*os_, hexDigits[(codepoint ) & 15]); + } + else { + RAPIDJSON_ASSERT(codepoint >= 0x010000 && codepoint <= 0x10FFFF); + // Surrogate pair + unsigned s = codepoint - 0x010000; + unsigned lead = (s >> 10) + 0xD800; + unsigned trail = (s & 0x3FF) + 0xDC00; + PutUnsafe(*os_, hexDigits[(lead >> 12) & 15]); + PutUnsafe(*os_, hexDigits[(lead >> 8) & 15]); + PutUnsafe(*os_, hexDigits[(lead >> 4) & 15]); + PutUnsafe(*os_, hexDigits[(lead ) & 15]); + PutUnsafe(*os_, '\\'); + PutUnsafe(*os_, 'u'); + PutUnsafe(*os_, hexDigits[(trail >> 12) & 15]); + PutUnsafe(*os_, hexDigits[(trail >> 8) & 15]); + PutUnsafe(*os_, hexDigits[(trail >> 4) & 15]); + PutUnsafe(*os_, hexDigits[(trail ) & 15]); + } + } + else if ((sizeof(Ch) == 1 || static_cast(c) < 256) && RAPIDJSON_UNLIKELY(escape[static_cast(c)])) { + is.Take(); + PutUnsafe(*os_, '\\'); + PutUnsafe(*os_, static_cast(escape[static_cast(c)])); + if (escape[static_cast(c)] == 'u') { + PutUnsafe(*os_, '0'); + PutUnsafe(*os_, '0'); + PutUnsafe(*os_, hexDigits[static_cast(c) >> 4]); + PutUnsafe(*os_, hexDigits[static_cast(c) & 0xF]); + } + } + else if (RAPIDJSON_UNLIKELY(!(writeFlags & kWriteValidateEncodingFlag ? + Transcoder::Validate(is, *os_) : + Transcoder::TranscodeUnsafe(is, *os_)))) + return false; + } + PutUnsafe(*os_, '\"'); + return true; + } + + bool ScanWriteUnescapedString(GenericStringStream& is, size_t length) { + return RAPIDJSON_LIKELY(is.Tell() < length); + } + + bool WriteStartObject() { os_->Put('{'); return true; } + bool WriteEndObject() { os_->Put('}'); return true; } + bool WriteStartArray() { os_->Put('['); return true; } + bool WriteEndArray() { os_->Put(']'); return true; } + + bool WriteRawValue(const Ch* json, size_t length) { + PutReserve(*os_, length); + GenericStringStream is(json); + while (RAPIDJSON_LIKELY(is.Tell() < length)) { + RAPIDJSON_ASSERT(is.Peek() != '\0'); + if (RAPIDJSON_UNLIKELY(!(writeFlags & kWriteValidateEncodingFlag ? + Transcoder::Validate(is, *os_) : + Transcoder::TranscodeUnsafe(is, *os_)))) + return false; + } + return true; + } + + void Prefix(Type type) { + (void)type; + if (RAPIDJSON_LIKELY(level_stack_.GetSize() != 0)) { // this value is not at root + Level* level = level_stack_.template Top(); + if (level->valueCount > 0) { + if (level->inArray) + os_->Put(','); // add comma if it is not the first element in array + else // in object + os_->Put((level->valueCount % 2 == 0) ? ',' : ':'); + } + if (!level->inArray && level->valueCount % 2 == 0) + RAPIDJSON_ASSERT(type == kStringType); // if it's in object, then even number should be a name + level->valueCount++; + } + else { + RAPIDJSON_ASSERT(!hasRoot_); // Should only has one and only one root. + hasRoot_ = true; + } + } + + // Flush the value if it is the top level one. + bool EndValue(bool ret) { + if (RAPIDJSON_UNLIKELY(level_stack_.Empty())) // end of json text + Flush(); + return ret; + } + + OutputStream* os_; + internal::Stack level_stack_; + int maxDecimalPlaces_; + bool hasRoot_; + +private: + // Prohibit copy constructor & assignment operator. + Writer(const Writer&); + Writer& operator=(const Writer&); +}; + +// Full specialization for StringStream to prevent memory copying + +template<> +inline bool Writer::WriteInt(int i) { + char *buffer = os_->Push(11); + const char* end = internal::i32toa(i, buffer); + os_->Pop(static_cast(11 - (end - buffer))); + return true; +} + +template<> +inline bool Writer::WriteUint(unsigned u) { + char *buffer = os_->Push(10); + const char* end = internal::u32toa(u, buffer); + os_->Pop(static_cast(10 - (end - buffer))); + return true; +} + +template<> +inline bool Writer::WriteInt64(int64_t i64) { + char *buffer = os_->Push(21); + const char* end = internal::i64toa(i64, buffer); + os_->Pop(static_cast(21 - (end - buffer))); + return true; +} + +template<> +inline bool Writer::WriteUint64(uint64_t u) { + char *buffer = os_->Push(20); + const char* end = internal::u64toa(u, buffer); + os_->Pop(static_cast(20 - (end - buffer))); + return true; +} + +template<> +inline bool Writer::WriteDouble(double d) { + if (internal::Double(d).IsNanOrInf()) { + // Note: This code path can only be reached if (RAPIDJSON_WRITE_DEFAULT_FLAGS & kWriteNanAndInfFlag). + if (!(kWriteDefaultFlags & kWriteNanAndInfFlag)) + return false; + if (internal::Double(d).IsNan()) { + PutReserve(*os_, 3); + PutUnsafe(*os_, 'N'); PutUnsafe(*os_, 'a'); PutUnsafe(*os_, 'N'); + return true; + } + if (internal::Double(d).Sign()) { + PutReserve(*os_, 9); + PutUnsafe(*os_, '-'); + } + else + PutReserve(*os_, 8); + PutUnsafe(*os_, 'I'); PutUnsafe(*os_, 'n'); PutUnsafe(*os_, 'f'); + PutUnsafe(*os_, 'i'); PutUnsafe(*os_, 'n'); PutUnsafe(*os_, 'i'); PutUnsafe(*os_, 't'); PutUnsafe(*os_, 'y'); + return true; + } + + char *buffer = os_->Push(25); + char* end = internal::dtoa(d, buffer, maxDecimalPlaces_); + os_->Pop(static_cast(25 - (end - buffer))); + return true; +} + +#if defined(RAPIDJSON_SSE2) || defined(RAPIDJSON_SSE42) +template<> +inline bool Writer::ScanWriteUnescapedString(StringStream& is, size_t length) { + if (length < 16) + return RAPIDJSON_LIKELY(is.Tell() < length); + + if (!RAPIDJSON_LIKELY(is.Tell() < length)) + return false; + + const char* p = is.src_; + const char* end = is.head_ + length; + const char* nextAligned = reinterpret_cast((reinterpret_cast(p) + 15) & static_cast(~15)); + const char* endAligned = reinterpret_cast(reinterpret_cast(end) & static_cast(~15)); + if (nextAligned > end) + return true; + + while (p != nextAligned) + if (*p < 0x20 || *p == '\"' || *p == '\\') { + is.src_ = p; + return RAPIDJSON_LIKELY(is.Tell() < length); + } + else + os_->PutUnsafe(*p++); + + // The rest of string using SIMD + static const char dquote[16] = { '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"' }; + static const char bslash[16] = { '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\' }; + static const char space[16] = { 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F }; + const __m128i dq = _mm_loadu_si128(reinterpret_cast(&dquote[0])); + const __m128i bs = _mm_loadu_si128(reinterpret_cast(&bslash[0])); + const __m128i sp = _mm_loadu_si128(reinterpret_cast(&space[0])); + + for (; p != endAligned; p += 16) { + const __m128i s = _mm_load_si128(reinterpret_cast(p)); + const __m128i t1 = _mm_cmpeq_epi8(s, dq); + const __m128i t2 = _mm_cmpeq_epi8(s, bs); + const __m128i t3 = _mm_cmpeq_epi8(_mm_max_epu8(s, sp), sp); // s < 0x20 <=> max(s, 0x1F) == 0x1F + const __m128i x = _mm_or_si128(_mm_or_si128(t1, t2), t3); + unsigned short r = static_cast(_mm_movemask_epi8(x)); + if (RAPIDJSON_UNLIKELY(r != 0)) { // some of characters is escaped + SizeType len; +#ifdef _MSC_VER // Find the index of first escaped + unsigned long offset; + _BitScanForward(&offset, r); + len = offset; +#else + len = static_cast(__builtin_ffs(r) - 1); +#endif + char* q = reinterpret_cast(os_->PushUnsafe(len)); + for (size_t i = 0; i < len; i++) + q[i] = p[i]; + + p += len; + break; + } + _mm_storeu_si128(reinterpret_cast<__m128i *>(os_->PushUnsafe(16)), s); + } + + is.src_ = p; + return RAPIDJSON_LIKELY(is.Tell() < length); +} +#elif defined(RAPIDJSON_NEON) +template<> +inline bool Writer::ScanWriteUnescapedString(StringStream& is, size_t length) { + if (length < 16) + return RAPIDJSON_LIKELY(is.Tell() < length); + + if (!RAPIDJSON_LIKELY(is.Tell() < length)) + return false; + + const char* p = is.src_; + const char* end = is.head_ + length; + const char* nextAligned = reinterpret_cast((reinterpret_cast(p) + 15) & static_cast(~15)); + const char* endAligned = reinterpret_cast(reinterpret_cast(end) & static_cast(~15)); + if (nextAligned > end) + return true; + + while (p != nextAligned) + if (*p < 0x20 || *p == '\"' || *p == '\\') { + is.src_ = p; + return RAPIDJSON_LIKELY(is.Tell() < length); + } + else + os_->PutUnsafe(*p++); + + // The rest of string using SIMD + const uint8x16_t s0 = vmovq_n_u8('"'); + const uint8x16_t s1 = vmovq_n_u8('\\'); + const uint8x16_t s2 = vmovq_n_u8('\b'); + const uint8x16_t s3 = vmovq_n_u8(32); + + for (; p != endAligned; p += 16) { + const uint8x16_t s = vld1q_u8(reinterpret_cast(p)); + uint8x16_t x = vceqq_u8(s, s0); + x = vorrq_u8(x, vceqq_u8(s, s1)); + x = vorrq_u8(x, vceqq_u8(s, s2)); + x = vorrq_u8(x, vcltq_u8(s, s3)); + + x = vrev64q_u8(x); // Rev in 64 + uint64_t low = vgetq_lane_u64(vreinterpretq_u64_u8(x), 0); // extract + uint64_t high = vgetq_lane_u64(vreinterpretq_u64_u8(x), 1); // extract + + SizeType len = 0; + bool escaped = false; + if (low == 0) { + if (high != 0) { + uint32_t lz = internal::clzll(high); + len = 8 + (lz >> 3); + escaped = true; + } + } else { + uint32_t lz = internal::clzll(low); + len = lz >> 3; + escaped = true; + } + if (RAPIDJSON_UNLIKELY(escaped)) { // some of characters is escaped + char* q = reinterpret_cast(os_->PushUnsafe(len)); + for (size_t i = 0; i < len; i++) + q[i] = p[i]; + + p += len; + break; + } + vst1q_u8(reinterpret_cast(os_->PushUnsafe(16)), s); + } + + is.src_ = p; + return RAPIDJSON_LIKELY(is.Tell() < length); +} +#endif // RAPIDJSON_NEON + +RAPIDJSON_NAMESPACE_END + +#if defined(_MSC_VER) || defined(__clang__) +RAPIDJSON_DIAG_POP +#endif + +#endif // RAPIDJSON_RAPIDJSON_H_ diff --git a/lib/gtest/libgmock.a b/lib/gtest/libgmock.a new file mode 100644 index 0000000000000000000000000000000000000000..82b7cafc7128d01cdccaabcf12f97edb64d02b24 GIT binary patch literal 849374 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

{&jmf@=YHQ%%SYRb<38o& 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

Q2Td?2yoEGlf$e(i6B2-v;qne)5 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⋙zlo3gmlkZ*QfRKBkeY%K7WF%{~7K) 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%0DO%AF4HBGyN* zaxveo*V~Oq5T3jv(M>t3rVY8*r<{6-g3u8`B8S-<={<8*BLir0s-Q`1z78H5+r8Bt zkg=}Yp@RrH-eR2qHOD~Hc6BxrpCeDUL}^1Gd%yswOe=eb&-4P$oqWfwtbe*YdB#9% zw08#@BY7R|X$PV^48&aUGLhrT>HlDDGDv4~-9$;*>vVVZiR!`Nk$A&wvOsh@e5dKp zLpzL!w_bJug?AxF1FymB4RvDm54pvl2m0$pfC+!ct)~~8mXms0Qp<^DaYll(puDW0 zVTM^f{E*wn>a=G>Wo)c{iVt8 z!D$mt#?c9{r1aK!J!g~u4Ada99-Bz`8AcCjSM$93wF864A#N_0`DOjH*kAWMOl;K* z{J2Y50H?x8%ryG2eMzYR8r2iiXGS=pu z5Z2?8v8IZ5*@(q6i)xw*#eVIsxj;FMieqzhijUT;4X!b^_shK-m=9d

m)De`#z0f>ky894`2GLn5> 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

g(0|@CWdyrl&p3wdfT>vL5)Xa;2rH-Ow*;N#3 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≫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 QxAvO+|M@%ctCDR09~I1s3jhEB literal 0 HcmV?d00001

W`*8nqS;{vwytnHxkspjD1;uD*LyIdS(58ab8~k3OH}8-}!g$QT=J4{yRba z@#D?>Wc`7SX8v6*^{?c-tUr$Pw)&S+9@U=@>K_B@Kh3_ZKghoP`zz%92Tj2HC!6!( zqJO#n`%@m(AF(0y{4);J|04Uc{#^EZ3t!g%uTp>5i)Q_7^Dj~AkLUWm`#%ZP-z5p} zpGfxQ^;6dW0O#fPFTrR1DU>grznD`xsDCo3|4G*u=bt3@ZR@|-^+mn_tpDA>f1iE1 z{;BL=C;F1}`&+3$%V+&(l={OqhVI{FP=BY1c>QzPx2^yEoR{a1VxRTTqC7hOVnO}) zfciHp^#`)d@e^g4|6!&6XwKXA&q1aBBvAh>P=A9-c>Zzh%kRI+{c{KB<@}R<)}Kgu zH2(}x|9znTHA?-d?7R2x?lQ>w4=eR&bKZ9TJE+v3$Mt*9e{(_ojV9yy7qZ{hEu5ca z{S!GaU;os6$?U(5A}>xefw7cF^ACS9^!R-c)Ss{T1KDqE@jq7lIQHH7JNKUt0l&=@ zGe6n?O!npf|55h8LCHVPd3pYk`Pz45f0xhxucSPhepzBl+xsW6lt=TA1%5j4A5;8v_HD=C zBE?Sv{!-w7!@j(J<+5+P{vJ^L4B#&Zew}19e|i5_sPrEx_Tw0+L<426X_@?i?m|!Cj<3C_{^u?qPJZO9&hNPZz=br8CbVnaNk8WIkoC=O!zx-%pgR!75o{Gl!vEjI%y`{ztQ%ADi#T-B2Bd&kE zv9a;)J)BLmV8OwKa&^HnM41cA7u*-iRjZlFSi~Rr7GHF|N)yF)gylI#m3x81H_2=P zShln168VzhIkr<`mgl)1fU|*vb{bc;=U{r#b8G3Qb?$1EJFKTF;+E%`93Oux-BX+o z91)~-UF*|&59uR;wDp4uVQd?W*$oZLHaT6yO}BXz4JCy z@nyiVp}c@UnVsD?+s2>nm<9rv=sSiu$Rz%d|H`KSJmMf{@Q3_urz8R6-$NYaMf@TE zOc;+)E5QZc#{Pu0-YV_#m1>2{P1EGq1y)K{C4{5zMmy_h;-aw+S}KAQA8UX(*`&`Y z9QPaw;a6n8qfvjtzg9S|4GQU>ppW^n<|w2eLLYHlTa;%)#r8l##Cw%Z>pQ0K8R97| z6kb*~jf-YG!xNxcK2!Xvf~m516<$|3&2wGh*Az~b?S{hOJZ~x-Xj0OEx4}QTrvQzY1yLo`L=X;0Ge}lzSEMOMp{tREShxCg2?K-I4u67o%-1+U`Ox8zoBb6E~`1dnPsIEU%r z&FKS`Q$~HvE|tJD<%lHONaZA+^#x$%@uZ)q8l@!ekxg#Vw9R$xeZ1Hg&i z7+_28oIZlH%nQS;6g!;$hAsRl zGn1JGtbg9{Vcssx(K2dNW`Sq&gM8*4Gn!lujc%F|G{|(UlBm7Slj`Ked!Jmxadm=d9Tgb=n0Gf}Y!yOTRDAT#0Rt}s`^gJDn z;OHZnQys*x(YbythEsl~`O7t7%!3wQsLRyztg4~G;tHlM7{XxMyzev3b3>!y)mpp{ zn)KrP#SPChmYHU{wVLHXD#sO;2A^h!;KeX2%7!CRTk_l`%M-wr!B&|sdPdD+TFG;R zB{l#5tGZ$B_%#==-+91xLW@8D12quw zRhmXT;b+0oYW>#K__R*=+_OSMjDq28SpC1kx6<1DiO2r~8@0&Z literal 0 HcmV?d00001 diff --git a/lib/gtest/libgtest.a b/lib/gtest/libgtest.a new file mode 100644 index 0000000000000000000000000000000000000000..e3997b08eac7555e8fdac7b837a9609a8469d775 GIT binary patch literal 2697788 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